4 * This file contains the management routines for the receive
5 * (incoming packet) queue. The actual packet handlers are in
13 #include <sys/socket.h>
19 faim_internal int aim_recv(int fd, void *buf, size_t count)
23 for (cur = 0, left = count; left; ) {
26 ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
40 * Read into a byte stream. Will not read more than count, but may read
41 * less if there is not enough room in the stream buffer.
43 faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
47 if (!bs || (fd < 0) || (count < 0))
50 if (count > (bs->len - bs->offset))
51 count = bs->len - bs->offset; /* truncate to remaining space */
55 red = aim_recv(fd, bs->data + bs->offset, count);
66 faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len)
79 faim_internal int aim_bstream_empty(aim_bstream_t *bs)
81 return bs->len - bs->offset;
84 faim_internal int aim_bstream_curpos(aim_bstream_t *bs)
89 faim_internal int aim_bstream_setpos(aim_bstream_t *bs, int off)
100 faim_internal void aim_bstream_rewind(aim_bstream_t *bs)
103 aim_bstream_setpos(bs, 0);
108 faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n)
111 if (aim_bstream_empty(bs) < n)
112 return 0; /* XXX throw an exception */
119 faim_internal fu8_t aimbs_get8(aim_bstream_t *bs)
122 if (aim_bstream_empty(bs) < 1)
123 return 0; /* XXX throw an exception */
127 return aimutil_get8(bs->data + bs->offset - 1);
130 faim_internal fu16_t aimbs_get16(aim_bstream_t *bs)
133 if (aim_bstream_empty(bs) < 2)
134 return 0; /* XXX throw an exception */
138 return aimutil_get16(bs->data + bs->offset - 2);
141 faim_internal fu32_t aimbs_get32(aim_bstream_t *bs)
144 if (aim_bstream_empty(bs) < 4)
145 return 0; /* XXX throw an exception */
149 return aimutil_get32(bs->data + bs->offset - 4);
152 faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v)
155 if (aim_bstream_empty(bs) < 1)
156 return 0; /* XXX throw an exception */
158 bs->offset += aimutil_put8(bs->data + bs->offset, v);
163 faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v)
166 if (aim_bstream_empty(bs) < 2)
167 return 0; /* XXX throw an exception */
169 bs->offset += aimutil_put16(bs->data + bs->offset, v);
174 faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v)
177 if (aim_bstream_empty(bs) < 4)
178 return 0; /* XXX throw an exception */
180 bs->offset += aimutil_put32(bs->data + bs->offset, v);
185 faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len)
188 if (aim_bstream_empty(bs) < len)
191 memcpy(buf, bs->data + bs->offset, len);
197 faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len)
201 if (!(ob = malloc(len)))
204 if (aimbs_getrawbuf(bs, ob, len) < len) {
212 faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len)
216 if (!(ob = malloc(len+1)))
219 if (aimbs_getrawbuf(bs, ob, len) < len) {
229 faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len)
232 if (aim_bstream_empty(bs) < len)
233 return 0; /* XXX throw an exception */
235 memcpy(bs->data + bs->offset, v, len);
241 faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len)
244 if (aim_bstream_empty(srcbs) < len)
245 return 0; /* XXX throw exception (underrun) */
247 if (aim_bstream_empty(bs) < len)
248 return 0; /* XXX throw exception (overflow) */
250 memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
252 srcbs->offset += len;
258 * aim_frame_destroy - free aim_frame_t
259 * @frame: the frame to free
261 * returns -1 on error; 0 on success.
264 faim_internal void aim_frame_destroy(aim_frame_t *frame)
267 free(frame->data.data); /* XXX aim_bstream_free */
269 if (frame->hdrtype == AIM_FRAMETYPE_OFT)
270 free(frame->hdr.oft.hdr2);
278 * Grab a single command sequence off the socket, and enqueue
279 * it in the incoming event queue in a seperate struct.
281 faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
283 fu8_t flaphdr_raw[6];
284 aim_bstream_t flaphdr;
292 return -1; /* its a aim_conn_close()'d connection */
294 if (conn->fd < 3) /* can happen when people abuse the interface */
297 if (conn->status & AIM_CONN_STATUS_INPROGRESS)
298 return aim_conn_completeconnect(sess, conn);
301 * Rendezvous (client-client) connections do not speak
302 * FLAP, so this function will break on them.
304 if (conn->type == AIM_CONN_TYPE_RENDEZVOUS)
305 return aim_get_command_rendezvous(sess, conn);
306 else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
307 faimdprintf(sess, 0, "AIM_CONN_TYPE_RENDEZVOUS_OUT on fd %d\n", conn->fd);
311 aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw));
314 * Read FLAP header. Six bytes:
316 * 0 char -- Always 0x2a
317 * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login.
318 * 2 short -- Sequence number
319 * 4 short -- Number of data bytes that follow.
321 if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
322 aim_conn_close(conn);
326 aim_bstream_rewind(&flaphdr);
329 * This shouldn't happen unless the socket breaks, the server breaks,
330 * or we break. We must handle it just in case.
332 if (aimbs_get8(&flaphdr) != 0x2a) {
333 faimdprintf(sess, 0, "FLAP framing disrupted");
334 aim_conn_close(conn);
338 /* allocate a new struct */
339 if (!(newrx = (aim_frame_t *)malloc(sizeof(aim_frame_t))))
341 memset(newrx, 0, sizeof(aim_frame_t));
343 /* we're doing FLAP if we're here */
344 newrx->hdrtype = AIM_FRAMETYPE_FLAP;
346 newrx->hdr.flap.type = aimbs_get8(&flaphdr);
347 newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr);
348 payloadlen = aimbs_get16(&flaphdr);
350 newrx->nofree = 0; /* free by default */
353 fu8_t *payload = NULL;
355 if (!(payload = (fu8_t *) malloc(payloadlen))) {
356 aim_frame_destroy(newrx);
360 aim_bstream_init(&newrx->data, payload, payloadlen);
362 /* read the payload */
363 if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
365 aim_frame_destroy(newrx);
366 aim_conn_close(conn);
370 aim_bstream_init(&newrx->data, NULL, 0);
373 aim_bstream_rewind(&newrx->data);
377 newrx->next = NULL; /* this will always be at the bottom */
379 if (!sess->queue_incoming)
380 sess->queue_incoming = newrx;
384 for (cur = sess->queue_incoming; cur->next; cur = cur->next)
389 newrx->conn->lastactivity = time(NULL);
395 * Purge recieve queue of all handled commands (->handled==1). Also
396 * allows for selective freeing using ->nofree so that the client can
397 * keep the data for various purposes.
399 * If ->nofree is nonzero, the frame will be delinked from the global list,
400 * but will not be free'ed. The client _must_ keep a pointer to the
401 * data -- libfaim will not! If the client marks ->nofree but
402 * does not keep a pointer, it's lost forever.
405 faim_export void aim_purge_rxqueue(aim_session_t *sess)
407 aim_frame_t *cur, **prev;
409 for (prev = &sess->queue_incoming; (cur = *prev); ) {
415 aim_frame_destroy(cur);
425 * Since aim_get_command will aim_conn_kill dead connections, we need
426 * to clean up the rxqueue of unprocessed connections on that socket.
428 * XXX: this is something that was handled better in the old connection
429 * handling method, but eh.
431 faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
435 for (currx = sess->queue_incoming; currx; currx = currx->next) {
436 if ((!currx->handled) && (currx->conn == conn))