Remove useless comparison
[pidgin-git.git] / libpurple / protocols / oscar / tlv.c
blobfb89c0de40bee72fd5aa636cce57ff813e1eaedb
1 /*
2 * Purple's oscar protocol plugin
3 * This file is the legal property of its developers.
4 * Please see the AUTHORS file distributed alongside this file.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include "oscar.h"
23 static aim_tlv_t *
24 createtlv(guint16 type, guint16 length, guint8 *value)
26 aim_tlv_t *ret;
28 ret = g_new(aim_tlv_t, 1);
29 ret->type = type;
30 ret->length = length;
31 ret->value = value;
33 return ret;
36 static void
37 freetlv(aim_tlv_t *oldtlv)
39 g_free(oldtlv->value);
40 g_free(oldtlv);
43 static GSList *
44 aim_tlv_read(GSList *list, ByteStream *bs)
46 guint16 type, length;
47 aim_tlv_t *tlv;
49 type = byte_stream_get16(bs);
50 length = byte_stream_get16(bs);
52 if (length > byte_stream_bytes_left(bs)) {
53 aim_tlvlist_free(list);
54 return NULL;
57 tlv = createtlv(type, length, NULL);
58 if (tlv->length > 0) {
59 tlv->value = byte_stream_getraw(bs, length);
60 if (!tlv->value) {
61 freetlv(tlv);
62 aim_tlvlist_free(list);
63 return NULL;
67 return g_slist_prepend(list, tlv);
70 /**
71 * Read a TLV chain from a buffer.
73 * Reads and parses a series of TLV patterns from a data buffer; the
74 * returned structure is manipulatable with the rest of the TLV
75 * routines. When done with a TLV chain, aim_tlvlist_free() should
76 * be called to free the dynamic substructures.
78 * TODO: There should be a flag setable here to have the tlvlist contain
79 * bstream references, so that at least the ->value portion of each
80 * element doesn't need to be malloc/memcpy'd. This could prove to be
81 * just as efficient as the in-place TLV parsing used in a couple places
82 * in libfaim.
84 * @param bs Input bstream
85 * @return Return the TLV chain read
87 GSList *aim_tlvlist_read(ByteStream *bs)
89 GSList *list = NULL;
91 while (byte_stream_bytes_left(bs) > 0) {
92 list = aim_tlv_read(list, bs);
93 if (list == NULL)
94 return NULL;
97 return g_slist_reverse(list);
101 * Read a TLV chain from a buffer.
103 * Reads and parses a series of TLV patterns from a data buffer; the
104 * returned structure is manipulatable with the rest of the TLV
105 * routines. When done with a TLV chain, aim_tlvlist_free() should
106 * be called to free the dynamic substructures.
108 * TODO: There should be a flag setable here to have the tlvlist contain
109 * bstream references, so that at least the ->value portion of each
110 * element doesn't need to be malloc/memcpy'd. This could prove to be
111 * just as efficient as the in-place TLV parsing used in a couple places
112 * in libfaim.
114 * @param bs Input bstream
115 * @param num The max number of TLVs that will be read, or -1 if unlimited.
116 * There are a number of places where you want to read in a tlvchain,
117 * but the chain is not at the end of the SNAC, and the chain is
118 * preceded by the number of TLVs. So you can limit that with this.
119 * @return Return the TLV chain read
121 GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num)
123 GSList *list = NULL;
125 while ((byte_stream_bytes_left(bs) > 0) && (num != 0)) {
126 list = aim_tlv_read(list, bs);
127 if (list == NULL)
128 return NULL;
129 num--;
132 return g_slist_reverse(list);
136 * Read a TLV chain from a buffer.
138 * Reads and parses a series of TLV patterns from a data buffer; the
139 * returned structure is manipulatable with the rest of the TLV
140 * routines. When done with a TLV chain, aim_tlvlist_free() should
141 * be called to free the dynamic substructures.
143 * TODO: There should be a flag setable here to have the tlvlist contain
144 * bstream references, so that at least the ->value portion of each
145 * element doesn't need to be malloc/memcpy'd. This could prove to be
146 * just as efficient as the in-place TLV parsing used in a couple places
147 * in libfaim.
149 * @param bs Input bstream
150 * @param len The max length in bytes that will be read.
151 * There are a number of places where you want to read in a tlvchain,
152 * but the chain is not at the end of the SNAC, and the chain is
153 * preceded by the length of the TLVs. So you can limit that with this.
154 * @return Return the TLV chain read
156 GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len)
158 GSList *list = NULL;
160 while ((byte_stream_bytes_left(bs) > 0) && (len > 0)) {
161 list = aim_tlv_read(list, bs);
162 if (list == NULL)
163 return NULL;
165 len -= 2 + 2 + ((aim_tlv_t *)list->data)->length;
168 return g_slist_reverse(list);
172 * Duplicate a TLV chain.
173 * This is pretty self explanatory.
175 * @param orig The TLV chain you want to make a copy of.
176 * @return A newly allocated TLV chain.
178 GSList *aim_tlvlist_copy(GSList *orig)
180 GSList *new = NULL;
181 aim_tlv_t *tlv;
183 while (orig != NULL) {
184 tlv = orig->data;
185 aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value);
186 orig = orig->next;
189 return new;
193 * Compare two TLV lists for equality. This probably is not the most
194 * efficient way to do this.
196 * @param one One of the TLV chains to compare.
197 * @param two The other TLV chain to compare.
198 * @return Return 0 if the lists are the same, return 1 if they are different.
200 int aim_tlvlist_cmp(GSList *one, GSList *two)
202 ByteStream bs1, bs2;
204 if (aim_tlvlist_size(one) != aim_tlvlist_size(two))
205 return 1;
207 byte_stream_new(&bs1, aim_tlvlist_size(one));
208 byte_stream_new(&bs2, aim_tlvlist_size(two));
210 aim_tlvlist_write(&bs1, &one);
211 aim_tlvlist_write(&bs2, &two);
213 if (memcmp(bs1.data, bs2.data, bs1.len)) {
214 byte_stream_destroy(&bs1);
215 byte_stream_destroy(&bs2);
216 return 1;
219 byte_stream_destroy(&bs1);
220 byte_stream_destroy(&bs2);
222 return 0;
226 * Free a TLV chain structure
228 * Walks the list of TLVs in the passed TLV chain and
229 * frees each one. Note that any references to this data
230 * should be removed before calling this.
232 * @param list Chain to be freed
234 void aim_tlvlist_free(GSList *list)
236 while (list != NULL)
238 freetlv(list->data);
239 list = g_slist_delete_link(list, list);
244 * Count the number of TLVs in a chain.
246 * @param list Chain to be counted.
247 * @return The number of TLVs stored in the passed chain.
249 int aim_tlvlist_count(GSList *list)
251 GSList *cur;
252 int count;
254 if (list == NULL)
255 return 0;
257 for (cur = list, count = 0; cur; cur = cur->next)
258 count++;
260 return count;
264 * Count the number of bytes in a TLV chain.
266 * @param list Chain to be sized
267 * @return The number of bytes that would be needed to
268 * write the passed TLV chain to a data buffer.
270 int aim_tlvlist_size(GSList *list)
272 GSList *cur;
273 int size;
275 if (list == NULL)
276 return 0;
278 for (cur = list, size = 0; cur; cur = cur->next)
279 size += (4 + ((aim_tlv_t *)cur->data)->length);
281 return size;
285 * Adds the passed string as a TLV element of the passed type
286 * to the TLV chain.
288 * @param list Desination chain (%NULL pointer if empty).
289 * @param type TLV type.
290 * @param length Length of string to add (not including %NULL).
291 * @param value String to add.
292 * @return The size of the value added.
294 int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
296 aim_tlv_t *tlv;
298 if (list == NULL)
299 return 0;
301 tlv = createtlv(type, length, NULL);
302 if (tlv->length > 0)
303 tlv->value = g_memdup(value, length);
305 *list = g_slist_append(*list, tlv);
307 return tlv->length;
311 * Add a one byte integer to a TLV chain.
313 * @param list Destination chain.
314 * @param type TLV type to add.
315 * @param value Value to add.
316 * @return The size of the value added.
318 int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value)
320 guint8 v8[1];
322 (void)aimutil_put8(v8, value);
324 return aim_tlvlist_add_raw(list, type, 1, v8);
328 * Add a two byte integer to a TLV chain.
330 * @param list Destination chain.
331 * @param type TLV type to add.
332 * @param value Value to add.
333 * @return The size of the value added.
335 int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value)
337 guint8 v16[2];
339 (void)aimutil_put16(v16, value);
341 return aim_tlvlist_add_raw(list, type, 2, v16);
345 * Add a four byte integer to a TLV chain.
347 * @param list Destination chain.
348 * @param type TLV type to add.
349 * @param value Value to add.
350 * @return The size of the value added.
352 int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value)
354 guint8 v32[4];
356 (void)aimutil_put32(v32, value);
358 return aim_tlvlist_add_raw(list, type, 4, v32);
362 * Add a string to a TLV chain.
364 * @param list Destination chain.
365 * @param type TLV type to add.
366 * @param value Value to add.
367 * @return The size of the value added.
369 int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value)
371 return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value);
374 static int
375 count_caps(guint64 caps)
377 int set_bits = 0;
378 while (caps) {
379 set_bits += caps & 1;
380 caps >>= 1;
382 return set_bits;
386 * Adds a block of capability blocks to a TLV chain. The bitfield
387 * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
389 * %OSCAR_CAPABILITY_BUDDYICON Supports Buddy Icons
390 * %OSCAR_CAPABILITY_TALK Supports Voice Chat
391 * %OSCAR_CAPABILITY_IMIMAGE Supports DirectIM/IMImage
392 * %OSCAR_CAPABILITY_CHAT Supports Chat
393 * %OSCAR_CAPABILITY_GETFILE Supports Get File functions
394 * %OSCAR_CAPABILITY_SENDFILE Supports Send File functions
396 * @param list Destination chain
397 * @param type TLV type to add
398 * @param caps Bitfield of capability flags to send
399 * @return The size of the value added.
401 int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint64 caps, const char *mood)
403 int len;
404 ByteStream bs;
405 guint32 bs_size;
406 guint8 *data;
408 if (caps == 0)
409 return 0; /* nothing there anyway */
411 data = icq_get_custom_icon_data(mood);
412 bs_size = 16*(count_caps(caps) + (data != NULL ? 1 : 0));
414 byte_stream_new(&bs, bs_size);
415 byte_stream_putcaps(&bs, caps);
417 /* adding of custom icon GUID */
418 if (data != NULL)
419 byte_stream_putraw(&bs, data, 16);
421 len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
423 byte_stream_destroy(&bs);
425 return len;
429 * Adds the given chatroom info to a TLV chain.
431 * @param list Destination chain.
432 * @param type TLV type to add.
433 * @param roomname The name of the chat.
434 * @param instance The instance.
435 * @return The size of the value added.
437 int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
439 int len;
440 ByteStream bs;
442 byte_stream_new(&bs, 2 + 1 + strlen(roomname) + 2);
444 byte_stream_put16(&bs, exchange);
445 byte_stream_put8(&bs, strlen(roomname));
446 byte_stream_putstr(&bs, roomname);
447 byte_stream_put16(&bs, instance);
449 len = aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
451 byte_stream_destroy(&bs);
453 return len;
457 * Adds a TLV with a zero length to a TLV chain.
459 * @param list Destination chain.
460 * @param type TLV type to add.
461 * @return The size of the value added.
463 int aim_tlvlist_add_noval(GSList **list, const guint16 type)
465 return aim_tlvlist_add_raw(list, type, 0, NULL);
469 * Note that the inner TLV chain will not be modifiable as a tlvchain once
470 * it is written using this. Or rather, it can be, but updates won't be
471 * made to this.
473 * TODO: Should probably support sublists for real.
475 * This is so neat.
477 * @param list Destination chain.
478 * @param type TLV type to add.
479 * @param t1 The TLV chain you want to write.
480 * @return The number of bytes written to the destination TLV chain.
481 * 0 is returned if there was an error or if the destination
482 * TLV chain has length 0.
484 int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist)
486 int buflen;
487 ByteStream bs;
489 buflen = aim_tlvlist_size(*tlvlist);
491 if (buflen <= 0)
492 return 0;
494 byte_stream_new(&bs, buflen);
496 aim_tlvlist_write(&bs, tlvlist);
498 aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data);
500 byte_stream_destroy(&bs);
502 return buflen;
506 * Substitute a TLV of a given type with a new TLV of the same type. If
507 * you attempt to replace a TLV that does not exist, this function will
508 * just add a new TLV as if you called aim_tlvlist_add_raw().
510 * @param list Desination chain (%NULL pointer if empty).
511 * @param type TLV type.
512 * @param length Length of string to add (not including %NULL).
513 * @param value String to add.
514 * @return The length of the TLV.
516 int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value)
518 GSList *cur;
519 aim_tlv_t *tlv;
521 if (list == NULL)
522 return 0;
524 for (cur = *list; cur != NULL; cur = cur->next)
526 tlv = cur->data;
527 if (tlv->type == type)
528 break;
531 if (cur == NULL)
532 /* TLV does not exist, so add a new one */
533 return aim_tlvlist_add_raw(list, type, length, value);
535 g_free(tlv->value);
536 tlv->length = length;
537 if (tlv->length > 0) {
538 tlv->value = g_memdup(value, length);
539 } else
540 tlv->value = NULL;
542 return tlv->length;
546 * Substitute a TLV of a given type with a new TLV of the same type. If
547 * you attempt to replace a TLV that does not exist, this function will
548 * just add a new TLV as if you called aim_tlvlist_add_str().
550 * @param list Desination chain (%NULL pointer if empty).
551 * @param type TLV type.
552 * @param str String to add.
553 * @return The length of the TLV.
555 int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str)
557 return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str);
561 * Substitute a TLV of a given type with a new TLV of the same type. If
562 * you attempt to replace a TLV that does not exist, this function will
563 * just add a new TLV as if you called aim_tlvlist_add_raw().
565 * @param list Desination chain (%NULL pointer if empty).
566 * @param type TLV type.
567 * @return The length of the TLV.
569 int aim_tlvlist_replace_noval(GSList **list, const guint16 type)
571 return aim_tlvlist_replace_raw(list, type, 0, NULL);
575 * Substitute a TLV of a given type with a new TLV of the same type. If
576 * you attempt to replace a TLV that does not exist, this function will
577 * just add a new TLV as if you called aim_tlvlist_add_raw().
579 * @param list Desination chain (%NULL pointer if empty).
580 * @param type TLV type.
581 * @param value 8 bit value to add.
582 * @return The length of the TLV.
584 int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value)
586 guint8 v8[1];
588 (void)aimutil_put8(v8, value);
590 return aim_tlvlist_replace_raw(list, type, 1, v8);
594 * Substitute a TLV of a given type with a new TLV of the same type. If
595 * you attempt to replace a TLV that does not exist, this function will
596 * just add a new TLV as if you called aim_tlvlist_add_raw().
598 * @param list Desination chain (%NULL pointer if empty).
599 * @param type TLV type.
600 * @param value 32 bit value to add.
601 * @return The length of the TLV.
603 int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value)
605 guint8 v32[4];
607 (void)aimutil_put32(v32, value);
609 return aim_tlvlist_replace_raw(list, type, 4, v32);
613 * Remove all TLVs of a given type. If you attempt to remove a TLV
614 * that does not exist, nothing happens.
616 * @param list Desination chain (%NULL pointer if empty).
617 * @param type TLV type.
619 void aim_tlvlist_remove(GSList **list, const guint16 type)
621 GSList *cur, *next;
622 aim_tlv_t *tlv;
624 if (list == NULL || *list == NULL)
625 return;
627 cur = *list;
628 while (cur != NULL)
630 tlv = cur->data;
631 next = cur->next;
633 if (tlv->type == type)
635 /* Delete this TLV */
636 *list = g_slist_delete_link(*list, cur);
637 g_free(tlv->value);
638 g_free(tlv);
641 cur = next;
646 * Write a TLV chain into a data buffer.
648 * Copies a TLV chain into a raw data buffer, writing only the number
649 * of bytes specified. This operation does not free the chain;
650 * aim_tlvlist_free() must still be called to free up the memory used
651 * by the chain structures.
653 * TODO: Clean this up, make better use of bstreams
655 * @param bs Input bstream
656 * @param list Source TLV chain
657 * @return Return 0 if the destination bstream is too small.
659 int aim_tlvlist_write(ByteStream *bs, GSList **list)
661 size_t goodbuflen;
662 GSList *cur;
663 aim_tlv_t *tlv;
665 /* do an initial run to test total length */
666 goodbuflen = aim_tlvlist_size(*list);
668 if (goodbuflen > byte_stream_bytes_left(bs))
669 return 0; /* not enough buffer */
671 /* do the real write-out */
672 for (cur = *list; cur; cur = cur->next) {
673 tlv = cur->data;
674 byte_stream_put16(bs, tlv->type);
675 byte_stream_put16(bs, tlv->length);
676 if (tlv->length > 0)
677 byte_stream_putraw(bs, tlv->value, tlv->length);
680 return 1; /* TODO: This is a nonsensical return */
685 * Grab the Nth TLV of type type in the TLV list list.
687 * Returns a pointer to an aim_tlv_t of the specified type;
688 * %NULL on error. The @nth parameter is specified starting at %1.
689 * In most cases, there will be no more than one TLV of any type
690 * in a chain.
692 * @param list Source chain.
693 * @param type Requested TLV type.
694 * @param nth Index of TLV of type to get.
695 * @return The TLV you were looking for, or NULL if one could not be found.
697 aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth)
699 GSList *cur;
700 aim_tlv_t *tlv;
701 int i;
703 for (cur = list, i = 0; cur != NULL; cur = cur->next) {
704 tlv = cur->data;
705 if (tlv->type == type)
706 i++;
707 if (i >= nth)
708 return tlv;
711 return NULL;
715 * Get the length of the data of the nth TLV in the given TLV chain.
717 * @param list Source chain.
718 * @param type Requested TLV type.
719 * @param nth Index of TLV of type to get.
720 * @return The length of the data in this TLV, or -1 if the TLV could not be
721 * found. Unless -1 is returned, this value will be 2 bytes.
723 int aim_tlv_getlength(GSList *list, const guint16 type, const int nth)
725 aim_tlv_t *tlv;
727 tlv = aim_tlv_gettlv(list, type, nth);
728 if (tlv == NULL)
729 return -1;
731 return tlv->length;
734 char *
735 aim_tlv_getvalue_as_string(aim_tlv_t *tlv)
737 char *ret;
739 ret = g_malloc(tlv->length + 1);
740 memcpy(ret, tlv->value, tlv->length);
741 ret[tlv->length] = '\0';
743 return ret;
747 * Retrieve the data from the nth TLV in the given TLV chain as a string.
749 * @param list Source TLV chain.
750 * @param type TLV type to search for.
751 * @param nth Index of TLV to return.
752 * @return The value of the TLV you were looking for, or NULL if one could
753 * not be found. This is a dynamic buffer and must be freed by the
754 * caller.
756 char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth)
758 aim_tlv_t *tlv;
760 tlv = aim_tlv_gettlv(list, type, nth);
761 if (tlv == NULL)
762 return NULL;
764 return aim_tlv_getvalue_as_string(tlv);
768 * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
769 * integer.
771 * @param list Source TLV chain.
772 * @param type TLV type to search for.
773 * @param nth Index of TLV to return.
774 * @return The value the TLV you were looking for, or 0 if one could
775 * not be found.
777 guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth)
779 aim_tlv_t *tlv;
781 tlv = aim_tlv_gettlv(list, type, nth);
782 if (tlv == NULL)
783 return 0; /* erm */
785 return aimutil_get8(tlv->value);
789 * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
790 * integer.
792 * @param list Source TLV chain.
793 * @param type TLV type to search for.
794 * @param nth Index of TLV to return.
795 * @return The value the TLV you were looking for, or 0 if one could
796 * not be found.
798 guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth)
800 aim_tlv_t *tlv;
802 tlv = aim_tlv_gettlv(list, type, nth);
803 if (tlv == NULL)
804 return 0; /* erm */
806 return aimutil_get16(tlv->value);
810 * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
811 * integer.
813 * @param list Source TLV chain.
814 * @param type TLV type to search for.
815 * @param nth Index of TLV to return.
816 * @return The value the TLV you were looking for, or 0 if one could
817 * not be found.
819 guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth)
821 aim_tlv_t *tlv;
823 tlv = aim_tlv_gettlv(list, type, nth);
824 if (tlv == NULL)
825 return 0; /* erm */
827 return aimutil_get32(tlv->value);