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
24 createtlv(guint16 type
, guint16 length
, guint8
*value
)
28 ret
= g_new(aim_tlv_t
, 1);
37 freetlv(aim_tlv_t
*oldtlv
)
39 g_free(oldtlv
->value
);
44 aim_tlv_read(GSList
*list
, ByteStream
*bs
)
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
);
57 tlv
= createtlv(type
, length
, NULL
);
58 if (tlv
->length
> 0) {
59 tlv
->value
= byte_stream_getraw(bs
, length
);
62 aim_tlvlist_free(list
);
67 return g_slist_prepend(list
, tlv
);
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
84 * @param bs Input bstream
85 * @return Return the TLV chain read
87 GSList
*aim_tlvlist_read(ByteStream
*bs
)
91 while (byte_stream_bytes_left(bs
) > 0) {
92 list
= aim_tlv_read(list
, bs
);
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
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
)
125 while ((byte_stream_bytes_left(bs
) > 0) && (num
!= 0)) {
126 list
= aim_tlv_read(list
, bs
);
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
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
)
160 while ((byte_stream_bytes_left(bs
) > 0) && (len
> 0)) {
161 list
= aim_tlv_read(list
, bs
);
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
)
183 while (orig
!= NULL
) {
185 aim_tlvlist_add_raw(&new, tlv
->type
, tlv
->length
, tlv
->value
);
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
)
204 if (aim_tlvlist_size(one
) != aim_tlvlist_size(two
))
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
);
219 byte_stream_destroy(&bs1
);
220 byte_stream_destroy(&bs2
);
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
)
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
)
257 for (cur
= list
, count
= 0; cur
; cur
= cur
->next
)
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
)
278 for (cur
= list
, size
= 0; cur
; cur
= cur
->next
)
279 size
+= (4 + ((aim_tlv_t
*)cur
->data
)->length
);
285 * Adds the passed string as a TLV element of the passed type
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
)
301 tlv
= createtlv(type
, length
, NULL
);
303 tlv
->value
= g_memdup(value
, length
);
305 *list
= g_slist_append(*list
, tlv
);
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
)
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
)
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
)
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
);
375 count_caps(guint64 caps
)
379 set_bits
+= caps
& 1;
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
)
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 */
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
);
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
)
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
);
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
473 * TODO: Should probably support sublists for real.
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
)
489 buflen
= aim_tlvlist_size(*tlvlist
);
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
);
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
)
524 for (cur
= *list
; cur
!= NULL
; cur
= cur
->next
)
527 if (tlv
->type
== type
)
532 /* TLV does not exist, so add a new one */
533 return aim_tlvlist_add_raw(list
, type
, length
, value
);
536 tlv
->length
= length
;
537 if (tlv
->length
> 0) {
538 tlv
->value
= g_memdup(value
, 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
)
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
)
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
)
624 if (list
== NULL
|| *list
== NULL
)
633 if (tlv
->type
== type
)
635 /* Delete this TLV */
636 *list
= g_slist_delete_link(*list
, cur
);
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
)
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
) {
674 byte_stream_put16(bs
, tlv
->type
);
675 byte_stream_put16(bs
, tlv
->length
);
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
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
)
703 for (cur
= list
, i
= 0; cur
!= NULL
; cur
= cur
->next
) {
705 if (tlv
->type
== type
)
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
)
727 tlv
= aim_tlv_gettlv(list
, type
, nth
);
735 aim_tlv_getvalue_as_string(aim_tlv_t
*tlv
)
739 ret
= g_malloc(tlv
->length
+ 1);
740 memcpy(ret
, tlv
->value
, tlv
->length
);
741 ret
[tlv
->length
] = '\0';
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
756 char *aim_tlv_getstr(GSList
*list
, const guint16 type
, const int nth
)
760 tlv
= aim_tlv_gettlv(list
, type
, nth
);
764 return aim_tlv_getvalue_as_string(tlv
);
768 * Retrieve the data from the nth TLV in the given TLV chain as an 8bit
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
777 guint8
aim_tlv_get8(GSList
*list
, const guint16 type
, const int nth
)
781 tlv
= aim_tlv_gettlv(list
, type
, nth
);
785 return aimutil_get8(tlv
->value
);
789 * Retrieve the data from the nth TLV in the given TLV chain as a 16bit
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
798 guint16
aim_tlv_get16(GSList
*list
, const guint16 type
, const int nth
)
802 tlv
= aim_tlv_gettlv(list
, type
, nth
);
806 return aimutil_get16(tlv
->value
);
810 * Retrieve the data from the nth TLV in the given TLV chain as a 32bit
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
819 guint32
aim_tlv_get32(GSList
*list
, const guint16 type
, const int nth
)
823 tlv
= aim_tlv_gettlv(list
, type
, nth
);
827 return aimutil_get32(tlv
->value
);