source: protocols/oscar/txqueue.c @ 0b1ae59

Last change on this file since 0b1ae59 was e88fe7da, checked in by Veres Lajos <vlajos@…>, at 2015-08-07T21:53:25Z

typofix - https://github.com/vlajos/misspell_fixer

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