source: protocols/oscar/txqueue.c @ e5854a8

Last change on this file since e5854a8 was 43462708, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-01-06T12:37:55Z

More consistency in error/warning errors. Until now "WARNING:" was usually
in upper case while "Error:" wasn't .... that doesn't really make sense.

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