source: protocols/oscar/tlv.c @ 68b50b5f

Last change on this file since 68b50b5f was b7d3cc34, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-06T18:23:18Z

Initial repository (0.99 release tree)

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