Annotation of ChivanetAimPidgin/oscarprpl/src/c/flap_connection.c, revision 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: #include "oscar.h"
        !            22: 
        !            23: #include "eventloop.h"
        !            24: #include "proxy.h"
        !            25: 
        !            26: #ifndef _WIN32
        !            27: #include <netdb.h>
        !            28: #include <sys/socket.h>
        !            29: #include <netinet/in.h>
        !            30: #endif
        !            31: 
        !            32: #ifdef _WIN32
        !            33: #include "win32dep.h"
        !            34: #endif
        !            35: 
        !            36: /**
        !            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.
        !            42:  */
        !            43: void
        !            44: flap_connection_send_version(OscarData *od, FlapConnection *conn)
        !            45: {
        !            46:        FlapFrame *frame;
        !            47: 
        !            48:        frame = flap_frame_new(od, 0x01, 4);
        !            49:        byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
        !            50:        flap_connection_send(conn, frame);
        !            51: }
        !            52: 
        !            53: /**
        !            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.
        !            59:  */
        !            60: void
        !            61: flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
        !            62: {
        !            63:        FlapFrame *frame;
        !            64:        GSList *tlvlist = NULL;
        !            65: 
        !            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);
        !            71: 
        !            72:        flap_connection_send(conn, frame);
        !            73: }
        !            74: 
        !            75: void
        !            76: flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins)
        !            77: {
        !            78:        FlapFrame *frame;
        !            79:        GSList *tlvlist = NULL;
        !            80: 
        !            81:        frame = flap_frame_new(od, 0x01, 1152 + length);
        !            82: 
        !            83:        byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
        !            84:        aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
        !            85: 
        !            86:        if (ci->clientstring != NULL)
        !            87:                aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
        !            88:        else {
        !            89:                gchar *clientstring = oscar_get_clientstring();
        !            90:                aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
        !            91:                g_free(clientstring);
        !            92:        }
        !            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));
        !            98: 
        !            99:        aim_tlvlist_write(&frame->data, &tlvlist);
        !           100: 
        !           101:        aim_tlvlist_free(tlvlist);
        !           102: 
        !           103:        flap_connection_send(conn, frame);
        !           104: }
        !           105: 
        !           106: static struct rateclass *
        !           107: flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
        !           108: {
        !           109:        gconstpointer key;
        !           110:        gpointer rateclass;
        !           111: 
        !           112:        key = GUINT_TO_POINTER((family << 16) + subtype);
        !           113:        rateclass = g_hash_table_lookup(conn->rateclass_members, key);
        !           114:        if (rateclass != NULL)
        !           115:                return rateclass;
        !           116: 
        !           117:        return conn->default_rateclass;
        !           118: }
        !           119: 
        !           120: /*
        !           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.
        !           123:  */
        !           124: static guint32
        !           125: rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now)
        !           126: {
        !           127:        unsigned long timediff; /* In milliseconds */
        !           128:        guint32 current;
        !           129: 
        !           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;
        !           133: 
        !           134:        return MIN(current, rateclass->max);
        !           135: }
        !           136: 
        !           137: /*
        !           138:  * Attempt to send the contents of a given queue
        !           139:  *
        !           140:  * @return TRUE if the queue was completely emptied or was initially
        !           141:  *         empty; FALSE if rate limiting prevented it from being
        !           142:  *         emptied.
        !           143:  */
        !           144: static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue)
        !           145: {
        !           146:        while (!g_queue_is_empty(queue))
        !           147:        {
        !           148:                QueuedSnac *queued_snac;
        !           149:                struct rateclass *rateclass;
        !           150: 
        !           151:                queued_snac = g_queue_peek_head(queue);
        !           152: 
        !           153:                rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype);
        !           154:                if (rateclass != NULL)
        !           155:                {
        !           156:                        guint32 new_current;
        !           157: 
        !           158:                        new_current = rateclass_get_new_current(conn, rateclass, &now);
        !           159: 
        !           160:                        if (rateclass->dropping_snacs || new_current <= rateclass->alert)
        !           161:                                /* Not ready to send this SNAC yet--keep waiting. */
        !           162:                                return FALSE;
        !           163: 
        !           164:                        rateclass->current = new_current;
        !           165:                        rateclass->last.tv_sec = now.tv_sec;
        !           166:                        rateclass->last.tv_usec = now.tv_usec;
        !           167:                }
        !           168: 
        !           169:                flap_connection_send(conn, queued_snac->frame);
        !           170:                g_free(queued_snac);
        !           171:                g_queue_pop_head(queue);
        !           172:        }
        !           173: 
        !           174:        /* We emptied the queue */
        !           175:        return TRUE;
        !           176: }
        !           177: 
        !           178: static gboolean flap_connection_send_queued(gpointer data)
        !           179: {
        !           180:        FlapConnection *conn;
        !           181:        struct timeval now;
        !           182: 
        !           183:        conn = data;
        !           184:        gettimeofday(&now, NULL);
        !           185: 
        !           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),
        !           189:                                          conn);
        !           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;
        !           194:                        return FALSE;
        !           195:                }
        !           196:        }
        !           197: 
        !           198:        /* We couldn't send all our SNACs. Keep trying */
        !           199:        return TRUE;
        !           200: }
        !           201: 
        !           202: /**
        !           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.
        !           207:  *
        !           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.
        !           213:  */
        !           214: void
        !           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)
        !           216: {
        !           217:        FlapFrame *frame;
        !           218:        guint32 length;
        !           219:        gboolean enqueue = FALSE;
        !           220:        struct rateclass *rateclass;
        !           221: 
        !           222:        length = data != NULL ? data->offset : 0;
        !           223: 
        !           224:        frame = flap_frame_new(od, 0x02, 10 + length);
        !           225:        aim_putsnac(&frame->data, family, subtype, snacid);
        !           226: 
        !           227:        if (length > 0)
        !           228:        {
        !           229:                byte_stream_rewind(data);
        !           230:                byte_stream_putbs(&frame->data, data, length);
        !           231:        }
        !           232: 
        !           233:        if (conn->queued_timeout != 0)
        !           234:                enqueue = TRUE;
        !           235:        else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
        !           236:        {
        !           237:                struct timeval now;
        !           238:                guint32 new_current;
        !           239: 
        !           240:                gettimeofday(&now, NULL);
        !           241:                new_current = rateclass_get_new_current(conn, rateclass, &now);
        !           242: 
        !           243:                if (rateclass->dropping_snacs || new_current <= rateclass->alert)
        !           244:                {
        !           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);
        !           246: 
        !           247:                        enqueue = TRUE;
        !           248:                }
        !           249:                else
        !           250:                {
        !           251:                        rateclass->current = new_current;
        !           252:                        rateclass->last.tv_sec = now.tv_sec;
        !           253:                        rateclass->last.tv_usec = now.tv_usec;
        !           254:                }
        !           255:        }
        !           256: 
        !           257:        if (enqueue)
        !           258:        {
        !           259:                /* We've been sending too fast, so delay this message */
        !           260:                QueuedSnac *queued_snac;
        !           261: 
        !           262:                queued_snac = g_new(QueuedSnac, 1);
        !           263:                queued_snac->family = family;
        !           264:                queued_snac->subtype = subtype;
        !           265:                queued_snac->frame = frame;
        !           266: 
        !           267:                if (high_priority) {
        !           268:                        if (!conn->queued_snacs)
        !           269:                                conn->queued_snacs = g_queue_new();
        !           270:                        g_queue_push_tail(conn->queued_snacs, queued_snac);
        !           271:                } else {
        !           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);
        !           275:                }
        !           276: 
        !           277:                if (conn->queued_timeout == 0)
        !           278:                        conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn);
        !           279: 
        !           280:                return;
        !           281:        }
        !           282: 
        !           283:        flap_connection_send(conn, frame);
        !           284: }
        !           285: 
        !           286: void
        !           287: flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data)
        !           288: {
        !           289:        flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE);
        !           290: }
        !           291: 
        !           292: /**
        !           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.
        !           297:  */
        !           298: static void
        !           299: flap_connection_send_close(OscarData *od, FlapConnection *conn)
        !           300: {
        !           301:        FlapFrame *frame;
        !           302: 
        !           303:        frame = flap_frame_new(od, 0x04, 0);
        !           304:        flap_connection_send(conn, frame);
        !           305: }
        !           306: 
        !           307: /**
        !           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.
        !           311:  */
        !           312: void
        !           313: flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
        !           314: {
        !           315:        FlapFrame *frame;
        !           316: 
        !           317:        frame = flap_frame_new(od, 0x05, 0);
        !           318:        flap_connection_send(conn, frame);
        !           319: 
        !           320:        /* clean out SNACs over 60sec old */
        !           321:        aim_cleansnacs(od, 60);
        !           322: }
        !           323: 
        !           324: /**
        !           325:  * Allocate a new empty connection structure.
        !           326:  *
        !           327:  * @param od The oscar session associated with this connection.
        !           328:  * @param type Type of connection to create
        !           329:  *
        !           330:  * @return Returns the new connection structure.
        !           331:  */
        !           332: FlapConnection *
        !           333: flap_connection_new(OscarData *od, int type)
        !           334: {
        !           335:        FlapConnection *conn;
        !           336: 
        !           337:        conn = g_new0(FlapConnection, 1);
        !           338:        conn->od = od;
        !           339:        conn->buffer_outgoing = purple_circ_buffer_new(0);
        !           340:        conn->fd = -1;
        !           341:        conn->subtype = -1;
        !           342:        conn->type = type;
        !           343:        conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
        !           344: 
        !           345:        od->oscar_connections = g_slist_prepend(od->oscar_connections, conn);
        !           346: 
        !           347:        return conn;
        !           348: }
        !           349: 
        !           350: /**
        !           351:  * Close (but not free) a connection.
        !           352:  *
        !           353:  * This cancels any currently pending connection attempt,
        !           354:  * closes any open fd and frees the auth cookie.
        !           355:  *
        !           356:  * @param conn The connection to close.
        !           357:  */
        !           358: void
        !           359: flap_connection_close(OscarData *od, FlapConnection *conn)
        !           360: {
        !           361:        if (conn->connect_data != NULL)
        !           362:        {
        !           363:                purple_proxy_connect_cancel(conn->connect_data);
        !           364:                conn->connect_data = NULL;
        !           365:        }
        !           366: 
        !           367:        if (conn->gsc != NULL && conn->gsc->connect_data != NULL)
        !           368:        {
        !           369:                purple_ssl_close(conn->gsc);
        !           370:                conn->gsc = NULL;
        !           371:        }
        !           372: 
        !           373:        if (conn->new_conn_data != NULL)
        !           374:        {
        !           375:                if (conn->type == SNAC_FAMILY_CHAT)
        !           376:                {
        !           377:                        oscar_chat_destroy(conn->new_conn_data);
        !           378:                        conn->new_conn_data = NULL;
        !           379:                }
        !           380:        }
        !           381: 
        !           382:        if ((conn->fd >= 0 || conn->gsc != NULL)
        !           383:                        && conn->type == SNAC_FAMILY_LOCATE)
        !           384:                flap_connection_send_close(od, conn);
        !           385: 
        !           386:        if (conn->watcher_incoming != 0)
        !           387:        {
        !           388:                purple_input_remove(conn->watcher_incoming);
        !           389:                conn->watcher_incoming = 0;
        !           390:        }
        !           391: 
        !           392:        if (conn->watcher_outgoing != 0)
        !           393:        {
        !           394:                purple_input_remove(conn->watcher_outgoing);
        !           395:                conn->watcher_outgoing = 0;
        !           396:        }
        !           397: 
        !           398:        if (conn->fd >= 0)
        !           399:        {
        !           400:                close(conn->fd);
        !           401:                conn->fd = -1;
        !           402:        }
        !           403: 
        !           404:        if (conn->gsc != NULL)
        !           405:        {
        !           406:                purple_ssl_close(conn->gsc);
        !           407:                conn->gsc = NULL;
        !           408:        }
        !           409: 
        !           410:        g_free(conn->buffer_incoming.data.data);
        !           411:        conn->buffer_incoming.data.data = NULL;
        !           412: 
        !           413:        purple_circ_buffer_destroy(conn->buffer_outgoing);
        !           414:        conn->buffer_outgoing = NULL;
        !           415: }
        !           416: 
        !           417: /**
        !           418:  * Free a FlapFrame
        !           419:  *
        !           420:  * @param frame The frame to free.
        !           421:  */
        !           422: static void
        !           423: flap_frame_destroy(FlapFrame *frame)
        !           424: {
        !           425:        g_free(frame->data.data);
        !           426:        g_free(frame);
        !           427: }
        !           428: 
        !           429: static gboolean
        !           430: flap_connection_destroy_cb(gpointer data)
        !           431: {
        !           432:        FlapConnection *conn;
        !           433:        OscarData *od;
        !           434:        PurpleAccount *account;
        !           435:        aim_rxcallback_t userfunc;
        !           436: 
        !           437:        conn = data;
        !           438:        /* Explicitly added for debugging #5927.  Don't re-order this, only
        !           439:         * consider removing it.
        !           440:         */
        !           441:        purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn);
        !           442: 
        !           443:        od = conn->od;
        !           444:        account = purple_connection_get_account(od->gc);
        !           445: 
        !           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);
        !           449: 
        !           450:        od->oscar_connections = g_slist_remove(od->oscar_connections, conn);
        !           451: 
        !           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);
        !           454: 
        !           455:        /*
        !           456:         * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
        !           457:         * we should try to request one instead of disconnecting.
        !           458:         */
        !           459:        if (!account->disconnecting && ((od->oscar_connections == NULL)
        !           460:                        || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE))))
        !           461:        {
        !           462:                /* No more FLAP connections!  Sign off this PurpleConnection! */
        !           463:                gchar *tmp;
        !           464:                PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
        !           465: 
        !           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);
        !           481:                else
        !           482:                        /*
        !           483:                         * We shouldn't print a message for some disconnect_reasons.
        !           484:                         * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
        !           485:                         */
        !           486:                        tmp = NULL;
        !           487: 
        !           488:                if (tmp != NULL)
        !           489:                {
        !           490:                        purple_connection_error_reason(od->gc, reason, tmp);
        !           491:                        g_free(tmp);
        !           492:                }
        !           493:        }
        !           494: 
        !           495:        flap_connection_close(od, conn);
        !           496: 
        !           497:        g_free(conn->error_message);
        !           498:        g_free(conn->cookie);
        !           499: 
        !           500:        /*
        !           501:         * Free conn->internal, if necessary
        !           502:         */
        !           503:        if (conn->type == SNAC_FAMILY_CHAT)
        !           504:                flap_connection_destroy_chat(od, conn);
        !           505: 
        !           506:        g_slist_free(conn->groups);
        !           507:        while (conn->rateclasses != NULL)
        !           508:        {
        !           509:                g_free(conn->rateclasses->data);
        !           510:                conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses);
        !           511:        }
        !           512: 
        !           513:        g_hash_table_destroy(conn->rateclass_members);
        !           514: 
        !           515:        if (conn->queued_snacs) {
        !           516:                while (!g_queue_is_empty(conn->queued_snacs))
        !           517:                {
        !           518:                        QueuedSnac *queued_snac;
        !           519:                        queued_snac = g_queue_pop_head(conn->queued_snacs);
        !           520:                        flap_frame_destroy(queued_snac->frame);
        !           521:                        g_free(queued_snac);
        !           522:                }
        !           523:                g_queue_free(conn->queued_snacs);
        !           524:        }
        !           525: 
        !           526:        if (conn->queued_lowpriority_snacs) {
        !           527:                while (!g_queue_is_empty(conn->queued_lowpriority_snacs))
        !           528:                {
        !           529:                        QueuedSnac *queued_snac;
        !           530:                        queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs);
        !           531:                        flap_frame_destroy(queued_snac->frame);
        !           532:                        g_free(queued_snac);
        !           533:                }
        !           534:                g_queue_free(conn->queued_lowpriority_snacs);
        !           535:        }
        !           536: 
        !           537:        if (conn->queued_timeout > 0)
        !           538:                purple_timeout_remove(conn->queued_timeout);
        !           539: 
        !           540:        g_free(conn);
        !           541: 
        !           542:        return FALSE;
        !           543: }
        !           544: 
        !           545: /**
        !           546:  * See the comments for the parameters of
        !           547:  * flap_connection_schedule_destroy().
        !           548:  */
        !           549: void
        !           550: flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
        !           551: {
        !           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);
        !           558: }
        !           559: 
        !           560: /**
        !           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
        !           564:  * for some reason.
        !           565:  *
        !           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().
        !           574:  */
        !           575: void
        !           576: flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
        !           577: {
        !           578:        if (conn->destroy_timeout != 0)
        !           579:                /* Already taken care of */
        !           580:                return;
        !           581: 
        !           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);
        !           588: }
        !           589: 
        !           590: /**
        !           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.
        !           594:  *
        !           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.
        !           599:  *
        !           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.
        !           606:  *
        !           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.
        !           612:  *
        !           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.
        !           622:  *
        !           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.
        !           626:  *
        !           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.
        !           630:  *
        !           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.)
        !           637:  *
        !           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.
        !           643:  *
        !           644:  */
        !           645: 
        !           646: /**
        !           647:  * Find a FlapConnection that supports the given oscar
        !           648:  * family.
        !           649:  */
        !           650: FlapConnection *
        !           651: flap_connection_findbygroup(OscarData *od, guint16 group)
        !           652: {
        !           653:        GSList *cur;
        !           654: 
        !           655:        for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
        !           656:        {
        !           657:                FlapConnection *conn;
        !           658:                GSList *l;
        !           659: 
        !           660:                conn = cur->data;
        !           661: 
        !           662:                for (l = conn->groups; l != NULL; l = l->next)
        !           663:                {
        !           664:                        if (GPOINTER_TO_UINT(l->data) == group)
        !           665:                                return conn;
        !           666:                }
        !           667:        }
        !           668: 
        !           669:        return NULL;
        !           670: }
        !           671: 
        !           672: /**
        !           673:  * Locates a connection of the specified type in the
        !           674:  * specified session.
        !           675:  *
        !           676:  * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
        !           677:  *
        !           678:  * @param od The session to search.
        !           679:  * @param type The type of connection to look for.
        !           680:  *
        !           681:  * @return Returns the first connection found of the given target type,
        !           682:  *         or NULL if none could be found.
        !           683:  */
        !           684: FlapConnection *
        !           685: flap_connection_getbytype(OscarData *od, int type)
        !           686: {
        !           687:        GSList *cur;
        !           688: 
        !           689:        for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
        !           690:        {
        !           691:                FlapConnection *conn;
        !           692:                conn = cur->data;
        !           693:                if ((conn->type == type) && (conn->connected))
        !           694:                        return conn;
        !           695:        }
        !           696: 
        !           697:        return NULL;
        !           698: }
        !           699: 
        !           700: FlapConnection *
        !           701: flap_connection_getbytype_all(OscarData *od, int type)
        !           702: {
        !           703:        GSList *cur;
        !           704: 
        !           705:        for (cur = od->oscar_connections; cur; cur = cur->next)
        !           706:        {
        !           707:                FlapConnection *conn;
        !           708:                conn = cur->data;
        !           709:                if (conn->type == type)
        !           710:                        return conn;
        !           711:        }
        !           712: 
        !           713:        return NULL;
        !           714: }
        !           715: 
        !           716: /**
        !           717:  * Allocate a new FLAP frame.
        !           718:  *
        !           719:  * @param channel The FLAP channel.  This is almost always 2.
        !           720:  */
        !           721: FlapFrame *
        !           722: flap_frame_new(OscarData *od, guint16 channel, int datalen)
        !           723: {
        !           724:        FlapFrame *frame;
        !           725: 
        !           726:        frame = g_new0(FlapFrame, 1);
        !           727:        frame->channel = channel;
        !           728: 
        !           729:        if (datalen > 0)
        !           730:                byte_stream_new(&frame->data, datalen);
        !           731: 
        !           732:        return frame;
        !           733: }
        !           734: 
        !           735: static void
        !           736: parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
        !           737: {
        !           738:        aim_module_t *cur;
        !           739:        aim_modsnac_t snac;
        !           740: 
        !           741:        if (byte_stream_bytes_left(&frame->data) < 10)
        !           742:                return;
        !           743: 
        !           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);
        !           748: 
        !           749:        /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
        !           750:        if (snac.flags & 0x0001) {
        !           751:                /*
        !           752:                 * This means the SNAC will be followed by another SNAC with
        !           753:                 * related information.  We don't need to do anything about
        !           754:                 * this here.
        !           755:                 */
        !           756:        }
        !           757:        if (snac.flags & 0x8000) {
        !           758:                /*
        !           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
        !           765:                 * number
        !           766:                 */
        !           767:                byte_stream_advance(&frame->data, byte_stream_get16(&frame->data));
        !           768:        }
        !           769: 
        !           770:        for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
        !           771: 
        !           772:                if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
        !           773:                                (cur->family != snac.family))
        !           774:                        continue;
        !           775: 
        !           776:                if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
        !           777:                        return;
        !           778:        }
        !           779: }
        !           780: 
        !           781: static void
        !           782: parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
        !           783: {
        !           784:        aim_module_t *cur;
        !           785:        aim_modsnac_t snac;
        !           786: 
        !           787:        snac.family = family;
        !           788:        snac.subtype = subtype;
        !           789:        snac.flags = snac.id = 0;
        !           790: 
        !           791:        for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
        !           792: 
        !           793:                if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
        !           794:                                (cur->family != snac.family))
        !           795:                        continue;
        !           796: 
        !           797:                if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
        !           798:                        return;
        !           799:        }
        !           800: }
        !           801: 
        !           802: static void
        !           803: parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
        !           804: {
        !           805:        GSList *tlvlist;
        !           806:        char *msg = NULL;
        !           807: 
        !           808:        if (byte_stream_bytes_left(&frame->data) == 0) {
        !           809:                /* XXX should do something with this */
        !           810:                return;
        !           811:        }
        !           812: 
        !           813:        /* An ICQ account is logging in */
        !           814:        if (conn->type == SNAC_FAMILY_AUTH)
        !           815:        {
        !           816:                parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
        !           817:                return;
        !           818:        }
        !           819: 
        !           820:        tlvlist = aim_tlvlist_read(&frame->data);
        !           821: 
        !           822:        if (aim_tlv_gettlv(tlvlist, 0x0009, 1))
        !           823:                conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1);
        !           824: 
        !           825:        if (aim_tlv_gettlv(tlvlist, 0x000b, 1))
        !           826:                msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
        !           827: 
        !           828:        /*
        !           829:         * The server ended this FLAP connnection, so let's be nice and
        !           830:         * close the physical TCP connection
        !           831:         */
        !           832:        flap_connection_schedule_destroy(conn,
        !           833:                        OSCAR_DISCONNECT_REMOTE_CLOSED, msg);
        !           834: 
        !           835:        aim_tlvlist_free(tlvlist);
        !           836: 
        !           837:        g_free(msg);
        !           838: }
        !           839: 
        !           840: /**
        !           841:  * Takes a new incoming FLAP frame and sends it to the appropriate
        !           842:  * handler function to be parsed.
        !           843:  */
        !           844: static void
        !           845: parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
        !           846: {
        !           847:        if (frame->channel == 0x01) {
        !           848:                guint32 flap_version = byte_stream_get32(&frame->data);
        !           849:                if (flap_version != 0x00000001)
        !           850:                {
        !           851:                                /* Error! */
        !           852:                                purple_debug_warning("oscar", "Expecting FLAP version "
        !           853:                                        "0x00000001 but received FLAP version %08x.  Closing connection.\n",
        !           854:                                        flap_version);
        !           855:                                flap_connection_schedule_destroy(conn,
        !           856:                                                OSCAR_DISCONNECT_INVALID_DATA, NULL);
        !           857:                }
        !           858:                else
        !           859:                        conn->connected = TRUE;
        !           860: 
        !           861:        } else if (frame->channel == 0x02) {
        !           862:                parse_snac(od, conn, frame);
        !           863: 
        !           864:        } else if (frame->channel == 0x04) {
        !           865:                parse_flap_ch4(od, conn, frame);
        !           866: 
        !           867:        } else if (frame->channel == 0x05) {
        !           868:                /* TODO: Reset our keepalive watchdog? */
        !           869: 
        !           870:        }
        !           871: }
        !           872: 
        !           873: /**
        !           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.
        !           878:  *
        !           879:  * This is called by flap_connection_recv_cb and
        !           880:  * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
        !           881:  */
        !           882: static void
        !           883: flap_connection_recv(FlapConnection *conn)
        !           884: {
        !           885:        gpointer buf;
        !           886:        gsize buflen;
        !           887:        gssize read;
        !           888: 
        !           889:        /* Read data until we run out of data and break out of the loop */
        !           890:        while (TRUE)
        !           891:        {
        !           892:                /* Start reading a new FLAP */
        !           893:                if (conn->buffer_incoming.data.data == NULL)
        !           894:                {
        !           895:                        buf = conn->header + conn->header_received;
        !           896:                        buflen = 6 - conn->header_received;
        !           897: 
        !           898:                        /* Read the first 6 bytes (the FLAP header) */
        !           899:                        if (conn->gsc)
        !           900:                                read = purple_ssl_read(conn->gsc, buf, buflen);
        !           901:                        else
        !           902:                                read = recv(conn->fd, buf, buflen, 0);
        !           903: 
        !           904:                        /* Check if the FLAP server closed the connection */
        !           905:                        if (read == 0)
        !           906:                        {
        !           907:                                flap_connection_schedule_destroy(conn,
        !           908:                                                OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
        !           909:                                break;
        !           910:                        }
        !           911: 
        !           912:                        /* If there was an error then close the connection */
        !           913:                        if (read < 0)
        !           914:                        {
        !           915:                                if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
        !           916:                                        /* No worries */
        !           917:                                        break;
        !           918: 
        !           919:                                /* Error! */
        !           920:                                flap_connection_schedule_destroy(conn,
        !           921:                                                OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
        !           922:                                break;
        !           923:                        }
        !           924:                        conn->od->gc->last_received = time(NULL);
        !           925: 
        !           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)
        !           929:                                break;
        !           930: 
        !           931:                        /* All FLAP frames must start with the byte 0x2a */
        !           932:                        if (aimutil_get8(&conn->header[0]) != 0x2a)
        !           933:                        {
        !           934:                                flap_connection_schedule_destroy(conn,
        !           935:                                                OSCAR_DISCONNECT_INVALID_DATA, NULL);
        !           936:                                break;
        !           937:                        }
        !           938: 
        !           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;
        !           945:                }
        !           946: 
        !           947:                buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
        !           948:                if (buflen)
        !           949:                {
        !           950:                        buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
        !           951:                        /* Read data into the temporary FlapFrame until it is complete */
        !           952:                        if (conn->gsc)
        !           953:                                read = purple_ssl_read(conn->gsc, buf, buflen);
        !           954:                        else
        !           955:                                read = recv(conn->fd, buf, buflen, 0);
        !           956: 
        !           957:                        /* Check if the FLAP server closed the connection */
        !           958:                        if (read == 0)
        !           959:                        {
        !           960:                                flap_connection_schedule_destroy(conn,
        !           961:                                                OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
        !           962:                                break;
        !           963:                        }
        !           964: 
        !           965:                        if (read < 0)
        !           966:                        {
        !           967:                                if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
        !           968:                                        /* No worries */
        !           969:                                        break;
        !           970: 
        !           971:                                /* Error! */
        !           972:                                flap_connection_schedule_destroy(conn,
        !           973:                                                OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
        !           974:                                break;
        !           975:                        }
        !           976: 
        !           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 */
        !           980:                                break;
        !           981:                }
        !           982: 
        !           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);
        !           987: 
        !           988:                g_free(conn->buffer_incoming.data.data);
        !           989:                conn->buffer_incoming.data.data = NULL;
        !           990: 
        !           991:                conn->header_received = 0;
        !           992:        }
        !           993: }
        !           994: 
        !           995: void
        !           996: flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
        !           997: {
        !           998:        FlapConnection *conn = data;
        !           999: 
        !          1000:        flap_connection_recv(conn);
        !          1001: }
        !          1002: 
        !          1003: void
        !          1004: flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
        !          1005: {
        !          1006:        FlapConnection *conn = data;
        !          1007: 
        !          1008:        flap_connection_recv(conn);
        !          1009: }
        !          1010: 
        !          1011: /**
        !          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.
        !          1017:  */
        !          1018: static void
        !          1019: send_cb(gpointer data, gint source, PurpleInputCondition cond)
        !          1020: {
        !          1021:        FlapConnection *conn;
        !          1022:        int writelen, ret;
        !          1023: 
        !          1024:        conn = data;
        !          1025:        writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
        !          1026: 
        !          1027:        if (writelen == 0)
        !          1028:        {
        !          1029:                purple_input_remove(conn->watcher_outgoing);
        !          1030:                conn->watcher_outgoing = 0;
        !          1031:                return;
        !          1032:        }
        !          1033: 
        !          1034:        if (conn->gsc)
        !          1035:                ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr,
        !          1036:                                writelen);
        !          1037:        else
        !          1038:                ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
        !          1039:        if (ret <= 0)
        !          1040:        {
        !          1041:                if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
        !          1042:                        /* No worries */
        !          1043:                        return;
        !          1044: 
        !          1045:                /* Error! */
        !          1046:                purple_input_remove(conn->watcher_outgoing);
        !          1047:                conn->watcher_outgoing = 0;
        !          1048:                if (conn->gsc) {
        !          1049:                        purple_ssl_close(conn->gsc);
        !          1050:                        conn->gsc = NULL;
        !          1051:                } else {
        !          1052:                        close(conn->fd);
        !          1053:                        conn->fd = -1;
        !          1054:                }
        !          1055:                flap_connection_schedule_destroy(conn,
        !          1056:                                OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
        !          1057:                return;
        !          1058:        }
        !          1059: 
        !          1060:        purple_circ_buffer_mark_read(conn->buffer_outgoing, ret);
        !          1061: }
        !          1062: 
        !          1063: static void
        !          1064: flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
        !          1065: {
        !          1066:        if (conn == NULL)
        !          1067:                return;
        !          1068: 
        !          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 */
        !          1072: 
        !          1073:        if (count == 0)
        !          1074:                return;
        !          1075: 
        !          1076:        /* Add everything to our outgoing buffer */
        !          1077:        purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
        !          1078: 
        !          1079:        /* If we haven't already started writing stuff, then start the cycle */
        !          1080:        if (conn->watcher_outgoing == 0)
        !          1081:        {
        !          1082:                if (conn->gsc) {
        !          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);
        !          1090:                }
        !          1091:        }
        !          1092: }
        !          1093: 
        !          1094: static void
        !          1095: sendframe_flap(FlapConnection *conn, FlapFrame *frame)
        !          1096: {
        !          1097:        ByteStream bs;
        !          1098:        int payloadlen, bslen;
        !          1099: 
        !          1100:        payloadlen = byte_stream_curpos(&frame->data);
        !          1101: 
        !          1102:        byte_stream_new(&bs, 6 + payloadlen);
        !          1103: 
        !          1104:        /* FLAP header */
        !          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);
        !          1109: 
        !          1110:        /* Payload */
        !          1111:        byte_stream_rewind(&frame->data);
        !          1112:        byte_stream_putbs(&bs, &frame->data, payloadlen);
        !          1113: 
        !          1114:        bslen = byte_stream_curpos(&bs);
        !          1115:        byte_stream_rewind(&bs);
        !          1116:        flap_connection_send_byte_stream(&bs, conn, bslen);
        !          1117: 
        !          1118:        byte_stream_destroy(&bs);
        !          1119: }
        !          1120: 
        !          1121: void
        !          1122: flap_connection_send(FlapConnection *conn, FlapFrame *frame)
        !          1123: {
        !          1124:        frame->seqnum = ++(conn->seqnum_out);
        !          1125:        sendframe_flap(conn, frame);
        !          1126:        flap_frame_destroy(frame);
        !          1127: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>