X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/9de3ca7e3fdcf62e088fe2a0c7fcdc219e1a5094..5daacaa384bc7b77255078b512634a407708fe2e:/aim_tlv.c diff --git a/aim_tlv.c b/aim_tlv.c index 536e259..b5f69a0 100644 --- a/aim_tlv.c +++ b/aim_tlv.c @@ -1,6 +1,438 @@ -#include +#include -struct aim_tlv_t *aim_grabtlv(u_char *src) +/** + * aim_readtlvchain - Read a TLV chain from a buffer. + * @buf: Input buffer + * @maxlen: Length of input buffer + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_freetlvchain() should + * be called to free the dynamic substructures. + * + */ +faim_export struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen) +{ + int pos; + struct aim_tlvlist_t *list; + struct aim_tlvlist_t *cur; + + u_short type; + u_short length; + + if (!buf) + return NULL; + + list = NULL; + + pos = 0; + + while (pos < maxlen) + { + type = aimutil_get16(buf+pos); + pos += 2; + + if (pos < maxlen) + { + length = aimutil_get16(buf+pos); + pos += 2; + + if ((pos+length) <= maxlen) + { + /* + * 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 + * theres no special cases to this special case. + * - mid (30jun2000) + */ + if ((type == 0x0013) && (length != 0x0002)) { + printf("faim: skipping TLV t(0013) with invalid length (0x%04x)\n", length); + length = 0x0002; + } else { + cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(cur, 0x00, sizeof(struct aim_tlvlist_t)); + + cur->tlv = aim_createtlv(); + cur->tlv->type = type; + cur->tlv->length = length; + if (length) { + cur->tlv->value = (unsigned char *)malloc(length); + memcpy(cur->tlv->value, buf+pos, length); + } + + cur->next = list; + list = cur; + } + pos += length; + } + } + } + + return list; +} + +/** + * aim_freetlvchain - Free a TLV chain structure + * @list: Chain to be freed + * + * Walks the list of TLVs in the passed TLV chain and + * frees each one. Note that any references to this data + * should be removed before calling this. + * + */ +faim_export void aim_freetlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur, *cur2; + + if (!list || !(*list)) + return; + + cur = *list; + while (cur) + { + aim_freetlv(&cur->tlv); + cur2 = cur->next; + free(cur); + cur = cur2; + } + list = NULL; + return; +} + +/** + * aim_counttlvchain - Count the number of TLVs in a chain + * @list: Chain to be counted + * + * Returns the number of TLVs stored in the passed chain. + * + */ +faim_export int aim_counttlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur; + int count = 0; + + if (!list || !(*list)) + return 0; + + for (cur = *list; cur; cur = cur->next) + count++; + + return count; +} + +/** + * aim_sizetlvchain - Count the number of bytes in a TLV chain + * @list: Chain to be sized + * + * Returns the number of bytes that would be needed to + * write the passed TLV chain to a data buffer. + * + */ +faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list) +{ + struct aim_tlvlist_t *cur; + int size = 0; + + if (!list || !(*list)) + return 0; + + for (cur = *list; cur; cur = cur->next) + size += (4 + cur->tlv->length); + + return size; +} + +/** + * aim_addtlvtochain_str - Add a string to a TLV chain + * @list: Desination chain (%NULL pointer if empty) + * @type: TLV type + * @str: String to add + * @len: Length of string to add (not including %NULL) + * + * Adds the passed string as a TLV element of the passed type + * to the TLV chain. + * + */ +faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len) +{ + struct aim_tlvlist_t *newtlv; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); + + newtlv->tlv = aim_createtlv(); + newtlv->tlv->type = type; + newtlv->tlv->length = len; + newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char)); + memcpy(newtlv->tlv->value, str, newtlv->tlv->length); + + newtlv->next = NULL; + + if (*list == NULL) { + *list = newtlv; + } else if ((*list)->next == NULL) { + (*list)->next = newtlv; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtlv; + } + return newtlv->tlv->length; +} + +/** + * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a two-byte unsigned integer to a TLV chain. + * + */ +faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val) +{ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + newtl->tlv->length = 2; + newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); + aimutil_put16(newtl->tlv->value, val); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return 2; +} + +/** + * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a four-byte unsigned integer to a TLV chain. + * + */ +faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val) +{ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if (!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + newtl->tlv->length = 4; + newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); + aimutil_put32(newtl->tlv->value, val); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return 4; +} + +/** + * aim_addtlvtochain_caps - Add a capability block to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @caps: Bitfield of capability flags to send + * + * Adds a block of capability blocks to a TLV chain. The bitfield + * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: + * + * %AIM_CAPS_BUDDYICON Supports Buddy Icons + * + * %AIM_CAPS_VOICE Supports Voice Chat + * + * %AIM_CAPS_IMIMAGE Supports DirectIM/IMImage + * + * %AIM_CAPS_CHAT Supports Chat + * + * %AIM_CAPS_GETFILE Supports Get File functions + * + * %AIM_CAPS_SENDFILE Supports Send File functions + * + */ +faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps) +{ + unsigned char buf[128]; /* icky fixed length buffer */ + struct aim_tlvlist_t *newtl; + struct aim_tlvlist_t *cur; + + if(!list) + return 0; + + newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); + memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); + + newtl->tlv = aim_createtlv(); + newtl->tlv->type = type; + + newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps); + newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length); + memcpy(newtl->tlv->value, buf, newtl->tlv->length); + + newtl->next = NULL; + + if (*list == NULL) { + *list = newtl; + } else if ((*list)->next == NULL) { + (*list)->next = newtl; + } else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtl; + } + return newtl->tlv->length; +} + +/** + * aim_writetlvchain - Write a TLV chain into a data buffer. + * @buf: Destination buffer + * @buflen: Maximum number of bytes that will be written to buffer + * @list: Source TLV chain + * + * Copies a TLV chain into a raw data buffer, writing only the number + * of bytes specified. This operation does not free the chain; + * aim_freetlvchain() must still be called to free up the memory used + * by the chain structures. + * + */ +faim_export int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list) +{ + int goodbuflen = 0; + int i = 0; + struct aim_tlvlist_t *cur; + + if (!list || !buf || !buflen) + return 0; + + /* do an initial run to test total length */ + for (cur = *list; cur; cur = cur->next) { + goodbuflen += 2 + 2; /* type + len */ + goodbuflen += cur->tlv->length; + } + + if (goodbuflen > buflen) + return 0; /* not enough buffer */ + + /* do the real write-out */ + for (cur = *list; cur; cur = cur->next) { + i += aimutil_put16(buf+i, cur->tlv->type); + i += aimutil_put16(buf+i, cur->tlv->length); + memcpy(buf+i, cur->tlv->value, cur->tlv->length); + i += cur->tlv->length; + } + + return i; +} + + +/** + * aim_gettlv - Grab the Nth TLV of type type in the TLV list list. + * @list: Source chain + * @type: Requested TLV type + * @nth: Index of TLV of type to get + * + * Returns a pointer to an aim_tlv_t of the specified type; + * %NULL on error. The @nth parameter is specified starting at %1. + * In most cases, there will be no more than one TLV of any type + * in a chain. + * + */ +faim_export struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, u_short type, int nth) +{ + int i; + struct aim_tlvlist_t *cur; + + i = 0; + for (cur = list; cur != NULL; cur = cur->next) + { + if (cur && cur->tlv) + { + if (cur->tlv->type == type) + i++; + if (i >= nth) + return cur->tlv; + } + } + return NULL; +} + +/** + * aim_gettlv_str - Retrieve the Nth TLV in chain as a string. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a %NULL- + * terminated string instead of an aim_tlv_t. This is a + * dynamic buffer and must be freed by the caller. + * + */ +faim_export char *aim_gettlv_str(struct aim_tlvlist_t *list, u_short type, int nth) +{ + struct aim_tlv_t *tlv; + char *newstr; + + if (!(tlv = aim_gettlv(list, type, nth))) + return NULL; + + newstr = (char *) malloc(tlv->length + 1); + memcpy(newstr, tlv->value, tlv->length); + *(newstr + tlv->length) = '\0'; + + return newstr; +} + +/** + * aim_grabtlv - Grab a single TLV from a data buffer + * @src: Source data buffer (must be at least 4 bytes long) + * + * Creates a TLV structure aim_tlv_t and returns it + * filled with values from a buffer, possibly including a + * dynamically allocated buffer for the value portion. + * + * Both the aim_tlv_t and the tlv->value pointer + * must be freed by the caller if non-%NULL. + * + */ +faim_export struct aim_tlv_t *aim_grabtlv(u_char *src) { struct aim_tlv_t *dest = NULL; @@ -20,7 +452,20 @@ struct aim_tlv_t *aim_grabtlv(u_char *src) return dest; } -struct aim_tlv_t *aim_grabtlvstr(u_char *src) +/** + * aim_grabtlvstr - Grab a single TLV from a data buffer as string + * @src: Source data buffer (must be at least 4 bytes long) + * + * Creates a TLV structure aim_tlv_t and returns it + * filled with values from a buffer, possibly including a + * dynamically allocated buffer for the value portion, which + * is %NULL-terminated as a string. + * + * Both the aim_tlv_t and the tlv->value pointer + * must be freed by the caller if non-%NULL. + * + */ +faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src) { struct aim_tlv_t *dest = NULL; @@ -41,7 +486,19 @@ struct aim_tlv_t *aim_grabtlvstr(u_char *src) return dest; } -int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv) +/** + * aim_puttlv - Write a aim_tlv_t into a data buffer + * @dest: Destination data buffer + * @newtlv: Source TLV structure + * + * Writes out the passed TLV structure into the buffer. No bounds + * checking is done on the output buffer. + * + * The passed aim_tlv_t is not freed. aim_freetlv() should + * still be called by the caller to free the structure. + * + */ +faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv) { int i=0; @@ -54,15 +511,31 @@ int aim_puttlv (u_char *dest, struct aim_tlv_t *newtlv) return i; } -struct aim_tlv_t *aim_createtlv(void) +/** + * aim_createtlv - Generate an aim_tlv_t structure. + * + * Allocates an empty TLV structure and returns a pointer + * to it; %NULL on error. + * + */ +faim_export struct aim_tlv_t *aim_createtlv(void) { - struct aim_tlv_t *newtlv = NULL; - newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t)); + struct aim_tlv_t *newtlv; + + if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t)))) + return NULL; memset(newtlv, 0, sizeof(struct aim_tlv_t)); return newtlv; } -int aim_freetlv(struct aim_tlv_t **oldtlv) +/** + * aim_freetlv - Free a aim_tlv_t structure + * @oldtlv: TLV to be destroyed + * + * Frees both the TLV structure and the value portion. + * + */ +faim_export int aim_freetlv(struct aim_tlv_t **oldtlv) { if (!oldtlv) return -1; @@ -76,7 +549,16 @@ int aim_freetlv(struct aim_tlv_t **oldtlv) return 0; } -int aim_puttlv_16(u_char *buf, u_short t, u_short v) +/** + * aim_puttlv_16 - Write a two-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a two-byte integer value portion. + * + */ +faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v) { int curbyte=0; curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); @@ -84,3 +566,46 @@ int aim_puttlv_16(u_char *buf, u_short t, u_short v) curbyte += aimutil_put16(buf+curbyte, (u_short)(v&0xffff)); return curbyte; } + +/** + * aim_puttlv_32 - Write a four-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a four-byte integer value portion. + * + */ +faim_export int aim_puttlv_32(u_char *buf, u_short t, u_long v) +{ + int curbyte=0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)0x0004); + curbyte += aimutil_put32(buf+curbyte, (u_long)(v&0xffffffff)); + return curbyte; +} + +/** + * aim_puttlv_str - Write a string TLV. + * @buf: Destination buffer + * @t: TLV type + * @l: Length of string + * @v: String to write + * + * Writes a TLV with a string value portion. (Only the first @l + * bytes of the passed string will be written, which should not + * include the terminating NULL.) + * + */ +faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, char *v) +{ + int curbyte; + + curbyte = 0; + curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); + curbyte += aimutil_put16(buf+curbyte, (u_short)(l&0xffff)); + if (v) + memcpy(buf+curbyte, (unsigned char *)v, l); + curbyte += l; + return curbyte; +}