source: protocols/oscar/txqueue.c @ a6cd799

Last change on this file since a6cd799 was e252d8c, checked in by dequis <dx@…>, at 2014-09-27T14:54:35Z

RIP native win32 support (use cygwin instead)

It has been broken for a very long time and nobody cared about it.

  • Property mode set to 100644
File size: 7.5 KB
Line 
1/*
2 *  aim_txqueue.c
3 *
4 * Herein lies all the mangement routines for the transmit (Tx) queue.
5 *
6 */
7
8#include <aim.h>
9#include "im.h"
10
11#include <sys/socket.h>
12
13/*
14 * Allocate a new tx frame.
15 *
16 * This is more for looks than anything else.
17 *
18 * Right now, that is.  If/when we implement a pool of transmit
19 * frames, this will become the request-an-unused-frame part.
20 *
21 * framing = AIM_FRAMETYPE_OFT/FLAP
22 * chan = channel for FLAP, hdrtype for OFT
23 *
24 */
25aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen)
26{
27        aim_frame_t *fr;
28
29        if (!conn) {
30                imcb_error(sess->aux_data, "no connection specified");
31                return NULL;
32        }
33
34        if (!(fr = (aim_frame_t *)g_new0(aim_frame_t,1)))
35                return NULL;
36
37        fr->conn = conn; 
38
39        fr->hdrtype = framing;
40
41        if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
42
43                fr->hdr.flap.type = chan;
44
45        } else 
46                imcb_error(sess->aux_data, "unknown framing");
47
48        if (datalen > 0) {
49                guint8 *data;
50
51                if (!(data = (unsigned char *)g_malloc(datalen))) {
52                        aim_frame_destroy(fr);
53                        return NULL;
54                }
55
56                aim_bstream_init(&fr->data, data, datalen);
57        }
58
59        return fr;
60}
61
62/*
63 * aim_tx_enqeue__queuebased()
64 *
65 * The overall purpose here is to enqueue the passed in command struct
66 * into the outgoing (tx) queue.  Basically...
67 *   1) Make a scope-irrelevent copy of the struct
68 *   3) Mark as not-sent-yet
69 *   4) Enqueue the struct into the list
70 *   6) Return
71 *
72 * Note that this is only used when doing queue-based transmitting;
73 * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
74 *
75 */
76static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr)
77{
78
79        if (!fr->conn) {
80                imcb_error(sess->aux_data, "Warning: enqueueing packet with no connection");
81                fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
82        }
83
84        if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
85                /* assign seqnum -- XXX should really not assign until hardxmit */
86                fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
87        }
88
89        fr->handled = 0; /* not sent yet */
90
91        /* see overhead note in aim_rxqueue counterpart */
92        if (!sess->queue_outgoing)
93                sess->queue_outgoing = fr;
94        else {
95                aim_frame_t *cur;
96
97                for (cur = sess->queue_outgoing; cur->next; cur = cur->next)
98                        ;
99                cur->next = fr;
100        }
101
102        return 0;
103}
104
105/*
106 * aim_tx_enqueue__immediate()
107 *
108 * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
109 * the whole queue mess when you want immediate writes to happen.
110 *
111 * Basically the same as its __queuebased couterpart, however
112 * instead of doing a list append, it just calls aim_tx_sendframe()
113 * right here.
114 *
115 */
116static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr)
117{
118
119        if (!fr->conn) {
120                imcb_error(sess->aux_data, "packet has no connection");
121                aim_frame_destroy(fr);
122                return 0;
123        }
124
125        if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
126                fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
127
128        fr->handled = 0; /* not sent yet */
129
130        aim_tx_sendframe(sess, fr);
131
132        aim_frame_destroy(fr);
133
134        return 0;
135}
136
137int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
138{
139       
140        if (what == AIM_TX_QUEUED)
141                sess->tx_enqueue = &aim_tx_enqueue__queuebased;
142        else if (what == AIM_TX_IMMEDIATE) 
143                sess->tx_enqueue = &aim_tx_enqueue__immediate;
144        else if (what == AIM_TX_USER) {
145                if (!func)
146                        return -EINVAL;
147                sess->tx_enqueue = func;
148        } else
149                return -EINVAL; /* unknown action */
150
151        return 0;
152}
153
154int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr)
155{
156       
157        /*
158         * If we want to send a connection thats inprogress, we have to force
159         * them to use the queue based version. Otherwise, use whatever they
160         * want.
161         */
162        if (fr && fr->conn && 
163                        (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
164                return aim_tx_enqueue__queuebased(sess, fr);
165        }
166
167        return (*sess->tx_enqueue)(sess, fr);
168}
169
170/*
171 *  aim_get_next_txseqnum()
172 *
173 *   This increments the tx command count, and returns the seqnum
174 *   that should be stamped on the next FLAP packet sent.  This is
175 *   normally called during the final step of packet preparation
176 *   before enqueuement (in aim_tx_enqueue()).
177 *
178 */
179flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn)
180{
181        flap_seqnum_t ret;
182       
183        ret = ++conn->seqnum;
184
185        return ret;
186}
187
188static int aim_send(int fd, const void *buf, size_t count)
189{
190        int left, cur;
191
192        for (cur = 0, left = count; left; ) {
193                int ret;
194
195                ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
196                if (ret == -1)
197                        return -1;
198                else if (ret == 0)
199                        return cur;
200
201                cur += ret;
202                left -= ret;
203        }
204
205        return cur;
206}
207
208static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
209{
210        int wrote = 0;
211        if (!bs || !conn || (count < 0))
212                return -EINVAL;
213
214        if (count > aim_bstream_empty(bs))
215                count = aim_bstream_empty(bs); /* truncate to remaining space */
216
217        if (count) {
218                if (count - wrote) {
219                        wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
220                }
221               
222        }
223       
224        bs->offset += wrote;
225
226        return wrote;   
227}
228
229static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
230{
231        aim_bstream_t obs;
232        guint8 *obs_raw;
233        int payloadlen, err = 0, obslen;
234
235        payloadlen = aim_bstream_curpos(&fr->data);
236
237        if (!(obs_raw = g_malloc(6 + payloadlen)))
238                return -ENOMEM;
239
240        aim_bstream_init(&obs, obs_raw, 6 + payloadlen);
241
242        /* FLAP header */
243        aimbs_put8(&obs, 0x2a);
244        aimbs_put8(&obs, fr->hdr.flap.type);
245        aimbs_put16(&obs, fr->hdr.flap.seqnum);
246        aimbs_put16(&obs, payloadlen);
247
248        /* payload */
249        aim_bstream_rewind(&fr->data);
250        aimbs_putbs(&obs, &fr->data, payloadlen);
251
252        obslen = aim_bstream_curpos(&obs);
253        aim_bstream_rewind(&obs);
254        if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
255                err = -errno;
256       
257        g_free(obs_raw); /* XXX aim_bstream_free */
258
259        fr->handled = 1;
260        fr->conn->lastactivity = time(NULL);
261
262        return err;
263}
264
265int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
266{
267        if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
268                return sendframe_flap(sess, fr);
269        return -1;
270}
271
272int aim_tx_flushqueue(aim_session_t *sess)
273{
274        aim_frame_t *cur;
275
276        for (cur = sess->queue_outgoing; cur; cur = cur->next) {
277
278                if (cur->handled)
279                        continue; /* already been sent */
280
281                if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
282                        continue;
283
284                /*
285                 * And now for the meager attempt to force transmit
286                 * latency and avoid missed messages.
287                 */
288                if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
289                        /*
290                         * XXX should be a break! we dont want to block the
291                         * upper layers
292                         *
293                         * XXX or better, just do this right.
294                         *
295                         */
296                        sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
297                }
298
299                /* XXX this should call the custom "queuing" function!! */
300                aim_tx_sendframe(sess, cur);
301        }
302
303        /* purge sent commands from queue */
304        aim_tx_purgequeue(sess);
305
306        return 0;
307}
308
309/*
310 *  aim_tx_purgequeue()
311 * 
312 *  This is responsable for removing sent commands from the transmit
313 *  queue. This is not a required operation, but it of course helps
314 *  reduce memory footprint at run time! 
315 *
316 */
317void aim_tx_purgequeue(aim_session_t *sess)
318{
319        aim_frame_t *cur, **prev;
320
321        for (prev = &sess->queue_outgoing; (cur = *prev); ) {
322
323                if (cur->handled) {
324                        *prev = cur->next;
325
326                        aim_frame_destroy(cur);
327
328                } else
329                        prev = &cur->next;
330        }
331
332        return;
333}
334
335/**
336 * aim_tx_cleanqueue - get rid of packets waiting for tx on a dying conn
337 * @sess: session
338 * @conn: connection that's dying
339 *
340 * for now this simply marks all packets as sent and lets them
341 * disappear without warning.
342 *
343 */
344void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
345{
346        aim_frame_t *cur;
347
348        for (cur = sess->queue_outgoing; cur; cur = cur->next) {
349                if (cur->conn == conn)
350                        cur->handled = 1;
351        }
352
353        return;
354}
355
356
Note: See TracBrowser for help on using the repository browser.