Mercurial > pidgin.yaz
diff libpurple/protocols/oscar/tlv.c @ 17445:f80f7e1047be
Cleanup and simplification of some tlvlist stuff in the oscar protocol.
No functionality change.
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 29 May 2007 09:51:51 +0000 |
parents | 1927f4ead3ca |
children | e333072d8680 |
line wrap: on
line diff
--- a/libpurple/protocols/oscar/tlv.c Tue May 29 05:40:45 2007 +0000 +++ b/libpurple/protocols/oscar/tlv.c Tue May 29 09:51:51 2007 +0000 @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "oscar.h" static aim_tlv_t * @@ -35,17 +34,57 @@ } static void -freetlv(aim_tlv_t **oldtlv) +freetlv(aim_tlv_t *oldtlv) { + g_free(oldtlv->value); + g_free(oldtlv); +} - if (!oldtlv || !*oldtlv) - return; +static GSList * +aim_tlv_read(GSList *list, ByteStream *bs) +{ + guint16 type, length; + aim_tlv_t *tlv; + + type = byte_stream_get16(bs); + length = byte_stream_get16(bs); - g_free((*oldtlv)->value); - g_free(*oldtlv); - *oldtlv = NULL; +#if 0 + /* + * This code hasn't been needed in years. It's been commented + * out since 2003, at the latest. It seems likely that it was + * just a bug in their server code that has since been fixed. + * In any case, here's the orignal comment, kept for historical + * purposes: + * + * Okay, so now AOL has decided that any TLV of + * type 0x0013 can only be two bytes, despite + * what the actual given length is. So here + * we dump any invalid TLVs of that sort. Hopefully + * there's no special cases to this special case. + * - mid (30jun2000) + */ + if ((type == 0x0013) && (length != 0x0002)) { + length = 0x0002; + return list; + } +#endif + if (length > byte_stream_empty(bs)) { + aim_tlvlist_free(list); + return NULL; + } - return; + tlv = createtlv(type, length, NULL); + if (tlv->length > 0) { + tlv->value = byte_stream_getraw(bs, length); + if (!tlv->value) { + freetlv(tlv); + aim_tlvlist_free(list); + return NULL; + } + } + + return g_slist_prepend(list, tlv); } /** @@ -56,7 +95,7 @@ * routines. When done with a TLV chain, aim_tlvlist_free() should * be called to free the dynamic substructures. * - * XXX There should be a flag setable here to have the tlvlist contain + * TODO: There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as efficient as the in-place TLV parsing used in a couple places @@ -65,56 +104,17 @@ * @param bs Input bstream * @return Return the TLV chain read */ -aim_tlvlist_t *aim_tlvlist_read(ByteStream *bs) +GSList *aim_tlvlist_read(ByteStream *bs) { - aim_tlvlist_t *list = NULL, *cur; + GSList *list = NULL; while (byte_stream_empty(bs) > 0) { - guint16 type, length; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - -#if 0 /* temporarily disabled until I know if they're still doing it or not */ - /* - * Okay, so now AOL has decided that any TLV of - * type 0x0013 can only be two bytes, despite - * what the actual given length is. So here - * we dump any invalid TLVs of that sort. Hopefully - * there's no special cases to this special case. - * - mid (30jun2000) - */ - if ((type == 0x0013) && (length != 0x0002)) - length = 0x0002; -#else - if (0) - ; -#endif - else { - - if (length > byte_stream_empty(bs)) { - aim_tlvlist_free(&list); - return NULL; - } - - cur = g_new0(aim_tlvlist_t, 1); - cur->tlv = createtlv(type, length, NULL); - if (cur->tlv->length > 0) { - cur->tlv->value = byte_stream_getraw(bs, length); - if (!cur->tlv->value) { - freetlv(&cur->tlv); - g_free(cur); - aim_tlvlist_free(&list); - return NULL; - } - } - - cur->next = list; - list = cur; - } + list = aim_tlv_read(list, bs); + if (list == NULL) + return NULL; } - return list; + return g_slist_reverse(list); } /** @@ -125,7 +125,7 @@ * routines. When done with a TLV chain, aim_tlvlist_free() should * be called to free the dynamic substructures. * - * XXX There should be a flag setable here to have the tlvlist contain + * TODO: There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as efficient as the in-place TLV parsing used in a couple places @@ -138,40 +138,18 @@ * preceded by the number of TLVs. So you can limit that with this. * @return Return the TLV chain read */ -aim_tlvlist_t *aim_tlvlist_readnum(ByteStream *bs, guint16 num) +GSList *aim_tlvlist_readnum(ByteStream *bs, guint16 num) { - aim_tlvlist_t *list = NULL, *cur; + GSList *list = NULL; while ((byte_stream_empty(bs) > 0) && (num != 0)) { - guint16 type, length; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - - if (length > byte_stream_empty(bs)) { - aim_tlvlist_free(&list); + list = aim_tlv_read(list, bs); + if (list == NULL) return NULL; - } - - cur = g_new0(aim_tlvlist_t, 1); - cur->tlv = createtlv(type, length, NULL); - if (cur->tlv->length > 0) { - cur->tlv->value = byte_stream_getraw(bs, length); - if (!cur->tlv->value) { - freetlv(&cur->tlv); - g_free(cur); - aim_tlvlist_free(&list); - return NULL; - } - } - - if (num > 0) - num--; - cur->next = list; - list = cur; + num--; } - return list; + return g_slist_reverse(list); } /** @@ -182,7 +160,7 @@ * routines. When done with a TLV chain, aim_tlvlist_free() should * be called to free the dynamic substructures. * - * XXX There should be a flag setable here to have the tlvlist contain + * TODO: There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as efficient as the in-place TLV parsing used in a couple places @@ -195,39 +173,19 @@ * preceded by the length of the TLVs. So you can limit that with this. * @return Return the TLV chain read */ -aim_tlvlist_t *aim_tlvlist_readlen(ByteStream *bs, guint16 len) +GSList *aim_tlvlist_readlen(ByteStream *bs, guint16 len) { - aim_tlvlist_t *list = NULL, *cur; + GSList *list = NULL; while ((byte_stream_empty(bs) > 0) && (len > 0)) { - guint16 type, length; - - type = byte_stream_get16(bs); - length = byte_stream_get16(bs); - - if (length > byte_stream_empty(bs)) { - aim_tlvlist_free(&list); + list = aim_tlv_read(list, bs); + if (list == NULL) return NULL; - } - cur = g_new0(aim_tlvlist_t, 1); - cur->tlv = createtlv(type, length, NULL); - if (cur->tlv->length > 0) { - cur->tlv->value = byte_stream_getraw(bs, length); - if (!cur->tlv->value) { - freetlv(&cur->tlv); - g_free(cur); - aim_tlvlist_free(&list); - return NULL; - } - } - - len -= aim_tlvlist_size(&cur); - cur->next = list; - list = cur; + len -= 2 + 2 + ((aim_tlv_t *)list->data)->length; } - return list; + return g_slist_reverse(list); } /** @@ -237,12 +195,14 @@ * @param orig The TLV chain you want to make a copy of. * @return A newly allocated TLV chain. */ -aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig) +GSList *aim_tlvlist_copy(GSList *orig) { - aim_tlvlist_t *new = NULL; + GSList *new = NULL; + aim_tlv_t *tlv; - while (orig) { - aim_tlvlist_add_raw(&new, orig->tlv->type, orig->tlv->length, orig->tlv->value); + while (orig != NULL) { + tlv = orig->data; + aim_tlvlist_add_raw(&new, tlv->type, tlv->length, tlv->value); orig = orig->next; } @@ -257,15 +217,15 @@ * @param two The other TLV chain to compare. * @return Return 0 if the lists are the same, return 1 if they are different. */ -int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two) +int aim_tlvlist_cmp(GSList *one, GSList *two) { ByteStream bs1, bs2; - if (aim_tlvlist_size(&one) != aim_tlvlist_size(&two)) + if (aim_tlvlist_size(one) != aim_tlvlist_size(two)) return 1; - byte_stream_new(&bs1, aim_tlvlist_size(&one)); - byte_stream_new(&bs2, aim_tlvlist_size(&two)); + byte_stream_new(&bs1, aim_tlvlist_size(one)); + byte_stream_new(&bs2, aim_tlvlist_size(two)); aim_tlvlist_write(&bs1, &one); aim_tlvlist_write(&bs2, &two); @@ -291,26 +251,13 @@ * * @param list Chain to be freed */ -void aim_tlvlist_free(aim_tlvlist_t **list) +void aim_tlvlist_free(GSList *list) { - aim_tlvlist_t *cur; - - if (!list || !*list) - return; - - for (cur = *list; cur; ) { - aim_tlvlist_t *tmp; - - freetlv(&cur->tlv); - - tmp = cur->next; - g_free(cur); - cur = tmp; + while (list != NULL) + { + freetlv(list->data); + list = g_slist_delete_link(list, list); } - - list = NULL; - - return; } /** @@ -319,15 +266,15 @@ * @param list Chain to be counted. * @return The number of TLVs stored in the passed chain. */ -int aim_tlvlist_count(aim_tlvlist_t **list) +int aim_tlvlist_count(GSList *list) { - aim_tlvlist_t *cur; + GSList *cur; int count; - if (!list || !*list) + if (list == NULL) return 0; - for (cur = *list, count = 0; cur; cur = cur->next) + for (cur = list, count = 0; cur; cur = cur->next) count++; return count; @@ -340,16 +287,16 @@ * @return The number of bytes that would be needed to * write the passed TLV chain to a data buffer. */ -int aim_tlvlist_size(aim_tlvlist_t **list) +int aim_tlvlist_size(GSList *list) { - aim_tlvlist_t *cur; + GSList *cur; int size; - if (!list || !*list) + if (list == NULL) return 0; - for (cur = *list, size = 0; cur; cur = cur->next) - size += (4 + cur->tlv->length); + for (cur = list, size = 0; cur; cur = cur->next) + size += (4 + ((aim_tlv_t *)cur->data)->length); return size; } @@ -364,27 +311,20 @@ * @param value String to add. * @return The size of the value added. */ -int aim_tlvlist_add_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) +int aim_tlvlist_add_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value) { - aim_tlvlist_t *newtlv, *cur; + aim_tlv_t *tlv; if (list == NULL) return 0; - newtlv = g_new0(aim_tlvlist_t, 1); - newtlv->tlv = createtlv(type, length, NULL); - if (newtlv->tlv->length > 0) - newtlv->tlv->value = g_memdup(value, length); + tlv = createtlv(type, length, NULL); + if (tlv->length > 0) + tlv->value = g_memdup(value, length); - if (!*list) - *list = newtlv; - else { - for(cur = *list; cur->next; cur = cur->next) - ; - cur->next = newtlv; - } + *list = g_slist_append(*list, tlv); - return newtlv->tlv->length; + return tlv->length; } /** @@ -395,7 +335,7 @@ * @param value Value to add. * @return The size of the value added. */ -int aim_tlvlist_add_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) +int aim_tlvlist_add_8(GSList **list, const guint16 type, const guint8 value) { guint8 v8[1]; @@ -412,7 +352,7 @@ * @param value Value to add. * @return The size of the value added. */ -int aim_tlvlist_add_16(aim_tlvlist_t **list, const guint16 type, const guint16 value) +int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value) { guint8 v16[2]; @@ -429,7 +369,7 @@ * @param value Value to add. * @return The size of the value added. */ -int aim_tlvlist_add_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) +int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value) { guint8 v32[4]; @@ -446,7 +386,7 @@ * @param value Value to add. * @return The size of the value added. */ -int aim_tlvlist_add_str(aim_tlvlist_t **list, const guint16 type, const char *value) +int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value) { return aim_tlvlist_add_raw(list, type, strlen(value), (guint8 *)value); } @@ -467,12 +407,12 @@ * @param caps Bitfield of capability flags to send * @return The size of the value added. */ -int aim_tlvlist_add_caps(aim_tlvlist_t **list, const guint16 type, const guint32 caps) +int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps) { - guint8 buf[16*16]; /* XXX icky fixed length buffer */ + guint8 buf[256]; /* TODO: Don't use a fixed length buffer */ ByteStream bs; - if (!caps) + if (caps == 0) return 0; /* nothing there anyway */ byte_stream_init(&bs, buf, sizeof(buf)); @@ -489,9 +429,9 @@ * @param type TLV type to add. * @return The size of the value added. */ -int aim_tlvlist_add_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *userinfo) +int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo) { - guint8 buf[1024]; /* bleh */ + guint8 buf[1024]; /* TODO: Don't use a fixed length buffer */ ByteStream bs; byte_stream_init(&bs, buf, sizeof(buf)); @@ -510,7 +450,7 @@ * @param instance The instance. * @return The size of the value added. */ -int aim_tlvlist_add_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) +int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) { int len; ByteStream bs; @@ -536,7 +476,7 @@ * @param type TLV type to add. * @return The size of the value added. */ -int aim_tlvlist_add_noval(aim_tlvlist_t **list, const guint16 type) +int aim_tlvlist_add_noval(GSList **list, const guint16 type) { return aim_tlvlist_add_raw(list, type, 0, NULL); } @@ -546,7 +486,7 @@ * it is written using this. Or rather, it can be, but updates won't be * made to this. * - * XXX should probably support sublists for real. + * TODO: Should probably support sublists for real. * * This is so neat. * @@ -557,19 +497,19 @@ * 0 is returned if there was an error or if the destination * TLV chain has length 0. */ -int aim_tlvlist_add_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl) +int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tlvlist) { int buflen; ByteStream bs; - buflen = aim_tlvlist_size(tl); + buflen = aim_tlvlist_size(*tlvlist); if (buflen <= 0) return 0; byte_stream_new(&bs, buflen); - aim_tlvlist_write(&bs, tl); + aim_tlvlist_write(&bs, tlvlist); aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), bs.data); @@ -589,25 +529,33 @@ * @param value String to add. * @return The length of the TLV. */ -int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const guint16 type, const guint16 length, const guint8 *value) +int aim_tlvlist_replace_raw(GSList **list, const guint16 type, const guint16 length, const guint8 *value) { - aim_tlvlist_t *cur; + GSList *cur; + aim_tlv_t *tlv; if (list == NULL) return 0; - for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next); + for (cur = *list; cur != NULL; cur = cur->next) + { + tlv = cur->data; + if (tlv->type == type) + break; + } + if (cur == NULL) + /* TLV does not exist, so add a new one */ return aim_tlvlist_add_raw(list, type, length, value); - g_free(cur->tlv->value); - cur->tlv->length = length; - if (cur->tlv->length > 0) { - cur->tlv->value = g_memdup(value, length); + g_free(tlv->value); + tlv->length = length; + if (tlv->length > 0) { + tlv->value = g_memdup(value, length); } else - cur->tlv->value = NULL; + tlv->value = NULL; - return cur->tlv->length; + return tlv->length; } /** @@ -620,7 +568,7 @@ * @param str String to add. * @return The length of the TLV. */ -int aim_tlvlist_replace_str(aim_tlvlist_t **list, const guint16 type, const char *str) +int aim_tlvlist_replace_str(GSList **list, const guint16 type, const char *str) { return aim_tlvlist_replace_raw(list, type, strlen(str), (const guchar *)str); } @@ -634,7 +582,7 @@ * @param type TLV type. * @return The length of the TLV. */ -int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const guint16 type) +int aim_tlvlist_replace_noval(GSList **list, const guint16 type) { return aim_tlvlist_replace_raw(list, type, 0, NULL); } @@ -649,7 +597,7 @@ * @param value 8 bit value to add. * @return The length of the TLV. */ -int aim_tlvlist_replace_8(aim_tlvlist_t **list, const guint16 type, const guint8 value) +int aim_tlvlist_replace_8(GSList **list, const guint16 type, const guint8 value) { guint8 v8[1]; @@ -668,7 +616,7 @@ * @param value 32 bit value to add. * @return The length of the TLV. */ -int aim_tlvlist_replace_32(aim_tlvlist_t **list, const guint16 type, const guint32 value) +int aim_tlvlist_replace_32(GSList **list, const guint16 type, const guint32 value) { guint8 v32[4]; @@ -678,36 +626,36 @@ } /** - * Remove a TLV of a given type. If you attempt to remove a TLV that - * does not exist, nothing happens. + * Remove all TLVs of a given type. If you attempt to remove a TLV + * that does not exist, nothing happens. * * @param list Desination chain (%NULL pointer if empty). * @param type TLV type. */ -void aim_tlvlist_remove(aim_tlvlist_t **list, const guint16 type) +void aim_tlvlist_remove(GSList **list, const guint16 type) { - aim_tlvlist_t *del; + GSList *cur, *next; + aim_tlv_t *tlv; - if (!list || !(*list)) + if (list == NULL || *list == NULL) return; - /* Remove the item from the list */ - if ((*list)->tlv->type == type) { - del = *list; - *list = (*list)->next; - } else { - aim_tlvlist_t *cur; - for (cur=*list; (cur->next && (cur->next->tlv->type!=type)); cur=cur->next); - if (!cur->next) - return; - del = cur->next; - cur->next = del->next; + cur = *list; + while (cur != NULL) + { + tlv = cur->data; + next = cur->next; + + if (tlv->type == type) + { + /* Delete this TLV */ + *list = g_slist_delete_link(*list, cur); + g_free(tlv->value); + g_free(tlv); + } + + cur = next; } - - /* Free the removed item */ - g_free(del->tlv->value); - g_free(del->tlv); - g_free(del); } /** @@ -718,32 +666,34 @@ * aim_tlvlist_free() must still be called to free up the memory used * by the chain structures. * - * XXX clean this up, make better use of bstreams + * TODO: Clean this up, make better use of bstreams * * @param bs Input bstream * @param list Source TLV chain * @return Return 0 if the destination bstream is too small. */ -int aim_tlvlist_write(ByteStream *bs, aim_tlvlist_t **list) +int aim_tlvlist_write(ByteStream *bs, GSList **list) { int goodbuflen; - aim_tlvlist_t *cur; + GSList *cur; + aim_tlv_t *tlv; /* do an initial run to test total length */ - goodbuflen = aim_tlvlist_size(list); + goodbuflen = aim_tlvlist_size(*list); if (goodbuflen > byte_stream_empty(bs)) return 0; /* not enough buffer */ /* do the real write-out */ for (cur = *list; cur; cur = cur->next) { - byte_stream_put16(bs, cur->tlv->type); - byte_stream_put16(bs, cur->tlv->length); - if (cur->tlv->length) - byte_stream_putraw(bs, cur->tlv->value, cur->tlv->length); + tlv = cur->data; + byte_stream_put16(bs, tlv->type); + byte_stream_put16(bs, tlv->length); + if (tlv->length > 0) + byte_stream_putraw(bs, tlv->value, tlv->length); } - return 1; /* XXX this is a nonsensical return */ + return 1; /* TODO: This is a nonsensical return */ } @@ -760,17 +710,19 @@ * @param nth Index of TLV of type to get. * @return The TLV you were looking for, or NULL if one could not be found. */ -aim_tlv_t *aim_tlv_gettlv(aim_tlvlist_t *list, const guint16 type, const int nth) +aim_tlv_t *aim_tlv_gettlv(GSList *list, const guint16 type, const int nth) { - aim_tlvlist_t *cur; + GSList *cur; + aim_tlv_t *tlv; int i; - for (cur = list, i = 0; cur; cur = cur->next) { - if (cur && cur->tlv) { - if (cur->tlv->type == type) + for (cur = list, i = 0; cur != NULL; cur = cur->next) { + tlv = cur->data; + if (tlv != NULL) { /* TODO: This NULL check shouldn't be needed */ + if (tlv->type == type) i++; if (i >= nth) - return cur->tlv; + return tlv; } } @@ -786,21 +738,15 @@ * @return The length of the data in this TLV, or -1 if the TLV could not be * found. Unless -1 is returned, this value will be 2 bytes. */ -int aim_tlv_getlength(aim_tlvlist_t *list, const guint16 type, const int nth) +int aim_tlv_getlength(GSList *list, const guint16 type, const int nth) { - aim_tlvlist_t *cur; - int i; + aim_tlv_t *tlv; - for (cur = list, i = 0; cur; cur = cur->next) { - if (cur && cur->tlv) { - if (cur->tlv->type == type) - i++; - if (i >= nth) - return cur->tlv->length; - } - } + tlv = aim_tlv_gettlv(list, type, nth); + if (tlv == NULL) + return -1; - return -1; + return tlv->length; } char * @@ -825,11 +771,12 @@ * not be found. This is a dynamic buffer and must be freed by the * caller. */ -char *aim_tlv_getstr(aim_tlvlist_t *list, const guint16 type, const int nth) +char *aim_tlv_getstr(GSList *list, const guint16 type, const int nth) { aim_tlv_t *tlv; - if (!(tlv = aim_tlv_gettlv(list, type, nth))) + tlv = aim_tlv_gettlv(list, type, nth); + if (tlv == NULL) return NULL; return aim_tlv_getvalue_as_string(tlv); @@ -845,12 +792,14 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -guint8 aim_tlv_get8(aim_tlvlist_t *list, const guint16 type, const int nth) +guint8 aim_tlv_get8(GSList *list, const guint16 type, const int nth) { aim_tlv_t *tlv; - if (!(tlv = aim_tlv_gettlv(list, type, nth))) + tlv = aim_tlv_gettlv(list, type, nth); + if (tlv == NULL) return 0; /* erm */ + return aimutil_get8(tlv->value); } @@ -864,12 +813,14 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -guint16 aim_tlv_get16(aim_tlvlist_t *list, const guint16 type, const int nth) +guint16 aim_tlv_get16(GSList *list, const guint16 type, const int nth) { aim_tlv_t *tlv; - if (!(tlv = aim_tlv_gettlv(list, type, nth))) + tlv = aim_tlv_gettlv(list, type, nth); + if (tlv == NULL) return 0; /* erm */ + return aimutil_get16(tlv->value); } @@ -883,11 +834,13 @@ * @return The value the TLV you were looking for, or 0 if one could * not be found. */ -guint32 aim_tlv_get32(aim_tlvlist_t *list, const guint16 type, const int nth) +guint32 aim_tlv_get32(GSList *list, const guint16 type, const int nth) { aim_tlv_t *tlv; - if (!(tlv = aim_tlv_gettlv(list, type, nth))) + tlv = aim_tlv_gettlv(list, type, nth); + if (tlv == NULL) return 0; /* erm */ + return aimutil_get32(tlv->value); }