source: protocols/oscar/tlv.c @ 8e3b7ac

Last change on this file since 8e3b7ac was 6042a54, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T23:38:33Z

Massive cleanup in OSCAR.

  • Property mode set to 100644
File size: 11.8 KB
Line 
1#include <aim.h>
2
3static void freetlv(aim_tlv_t **oldtlv)
4{
5        if (!oldtlv || !*oldtlv)
6                return;
7       
8        g_free((*oldtlv)->value);
9        g_free(*oldtlv);
10        *oldtlv = NULL;
11}
12
13/**
14 * aim_readtlvchain - Read a TLV chain from a buffer.
15 * @buf: Input buffer
16 * @maxlen: Length of input buffer
17 *
18 * Reads and parses a series of TLV patterns from a data buffer; the
19 * returned structure is manipulatable with the rest of the TLV
20 * routines.  When done with a TLV chain, aim_freetlvchain() should
21 * be called to free the dynamic substructures.
22 *
23 * XXX There should be a flag setable here to have the tlvlist contain
24 * bstream references, so that at least the ->value portion of each
25 * element doesn't need to be malloc/memcpy'd.  This could prove to be
26 * just as effecient as the in-place TLV parsing used in a couple places
27 * in libfaim.
28 *
29 */
30aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs)
31{
32        aim_tlvlist_t *list = NULL, *cur;
33        guint16 type, length;
34
35        while (aim_bstream_empty(bs)) {
36
37                type = aimbs_get16(bs);
38                length = aimbs_get16(bs);
39
40                cur = g_new0(aim_tlvlist_t, 1);
41
42                cur->tlv = g_new0(aim_tlv_t, 1);
43                cur->tlv->type = type;
44                if ((cur->tlv->length = length))
45                        cur->tlv->value = aimbs_getraw(bs, length);     
46
47                cur->next = list;
48                list = cur;
49        }
50
51        return list;
52}
53
54/**
55 * aim_freetlvchain - Free a TLV chain structure
56 * @list: Chain to be freed
57 *
58 * Walks the list of TLVs in the passed TLV chain and
59 * frees each one. Note that any references to this data
60 * should be removed before calling this.
61 *
62 */
63void aim_freetlvchain(aim_tlvlist_t **list)
64{
65        aim_tlvlist_t *cur;
66
67        if (!list || !*list)
68                return;
69
70        for (cur = *list; cur; ) {
71                aim_tlvlist_t *tmp;
72               
73                freetlv(&cur->tlv);
74
75                tmp = cur->next;
76                g_free(cur);
77                cur = tmp;
78        }
79
80        list = NULL;
81
82        return;
83}
84
85/**
86 * aim_counttlvchain - Count the number of TLVs in a chain
87 * @list: Chain to be counted
88 *
89 * Returns the number of TLVs stored in the passed chain.
90 *
91 */
92int aim_counttlvchain(aim_tlvlist_t **list)
93{
94        aim_tlvlist_t *cur;
95        int count;
96
97        if (!list || !*list)
98                return 0;
99
100        for (cur = *list, count = 0; cur; cur = cur->next)
101                count++;
102
103        return count;
104}
105
106/**
107 * aim_sizetlvchain - Count the number of bytes in a TLV chain
108 * @list: Chain to be sized
109 *
110 * Returns the number of bytes that would be needed to
111 * write the passed TLV chain to a data buffer.
112 *
113 */
114int aim_sizetlvchain(aim_tlvlist_t **list)
115{
116        aim_tlvlist_t *cur;
117        int size;
118
119        if (!list || !*list)
120                return 0;
121
122        for (cur = *list, size = 0; cur; cur = cur->next)
123                size += (4 + cur->tlv->length);
124
125        return size;
126}
127
128/**
129 * aim_addtlvtochain_str - Add a string to a TLV chain
130 * @list: Desination chain (%NULL pointer if empty)
131 * @type: TLV type
132 * @str: String to add
133 * @len: Length of string to add (not including %NULL)
134 *
135 * Adds the passed string as a TLV element of the passed type
136 * to the TLV chain.
137 *
138 */
139int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v)
140{
141        aim_tlvlist_t *newtlv, *cur;
142
143        if (!list)
144                return 0;
145
146        if (!(newtlv = g_new0(aim_tlvlist_t, 1)))
147                return 0;
148
149        if (!(newtlv->tlv = g_new0(aim_tlv_t, 1))) {
150                g_free(newtlv);
151                return 0;
152        }
153        newtlv->tlv->type = t;
154        if ((newtlv->tlv->length = l)) {
155                newtlv->tlv->value = (guint8 *)g_malloc(newtlv->tlv->length);
156                memcpy(newtlv->tlv->value, v, newtlv->tlv->length);
157        }
158
159        if (!*list)
160                *list = newtlv;
161        else {
162                for(cur = *list; cur->next; cur = cur->next)
163                        ;
164                cur->next = newtlv;
165        }
166
167        return newtlv->tlv->length;
168}
169
170/**
171 * aim_addtlvtochain8 - Add a 8bit integer to a TLV chain
172 * @list: Destination chain
173 * @type: TLV type to add
174 * @val: Value to add
175 *
176 * Adds a one-byte unsigned integer to a TLV chain.
177 *
178 */
179int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v)
180{
181        guint8 v8[1];
182
183        aimutil_put8(v8, v);
184
185        return aim_addtlvtochain_raw(list, t, 1, v8);
186}
187
188/**
189 * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
190 * @list: Destination chain
191 * @type: TLV type to add
192 * @val: Value to add
193 *
194 * Adds a two-byte unsigned integer to a TLV chain.
195 *
196 */
197int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v)
198{
199        guint8 v16[2];
200
201        aimutil_put16(v16, v);
202
203        return aim_addtlvtochain_raw(list, t, 2, v16);
204}
205
206/**
207 * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain
208 * @list: Destination chain
209 * @type: TLV type to add
210 * @val: Value to add
211 *
212 * Adds a four-byte unsigned integer to a TLV chain.
213 *
214 */
215int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 t, const guint32 v)
216{
217        guint8 v32[4];
218
219        aimutil_put32(v32, v);
220
221        return aim_addtlvtochain_raw(list, t, 4, v32);
222}
223
224/**
225 * aim_addtlvtochain_caps - Add a capability block to a TLV chain
226 * @list: Destination chain
227 * @type: TLV type to add
228 * @caps: Bitfield of capability flags to send
229 *
230 * Adds a block of capability blocks to a TLV chain. The bitfield
231 * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
232 *
233 */
234int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps)
235{
236        guint8 buf[16*16]; /* XXX icky fixed length buffer */
237        aim_bstream_t bs;
238
239        if (!caps)
240                return 0; /* nothing there anyway */
241
242        aim_bstream_init(&bs, buf, sizeof(buf));
243
244        aim_putcap(&bs, caps);
245
246        return aim_addtlvtochain_raw(list, t, aim_bstream_curpos(&bs), buf);
247}
248
249/**
250 * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain
251 * @list: Destination chain
252 * @type: TLV type to add
253 *
254 * Adds a TLV with a zero length to a TLV chain.
255 *
256 */
257int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 t)
258{
259        return aim_addtlvtochain_raw(list, t, 0, NULL);
260}
261
262/*
263 * Note that the inner TLV chain will not be modifiable as a tlvchain once
264 * it is written using this.  Or rather, it can be, but updates won't be
265 * made to this.
266 *
267 * XXX should probably support sublists for real.
268 *
269 * This is so neat.
270 *
271 */
272int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
273{
274        guint8 *buf;
275        int buflen;
276        aim_bstream_t bs;
277
278        buflen = aim_sizetlvchain(tl);
279
280        if (buflen <= 0)
281                return 0;
282
283        if (!(buf = g_malloc(buflen)))
284                return 0;
285
286        aim_bstream_init(&bs, buf, buflen);
287
288        aim_writetlvchain(&bs, tl);
289
290        aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
291
292        g_free(buf);
293
294        return buflen;
295}
296
297int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
298{
299        guint8 *buf;
300        int buflen;
301        aim_bstream_t bs;
302
303        buflen = 2 + 1 + strlen(roomname) + 2;
304       
305        if (!(buf = g_malloc(buflen)))
306                return 0;
307
308        aim_bstream_init(&bs, buf, buflen);
309
310        aimbs_put16(&bs, exchange);
311        aimbs_put8(&bs, strlen(roomname));
312        aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname));
313        aimbs_put16(&bs, instance);
314
315        aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
316
317        g_free(buf);
318
319        return 0;
320}
321
322/**
323 * aim_writetlvchain - Write a TLV chain into a data buffer.
324 * @buf: Destination buffer
325 * @buflen: Maximum number of bytes that will be written to buffer
326 * @list: Source TLV chain
327 *
328 * Copies a TLV chain into a raw data buffer, writing only the number
329 * of bytes specified. This operation does not free the chain;
330 * aim_freetlvchain() must still be called to free up the memory used
331 * by the chain structures.
332 *
333 * XXX clean this up, make better use of bstreams
334 */
335int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list)
336{
337        int goodbuflen;
338        aim_tlvlist_t *cur;
339
340        /* do an initial run to test total length */
341        for (cur = *list, goodbuflen = 0; cur; cur = cur->next) {
342                goodbuflen += 2 + 2; /* type + len */
343                goodbuflen += cur->tlv->length;
344        }
345
346        if (goodbuflen > aim_bstream_empty(bs))
347                return 0; /* not enough buffer */
348
349        /* do the real write-out */
350        for (cur = *list; cur; cur = cur->next) {
351                aimbs_put16(bs, cur->tlv->type);
352                aimbs_put16(bs, cur->tlv->length);
353                if (cur->tlv->length)
354                        aimbs_putraw(bs, cur->tlv->value, cur->tlv->length);
355        }
356
357        return 1; /* XXX this is a nonsensical return */
358}
359
360
361/**
362 * aim_gettlv - Grab the Nth TLV of type type in the TLV list list.
363 * @list: Source chain
364 * @type: Requested TLV type
365 * @nth: Index of TLV of type to get
366 *
367 * Returns a pointer to an aim_tlv_t of the specified type;
368 * %NULL on error.  The @nth parameter is specified starting at %1.
369 * In most cases, there will be no more than one TLV of any type
370 * in a chain.
371 *
372 */
373aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const guint16 t, const int n)
374{
375        aim_tlvlist_t *cur;
376        int i;
377
378        for (cur = list, i = 0; cur; cur = cur->next) {
379                if (cur && cur->tlv) {
380                        if (cur->tlv->type == t)
381                                i++;
382                        if (i >= n)
383                                return cur->tlv;
384                }
385        }
386
387        return NULL;
388}
389
390/**
391 * aim_gettlv_str - Retrieve the Nth TLV in chain as a string.
392 * @list: Source TLV chain
393 * @type: TLV type to search for
394 * @nth: Index of TLV to return
395 *
396 * Same as aim_gettlv(), except that the return value is a %NULL-
397 * terminated string instead of an aim_tlv_t.  This is a
398 * dynamic buffer and must be freed by the caller.
399 *
400 */
401char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n)
402{
403        aim_tlv_t *tlv;
404        char *newstr;
405
406        if (!(tlv = aim_gettlv(list, t, n)))
407                return NULL;
408
409        newstr = (char *) g_malloc(tlv->length + 1);
410        memcpy(newstr, tlv->value, tlv->length);
411        *(newstr + tlv->length) = '\0';
412
413        return newstr;
414}
415
416/**
417 * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer.
418 * @list: Source TLV chain
419 * @type: TLV type to search for
420 * @nth: Index of TLV to return
421 *
422 * Same as aim_gettlv(), except that the return value is a
423 * 8bit integer instead of an aim_tlv_t.
424 *
425 */
426guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 t, const int n)
427{
428        aim_tlv_t *tlv;
429
430        if (!(tlv = aim_gettlv(list, t, n)))
431                return 0; /* erm */
432        return aimutil_get8(tlv->value);
433}
434
435/**
436 * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer.
437 * @list: Source TLV chain
438 * @type: TLV type to search for
439 * @nth: Index of TLV to return
440 *
441 * Same as aim_gettlv(), except that the return value is a
442 * 16bit integer instead of an aim_tlv_t.
443 *
444 */
445guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n)
446{
447        aim_tlv_t *tlv;
448
449        if (!(tlv = aim_gettlv(list, t, n)))
450                return 0; /* erm */
451        return aimutil_get16(tlv->value);
452}
453
454/**
455 * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer.
456 * @list: Source TLV chain
457 * @type: TLV type to search for
458 * @nth: Index of TLV to return
459 *
460 * Same as aim_gettlv(), except that the return value is a
461 * 32bit integer instead of an aim_tlv_t.
462 *
463 */
464guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n)
465{
466        aim_tlv_t *tlv;
467
468        if (!(tlv = aim_gettlv(list, t, n)))
469                return 0; /* erm */
470        return aimutil_get32(tlv->value);
471}
472
473#if 0
474/**
475 * aim_puttlv_8 - Write a one-byte TLV.
476 * @buf: Destination buffer
477 * @t: TLV type
478 * @v: Value
479 *
480 * Writes a TLV with a one-byte integer value portion.
481 *
482 */
483int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v)
484{
485        guint8 v8[1];
486
487        aimutil_put8(v8, v);
488
489        return aim_puttlv_raw(buf, t, 1, v8);
490}
491
492/**
493 * aim_puttlv_16 - Write a two-byte TLV.
494 * @buf: Destination buffer
495 * @t: TLV type
496 * @v: Value
497 *
498 * Writes a TLV with a two-byte integer value portion.
499 *
500 */
501int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v)
502{
503        guint8 v16[2];
504
505        aimutil_put16(v16, v);
506
507        return aim_puttlv_raw(buf, t, 2, v16);
508}
509
510
511/**
512 * aim_puttlv_32 - Write a four-byte TLV.
513 * @buf: Destination buffer
514 * @t: TLV type
515 * @v: Value
516 *
517 * Writes a TLV with a four-byte integer value portion.
518 *
519 */
520int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v)
521{
522        guint8 v32[4];
523
524        aimutil_put32(v32, v);
525
526        return aim_puttlv_raw(buf, t, 4, v32);
527}
528
529/**
530 * aim_puttlv_raw - Write a raw TLV.
531 * @buf: Destination buffer
532 * @t: TLV type
533 * @l: Length of string
534 * @v: String to write
535 *
536 * Writes a TLV with a raw value portion.  (Only the first @l
537 * bytes of the passed buffer will be written, which should not
538 * include a terminating NULL.)
539 *
540 */
541int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v)
542{
543        int i;
544
545        i = aimutil_put16(buf, t);
546        i += aimutil_put16(buf+i, l);
547        if (l)
548                memcpy(buf+i, v, l);
549        i += l;
550
551        return i;
552}
553#endif
554
Note: See TracBrowser for help on using the repository browser.