comparison libpurple/protocols/oscar/tlv.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2 * Gaim's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 #include "oscar.h"
23
24 static aim_tlv_t *
25 createtlv(guint16 type, guint16 length, guint8 *value)
26 {
27 aim_tlv_t *ret;
28
29 ret = g_new(aim_tlv_t, 1);
30 ret->type = type;
31 ret->length = length;
32 ret->value = value;
33
34 return ret;
35 }
36
37 static void
38 freetlv(aim_tlv_t **oldtlv)
39 {
40
41 if (!oldtlv || !*oldtlv)
42 return;
43
44 free((*oldtlv)->value);
45 free(*oldtlv);
46 *oldtlv = NULL;
47
48 return;
49 }
50
51 /**
52 * Read a TLV chain from a buffer.
53 *
54 * Reads and parses a series of TLV patterns from a data buffer; the
55 * returned structure is manipulatable with the rest of the TLV
56 * routines. When done with a TLV chain, aim_tlvlist_free() should
57 * be called to free the dynamic substructures.
58 *
59 * XXX There should be a flag setable here to have the tlvlist contain
60 * bstream references, so that at least the ->value portion of each
61 * element doesn't need to be malloc/memcpy'd. This could prove to be
62 * just as efficient as the in-place TLV parsing used in a couple places
63 * in libfaim.
64 *
65 * @param bs Input bstream
66 * @return Return the TLV chain read
67 */
68 aim_tlvlist_t *aim_tlvlist_read(ByteStream *bs)
69 {
70 aim_tlvlist_t *list = NULL, *cur;
71
72 while (byte_stream_empty(bs) > 0) {
73 guint16 type, length;
74
75 type = byte_stream_get16(bs);
76 length = byte_stream_get16(bs);
77
78 #if 0 /* temporarily disabled until I know if they're still doing it or not */
79 /*
80 * Okay, so now AOL has decided that any TLV of
81 * type 0x0013 can only be two bytes, despite
82 * what the actual given length is. So here
83 * we dump any invalid TLVs of that sort. Hopefully
84 * there's no special cases to this special case.
85 * - mid (30jun2000)
86 */
87 if ((type == 0x0013) && (length != 0x0002))
88 length = 0x0002;
89 #else
90 if (0)
91 ;
92 #endif
93 else {
94
95 if (length > byte_stream_empty(bs)) {
96 aim_tlvlist_free(&list);
97 return NULL;
98 }
99
100 cur = g_new0(aim_tlvlist_t, 1);
101 cur->tlv = createtlv(type, length, NULL);
102 if (cur->tlv->length > 0) {
103 cur->tlv->value = byte_stream_getraw(bs, length);
104 if (!cur->tlv->value) {
105 freetlv(&cur->tlv);
106 free(cur);
107 aim_tlvlist_free(&list);
108 return NULL;
109 }
110 }
111
112 cur->next = list;
113 list = cur;
114 }
115 }
116
117 return list;
118 }
119
120 /**
121 * Read a TLV chain from a buffer.
122 *
123 * Reads and parses a series of TLV patterns from a data buffer; the
124 * returned structure is manipulatable with the rest of the TLV
125 * routines. When done with a TLV chain, aim_tlvlist_free() should
126 * be called to free the dynamic substructures.
127 *
128 * XXX There should be a flag setable here to have the tlvlist contain
129 * bstream references, so that at least the ->value portion of each
130 * element doesn't need to be malloc/memcpy'd. This could prove to be
131 * just as efficient as the in-place TLV parsing used in a couple places
132 * in libfaim.
133 *
134 * @param bs Input bstream
135 * @param num The max number of TLVs that will be read, or -1 if unlimited.
136 * There are a number of places where you want to read in a tlvchain,
137 * but the chain is not at the end of the SNAC, and the chain is
138 * preceded by the number of TLVs. So you can limit that with this.
139 * @return Return the TLV chain read
140 */
141 aim_tlvlist_t *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
142 {
143 aim_tlvlist_t *list = NULL, *cur;
144
145 while ((byte_stream_empty(bs) > 0) && (num != 0)) {
146 guint16 type, length;
147
148 type = byte_stream_get16(bs);
149 length = byte_stream_get16(bs);
150
151 if (length > byte_stream_empty(bs)) {
152 aim_tlvlist_free(&list);
153 return NULL;
154 }
155
156 cur = g_new0(aim_tlvlist_t, 1);
157 cur->tlv = createtlv(type, length, NULL);
158 if (cur->tlv->length > 0) {
159 cur->tlv->value = byte_stream_getraw(bs, length);
160 if (!cur->tlv->value) {
161 freetlv(&cur->tlv);
162 free(cur);
163 aim_tlvlist_free(&list);
164 return NULL;
165 }
166 }
167
168 if (num > 0)
169 num--;
170 cur->next = list;
171 list = cur;
172 }
173
174 return list;
175 }
176
177 /**
178 * Read a TLV chain from a buffer.
179 *
180 * Reads and parses a series of TLV patterns from a data buffer; the
181 * returned structure is manipulatable with the rest of the TLV
182 * routines. When done with a TLV chain, aim_tlvlist_free() should
183 * be called to free the dynamic substructures.
184 *
185 * XXX There should be a flag setable here to have the tlvlist contain
186 * bstream references, so that at least the ->value portion of each
187 * element doesn't need to be malloc/memcpy'd. This could prove to be
188 * just as efficient as the in-place TLV parsing used in a couple places
189 * in libfaim.
190 *
191 * @param bs Input bstream
192 * @param len The max length in bytes that will be read.
193 * There are a number of places where you want to read in a tlvchain,
194 * but the chain is not at the end of the SNAC, and the chain is
195 * preceded by the length of the TLVs. So you can limit that with this.
196 * @return Return the TLV chain read
197 */
198 aim_tlvlist_t *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
199 {
200 aim_tlvlist_t *list = NULL, *cur;
201
202 while ((byte_stream_empty(bs) > 0) && (len > 0)) {
203 guint16 type, length;
204
205 type = byte_stream_get16(bs);
206 length = byte_stream_get16(bs);
207
208 if (length > byte_stream_empty(bs)) {
209 aim_tlvlist_free(&list);
210 return NULL;
211 }
212
213 cur = g_new0(aim_tlvlist_t, 1);
214 cur->tlv = createtlv(type, length, NULL);
215 if (cur->tlv->length > 0) {
216 cur->tlv->value = byte_stream_getraw(bs, length);
217 if (!cur->tlv->value) {
218 freetlv(&cur->tlv);
219 free(cur);
220 aim_tlvlist_free(&list);
221 return NULL;
222 }
223 }
224
225 len -= aim_tlvlist_size(&cur);
226 cur->next = list;
227 list = cur;
228 }
229
230 return list;
231 }
232
233 /**
234 * Duplicate a TLV chain.
235 * This is pretty self explanatory.
236 *
237 * @param orig The TLV chain you want to make a copy of.
238 * @return A newly allocated TLV chain.
239 */
240 aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig)
241 {
242 aim_tlvlist_t *new = NULL;
243
244 while (orig) {
245 aim_tlvlist_add_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value);
246 orig = orig->next;
247 }
248
249 return new;
250 }
251
252 /*
253 * Compare two TLV lists for equality. This probably is not the most
254 * efficient way to do this.
255 *
256 * @param one One of the TLV chains to compare.
257 * @param two The other TLV chain to compare.
258 * @return Return 0 if the lists are the same, return 1 if they are different.
259 */
260 int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two)
261 {
262 ByteStream bs1, bs2;
263
264 if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two))
265 return 1;
266
267 byte_stream_new(&bs1, aim_tlvlist_size(&one));
268 byte_stream_new(&bs2, aim_tlvlist_size(&two));
269
270 aim_tlvlist_write(&bs1, &one);
271 aim_tlvlist_write(&bs2, &two);
272
273 if (memcmp(bs1.data, bs2.data, bs1.len)) {
274 free(bs1.data);
275 free(bs2.data);
276 return 1;
277 }
278
279 g_free(bs1.data);
280 g_free(bs2.data);
281
282 return 0;
283 }
284
285 /**
286 * Free a TLV chain structure
287 *
288 * Walks the list of TLVs in the passed TLV chain and
289 * frees each one. Note that any references to this data
290 * should be removed before calling this.
291 *
292 * @param list Chain to be freed
293 */
294 void aim_tlvlist_free(aim_tlvlist_t **list)
295 {
296 aim_tlvlist_t *cur;
297
298 if (!list || !*list)
299 return;
300
301 for (cur = *list; cur; ) {
302 aim_tlvlist_t *tmp;
303
304 freetlv(&cur->tlv);
305
306 tmp = cur->next;
307 free(cur);
308 cur = tmp;
309 }
310
311 list = NULL;
312
313 return;
314 }
315
316 /**
317 * Count the number of TLVs in a chain.
318 *
319 * @param list Chain to be counted.
320 * @return The number of TLVs stored in the passed chain.
321 */
322 int aim_tlvlist_count(aim_tlvlist_t **list)
323 {
324 aim_tlvlist_t *cur;
325 int count;
326
327 if (!list || !*list)
328 return 0;
329
330 for (cur = *list, count = 0; cur; cur = cur->next)
331 count++;
332
333 return count;
334 }
335
336 /**
337 * Count the number of bytes in a TLV chain.
338 *
339 * @param list Chain to be sized
340 * @return The number of bytes that would be needed to
341 * write the passed TLV chain to a data buffer.
342 */
343 int aim_tlvlist_size(aim_tlvlist_t **list)
344 {
345 aim_tlvlist_t *cur;
346 int size;
347
348 if (!list || !*list)
349 return 0;
350
351 for (cur = *list, size = 0; cur; cur = cur->next)
352 size += (4 + cur->tlv->length);
353
354 return size;
355 }
356
357 /**
358 * Adds the passed string as a TLV element of the passed type
359 * to the TLV chain.
360 *
361 * @param list Desination chain (%NULL pointer if empty).
362 * @param type TLV type.
363 * @param length Length of string to add (not including %NULL).
364 * @param value String to add.
365 * @return The size of the value added.
366 */
367 int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
368 {
369 aim_tlvlist_t *newtlv, *cur;
370
371 if (list == NULL)
372 return 0;
373
374 newtlv = g_new0(aim_tlvlist_t, 1);
375 newtlv->tlv = createtlv(type, length, NULL);
376 if (newtlv->tlv->length > 0)
377 newtlv->tlv->value = g_memdup(value, length);
378
379 if (!*list)
380 *list = newtlv;
381 else {
382 for(cur = *list; cur->next; cur = cur->next)
383 ;
384 cur->next = newtlv;
385 }
386
387 return newtlv->tlv->length;
388 }
389
390 /**
391 * Add a one byte integer to a TLV chain.
392 *
393 * @param list Destination chain.
394 * @param type TLV type to add.
395 * @param value Value to add.
396 * @return The size of the value added.
397 */
398 int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
399 {
400 guint8 v8[1];
401
402 aimutil_put8(v8, value);
403
404 return aim_tlvlist_add_raw(list, type, 1, v8);
405 }
406
407 /**
408 * Add a two byte integer to a TLV chain.
409 *
410 * @param list Destination chain.
411 * @param type TLV type to add.
412 * @param value Value to add.
413 * @return The size of the value added.
414 */
415 int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value)
416 {
417 guint8 v16[2];
418
419 aimutil_put16(v16, value);
420
421 return aim_tlvlist_add_raw(list, type, 2, v16);
422 }
423
424 /**
425 * Add a four byte integer to a TLV chain.
426 *
427 * @param list Destination chain.
428 * @param type TLV type to add.
429 * @param value Value to add.
430 * @return The size of the value added.
431 */
432 int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
433 {
434 guint8 v32[4];
435
436 aimutil_put32(v32, value);
437
438 return aim_tlvlist_add_raw(list, type, 4, v32);
439 }
440
441 /**
442 * Add a string to a TLV chain.
443 *
444 * @param list Destination chain.
445 * @param type TLV type to add.
446 * @param value Value to add.
447 * @return The size of the value added.
448 */
449 int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value)
450 {
451 return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
452 }
453
454 /**
455 * Adds a block of capability blocks to a TLV chain. The bitfield
456 * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
457 *
458 * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons
459 * %OSCAR_CAPABILITY_TALK Supports Voice Chat
460 * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage
461 * %OSCAR_CAPABILITY_CHAT Supports Chat
462 * %OSCAR_CAPABILITY_GETFILE Supports Get File functions
463 * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions
464 *
465 * @param list Destination chain
466 * @param type TLV type to add
467 * @param caps Bitfield of capability flags to send
468 * @return The size of the value added.
469 */
470 int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps)
471 {
472 guint8 buf[16*16]; /* XXX icky fixed length buffer */
473 ByteStream bs;
474
475 if (!caps)
476 return 0; /* nothing there anyway */
477
478 byte_stream_init(&bs, buf, sizeof(buf));
479
480 byte_stream_putcaps(&bs, caps);
481
482 return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
483 }
484
485 /**
486 * Adds the given userinfo struct to a TLV chain.
487 *
488 * @param list Destination chain.
489 * @param type TLV type to add.
490 * @return The size of the value added.
491 */
492 int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo)
493 {
494 guint8 buf[1024]; /* bleh */
495 ByteStream bs;
496
497 byte_stream_init(&bs, buf, sizeof(buf));
498
499 aim_putuserinfo(&bs, userinfo);
500
501 return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
502 }
503
504 /**
505 * Adds the given chatroom info to a TLV chain.
506 *
507 * @param list Destination chain.
508 * @param type TLV type to add.
509 * @param roomname The name of the chat.
510 * @param instance The instance.
511 * @return The size of the value added.
512 */
513 int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
514 {
515 int len;
516 ByteStream bs;
517
518 byte_stream_new(&bs, 2 + 1 + strlen(roomname) + 2);
519
520 byte_stream_put16(&bs, exchange);
521 byte_stream_put8(&bs, strlen(roomname));
522 byte_stream_putstr(&bs, roomname);
523 byte_stream_put16(&bs, instance);
524
525 len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
526
527 g_free(bs.data);
528
529 return len;
530 }
531
532 /**
533 * Adds a TLV with a zero length to a TLV chain.
534 *
535 * @param list Destination chain.
536 * @param type TLV type to add.
537 * @return The size of the value added.
538 */
539 int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type)
540 {
541 return aim_tlvlist_add_raw(list, type, 0, NULL);
542 }
543
544 /*
545 * Note that the inner TLV chain will not be modifiable as a tlvchain once
546 * it is written using this. Or rather, it can be, but updates won't be
547 * made to this.
548 *
549 * XXX should probably support sublists for real.
550 *
551 * This is so neat.
552 *
553 * @param list Destination chain.
554 * @param type TLV type to add.
555 * @param t1 The TLV chain you want to write.
556 * @return The number of bytes written to the destination TLV chain.
557 * 0 is returned if there was an error or if the destination
558 * TLV chain has length 0.
559 */
560 int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
561 {
562 int buflen;
563 ByteStream bs;
564
565 buflen = aim_tlvlist_size(tl);
566
567 if (buflen <= 0)
568 return 0;
569
570 byte_stream_new(&bs, buflen);
571
572 aim_tlvlist_write(&bs, tl);
573
574 aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
575
576 g_free(bs.data);
577
578 return buflen;
579 }
580
581 /**
582 * Substitute a TLV of a given type with a new TLV of the same type. If
583 * you attempt to replace a TLV that does not exist, this function will
584 * just add a new TLV as if you called aim_tlvlist_add_raw().
585 *
586 * @param list Desination chain (%NULL pointer if empty).
587 * @param type TLV type.
588 * @param length Length of string to add (not including %NULL).
589 * @param value String to add.
590 * @return The length of the TLV.
591 */
592 int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value)
593 {
594 aim_tlvlist_t *cur;
595
596 if (list == NULL)
597 return 0;
598
599 for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next);
600 if (cur == NULL)
601 return aim_tlvlist_add_raw(list, type, length, value);
602
603 free(cur->tlv->value);
604 cur->tlv->length = length;
605 if (cur->tlv->length > 0) {
606 cur->tlv->value = g_memdup(value, length);
607 } else
608 cur->tlv->value = NULL;
609
610 return cur->tlv->length;
611 }
612
613 /**
614 * Substitute a TLV of a given type with a new TLV of the same type. If
615 * you attempt to replace a TLV that does not exist, this function will
616 * just add a new TLV as if you called aim_tlvlist_add_str().
617 *
618 * @param list Desination chain (%NULL pointer if empty).
619 * @param type TLV type.
620 * @param str String to add.
621 * @return The length of the TLV.
622 */
623 int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str)
624 {
625 return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
626 }
627
628 /**
629 * Substitute a TLV of a given type with a new TLV of the same type. If
630 * you attempt to replace a TLV that does not exist, this function will
631 * just add a new TLV as if you called aim_tlvlist_add_raw().
632 *
633 * @param list Desination chain (%NULL pointer if empty).
634 * @param type TLV type.
635 * @return The length of the TLV.
636 */
637 int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type)
638 {
639 return aim_tlvlist_replace_raw(list, type, 0, NULL);
640 }
641
642 /**
643 * Substitute a TLV of a given type with a new TLV of the same type. If
644 * you attempt to replace a TLV that does not exist, this function will
645 * just add a new TLV as if you called aim_tlvlist_add_raw().
646 *
647 * @param list Desination chain (%NULL pointer if empty).
648 * @param type TLV type.
649 * @param value 8 bit value to add.
650 * @return The length of the TLV.
651 */
652 int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value)
653 {
654 guint8 v8[1];
655
656 aimutil_put8(v8, value);
657
658 return aim_tlvlist_replace_raw(list, type, 1, v8);
659 }
660
661 /**
662 * Substitute a TLV of a given type with a new TLV of the same type. If
663 * you attempt to replace a TLV that does not exist, this function will
664 * just add a new TLV as if you called aim_tlvlist_add_raw().
665 *
666 * @param list Desination chain (%NULL pointer if empty).
667 * @param type TLV type.
668 * @param value 32 bit value to add.
669 * @return The length of the TLV.
670 */
671 int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value)
672 {
673 guint8 v32[4];
674
675 aimutil_put32(v32, value);
676
677 return aim_tlvlist_replace_raw(list, type, 4, v32);
678 }
679
680 /**
681 * Remove a TLV of a given type. If you attempt to remove a TLV that
682 * does not exist, nothing happens.
683 *
684 * @param list Desination chain (%NULL pointer if empty).
685 * @param type TLV type.
686 */
687 void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type)
688 {
689 aim_tlvlist_t *del;
690
691 if (!list || !(*list))
692 return;
693
694 /* Remove the item from the list */
695 if ((*list)->tlv->type == type) {
696 del = *list;
697 *list = (*list)->next;
698 } else {
699 aim_tlvlist_t *cur;
700 for (cur=*list; (cur->next && (cur->next->tlv->type!=type)); cur=cur->next);
701 if (!cur->next)
702 return;
703 del = cur->next;
704 cur->next = del->next;
705 }
706
707 /* Free the removed item */
708 free(del->tlv->value);
709 free(del->tlv);
710 free(del);
711 }
712
713 /**
714 * Write a TLV chain into a data buffer.
715 *
716 * Copies a TLV chain into a raw data buffer, writing only the number
717 * of bytes specified. This operation does not free the chain;
718 * aim_tlvlist_free() must still be called to free up the memory used
719 * by the chain structures.
720 *
721 * XXX clean this up, make better use of bstreams
722 *
723 * @param bs Input bstream
724 * @param list Source TLV chain
725 * @return Return 0 if the destination bstream is too small.
726 */
727 int aim_tlvlist_write(ByteStream *bs, aim_tlvlist_t **list)
728 {
729 int goodbuflen;
730 aim_tlvlist_t *cur;
731
732 /* do an initial run to test total length */
733 goodbuflen = aim_tlvlist_size(list);
734
735 if (goodbuflen > byte_stream_empty(bs))
736 return 0; /* not enough buffer */
737
738 /* do the real write-out */
739 for (cur = *list; cur; cur = cur->next) {
740 byte_stream_put16(bs, cur->tlv->type);
741 byte_stream_put16(bs, cur->tlv->length);
742 if (cur->tlv->length)
743 byte_stream_putraw(bs, cur->tlv->value, cur->tlv->length);
744 }
745
746 return 1; /* XXX this is a nonsensical return */
747 }
748
749
750 /**
751 * Grab the Nth TLV of type type in the TLV list list.
752 *
753 * Returns a pointer to an aim_tlv_t of the specified type;
754 * %NULL on error. The @nth parameter is specified starting at %1.
755 * In most cases, there will be no more than one TLV of any type
756 * in a chain.
757 *
758 * @param list Source chain.
759 * @param type Requested TLV type.
760 * @param nth Index of TLV of type to get.
761 * @return The TLV you were looking for, or NULL if one could not be found.
762 */
763 aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth)
764 {
765 aim_tlvlist_t *cur;
766 int i;
767
768 for (cur = list, i = 0; cur; cur = cur->next) {
769 if (cur && cur->tlv) {
770 if (cur->tlv->type == type)
771 i++;
772 if (i >= nth)
773 return cur->tlv;
774 }
775 }
776
777 return NULL;
778 }
779
780 /**
781 * Get the length of the data of the nth TLV in the given TLV chain.
782 *
783 * @param list Source chain.
784 * @param type Requested TLV type.
785 * @param nth Index of TLV of type to get.
786 * @return The length of the data in this TLV, or -1 if the TLV could not be
787 * found. Unless -1 is returned, this value will be 2 bytes.
788 */
789 int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth)
790 {
791 aim_tlvlist_t *cur;
792 int i;
793
794 for (cur = list, i = 0; cur; cur = cur->next) {
795 if (cur && cur->tlv) {
796 if (cur->tlv->type == type)
797 i++;
798 if (i >= nth)
799 return cur->tlv->length;
800 }
801 }
802
803 return -1;
804 }
805
806 char *
807 aim_tlv_getvalue_as_string(aim_tlv_t *tlv)
808 {
809 char *ret;
810
811 ret = malloc(tlv->length + 1);
812 memcpy(ret, tlv->value, tlv->length);
813 ret[tlv->length] = '\0';
814
815 return ret;
816 }
817
818 /**
819 * Retrieve the data from the nth TLV in the given TLV chain as a string.
820 *
821 * @param list Source TLV chain.
822 * @param type TLV type to search for.
823 * @param nth Index of TLV to return.
824 * @return The value of the TLV you were looking for, or NULL if one could
825 * not be found. This is a dynamic buffer and must be freed by the
826 * caller.
827 */
828 char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth)
829 {
830 aim_tlv_t *tlv;
831
832 if (!(tlv = aim_tlv_gettlv(list, type, nth)))
833 return NULL;
834
835 return aim_tlv_getvalue_as_string(tlv);
836 }
837
838 /**
839 * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
840 * integer.
841 *
842 * @param list Source TLV chain.
843 * @param type TLV type to search for.
844 * @param nth Index of TLV to return.
845 * @return The value the TLV you were looking for, or 0 if one could
846 * not be found.
847 */
848 guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth)
849 {
850 aim_tlv_t *tlv;
851
852 if (!(tlv = aim_tlv_gettlv(list, type, nth)))
853 return 0; /* erm */
854 return aimutil_get8(tlv->value);
855 }
856
857 /**
858 * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
859 * integer.
860 *
861 * @param list Source TLV chain.
862 * @param type TLV type to search for.
863 * @param nth Index of TLV to return.
864 * @return The value the TLV you were looking for, or 0 if one could
865 * not be found.
866 */
867 guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth)
868 {
869 aim_tlv_t *tlv;
870
871 if (!(tlv = aim_tlv_gettlv(list, type, nth)))
872 return 0; /* erm */
873 return aimutil_get16(tlv->value);
874 }
875
876 /**
877 * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
878 * integer.
879 *
880 * @param list Source TLV chain.
881 * @param type TLV type to search for.
882 * @param nth Index of TLV to return.
883 * @return The value the TLV you were looking for, or 0 if one could
884 * not be found.
885 */
886 guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth)
887 {
888 aim_tlv_t *tlv;
889
890 if (!(tlv = aim_tlv_gettlv(list, type, nth)))
891 return 0; /* erm */
892 return aimutil_get32(tlv->value);
893 }