comparison src/protocols/oscar/flap_connection.c @ 13592:6519aeb66b31

[gaim-migrate @ 15978] Holy cow this is crazy. 34 files changed, 5760 insertions(+), 8517 deletions(-) * Non-blocking I/O for all of oscar. That includes normal FLAP connections as well as file transfers and direct IM. * Kick-ass file transfer and direct IM. Either party can request the connection. Gaim will try both the "public" IP and the "client" IP. It'll fall back to transferring through a proxy if that fails. Should be relatively few memleaks (I didn't have a lot of confidence in the non-memleakiness of the old code). And the code is reasonably generic, so it shouldn't be too much work to add voice chat. This might still be a LITTLE buggy, but it shouldn't be too bad. If anything, file transfer will be more buggy than direct IM. And sending a file will be more buggy than receiving a file. Bug reports with a series of steps to reproduce are welcome. * I merged OscarData and aim_session_t * Somewhere between 50 and 100 hours of work. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Fri, 07 Apr 2006 05:10:56 +0000
parents
children 57e5d1455ad7
comparison
equal deleted inserted replaced
13591:dcfda39ad547 13592:6519aeb66b31
1 /*
2 * Gaim'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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "oscar.h"
22
23 #include "eventloop.h"
24
25 #ifndef _WIN32
26 #include <netdb.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #endif
30
31 #ifdef _WIN32
32 #include "win32dep.h"
33 #endif
34
35 /**
36 * This sends a channel 1 SNAC containing the FLAP version.
37 * The FLAP version is sent by itself at the beginning of every
38 * connection to a FLAP server. It is always the very first
39 * packet sent by both the server and the client after the SYN,
40 * SYN/ACK, ACK handshake.
41 */
42 void
43 flap_connection_send_version(OscarData *od, FlapConnection *conn)
44 {
45 FlapFrame *frame;
46
47 frame = flap_frame_new(od, 0x01, 4);
48 byte_stream_put32(&frame->data, 0x00000001);
49 flap_connection_send(conn, frame);
50 }
51
52 /**
53 * This sends a channel 1 SNAC containing the FLAP version and
54 * the authentication cookie. This is sent when connecting to
55 * any FLAP server after the initial connection to the auth
56 * server. It is always the very first packet sent by both the
57 * server and the client after the SYN, SYN/ACK, ACK handshake.
58 */
59 void
60 flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
61 {
62 FlapFrame *frame;
63 aim_tlvlist_t *tl = NULL;
64
65 frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length);
66 byte_stream_put32(&frame->data, 0x00000001);
67 aim_tlvlist_add_raw(&tl, 0x0006, length, chipsahoy);
68 aim_tlvlist_write(&frame->data, &tl);
69 aim_tlvlist_free(&tl);
70
71 flap_connection_send(conn, frame);
72 }
73
74 /**
75 * This sends an empty channel 4 SNAC. This is sent to signify
76 * that we're logging off. This shouldn't really be necessary--
77 * usually the AIM server will detect that the TCP connection has
78 * been destroyed--but it's good practice.
79 */
80 static void
81 flap_connection_send_close(OscarData *od, FlapConnection *conn)
82 {
83 FlapFrame *frame;
84
85 frame = flap_frame_new(od, 0x04, 0);
86 flap_connection_send(conn, frame);
87 }
88
89 /**
90 * This sends an empty channel 5 SNAC. This is used as a keepalive
91 * packet in FLAP connections. WinAIM 4.x and higher send these
92 * _every minute_ to keep the connection alive.
93 */
94 void
95 flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
96 {
97 FlapFrame *frame;
98
99 frame = flap_frame_new(od, 0x05, 0);
100 flap_connection_send(conn, frame);
101
102 /* clean out SNACs over 60sec old */
103 aim_cleansnacs(od, 60);
104 }
105
106 /**
107 * Allocate a new empty connection structure.
108 *
109 * @param od The oscar session associated with this connection.
110 * @param type Type of connection to create
111 *
112 * @return Returns the new connection structure.
113 */
114 FlapConnection *
115 flap_connection_new(OscarData *od, int type)
116 {
117 FlapConnection *conn;
118
119 conn = g_new0(FlapConnection, 1);
120 conn->od = od;
121 conn->buffer_outgoing = gaim_circ_buffer_new(0);
122 conn->inside = g_new0(aim_conn_inside_t, 1);
123 conn->fd = -1;
124 conn->subtype = -1;
125 conn->type = type;
126
127 od->oscar_connections = g_list_prepend(od->oscar_connections, conn);
128
129 return conn;
130 }
131
132 /**
133 * Clone a FlapConnection.
134 *
135 * A new connection is allocated, and the values are filled in
136 * appropriately.
137 *
138 * @param od The session containing this connection.
139 * @param src The connection to clone.
140 * @return Returns a pointer to the new FlapConnection, or %NULL on error.
141 */
142 FlapConnection *
143 flap_connection_clone(OscarData *od, FlapConnection *src)
144 {
145 FlapConnection *conn;
146
147 conn = flap_connection_new(od, src->type);
148 conn->fd = src->fd;
149 conn->type = src->type;
150 conn->subtype = src->subtype;
151 conn->seqnum = src->seqnum;
152 conn->internal = src->internal;
153 conn->lastactivity = src->lastactivity;
154
155 if (src->inside != NULL)
156 {
157 /*
158 * XXX should clone this section as well, but since currently
159 * this function only gets called for some of that rendezvous
160 * crap, and not on SNAC connections, its probably okay for
161 * now.
162 *
163 */
164 }
165
166 return conn;
167 }
168
169 /**
170 * Close (but not free) a connection.
171 *
172 * This leaves everything untouched except for setting the fd
173 * to -1 (used to recognize dead connections).
174 *
175 * @param conn The connection to close.
176 */
177 void
178 flap_connection_close(OscarData *od, FlapConnection *conn)
179 {
180 if (conn->fd == -1)
181 return;
182
183 if (conn->type == SNAC_FAMILY_LOCATE)
184 flap_connection_send_close(od, conn);
185
186 close(conn->fd);
187 }
188
189 static void
190 flap_connection_destroy_snacgroups(struct snacgroup *head)
191 {
192 struct snacgroup *sg;
193 for (sg = head; sg; )
194 {
195 struct snacgroup *tmp;
196
197 tmp = sg->next;
198 free(sg);
199 sg = tmp;
200 }
201 }
202
203 static void
204 flap_connection_destroy_rates(struct rateclass *head)
205 {
206 struct rateclass *rc;
207
208 for (rc = head; rc; )
209 {
210 struct rateclass *tmp;
211 struct snacpair *sp;
212
213 tmp = rc->next;
214
215 for (sp = rc->members; sp; ) {
216 struct snacpair *tmpsp;
217
218 tmpsp = sp->next;
219 free(sp);
220 sp = tmpsp;
221 }
222 free(rc);
223
224 rc = tmp;
225 }
226 }
227
228 static gboolean
229 flap_connection_destroy_cb(gpointer data)
230 {
231 FlapConnection *conn;
232
233 conn = data;
234
235 gaim_debug_info("oscar", "Destroying oscar connection of "
236 "type 0x%04hx\n", conn->type);
237
238 flap_connection_close(conn->od, conn);
239
240 if (conn->watcher_incoming != 0)
241 gaim_input_remove(conn->watcher_incoming);
242 if (conn->watcher_outgoing != 0)
243 gaim_input_remove(conn->watcher_outgoing);
244 g_free(conn->buffer_incoming.data.data);
245 gaim_circ_buffer_destroy(conn->buffer_outgoing);
246
247 /*
248 * Free conn->internal, if necessary
249 */
250 if (conn->type == SNAC_FAMILY_CHAT)
251 flap_connection_destroy_chat(conn->od, conn);
252
253 if (conn->inside != NULL)
254 {
255 aim_conn_inside_t *inside = (aim_conn_inside_t *)conn->inside;
256
257 flap_connection_destroy_snacgroups(inside->groups);
258 flap_connection_destroy_rates(inside->rates);
259
260 free(inside);
261 }
262
263 conn->od->oscar_connections = g_list_remove(conn->od->oscar_connections, conn);
264
265 g_free(conn);
266
267 return FALSE;
268 }
269
270 void
271 flap_connection_destroy(FlapConnection *conn)
272 {
273 if (conn->destroy_timeout != 0)
274 gaim_timeout_remove(conn->destroy_timeout);
275 flap_connection_destroy_cb(conn);
276 }
277
278 /**
279 * Schedule Gaim to destroy the given FlapConnection as soon as we
280 * return control back to the program's main loop. We must do this
281 * if we want to destroy the connection but we are still using it
282 * for some reason.
283 */
284 void
285 flap_connection_schedule_destroy(FlapConnection *conn)
286 {
287 if (conn->destroy_timeout != 0)
288 /* Already taken care of */
289 return;
290
291 gaim_debug_info("oscar", "Scheduling destruction of FLAP "
292 "connection of type 0x%04hx\n", conn->type);
293 conn->destroy_timeout = gaim_timeout_add(0, flap_connection_destroy_cb, conn);
294 }
295
296 /**
297 * In OSCAR, every connection has a set of SNAC groups associated
298 * with it. These are the groups that you can send over this connection
299 * without being guaranteed a "Not supported" SNAC error.
300 *
301 * The grand theory of things says that these associations transcend
302 * what libfaim calls "connection types" (conn->type). You can probably
303 * see the elegance here, but since I want to revel in it for a bit, you
304 * get to hear it all spelled out.
305 *
306 * So let us say that you have your core BOS connection running. One
307 * of your modules has just given you a SNAC of the group 0x0004 to send
308 * you. Maybe an IM destined for some twit in Greenland. So you start
309 * at the top of your connection list, looking for a connection that
310 * claims to support group 0x0004. You find one. Why, that neat BOS
311 * connection of yours can do that. So you send it on its way.
312 *
313 * Now, say, that fellow from Greenland has friends and they all want to
314 * meet up with you in a lame chat room. This has landed you a SNAC
315 * in the family 0x000e and you have to admit you're a bit lost. You've
316 * searched your connection list for someone who wants to make your life
317 * easy and deliver this SNAC for you, but there isn't one there.
318 *
319 * Here comes the good bit. Without even letting anyone know, particularly
320 * the module that decided to send this SNAC, and definitely not that twit
321 * in Greenland, you send out a service request. In this request, you have
322 * marked the need for a connection supporting group 0x000e. A few seconds
323 * later, you receive a service redirect with an IP address and a cookie in
324 * it. Great, you say. Now I have something to do. Off you go, making
325 * that connection. One of the first things you get from this new server
326 * is a message saying that indeed it does support the group you were looking
327 * for. So you continue and send rate confirmation and all that.
328 *
329 * Then you remember you had that SNAC to send, and now you have a means to
330 * do it, and you do, and everyone is happy. Except the Greenlander, who is
331 * still stuck in the bitter cold.
332 *
333 * Oh, and this is useful for building the Migration SNACs, too. In the
334 * future, this may help convince me to implement rate limit mitigation
335 * for real. We'll see.
336 *
337 * Just to make me look better, I'll say that I've known about this great
338 * scheme for quite some time now. But I still haven't convinced myself
339 * to make libfaim work that way. It would take a fair amount of effort,
340 * and probably some client API changes as well. (Whenever I don't want
341 * to do something, I just say it would change the client API. Then I
342 * instantly have a couple of supporters of not doing it.)
343 *
344 * Generally, addgroup is only called by the internal handling of the
345 * server ready SNAC. So if you want to do something before that, you'll
346 * have to be more creative. That is done rather early, though, so I don't
347 * think you have to worry about it. Unless you're me. I care deeply
348 * about such inane things.
349 *
350 */
351 void
352 flap_connection_addgroup(FlapConnection *conn, guint16 group)
353 {
354 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
355 struct snacgroup *sg;
356
357 sg = g_new0(struct snacgroup, 1);
358
359 gaim_debug_misc("oscar", "Adding group 0x%04x to connection "
360 "of type 0x%04hx\n", group, conn->type);
361 sg->group = group;
362
363 sg->next = ins->groups;
364 ins->groups = sg;
365 }
366
367 /**
368 * Find a FlapConnection that supports the given oscar
369 * family.
370 *
371 * TODO: This should be implemented to use a hash table.
372 */
373 FlapConnection *
374 flap_connection_findbygroup(OscarData *od, guint16 group)
375 {
376 GList *cur;
377
378 for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
379 {
380 FlapConnection *conn;
381 aim_conn_inside_t *ins;
382 struct snacgroup *sg;
383
384 conn = cur->data;
385 ins = (aim_conn_inside_t *)conn->inside;
386
387 for (sg = ins->groups; sg != NULL; sg = sg->next)
388 {
389 if (sg->group == group)
390 return conn;
391 }
392 }
393
394 return NULL;
395 }
396
397 /**
398 * Locates a connection of the specified type in the
399 * specified session.
400 *
401 * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
402 *
403 * @param od The session to search.
404 * @param type The type of connection to look for.
405 *
406 * @return Returns the first connection found of the given target type,
407 * or NULL if none could be found.
408 */
409 FlapConnection *
410 flap_connection_getbytype(OscarData *od, int type)
411 {
412 GList *cur;
413
414 for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
415 {
416 FlapConnection *conn;
417 conn = cur->data;
418 if ((conn->type == type) && (conn->connected))
419 return conn;
420 }
421
422 return NULL;
423 }
424
425 FlapConnection *
426 flap_connection_getbytype_all(OscarData *od, int type)
427 {
428 GList *cur;
429
430 for (cur = od->oscar_connections; cur; cur = cur->next)
431 {
432 FlapConnection *conn;
433 conn = cur->data;
434 if (conn->type == type)
435 return conn;
436 }
437
438 return NULL;
439 }
440
441 /**
442 * Allocate a new FLAP frame.
443 *
444 * @param channel The FLAP channel. This is almost always 2.
445 */
446 FlapFrame *
447 flap_frame_new(OscarData *od, guint16 channel, int datalen)
448 {
449 FlapFrame *frame;
450
451 frame = g_new0(FlapFrame, 1);
452 frame->channel = channel;
453
454 if (datalen > 0)
455 {
456 guint8 *data;
457 data = g_malloc(datalen);
458 byte_stream_init(&frame->data, data, datalen);
459 }
460
461 return frame;
462 }
463
464 /**
465 * Free a FlapFrame
466 *
467 * @param frame The frame to free.
468 * @return -1 on error; 0 on success.
469 */
470 static void
471 flap_frame_destroy(FlapFrame *frame)
472 {
473 free(frame->data.data);
474 free(frame);
475
476 return;
477 }
478
479 static void
480 parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
481 {
482 aim_module_t *cur;
483 aim_modsnac_t snac;
484
485 if (byte_stream_empty(&frame->data) < 10)
486 return;
487
488 snac.family = byte_stream_get16(&frame->data);
489 snac.subtype = byte_stream_get16(&frame->data);
490 snac.flags = byte_stream_get16(&frame->data);
491 snac.id = byte_stream_get32(&frame->data);
492
493 /* SNAC flags are apparently uniform across all SNACs, so we handle them here */
494 if (snac.flags & 0x0001) {
495 /*
496 * This means the SNAC will be followed by another SNAC with
497 * related information. We don't need to do anything about
498 * this here.
499 */
500 }
501 if (snac.flags & 0x8000) {
502 /*
503 * This packet contains the version of the family that this SNAC is
504 * in. You get this when your SSI module is version 2 or higher.
505 * For now we have no need for this, but you could always save
506 * it as a part of aim_modnsac_t, or something. The format is...
507 * 2 byte length of total mini-header (which is 6 bytes), then TLV
508 * of type 0x0001, length 0x0002, value is the 2 byte version
509 * number
510 */
511 byte_stream_advance(&frame->data, byte_stream_get16(&frame->data));
512 }
513
514 for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
515
516 if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
517 (cur->family != snac.family))
518 continue;
519
520 if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
521 return;
522 }
523 }
524
525 static void
526 parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
527 {
528 aim_module_t *cur;
529 aim_modsnac_t snac;
530
531 snac.family = family;
532 snac.subtype = subtype;
533 snac.flags = snac.id = 0;
534
535 for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
536
537 if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
538 (cur->family != snac.family))
539 continue;
540
541 if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
542 return;
543 }
544 }
545
546 static void
547 parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
548 {
549 aim_tlvlist_t *tlvlist;
550 char *msg = NULL;
551 guint16 code = 0;
552 aim_rxcallback_t userfunc;
553
554 if (byte_stream_empty(&frame->data) == 0) {
555 /* XXX should do something with this */
556 return;
557 }
558
559 /* Used only by the older login protocol */
560 /* XXX remove this special case? */
561 if (conn->type == SNAC_FAMILY_AUTH)
562 {
563 parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
564 return;
565 }
566
567 tlvlist = aim_tlvlist_read(&frame->data);
568
569 if (aim_tlv_gettlv(tlvlist, 0x0009, 1))
570 code = aim_tlv_get16(tlvlist, 0x0009, 1);
571
572 if (aim_tlv_gettlv(tlvlist, 0x000b, 1))
573 msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
574
575 if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
576 userfunc(od, conn, frame, code, msg);
577
578 aim_tlvlist_free(&tlvlist);
579
580 free(msg);
581 }
582
583 /**
584 * Takes a new incoming FLAP frame and sends it to the appropriate
585 * handler function to be parsed.
586 */
587 static void
588 parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
589 {
590 if (frame->channel == 0x01) {
591 guint32 flap_version = byte_stream_get32(&frame->data);
592 if (flap_version != 0x00000001)
593 {
594 /* Error! */
595 gaim_debug_warning("oscar", "Expecting FLAP version "
596 "0x00000001 but received FLAP version %08lx. Closing connection.\n",
597 flap_version);
598 flap_connection_schedule_destroy(conn);
599 }
600 else
601 conn->connected = TRUE;
602
603 } else if (frame->channel == 0x02) {
604 parse_snac(od, conn, frame);
605
606 } else if (frame->channel == 0x04) {
607 parse_flap_ch4(od, conn, frame);
608
609 } else if (frame->channel == 0x05) {
610 /* TODO: Reset our keepalive watchdog? */
611
612 }
613 }
614
615 /**
616 * Read in all available data on the socket for a given connection.
617 * All complete FLAPs handled immedate after they're received.
618 * Incomplete FLAP data is stored locally and appended to the next
619 * time this callback is triggered.
620 */
621 void
622 flap_connection_recv_cb(gpointer data, gint source, GaimInputCondition cond)
623 {
624 FlapConnection *conn;
625 ssize_t read;
626 guint8 header[6];
627
628 conn = data;
629
630 /* Read data until we run out of data and break out of the loop */
631 while (TRUE)
632 {
633 /* Start reading a new FLAP */
634 if (conn->buffer_incoming.data.data == NULL)
635 {
636 /* Peek at the first 6 bytes to get the length */
637 read = recv(conn->fd, &header, 6, MSG_PEEK);
638
639 /* Check if the FLAP server closed the connection */
640 if (read == 0)
641 {
642 /* TODO: Print an error? Server closed connection. */
643 flap_connection_schedule_destroy(conn);
644 break;
645 }
646
647 /* If there was an error then close the connection */
648 if (read == -1)
649 {
650 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
651 /* No worries */
652 break;
653
654 /* Error! */
655 /* TODO: Print an error? Lost connection with server. */
656 flap_connection_schedule_destroy(conn);
657 break;
658 }
659
660 /* If we don't even have a complete FLAP header then do nothing */
661 if (read < 6)
662 break;
663
664 /* Read the first 6 bytes (the FLAP header) */
665 read = recv(conn->fd, &header, 6, 0);
666
667 /* All FLAP frames must start with the byte 0x2a */
668 if (aimutil_get8(&header[0]) != 0x2a)
669 {
670 flap_connection_schedule_destroy(conn);
671 break;
672 }
673
674 /* Initialize a new temporary FlapFrame for incoming data */
675 conn->buffer_incoming.channel = aimutil_get8(&header[1]);
676 conn->buffer_incoming.seqnum = aimutil_get16(&header[2]);
677 conn->buffer_incoming.data.len = aimutil_get16(&header[4]);
678 conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len);
679 conn->buffer_incoming.data.offset = 0;
680 }
681
682 if (conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset)
683 {
684 /* Read data into the temporary FlapFrame until it is complete */
685 read = recv(conn->fd,
686 &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset],
687 conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset,
688 0);
689
690 /* Check if the FLAP server closed the connection */
691 if (read == 0)
692 {
693 flap_connection_schedule_destroy(conn);
694 break;
695 }
696
697 if (read == -1)
698 {
699 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
700 /* No worries */
701 break;
702
703 /* Error! */
704 /* TODO: Print an error? Lost connection with server. */
705 flap_connection_schedule_destroy(conn);
706 break;
707 }
708
709 conn->buffer_incoming.data.offset += read;
710 if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
711 /* Waiting for more data to arrive */
712 break;
713 }
714
715 /* We have a complete FLAP! Handle it and continue reading */
716 byte_stream_rewind(&conn->buffer_incoming.data);
717 parse_flap(conn->od, conn, &conn->buffer_incoming);
718 conn->lastactivity = time(NULL);
719
720 g_free(conn->buffer_incoming.data.data);
721 conn->buffer_incoming.data.data = NULL;
722 }
723 }
724
725 static void
726 send_cb(gpointer data, gint source, GaimInputCondition cond)
727 {
728 FlapConnection *conn;
729 int writelen, ret;
730
731 conn = data;
732 writelen = gaim_circ_buffer_get_max_read(conn->buffer_outgoing);
733
734 if (writelen == 0)
735 {
736 gaim_input_remove(conn->watcher_outgoing);
737 conn->watcher_outgoing = 0;
738 return;
739 }
740
741 ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
742 if (ret <= 0)
743 {
744 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
745 /* No worries */
746 return;
747
748 /* Error! */
749 flap_connection_schedule_destroy(conn);
750 return;
751 }
752
753 gaim_circ_buffer_mark_read(conn->buffer_outgoing, ret);
754 }
755
756 static void
757 flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
758 {
759 if (conn == NULL)
760 return;
761
762 /* Make sure we don't send past the end of the bs */
763 if (count > byte_stream_empty(bs))
764 count = byte_stream_empty(bs); /* truncate to remaining space */
765
766 if (count == 0)
767 return;
768
769 /* Add everything to our outgoing buffer */
770 gaim_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
771
772 /* If we haven't already started writing stuff, then start the cycle */
773 if (conn->watcher_outgoing == 0)
774 {
775 conn->watcher_outgoing = gaim_input_add(conn->fd,
776 GAIM_INPUT_WRITE, send_cb, conn);
777 send_cb(conn, conn->fd, 0);
778 }
779 }
780
781 static void
782 sendframe_flap(FlapConnection *conn, FlapFrame *frame)
783 {
784 ByteStream bs;
785 int payloadlen, bslen;
786
787 payloadlen = byte_stream_curpos(&frame->data);
788
789 byte_stream_init(&bs, malloc(6 + payloadlen), 6 + payloadlen);
790
791 /* FLAP header */
792 byte_stream_put8(&bs, 0x2a);
793 byte_stream_put8(&bs, frame->channel);
794 byte_stream_put16(&bs, frame->seqnum);
795 byte_stream_put16(&bs, payloadlen);
796
797 /* Payload */
798 byte_stream_rewind(&frame->data);
799 byte_stream_putbs(&bs, &frame->data, payloadlen);
800
801 bslen = byte_stream_curpos(&bs);
802 byte_stream_rewind(&bs);
803 flap_connection_send_byte_stream(&bs, conn, bslen);
804
805 free(bs.data); /* XXX byte_stream_free */
806 }
807
808 void
809 flap_connection_send(FlapConnection *conn, FlapFrame *frame)
810 {
811 frame->seqnum = ++(conn->seqnum);
812 sendframe_flap(conn, frame);
813 flap_frame_destroy(frame);
814 }
815