changeset 7166:e6b01bd8d6cd

[gaim-migrate @ 7733] I fixed the notify_added function, Chip had two of the variables swapped :-) I changed the way tlvlists are done in ssi.c a little bit. You can alias ICQ buddies that you don't have authorization for yet, and when you alias a buddy you will no longer lose any Buddy Alerts that may be set. Why is this? Previously tlvlists (data associated with each server stored item) were just deleted and recreated when you set an alias, or changed your permit/deny setting. Now the lists are modified and TLVs that libfaim doesn't know about are just left in place. Why did I wait so long to do this? I'm lazy, it's not really all that fun, and I don't consider it all that important. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sun, 05 Oct 2003 22:41:29 +0000
parents a230b7bca1fb
children 6d3d8f11e765
files src/account.c src/protocols/oscar/aim.h src/protocols/oscar/locate.c src/protocols/oscar/oscar.c src/protocols/oscar/search.c src/protocols/oscar/ssi.c src/protocols/oscar/tlv.c
diffstat 7 files changed, 207 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/src/account.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/account.c	Sun Oct 05 22:41:29 2003 +0000
@@ -235,8 +235,8 @@
 }
 
 void
-gaim_account_notify_added(GaimAccount *account, const char *remote_user,
-						  const char *id, const char *alias,
+gaim_account_notify_added(GaimAccount *account, const char *id,
+						  const char *remote_user, const char *alias,
 						  const char *message)
 {
 	GaimAccountUiOps *ui_ops;
--- a/src/protocols/oscar/aim.h	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/aim.h	Sun Oct 05 22:41:29 2003 +0000
@@ -1374,24 +1374,34 @@
 faim_internal aim_tlvlist_t *aim_readtlvchain_num(aim_bstream_t *bs, fu16_t num);
 faim_internal aim_tlvlist_t *aim_readtlvchain_len(aim_bstream_t *bs, fu16_t len);
 faim_internal aim_tlvlist_t *aim_tlvlist_copy(aim_tlvlist_t *orig);
+faim_internal int aim_counttlvchain(aim_tlvlist_t **list);
+faim_internal int aim_sizetlvchain(aim_tlvlist_t **list);
 faim_internal int aim_tlvlist_cmp(aim_tlvlist_t *one, aim_tlvlist_t *two);
+faim_internal int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
 faim_internal void aim_freetlvchain(aim_tlvlist_t **list);
-faim_internal aim_tlv_t *aim_gettlv(aim_tlvlist_t *, fu16_t t, const int n);
-faim_internal char *aim_gettlv_str(aim_tlvlist_t *, const fu16_t t, const int n);
-faim_internal fu8_t aim_gettlv8(aim_tlvlist_t *list, const fu16_t type, const int num);
-faim_internal fu16_t aim_gettlv16(aim_tlvlist_t *list, const fu16_t t, const int n);
-faim_internal fu32_t aim_gettlv32(aim_tlvlist_t *list, const fu16_t t, const int n);
-faim_internal int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
-faim_internal int aim_addtlvtochain8(aim_tlvlist_t **list, const fu16_t t, const fu8_t v);
-faim_internal int aim_addtlvtochain16(aim_tlvlist_t **list, const fu16_t t, const fu16_t v);
-faim_internal int aim_addtlvtochain32(aim_tlvlist_t **list, const fu16_t type, const fu32_t v);
-faim_internal int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t t, const fu16_t l, const fu8_t *v);
-faim_internal int aim_addtlvtochain_caps(aim_tlvlist_t **list, const fu16_t t, const fu32_t caps);
+
+faim_internal aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, fu16_t type, const int nth);
+faim_internal char *aim_gettlv_str(aim_tlvlist_t *list, const fu16_t type, const int nth);
+faim_internal fu8_t aim_gettlv8(aim_tlvlist_t *list, const fu16_t type, const int nth);
+faim_internal fu16_t aim_gettlv16(aim_tlvlist_t *list, const fu16_t type, const int nth);
+faim_internal fu32_t aim_gettlv32(aim_tlvlist_t *list, const fu16_t type, const int nth);
+
+faim_internal int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value);
 faim_internal int aim_addtlvtochain_noval(aim_tlvlist_t **list, const fu16_t type);
+faim_internal int aim_addtlvtochain8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value);
+faim_internal int aim_addtlvtochain16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value);
+faim_internal int aim_addtlvtochain32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value);
+faim_internal int aim_addtlvtochain_caps(aim_tlvlist_t **list, const fu16_t type, const fu32_t caps);
 faim_internal int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, fu16_t type, aim_userinfo_t *ui);
 faim_internal int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, fu16_t type, aim_tlvlist_t **tl);
-faim_internal int aim_counttlvchain(aim_tlvlist_t **list);
-faim_internal int aim_sizetlvchain(aim_tlvlist_t **list);
+
+faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t lenth, const fu8_t *value);
+faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type);
+faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value);
+faim_internal int aim_tlvlist_replace_16(aim_tlvlist_t **list, const fu16_t type, const fu16_t value);
+faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value);
+
+faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type);
 #endif /* FAIM_INTERNAL */
 
 
--- a/src/protocols/oscar/locate.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/locate.c	Sun Oct 05 22:41:29 2003 +0000
@@ -242,7 +242,7 @@
 		return;
 
 	sess->locate.waiting_for_response = TRUE;
-	aim_locate_getinfoshort(sess, cur->sn, 0x00000007);
+	aim_locate_getinfoshort(sess, cur->sn, 0x00000003);
 }
 
 faim_internal void aim_locate_requestuserinfo(aim_session_t *sess, const char *sn) {
--- a/src/protocols/oscar/oscar.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Sun Oct 05 22:41:29 2003 +0000
@@ -4793,9 +4793,9 @@
 			case 0x0000: { /* Buddy */
 				if (curitem->name) {
 					char *gname = aim_ssi_itemlist_findparentname(sess->ssi.local, curitem->name);
-					char *gname_utf8 = gaim_utf8_try_convert(gname);
+					char *gname_utf8 = gname ? gaim_utf8_try_convert(gname) : NULL;
 					char *alias = aim_ssi_getalias(sess->ssi.local, gname, curitem->name);
-					char *alias_utf8 = gaim_utf8_try_convert(alias);
+					char *alias_utf8 = alias ? gaim_utf8_try_convert(alias) : NULL;
 					GaimBuddy *buddy = gaim_find_buddy(gc->account, curitem->name);
 					/* Should gname be freed here? -- elb */
 					/* Not with the current code, but that might be cleaner -- med */
--- a/src/protocols/oscar/search.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/search.c	Sun Oct 05 22:41:29 2003 +0000
@@ -80,7 +80,10 @@
 	tlvlist = aim_readtlvchain(bs);
 	m = aim_counttlvchain(&tlvlist);
 
-	/* XXX uhm. */
+	/* XXX uhm.
+	 * This is the only place that uses something other than 1 for the 3rd 
+	 * parameter to aim_gettlv_whatever().
+	 */
 	while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
 		buf = realloc(buf, (j+1) * (MAXSNLEN+1));
 
--- a/src/protocols/oscar/ssi.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/ssi.c	Sun Oct 05 22:41:29 2003 +0000
@@ -46,10 +46,6 @@
 	if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
 		return NULL;
 
-	/* Free the old data */
-	aim_freetlvchain(&group->data);
-	group->data = NULL;
-
 	/* Find the length for the new additional data */
 	newlen = 0;
 	if (group->gid == 0x0000) {
@@ -78,7 +74,7 @@
 				if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
 						newlen += aimutil_put16(newdata+newlen, cur->bid);
 		}
-		aim_addtlvtochain_raw(&group->data, 0x00c8, newlen, newdata);
+		aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
 
 		free(newdata);
 	}
@@ -183,10 +179,10 @@
 		struct aim_ssi_item *cur;
 		for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
 		if (cur->next)
-			cur->next = cur->next->next;
+			cur->next = del->next;
 	}
 
-	/* Free the deleted item */
+	/* Free the removed item */
 	free(del->name);
 	aim_freetlvchain(&del->data);
 	free(del);
@@ -357,12 +353,9 @@
 {
 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
 	if (cur) {
-		aim_tlvlist_t *tlvlist = cur->data;
-		if (tlvlist) {
-			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
-			if (tlv && tlv->value)
-				return aimutil_get8(tlv->value);
-		}
+		aim_tlv_t *tlv = aim_gettlv(cur->data, 0x00ca, 1);
+		if (tlv && tlv->value)
+			return aimutil_get8(tlv->value);
 	}
 	return 0;
 }
@@ -379,12 +372,9 @@
 {
 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
 	if (cur) {
-		aim_tlvlist_t *tlvlist = cur->data;
-		if (tlvlist) {
-			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
-			if (tlv && tlv->length)
-				return aimutil_get32(tlv->value);
-		}
+		aim_tlv_t *tlv = aim_gettlv(cur->data, 0x00c9, 1);
+		if (tlv && tlv->length)
+			return aimutil_get32(tlv->value);
 	}
 	return 0xFFFFFFFF;
 }
@@ -403,15 +393,12 @@
 {
 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
 	if (cur) {
-		aim_tlvlist_t *tlvlist = cur->data;
-		if (tlvlist) {
-			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x0131, 1);
-			if (tlv && tlv->length) {
-				char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
-				strncpy(alias, tlv->value, tlv->length);
-				alias[tlv->length] = 0;
-				return alias;
-			}
+		aim_tlv_t *tlv = aim_gettlv(cur->data, 0x0131, 1);
+		if (tlv && tlv->length) {
+			char *alias = (char *)malloc((tlv->length+1)*sizeof(char));
+			strncpy(alias, tlv->value, tlv->length);
+			alias[tlv->length] = 0;
+			return alias;
 		}
 	}
 	return NULL;
@@ -431,10 +418,8 @@
 {
 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, sn, AIM_SSI_TYPE_BUDDY);
 	if (cur) {
-		aim_tlvlist_t *tlvlist = cur->data;
-		if (tlvlist)
-			if (aim_gettlv(tlvlist, 0x0066, 1))
-				return 1;
+		if (aim_gettlv(cur->data, 0x0066, 1))
+			return 1;
 	}
 	return 0;
 }
@@ -896,13 +881,13 @@
  * @param sess The oscar session.
  * @param gn The group that the buddy is currently in.
  * @param sn The screen name of the buddy.
- * @param alias The new alias for the buddy.
+ * @param alias The new alias for the buddy, or NULL if you want to remove 
+ *        a buddies alias.
  * @return Return 0 if no errors, otherwise return the error number.
  */
 faim_export int aim_ssi_aliasbuddy(aim_session_t *sess, const char *gn, const char *sn, const char *alias)
 {
 	struct aim_ssi_item *tmp;
-	aim_tlvlist_t *data = NULL;
 
 	if (!sess || !gn || !sn)
 		return -EINVAL;
@@ -910,15 +895,11 @@
 	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, gn, sn, AIM_SSI_TYPE_BUDDY)))
 		return -EINVAL;
 
-	if (alias && !strlen(alias))
-		alias = NULL;
-
-	/* Need to add the x0131 TLV to the TLV chain */
-	if (alias)
-		aim_addtlvtochain_raw(&data, 0x0131, strlen(alias), alias);
-
-	aim_freetlvchain(&tmp->data);
-	tmp->data = data;
+	/* Either add or remove the 0x0131 TLV from the TLV chain */
+	if ((alias != NULL) && (strlen(alias) > 0))
+		aim_tlvlist_replace_raw(&tmp->data, 0x0131, strlen(alias), alias);
+	else
+		aim_tlvlist_remove(&tmp->data, 0x0131);
 
 	/* Sync our local list with the server list */
 	aim_ssi_sync(sess);
@@ -971,24 +952,19 @@
 faim_export int aim_ssi_setpermdeny(aim_session_t *sess, fu8_t permdeny, fu32_t vismask)
 {
 	struct aim_ssi_item *tmp;
-	aim_tlvlist_t *data = NULL;
 
 	if (!sess)
 		return -EINVAL;
 
-	/* Need to add the x00ca TLV to the TLV chain */
-	aim_addtlvtochain8(&data, 0x00ca, permdeny);
-
-	/* Need to add the x00cb TLV to the TLV chain */
-	aim_addtlvtochain32(&data, 0x00cb, vismask);
+	/* Find the PDINFO item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
 
-	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
-		aim_freetlvchain(&tmp->data);
-		tmp->data = data;
-	} else {
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, data);
-		aim_freetlvchain(&data);
-	}
+	/* Need to add the 0x00ca TLV to the TLV chain */
+	aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
+
+	/* Need to add the 0x00cb TLV to the TLV chain */
+	aim_tlvlist_replace_32(&tmp->data, 0x00cb, vismask);
 
 	/* Sync our local list with the server list */
 	aim_ssi_sync(sess);
@@ -1007,42 +983,30 @@
 faim_export int aim_ssi_seticon(aim_session_t *sess, fu8_t *iconsum, fu16_t iconsumlen)
 {
 	struct aim_ssi_item *tmp;
-	aim_tlvlist_t *data = NULL;
 	fu8_t *csumdata;
 
 	if (!sess || !iconsum || !iconsumlen)
 		return -EINVAL;
 
+	/* Find the ICONINFO item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, NULL);
+	}
+
+	/* Need to add the 0x00d5 TLV to the TLV chain */
 	if (!(csumdata = (fu8_t *)malloc((iconsumlen+2)*sizeof(fu8_t))))
 		return -ENOMEM;
 	csumdata[0] = 0x00;
 	csumdata[1] = 0x10;
 	memcpy(&csumdata[2], iconsum, iconsumlen);
-
-	/* Need to add the x00d5 TLV to the TLV chain */
-	aim_addtlvtochain_raw(&data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);
-
-	/* This TLV is added to cache the icon. */
-	aim_addtlvtochain_noval(&data, 0x0131);
+	aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(fu8_t), csumdata);
+	free(csumdata);
 
-	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
-		/* If the new tlvchain and oldtlvchain are the same, then do nothing */
-		if (!aim_tlvlist_cmp(tmp->data, data)) {
-			/* The new tlvlist is the identical to the old one */
-			aim_freetlvchain(&data);
-			free(csumdata);
-			return 0;
-		}
-		aim_freetlvchain(&tmp->data);
-		tmp->data = data;
-	} else {
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, "1", 0x0000, 0x51F4, AIM_SSI_TYPE_ICONINFO, data);
-		aim_freetlvchain(&data);
-	}
+	/* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
+	aim_tlvlist_replace_noval(&tmp->data, 0x0131);
 
 	/* Sync our local list with the server list */
 	aim_ssi_sync(sess);
-	free(csumdata);
 	return 0;
 }
 
@@ -1056,21 +1020,16 @@
  */
 faim_export int aim_ssi_setpresence(aim_session_t *sess, fu32_t presence) {
 	struct aim_ssi_item *tmp;
-	aim_tlvlist_t *data = NULL;
 
 	if (!sess)
 		return -EINVAL;
 
-	/* Need to add the x00c9 TLV to the TLV chain */
-	aim_addtlvtochain32(&data, 0x00c9, presence);
+	/* Find the PRESENCEPREFS item, or add it if it does not exist */
+	if (!(tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
+		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
 
-	if ((tmp = aim_ssi_itemlist_finditem(sess->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
-		aim_freetlvchain(&tmp->data);
-		tmp->data = data;
-	} else {
-		tmp = aim_ssi_itemlist_add(&sess->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, data);
-		aim_freetlvchain(&data);
-	}
+	/* Need to add the x00c9 TLV to the TLV chain */
+	aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
 
 	/* Sync our local list with the server list */
 	aim_ssi_sync(sess);
--- a/src/protocols/oscar/tlv.c	Sun Oct 05 22:15:25 2003 +0000
+++ b/src/protocols/oscar/tlv.c	Sun Oct 05 22:41:29 2003 +0000
@@ -371,20 +371,20 @@
 
 /**
  * aim_addtlvtochain_raw - Add a string to a TLV chain
- * @list: Desination chain (%NULL pointer if empty)
- * @t: TLV type
- * @l: Length of string to add (not including %NULL)
- * @v: String to add
  *
  * Adds the passed string as a TLV element of the passed type
  * to the TLV chain.
  *
+ * @param list Desination chain (%NULL pointer if empty)
+ * @param type TLV type
+ * @length Length of string to add (not including %NULL)
+ * @value String to add
  */
 faim_internal int aim_addtlvtochain_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value)
 {
 	aim_tlvlist_t *newtlv, *cur;
 
-	if (!list)
+	if (list == NULL)
 		return 0;
 
 	if (!(newtlv = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t))))
@@ -558,6 +558,124 @@
 }
 
 /**
+ * Substitute a TLV of a given type with a new TLV of the same type.  If 
+ * you attempt to replace a TLV that does not exist, this function will 
+ * just add a new TLV as if you called aim_addtlvtochain_raw().
+ *
+ * @param list Desination chain (%NULL pointer if empty).
+ * @param type TLV type.
+ * @param length Length of string to add (not including %NULL).
+ * @param value String to add.
+ * @return The length of the TLV.
+ */
+faim_internal int aim_tlvlist_replace_raw(aim_tlvlist_t **list, const fu16_t type, const fu16_t length, const fu8_t *value)
+{
+	aim_tlvlist_t *cur;
+
+	if (list == NULL)
+		return 0;
+
+	for (cur = *list; ((cur != NULL) && (cur->tlv->type != type)); cur = cur->next);
+	if (cur == NULL)
+		return aim_addtlvtochain_raw(list, type, length, value);
+
+	free(cur->tlv->value);
+	cur->tlv->length = length;
+	if (cur->tlv->length > 0) {
+		cur->tlv->value = (fu8_t *)malloc(cur->tlv->length);
+		memcpy(cur->tlv->value, value, cur->tlv->length);
+	} else
+		cur->tlv->value = NULL;
+
+	return cur->tlv->length;
+}
+
+/**
+ * Substitute a TLV of a given type with a new TLV of the same type.  If 
+ * you attempt to replace a TLV that does not exist, this function will 
+ * just add a new TLV as if you called aim_addtlvtochain_raw().
+ *
+ * @param list Desination chain (%NULL pointer if empty).
+ * @param type TLV type.
+ * @return The length of the TLV.
+ */
+faim_internal int aim_tlvlist_replace_noval(aim_tlvlist_t **list, const fu16_t type)
+{
+	return aim_tlvlist_replace_raw(list, type, 0, NULL);
+}
+
+/**
+ * Substitute a TLV of a given type with a new TLV of the same type.  If 
+ * you attempt to replace a TLV that does not exist, this function will 
+ * just add a new TLV as if you called aim_addtlvtochain_raw().
+ *
+ * @param list Desination chain (%NULL pointer if empty).
+ * @param type TLV type.
+ * @param value 8 bit value to add.
+ * @return The length of the TLV.
+ */
+faim_internal int aim_tlvlist_replace_8(aim_tlvlist_t **list, const fu16_t type, const fu8_t value)
+{
+	fu8_t v8[1];
+
+	aimutil_put8(v8, value);
+
+	return aim_tlvlist_replace_raw(list, type, 1, v8);
+}
+
+/**
+ * Substitute a TLV of a given type with a new TLV of the same type.  If 
+ * you attempt to replace a TLV that does not exist, this function will 
+ * just add a new TLV as if you called aim_addtlvtochain_raw().
+ *
+ * @param list Desination chain (%NULL pointer if empty).
+ * @param type TLV type.
+ * @param value 32 bit value to add.
+ * @return The length of the TLV.
+ */
+faim_internal int aim_tlvlist_replace_32(aim_tlvlist_t **list, const fu16_t type, const fu32_t value)
+{
+	fu8_t v32[4];
+
+	aimutil_put32(v32, value);
+
+	return aim_tlvlist_replace_raw(list, type, 4, v32);
+}
+
+/**
+ * Remove a TLV 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.
+ */
+faim_internal void aim_tlvlist_remove(aim_tlvlist_t **list, const fu16_t type)
+{
+	aim_tlvlist_t *del;
+
+	if (!list || !(*list))
+		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;
+	}
+
+	/* Free the removed item */
+	free(del->tlv->value);
+	free(del->tlv);
+	free(del);
+}
+
+/**
  * aim_writetlvchain - Write a TLV chain into a data buffer.
  * @buf: Destination buffer
  * @buflen: Maximum number of bytes that will be written to buffer
@@ -594,16 +712,17 @@
 
 
 /**
- * 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
+ * Grab the Nth TLV of type type in the TLV list list.
  *
  * 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.
  *
+ * @param list Source chain
+ * @param type Requested TLV type
+ * @param nth Index of TLV of type to get
+ * @return The TLV you were looking for, or NULL if one could not be found.
  */
 faim_internal aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const fu16_t t, const int n)
 {