File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / family_feedbag.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Mon Jan 27 19:48:25 2025 UTC (6 months ago) by snw
Branches: MAIN, CoherentLogicDevelopment
CVS tags: test-tag, start, HEAD
Pidgin AIM Plugin for ChivaNet

    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.
    5:  *
    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.
   10:  *
   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.
   15:  *
   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
   19: */
   20: 
   21: /*
   22:  * Family 0x0013 - Server-Side/Stored Information.
   23:  *
   24:  * Relatively new facility that allows certain types of information, such as
   25:  * a user's buddy list, permit/deny list, and permit/deny preferences, to be
   26:  * stored on the server, so that they can be accessed from any client.
   27:  *
   28:  * We keep 2 copies of SSI data:
   29:  * 1) An exact copy of what is stored on the AIM servers.
   30:  * 2) A local copy that we make changes to, and then send diffs
   31:  *    between this and the exact copy to keep them in sync.
   32:  *
   33:  * All the "aim_ssi_itemlist_bleh" functions near the top just modify the list
   34:  * that is given to them (i.e. they don't send SNACs).
   35:  *
   36:  * The SNAC sending and receiving functions are lower down in the file, and
   37:  * they're simpler.  They are in the order of the subtypes they deal with,
   38:  * starting with the request rights function (subtype 0x0002), then parse
   39:  * rights (subtype 0x0003), then--well, you get the idea.
   40:  *
   41:  * This is entirely too complicated.
   42:  * You don't know the half of it.
   43:  *
   44:  */
   45: 
   46: #include "oscar.h"
   47: #include "debug.h"
   48: 
   49: static int aim_ssi_addmoddel(OscarData *od);
   50: 
   51: /**
   52:  * List types based on http://dev.aol.com/aim/oscar/#FEEDBAG (archive.org)
   53:  * and http://iserverd.khstu.ru/oscar/ssi_item.html
   54:  *
   55:  * @param type The type of a list item as integer number, as provided by an aim_ssi_item struct.
   56:  * @return Returns the name of the item type as a character string.
   57:  */
   58: static const gchar*
   59: aim_ssi_type_to_string(guint16 type)
   60: {
   61: 	struct TypeStringPair
   62: 	{
   63: 		guint16 type;
   64: 		const gchar *string;
   65: 	};
   66: 	static const struct TypeStringPair type_strings[] = {
   67: 		{ 0x0000, "Buddy" },
   68: 		{ 0x0001, "Group" },
   69: 		{ 0x0002, "Permit/Visible" },
   70: 		{ 0x0003, "Deny/Invisible" },
   71: 		{ 0x0004, "PDInfo" },
   72: 		{ 0x0005, "PresencePrefs" }, 
   73: 		{ 0x0006, "Non-Buddy Info" },
   74: 		{ 0x0009, "ClientPrefs" },
   75: 		{ 0x000e, "ICQDeny/Ignore" },
   76: 		{ 0x0014, "Buddy Icon" }, 
   77: 		{ 0x0015, "Recent Buddies" },
   78: 		{ 0x0019, "Non-Buddy" },
   79: 		{ 0x001d, "Vanity Info" },
   80: 		{ 0x0020, "ICQ-MDir" },
   81: 		{ 0x0029, "Facebook" },
   82: 	};
   83: 	size_t i;
   84: 	for (i = 0; i < G_N_ELEMENTS(type_strings); i++) {
   85: 		if (type_strings[i].type == type) {
   86: 			return type_strings[i].string;
   87: 		}
   88: 	}
   89: 	return "unknown";
   90: }
   91: 
   92: /** For debug log output: Appends a line containing information about a given list item to a string.
   93:  *
   94:  * @param str String to which the line will be appended.
   95:  * @param prefix A string which will be prepended to the line.
   96:  * @param item List item from which information is extracted.
   97:  */
   98: static void
   99: aim_ssi_item_debug_append(GString *str, char *prefix, struct aim_ssi_item *item)
  100: {
  101: 	g_string_append_printf(str, 
  102: 		"%s gid=0x%04hx, bid=0x%04hx, list_type=0x%04hx [%s], name=%s.\n",
  103: 		prefix, item->gid, item->bid, item->type, aim_ssi_type_to_string(item->type),
  104: 		item->name ? item->name : "(null)");
  105: }
  106: 
  107: /**
  108:  * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
  109:  *
  110:  * @param list A pointer to a pointer to the current list of items.
  111:  * @param name A null terminated string containing the group name, or NULL
  112:  *        if you want to modify the master group.
  113:  * @return Return a pointer to the modified item.
  114:  */
  115: static void
  116: aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item *list, const char *name)
  117: {
  118: 	int newlen;
  119: 	struct aim_ssi_item *cur, *group;
  120: 
  121: 	/* Find the group */
  122: 	if (!(group = aim_ssi_itemlist_finditem(list, name, NULL, AIM_SSI_TYPE_GROUP)))
  123: 		return;
  124: 
  125: 	/* Find the length for the new additional data */
  126: 	newlen = 0;
  127: 	if (group->gid == 0x0000) {
  128: 		for (cur=list; cur; cur=cur->next)
  129: 			if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
  130: 				newlen += 2;
  131: 	} else {
  132: 		for (cur=list; cur; cur=cur->next)
  133: 			if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
  134: 				newlen += 2;
  135: 	}
  136: 
  137: 	/* Build the new TLV list */
  138: 	if (newlen > 0) {
  139: 		guint8 *newdata;
  140: 
  141: 		newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8));
  142: 		newlen = 0;
  143: 		if (group->gid == 0x0000) {
  144: 			for (cur=list; cur; cur=cur->next)
  145: 				if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid != 0x0000))
  146: 						newlen += aimutil_put16(newdata+newlen, cur->gid);
  147: 		} else {
  148: 			for (cur=list; cur; cur=cur->next)
  149: 				if ((cur->gid == group->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
  150: 						newlen += aimutil_put16(newdata+newlen, cur->bid);
  151: 		}
  152: 		aim_tlvlist_replace_raw(&group->data, 0x00c8, newlen, newdata);
  153: 
  154: 		g_free(newdata);
  155: 	}
  156: }
  157: 
  158: /**
  159:  * Locally add a new item to the given item list.
  160:  *
  161:  * @param list A pointer to a pointer to the current list of items.
  162:  * @param name A null terminated string of the name of the new item, or NULL if the
  163:  *        item should have no name.
  164:  * @param gid The group ID# you want the new item to have, or 0xFFFF if we should pick something.
  165:  * @param bid The buddy ID# you want the new item to have, or 0xFFFF if we should pick something.
  166:  * @param type The type of the item, 0x0000 for a contact, 0x0001 for a group, etc.
  167:  * @param data The additional data for the new item.
  168:  * @return A pointer to the newly created item.
  169:  */
  170: static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, const char *name, guint16 gid, guint16 bid, guint16 type, GSList *data)
  171: {
  172: 	gboolean exists;
  173: 	struct aim_ssi_item *cur, *new;
  174: 
  175: 	new = g_new(struct aim_ssi_item, 1);
  176: 
  177: 	/* Set the name */
  178: 	new->name = g_strdup(name);
  179: 
  180: 	/* Set the group ID# and buddy ID# */
  181: 	new->gid = gid;
  182: 	new->bid = bid;
  183: 	if (type == AIM_SSI_TYPE_GROUP) {
  184: 		if ((new->gid == 0xFFFF) && name) {
  185: 			do {
  186: 				new->gid += 0x0001;
  187: 				exists = FALSE;
  188: 				for (cur = *list; cur != NULL; cur = cur->next)
  189: 					if ((cur->type == AIM_SSI_TYPE_GROUP) && (cur->gid == new->gid)) {
  190: 						exists = TRUE;
  191: 						break;
  192: 					}
  193: 			} while (exists);
  194: 		}
  195: 	} else if (new->gid == 0x0000) {
  196: 		/*
  197: 		 * This is weird, but apparently items in the root group can't
  198: 		 * have a buddy ID equal to any group ID.  You'll get error
  199: 		 * 0x0003 when trying to add, which is "item already exists"
  200: 		 */
  201: 		if (new->bid == 0xFFFF) {
  202: 			do {
  203: 				new->bid += 0x0001;
  204: 				exists = FALSE;
  205: 				for (cur = *list; cur != NULL; cur = cur->next)
  206: 					if (cur->bid == new->bid || cur->gid == new->bid) {
  207: 						exists = TRUE;
  208: 						break;
  209: 					}
  210: 			} while (exists);
  211: 		}
  212: 	} else {
  213: 		if (new->bid == 0xFFFF) {
  214: 			do {
  215: 				new->bid += 0x0001;
  216: 				exists = FALSE;
  217: 				for (cur = *list; cur != NULL; cur = cur->next)
  218: 					if (cur->bid == new->bid && cur->gid == new->gid) {
  219: 						exists = TRUE;
  220: 						break;
  221: 					}
  222: 			} while (exists);
  223: 		}
  224: 	}
  225: 
  226: 	/* Set the type */
  227: 	new->type = type;
  228: 
  229: 	/* Set the TLV list */
  230: 	new->data = aim_tlvlist_copy(data);
  231: 
  232: 	/* Add the item to the list in the correct numerical position.  Fancy, eh? */
  233: 	if (*list) {
  234: 		if ((new->gid < (*list)->gid) || ((new->gid == (*list)->gid) && (new->bid < (*list)->bid))) {
  235: 			new->next = *list;
  236: 			*list = new;
  237: 		} else {
  238: 			struct aim_ssi_item *prev;
  239: 			for ((prev=*list, cur=(*list)->next); (cur && ((new->gid > cur->gid) || ((new->gid == cur->gid) && (new->bid > cur->bid)))); prev=cur, cur=cur->next);
  240: 			new->next = prev->next;
  241: 			prev->next = new;
  242: 		}
  243: 	} else {
  244: 		new->next = *list;
  245: 		*list = new;
  246: 	}
  247: 
  248: 	return new;
  249: }
  250: 
  251: /**
  252:  * Locally delete an item from the given item list.
  253:  *
  254:  * @param list A pointer to a pointer to the current list of items.
  255:  * @param del A pointer to the item you want to remove from the list.
  256:  * @return Return 0 if no errors, otherwise return the error number.
  257:  */
  258: static int aim_ssi_itemlist_del(struct aim_ssi_item **list, struct aim_ssi_item *del)
  259: {
  260: 	if (!(*list) || !del)
  261: 		return -EINVAL;
  262: 
  263: 	/* Remove the item from the list */
  264: 	if (*list == del) {
  265: 		*list = (*list)->next;
  266: 	} else {
  267: 		struct aim_ssi_item *cur;
  268: 		for (cur=*list; (cur->next && (cur->next!=del)); cur=cur->next);
  269: 		if (cur->next)
  270: 			cur->next = del->next;
  271: 	}
  272: 
  273: 	/* Free the removed item */
  274: 	g_free(del->name);
  275: 	aim_tlvlist_free(del->data);
  276: 	g_free(del);
  277: 
  278: 	return 0;
  279: }
  280: 
  281: /**
  282:  * Compare two items to see if they have the same data.
  283:  *
  284:  * @param cur1 A pointer to a pointer to the first item.
  285:  * @param cur2 A pointer to a pointer to the second item.
  286:  * @return Return 0 if no differences, or a number if there are differences.
  287:  */
  288: static int aim_ssi_itemlist_cmp(struct aim_ssi_item *cur1, struct aim_ssi_item *cur2)
  289: {
  290: 	if (!cur1 || !cur2)
  291: 		return 1;
  292: 
  293: 	if (cur1->data && !cur2->data)
  294: 		return 2;
  295: 
  296: 	if (!cur1->data && cur2->data)
  297: 		return 3;
  298: 
  299: 	if ((cur1->data && cur2->data) && (aim_tlvlist_cmp(cur1->data, cur2->data)))
  300: 		return 4;
  301: 
  302: 	if (cur1->name && !cur2->name)
  303: 		return 5;
  304: 
  305: 	if (!cur1->name && cur2->name)
  306: 		return 6;
  307: 
  308: 	if (cur1->name && cur2->name && oscar_util_name_compare(cur1->name, cur2->name))
  309: 		return 7;
  310: 
  311: 	if (cur1->gid != cur2->gid)
  312: 		return 8;
  313: 
  314: 	if (cur1->bid != cur2->bid)
  315: 		return 9;
  316: 
  317: 	if (cur1->type != cur2->type)
  318: 		return 10;
  319: 
  320: 	return 0;
  321: }
  322: 
  323: static gboolean aim_ssi_itemlist_valid(struct aim_ssi_item *list, struct aim_ssi_item *item)
  324: {
  325: 	struct aim_ssi_item *cur;
  326: 	for (cur=list; cur; cur=cur->next)
  327: 		if (cur == item)
  328: 			return TRUE;
  329: 	return FALSE;
  330: }
  331: 
  332: /**
  333:  * Locally find an item given a group ID# and a buddy ID#.
  334:  *
  335:  * @param list A pointer to the current list of items.
  336:  * @param gid The group ID# of the desired item.
  337:  * @param bid The buddy ID# of the desired item.
  338:  * @return Return a pointer to the item if found, else return NULL;
  339:  */
  340: struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
  341: {
  342: 	struct aim_ssi_item *cur;
  343: 	for (cur=list; cur; cur=cur->next)
  344: 		if ((cur->gid == gid) && (cur->bid == bid))
  345: 			return cur;
  346: 	return NULL;
  347: }
  348: 
  349: /**
  350:  * Locally find an item given a group name, buddy name, and type.  If group name
  351:  * and buddy name are null, then just return the first item of the given type.
  352:  *
  353:  * @param list A pointer to the current list of items.
  354:  * @param gn The group name of the desired item.
  355:  * @param bn The buddy name of the desired item.
  356:  * @param type The type of the desired item.
  357:  * @return Return a pointer to the item if found, else return NULL.
  358:  */
  359: struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, const char *gn, const char *bn, guint16 type)
  360: {
  361: 	struct aim_ssi_item *cur;
  362: 	if (!list)
  363: 		return NULL;
  364: 
  365: 	if (gn && bn) { /* For finding buddies in groups */
  366: 		for (cur=list; cur; cur=cur->next)
  367: 			if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
  368: 				struct aim_ssi_item *curg;
  369: 				for (curg=list; curg; curg=curg->next)
  370: 					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(oscar_util_name_compare(curg->name, gn)))
  371: 						return cur;
  372: 			}
  373: 
  374: 	} else if (gn) { /* For finding groups */
  375: 		for (cur=list; cur; cur=cur->next) {
  376: 			if ((cur->type == type) && (cur->bid == 0x0000) && (cur->name) && !(oscar_util_name_compare(cur->name, gn))) {
  377: 				return cur;
  378: 			}
  379: 		}
  380: 
  381: 	} else if (bn) { /* For finding permits, denies, and ignores */
  382: 		for (cur=list; cur; cur=cur->next) {
  383: 			if ((cur->type == type) && (cur->name) && !(oscar_util_name_compare(cur->name, bn))) {
  384: 				return cur;
  385: 			}
  386: 		}
  387: 
  388: 	/* For stuff without names--permit deny setting, visibility mask, etc. */
  389: 	} else for (cur=list; cur; cur=cur->next) {
  390: 		if ((cur->type == type) && (!cur->name))
  391: 			return cur;
  392: 	}
  393: 
  394: 	return NULL;
  395: }
  396: 
  397: /**
  398:  * Check if the given buddy exists in any group in the buddy list.
  399:  *
  400:  * @param list A pointer to the current list of items.
  401:  * @param bn The group name of the desired item.
  402:  * @return Return a pointer to the name of the item if found, else return NULL;
  403:  */
  404: struct aim_ssi_item *aim_ssi_itemlist_exists(struct aim_ssi_item *list, const char *bn)
  405: {
  406: 	if (!bn)
  407: 		return NULL;
  408: 	return aim_ssi_itemlist_finditem(list, NULL, bn, AIM_SSI_TYPE_BUDDY);
  409: }
  410: 
  411: /**
  412:  * Locally find the parent item of the given buddy name.
  413:  *
  414:  * @param list A pointer to the current list of items.
  415:  * @param bn The buddy name of the desired item.
  416:  * @return Return a pointer to the name of the item if found, else return NULL;
  417:  */
  418: char *aim_ssi_itemlist_findparentname(struct aim_ssi_item *list, const char *bn)
  419: {
  420: 	struct aim_ssi_item *cur, *curg;
  421: 	if (!list || !bn)
  422: 		return NULL;
  423: 	if (!(cur = aim_ssi_itemlist_exists(list, bn)))
  424: 		return NULL;
  425: 	if (!(curg = aim_ssi_itemlist_find(list, cur->gid, 0x0000)))
  426: 		return NULL;
  427: 	return curg->name;
  428: }
  429: 
  430: /**
  431:  * Locally find the permit/deny setting item, and return the setting.
  432:  *
  433:  * @param list A pointer to the current list of items.
  434:  * @return Return the current SSI permit deny setting, or 0 if no setting was found.
  435:  */
  436: int aim_ssi_getpermdeny(struct aim_ssi_item *list)
  437: {
  438: 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
  439: 	if (cur) {
  440: 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00ca, 1);
  441: 		if (tlv && tlv->value)
  442: 			return aimutil_get8(tlv->value);
  443: 	}
  444: 	return 0;
  445: }
  446: 
  447: /**
  448:  * Locally find the presence flag item, and return the setting.  The returned setting is a
  449:  * bitmask of the preferences.  See the AIM_SSI_PRESENCE_FLAG_* #defines in oscar.h.
  450:  *
  451:  * @param list A pointer to the current list of items.
  452:  * @return Return the current set of preferences.
  453:  */
  454: guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
  455: {
  456: 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
  457: 	if (cur) {
  458: 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x00c9, 1);
  459: 		if (tlv && tlv->length)
  460: 			return aimutil_get32(tlv->value);
  461: 	}
  462: 	return 0xFFFFFFFF;
  463: }
  464: 
  465: /**
  466:  * Locally find the alias of the given buddy.
  467:  *
  468:  * @param list A pointer to the current list of items.
  469:  * @param gn The group of the buddy.
  470:  * @param bn The name of the buddy.
  471:  * @return A pointer to a NULL terminated string that is the buddy's
  472:  *         alias, or NULL if the buddy has no alias.  You should free
  473:  *         this returned value!
  474:  */
  475: char *aim_ssi_getalias(struct aim_ssi_item *list, const char *gn, const char *bn)
  476: {
  477: 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
  478: 	if (cur) {
  479: 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x0131, 1);
  480: 		if (tlv && tlv->length)
  481: 			return g_strndup((const gchar *)tlv->value, tlv->length);
  482: 	}
  483: 	return NULL;
  484: }
  485: 
  486: /**
  487:  * Locally find the comment of the given buddy.
  488:  *
  489:  * @param list A pointer to the current list of items.
  490:  * @param gn The group of the buddy.
  491:  * @param bn The name of the buddy.
  492:  * @return A pointer to a NULL terminated string that is the buddy's
  493:  *         comment, or NULL if the buddy has no comment.  You should free
  494:  *         this returned value!
  495:  */
  496: char *aim_ssi_getcomment(struct aim_ssi_item *list, const char *gn, const char *bn)
  497: {
  498: 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
  499: 	if (cur) {
  500: 		aim_tlv_t *tlv = aim_tlv_gettlv(cur->data, 0x013c, 1);
  501: 		if (tlv && tlv->length) {
  502: 			return g_strndup((const gchar *)tlv->value, tlv->length);
  503: 		}
  504: 	}
  505: 	return NULL;
  506: }
  507: 
  508: /**
  509:  * Locally find if you are waiting for authorization for a buddy.
  510:  *
  511:  * @param list A pointer to the current list of items.
  512:  * @param gn The group of the buddy.
  513:  * @param bn The name of the buddy.
  514:  * @return 1 if you are waiting for authorization; 0 if you are not
  515:  */
  516: gboolean aim_ssi_waitingforauth(struct aim_ssi_item *list, const char *gn, const char *bn)
  517: {
  518: 	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, gn, bn, AIM_SSI_TYPE_BUDDY);
  519: 	if (cur) {
  520: 		if (aim_tlv_gettlv(cur->data, 0x0066, 1))
  521: 			return TRUE;
  522: 	}
  523: 	return FALSE;
  524: }
  525: 
  526: /**
  527:  * If there are changes, then create temporary items and
  528:  * call addmoddel.
  529:  *
  530:  * @param od The oscar session.
  531:  * @return Return 0 if no errors, otherwise return the error number.
  532:  */
  533: static int aim_ssi_sync(OscarData *od)
  534: {
  535: 	struct aim_ssi_item *cur1, *cur2;
  536: 	struct aim_ssi_tmp *cur, *new;
  537: 	int n = 0;
  538: 	GString *debugstr = g_string_new("");
  539: 
  540: 	/*
  541: 	 * The variable "n" is used to limit the number of addmoddel's that
  542: 	 * are performed in a single SNAC.  It will hopefully keep the size
  543: 	 * of the SNAC below the maximum SNAC size.
  544: 	 */
  545: 
  546: 	if (!od)
  547: 		return -EINVAL;
  548: 
  549: 	/* If we're waiting for an ack, we shouldn't do anything else */
  550: 	if (od->ssi.waiting_for_ack)
  551: 		return 0;
  552: 
  553: 	/*
  554: 	 * Compare the 2 lists and create an aim_ssi_tmp for each difference.
  555: 	 * We should only send either additions, modifications, or deletions
  556: 	 * before waiting for an acknowledgement.  So first do deletions, then
  557: 	 * additions, then modifications.  Also, both the official and the local
  558: 	 * list should be in ascending numerical order for the group ID#s and the
  559: 	 * buddy ID#s, which makes things more efficient.  I think.
  560: 	 */
  561: 
  562: 	/* Deletions */
  563: 	if (!od->ssi.pending) {
  564: 		for (cur1=od->ssi.official; cur1 && (n < 15); cur1=cur1->next) {
  565: 			if (!aim_ssi_itemlist_find(od->ssi.local, cur1->gid, cur1->bid)) {
  566: 				n++;
  567: 				new = g_new(struct aim_ssi_tmp, 1);
  568: 				new->action = SNAC_SUBTYPE_FEEDBAG_DEL;
  569: 				new->ack = 0xffff;
  570: 				new->name = NULL;
  571: 				new->item = cur1;
  572: 				new->next = NULL;
  573: 				if (od->ssi.pending) {
  574: 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
  575: 					cur->next = new;
  576: 				} else
  577: 					od->ssi.pending = new;
  578: 			       	aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
  579: 			}
  580: 		}
  581: 	}
  582: 
  583: 	/* Additions */
  584: 	if (!od->ssi.pending) {
  585: 		for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
  586: 			if (!aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid)) {
  587: 				n++;
  588: 				new = g_new(struct aim_ssi_tmp, 1);
  589: 				new->action = SNAC_SUBTYPE_FEEDBAG_ADD;
  590: 				new->ack = 0xffff;
  591: 				new->name = NULL;
  592: 				new->item = cur1;
  593: 				new->next = NULL;
  594: 				if (od->ssi.pending) {
  595: 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
  596: 					cur->next = new;
  597: 				} else
  598: 					od->ssi.pending = new;
  599: 			       	aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
  600: 			}
  601: 		}
  602: 	}
  603: 
  604: 	/* Modifications */
  605: 	if (!od->ssi.pending) {
  606: 		for (cur1=od->ssi.local; cur1 && (n < 15); cur1=cur1->next) {
  607: 			cur2 = aim_ssi_itemlist_find(od->ssi.official, cur1->gid, cur1->bid);
  608: 			if (cur2 && (aim_ssi_itemlist_cmp(cur1, cur2))) {
  609: 				n++;
  610: 				new = g_new(struct aim_ssi_tmp, 1);
  611: 				new->action = SNAC_SUBTYPE_FEEDBAG_MOD;
  612: 				new->ack = 0xffff;
  613: 				new->name = NULL;
  614: 				new->item = cur1;
  615: 				new->next = NULL;
  616: 				if (od->ssi.pending) {
  617: 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
  618: 					cur->next = new;
  619: 				} else
  620: 					od->ssi.pending = new;
  621: 			       	aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
  622: 			}
  623: 		}
  624: 	}
  625: 	if (debugstr->len > 0) {
  626: 		purple_debug_info("oscar", "%s", debugstr->str);
  627: 		if (purple_debug_is_verbose()) {
  628: 	    		g_string_truncate(debugstr, 0);
  629: 			for (cur1 = od->ssi.local; cur1; cur1 = cur1->next) 
  630: 				aim_ssi_item_debug_append(debugstr, "\t", cur1);
  631: 			purple_debug_misc("oscar", "Dumping item list of account %s:\n%s",
  632: 				purple_connection_get_account(od->gc)->username, debugstr->str);
  633: 		}
  634: 	}
  635: 	g_string_free(debugstr, TRUE);
  636: 
  637: 	/* We're out of stuff to do, so tell the AIM servers we're done and exit */
  638: 	if (!od->ssi.pending) {
  639: 		if (od->ssi.in_transaction) {
  640: 			aim_ssi_modend(od);
  641: 			od->ssi.in_transaction = FALSE;
  642: 		}
  643: 		return 0;
  644: 	}
  645: 
  646: 	/* If this is the first in a series of add/mod/del
  647: 	 * requests then send the "begin transaction" message. */
  648: 	if (!od->ssi.in_transaction)
  649: 	{
  650: 		aim_ssi_modbegin(od);
  651: 		od->ssi.in_transaction = TRUE;
  652: 	}
  653: 
  654: 	/* Make sure we don't send anything else between now
  655: 	 * and when we receive the ack for the following operation */
  656: 	od->ssi.waiting_for_ack = TRUE;
  657: 
  658: 	/* Now go mail off our data and wait 4 to 6 weeks */
  659: 	return aim_ssi_addmoddel(od);;
  660: }
  661: 
  662: /**
  663:  * Free all SSI data.
  664:  *
  665:  * This doesn't remove it from the server, that's different.
  666:  *
  667:  * @param od The oscar odion.
  668:  * @return Return 0 if no errors, otherwise return the error number.
  669:  */
  670: static void
  671: aim_ssi_freelist(OscarData *od)
  672: {
  673: 	struct aim_ssi_item *cur, *del;
  674: 	struct aim_ssi_tmp *curtmp, *deltmp;
  675: 
  676: 	cur = od->ssi.official;
  677: 	while (cur) {
  678: 		del = cur;
  679: 		cur = cur->next;
  680: 		g_free(del->name);
  681: 		aim_tlvlist_free(del->data);
  682: 		g_free(del);
  683: 	}
  684: 
  685: 	cur = od->ssi.local;
  686: 	while (cur) {
  687: 		del = cur;
  688: 		cur = cur->next;
  689: 		g_free(del->name);
  690: 		aim_tlvlist_free(del->data);
  691: 		g_free(del);
  692: 	}
  693: 
  694: 	curtmp = od->ssi.pending;
  695: 	while (curtmp) {
  696: 		deltmp = curtmp;
  697: 		curtmp = curtmp->next;
  698: 		g_free(deltmp);
  699: 	}
  700: 
  701: 	od->ssi.numitems = 0;
  702: 	od->ssi.official = NULL;
  703: 	od->ssi.local = NULL;
  704: 	od->ssi.pending = NULL;
  705: 	od->ssi.timestamp = (time_t)0;
  706: }
  707: 
  708: /**
  709:  * This "cleans" the ssi list.  It does the following:
  710:  * 1) Makes sure all buddies, permits, and denies have names.
  711:  * 2) Makes sure that all buddies are in a group that exist.
  712:  * 3) Deletes any empty groups
  713:  *
  714:  * @param od The oscar odion.
  715:  * @return Return 0 if no errors, otherwise return the error number.
  716:  */
  717: int aim_ssi_cleanlist(OscarData *od)
  718: {
  719: 	struct aim_ssi_item *cur, *next;
  720: 
  721: 	if (!od)
  722: 		return -EINVAL;
  723: 
  724: 	/* Delete any buddies, permits, or denies with empty names. */
  725: 	/* If there are any buddies directly in the master group, add them to a real group. */
  726: 	/* DESTROY any buddies that are directly in the master group. */
  727: 	/* Do the same for buddies that are in a non-existant group. */
  728: 	/* This will kind of mess up if you hit the item limit, but this function isn't too critical */
  729: 	cur = od->ssi.local;
  730: 	while (cur) {
  731: 		next = cur->next;
  732: 		if (!cur->name) {
  733: 			if (cur->type == AIM_SSI_TYPE_BUDDY)
  734: 				aim_ssi_delbuddy(od, NULL, NULL);
  735: 			else if (cur->type == AIM_SSI_TYPE_PERMIT || cur->type == AIM_SSI_TYPE_DENY || cur->type == AIM_SSI_TYPE_ICQDENY)
  736: 				aim_ssi_del_from_private_list(od, NULL, cur->type);
  737: 		} else if ((cur->type == AIM_SSI_TYPE_BUDDY) && ((cur->gid == 0x0000) || (!aim_ssi_itemlist_find(od->ssi.local, cur->gid, 0x0000)))) {
  738: 			char *alias = aim_ssi_getalias(od->ssi.local, NULL, cur->name);
  739: 			aim_ssi_addbuddy(od, cur->name, "orphans", NULL, alias, NULL, NULL, FALSE);
  740: 			aim_ssi_delbuddy(od, cur->name, NULL);
  741: 			g_free(alias);
  742: 		}
  743: 		cur = next;
  744: 	}
  745: 
  746: 	/* Make sure there aren't any duplicate buddies in a group, or duplicate permits or denies */
  747: 	cur = od->ssi.local;
  748: 	while (cur) {
  749: 		if ((cur->type == AIM_SSI_TYPE_BUDDY) || (cur->type == AIM_SSI_TYPE_PERMIT) || (cur->type == AIM_SSI_TYPE_DENY))
  750: 		{
  751: 			struct aim_ssi_item *cur2, *next2;
  752: 			cur2 = cur->next;
  753: 			while (cur2) {
  754: 				next2 = cur2->next;
  755: 				if ((cur->type == cur2->type) && (cur->gid == cur2->gid) && (cur->name != NULL) && (cur2->name != NULL) && (!oscar_util_name_compare(cur->name, cur2->name))) {
  756: 					aim_ssi_itemlist_del(&od->ssi.local, cur2);
  757: 				}
  758: 				cur2 = next2;
  759: 			}
  760: 		}
  761: 		cur = cur->next;
  762: 	}
  763: 
  764: 	/* If we've made any changes then sync our list with the server's */
  765: 	return aim_ssi_sync(od);
  766: }
  767: 
  768: /**
  769:  * Add a buddy to the list.
  770:  *
  771:  * @param od The oscar odion.
  772:  * @param name The name of the item.
  773:  * @param group The group of the item.
  774:  * @param data A TLV list to use as the additional data for this item.
  775:  * @param alias The alias/nickname of the item, or NULL.
  776:  * @param comment The buddy comment for the item, or NULL.
  777:  * @param smsnum The locally assigned SMS number, or NULL.
  778:  * @return Return 0 if no errors, otherwise return the error number.
  779:  */
  780: int aim_ssi_addbuddy(OscarData *od, const char *name, const char *group, GSList *data, const char *alias, const char *comment, const char *smsnum, gboolean needauth)
  781: {
  782: 	struct aim_ssi_item *parent;
  783: 
  784: 	if (!od || !name || !group)
  785: 		return -EINVAL;
  786: 
  787: 	/* Find the parent */
  788: 	if (!(parent = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP))) {
  789: 		/* Find the parent's parent (the master group) */
  790: 		if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
  791: 			aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
  792: 
  793: 		/* Add the parent */
  794: 		parent = aim_ssi_itemlist_add(&od->ssi.local, group, 0xFFFF, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
  795: 
  796: 		/* Modify the parent's parent (the master group) */
  797: 		aim_ssi_itemlist_rebuildgroup(od->ssi.local, NULL);
  798: 	}
  799: 
  800: 	/* Create a TLV list for the new buddy */
  801: 	if (needauth)
  802: 		aim_tlvlist_add_noval(&data, 0x0066);
  803: 	if (alias != NULL)
  804: 		aim_tlvlist_add_str(&data, 0x0131, alias);
  805: 	if (smsnum != NULL)
  806: 		aim_tlvlist_add_str(&data, 0x013a, smsnum);
  807: 	if (comment != NULL)
  808: 		aim_tlvlist_add_str(&data, 0x013c, comment);
  809: 
  810: 	/* Add that bad boy */
  811: 	aim_ssi_itemlist_add(&od->ssi.local, name, parent->gid, 0xFFFF, AIM_SSI_TYPE_BUDDY, data);
  812: 	aim_tlvlist_free(data);
  813: 
  814: 	/* Modify the parent group */
  815: 	aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
  816: 
  817: 	/* Sync our local list with the server list */
  818: 	return aim_ssi_sync(od);
  819: }
  820: 
  821: int
  822: aim_ssi_add_to_private_list(OscarData *od, const char* name, guint16 list_type)
  823: {
  824: 	if (!od || !name || !od->ssi.received_data)
  825: 		return -EINVAL;
  826: 
  827: 	if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
  828: 		aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
  829: 
  830: 	aim_ssi_itemlist_add(&od->ssi.local, name, 0x0000, 0xFFFF, list_type, NULL);
  831: 	return aim_ssi_sync(od);
  832: }
  833: 
  834: int
  835: aim_ssi_del_from_private_list(OscarData* od, const char* name, guint16 list_type)
  836: {
  837: 	struct aim_ssi_item *del;
  838: 
  839: 	if (!od)
  840: 		return -EINVAL;
  841: 
  842: 	if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, NULL, name, list_type)))
  843: 		return -EINVAL;
  844: 
  845: 	aim_ssi_itemlist_del(&od->ssi.local, del);
  846: 	return aim_ssi_sync(od);
  847: }
  848: 
  849: /**
  850:  * Deletes a buddy from the list.
  851:  *
  852:  * @param od The oscar odion.
  853:  * @param name The name of the item, or NULL.
  854:  * @param group The group of the item, or NULL.
  855:  * @return Return 0 if no errors, otherwise return the error number.
  856:  */
  857: int aim_ssi_delbuddy(OscarData *od, const char *name, const char *group)
  858: {
  859: 	struct aim_ssi_item *del;
  860: 
  861: 	if (!od)
  862: 		return -EINVAL;
  863: 
  864: 	/* Find the buddy */
  865: 	if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, name, AIM_SSI_TYPE_BUDDY)))
  866: 		return -EINVAL;
  867: 
  868: 	/* Remove the item from the list */
  869: 	aim_ssi_itemlist_del(&od->ssi.local, del);
  870: 
  871: 	/* Modify the parent group */
  872: 	aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
  873: 
  874: 	/* Sync our local list with the server list */
  875: 	return aim_ssi_sync(od);
  876: }
  877: 
  878: /**
  879:  * Deletes a group from the list.
  880:  *
  881:  * @param od The oscar odion.
  882:  * @param group The name of the group.
  883:  * @return Return 0 if no errors, otherwise return the error number.
  884:  */
  885: int aim_ssi_delgroup(OscarData *od, const char *group)
  886: {
  887: 	struct aim_ssi_item *del;
  888: 	aim_tlv_t *tlv;
  889: 
  890: 	if (!od)
  891: 		return -EINVAL;
  892: 
  893: 	/* Find the group */
  894: 	if (!(del = aim_ssi_itemlist_finditem(od->ssi.local, group, NULL, AIM_SSI_TYPE_GROUP)))
  895: 		return -EINVAL;
  896: 
  897: 	/* Don't delete the group if it's not empty */
  898: 	tlv = aim_tlv_gettlv(del->data, 0x00c8, 1);
  899: 	if (tlv && tlv->length > 0)
  900: 		return -EINVAL;
  901: 
  902: 	/* Remove the item from the list */
  903: 	aim_ssi_itemlist_del(&od->ssi.local, del);
  904: 
  905: 	/* Modify the parent group */
  906: 	aim_ssi_itemlist_rebuildgroup(od->ssi.local, group);
  907: 
  908: 	/* Sync our local list with the server list */
  909: 	return aim_ssi_sync(od);
  910: }
  911: 
  912: /**
  913:  * Move a buddy from one group to another group.  This basically just deletes the
  914:  * buddy and re-adds it.
  915:  *
  916:  * @param od The oscar odion.
  917:  * @param oldgn The group that the buddy is currently in.
  918:  * @param newgn The group that the buddy should be moved in to.
  919:  * @param bn The name of the buddy to be moved.
  920:  * @return Return 0 if no errors, otherwise return the error number.
  921:  */
  922: int aim_ssi_movebuddy(OscarData *od, const char *oldgn, const char *newgn, const char *bn)
  923: {
  924: 	struct aim_ssi_item *buddy;
  925: 	GSList *data;
  926: 
  927: 	/* Find the buddy */
  928: 	buddy = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, bn, AIM_SSI_TYPE_BUDDY);
  929: 	if (buddy == NULL)
  930: 		return -EINVAL;
  931: 
  932: 	/* Make a copy of the buddy's TLV list */
  933: 	data = aim_tlvlist_copy(buddy->data);
  934: 
  935: 	/* Delete the old item */
  936: 	aim_ssi_delbuddy(od, bn, oldgn);
  937: 
  938: 	/* Add the new item using the EXACT SAME TLV list */
  939: 	aim_ssi_addbuddy(od, bn, newgn, data, NULL, NULL, NULL, FALSE);
  940: 
  941: 	return 0;
  942: }
  943: 
  944: /**
  945:  * Change the alias stored on the server for a given buddy.
  946:  *
  947:  * @param od The oscar odion.
  948:  * @param gn The group that the buddy is currently in.
  949:  * @param bn The name of the buddy.
  950:  * @param alias The new alias for the buddy, or NULL if you want to remove
  951:  *        a buddy's comment.
  952:  * @return Return 0 if no errors, otherwise return the error number.
  953:  */
  954: int aim_ssi_aliasbuddy(OscarData *od, const char *gn, const char *bn, const char *alias)
  955: {
  956: 	struct aim_ssi_item *tmp;
  957: 
  958: 	if (!od || !gn || !bn)
  959: 		return -EINVAL;
  960: 
  961: 	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
  962: 		return -EINVAL;
  963: 
  964: 	/* Either add or remove the 0x0131 TLV from the TLV chain */
  965: 	if ((alias != NULL) && (strlen(alias) > 0))
  966: 		aim_tlvlist_replace_str(&tmp->data, 0x0131, alias);
  967: 	else
  968: 		aim_tlvlist_remove(&tmp->data, 0x0131);
  969: 
  970: 	/* Sync our local list with the server list */
  971: 	return aim_ssi_sync(od);
  972: }
  973: 
  974: /**
  975:  * Change the comment stored on the server for a given buddy.
  976:  *
  977:  * @param od The oscar odion.
  978:  * @param gn The group that the buddy is currently in.
  979:  * @param bn The name of the buddy.
  980:  * @param alias The new comment for the buddy, or NULL if you want to remove
  981:  *        a buddy's comment.
  982:  * @return Return 0 if no errors, otherwise return the error number.
  983:  */
  984: int aim_ssi_editcomment(OscarData *od, const char *gn, const char *bn, const char *comment)
  985: {
  986: 	struct aim_ssi_item *tmp;
  987: 
  988: 	if (!od || !gn || !bn)
  989: 		return -EINVAL;
  990: 
  991: 	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, gn, bn, AIM_SSI_TYPE_BUDDY)))
  992: 		return -EINVAL;
  993: 
  994: 	/* Either add or remove the 0x0131 TLV from the TLV chain */
  995: 	if ((comment != NULL) && (strlen(comment) > 0))
  996: 		aim_tlvlist_replace_str(&tmp->data, 0x013c, comment);
  997: 	else
  998: 		aim_tlvlist_remove(&tmp->data, 0x013c);
  999: 
 1000: 	/* Sync our local list with the server list */
 1001: 	return aim_ssi_sync(od);
 1002: }
 1003: 
 1004: /**
 1005:  * Rename a group.
 1006:  *
 1007:  * @param od The oscar odion.
 1008:  * @param oldgn The old group name.
 1009:  * @param newgn The new group name.
 1010:  * @return Return 0 if no errors, otherwise return the error number.
 1011:  */
 1012: int aim_ssi_rename_group(OscarData *od, const char *oldgn, const char *newgn)
 1013: {
 1014: 	struct aim_ssi_item *group;
 1015: 
 1016: 	if (!od || !oldgn || !newgn)
 1017: 		return -EINVAL;
 1018: 
 1019: 	if (!(group = aim_ssi_itemlist_finditem(od->ssi.local, oldgn, NULL, AIM_SSI_TYPE_GROUP)))
 1020: 		return -EINVAL;
 1021: 
 1022: 	g_free(group->name);
 1023: 	group->name = g_strdup(newgn);
 1024: 
 1025: 	/* Sync our local list with the server list */
 1026: 	return aim_ssi_sync(od);
 1027: }
 1028: 
 1029: /**
 1030:  * Stores your permit/deny setting on the server, and starts using it.
 1031:  *
 1032:  * @param od The oscar odion.
 1033:  * @param permdeny Your permit/deny setting. For ICQ accounts, it actually affects your visibility
 1034:  *        and has nothing to do with blocking. Can be one of the following:
 1035:  *        1 - Allow all users
 1036:  *        2 - Block all users
 1037:  *        3 - Allow only the users below
 1038:  *        4 - Block only the users below
 1039:  *        5 - Allow only users on my buddy list
 1040:  * @return Return 0 if no errors, otherwise return the error number.
 1041:  */
 1042: int aim_ssi_setpermdeny(OscarData *od, guint8 permdeny)
 1043: {
 1044: 	struct aim_ssi_item *tmp;
 1045: 
 1046: 	if (!od || !od->ssi.received_data)
 1047: 		return -EINVAL;
 1048: 
 1049: 	/* Find the PDINFO item, or add it if it does not exist */
 1050: 	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PDINFO))) {
 1051: 		/* Make sure the master group exists */
 1052: 		if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
 1053: 			aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
 1054: 
 1055: 		tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PDINFO, NULL);
 1056: 	}
 1057: 
 1058: 	/* Need to add the 0x00ca TLV to the TLV chain */
 1059: 	aim_tlvlist_replace_8(&tmp->data, 0x00ca, permdeny);
 1060: 
 1061: 	/* Sync our local list with the server list */
 1062: 	return aim_ssi_sync(od);
 1063: }
 1064: 
 1065: /**
 1066:  * Set buddy icon information
 1067:  *
 1068:  * @param od The oscar odion.
 1069:  * @param iconcsum The MD5 checksum of the icon you are using.
 1070:  * @param iconcsumlen Length of the MD5 checksum given above.  Should be 0x10 bytes.
 1071:  * @return Return 0 if no errors, otherwise return the error number.
 1072:  */
 1073: int aim_ssi_seticon(OscarData *od, const guint8 *iconsum, guint8 iconsumlen)
 1074: {
 1075: 	struct aim_ssi_item *tmp;
 1076: 	guint8 *csumdata;
 1077: 
 1078: 	if (!od || !iconsum || !iconsumlen || !od->ssi.received_data)
 1079: 		return -EINVAL;
 1080: 
 1081: 	/* Find the ICONINFO item, or add it if it does not exist */
 1082: 	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, "1", AIM_SSI_TYPE_ICONINFO))) {
 1083: 		/* Make sure the master group exists */
 1084: 		if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
 1085: 			aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
 1086: 
 1087: 		tmp = aim_ssi_itemlist_add(&od->ssi.local, "1", 0x0000, 0xFFFF, AIM_SSI_TYPE_ICONINFO, NULL);
 1088: 	}
 1089: 
 1090: 	/* Need to add the 0x00d5 TLV to the TLV chain */
 1091: 	csumdata = (guint8 *)g_malloc((iconsumlen+2)*sizeof(guint8));
 1092: 	(void)aimutil_put8(&csumdata[0], 0x00);
 1093: 	(void)aimutil_put8(&csumdata[1], iconsumlen);
 1094: 	memcpy(&csumdata[2], iconsum, iconsumlen);
 1095: 	aim_tlvlist_replace_raw(&tmp->data, 0x00d5, (iconsumlen+2) * sizeof(guint8), csumdata);
 1096: 	g_free(csumdata);
 1097: 
 1098: 	/* Need to add the 0x0131 TLV to the TLV chain, used to cache the icon */
 1099: 	aim_tlvlist_replace_noval(&tmp->data, 0x0131);
 1100: 
 1101: 	/* Sync our local list with the server list */
 1102: 	aim_ssi_sync(od);
 1103: 	return 0;
 1104: }
 1105: 
 1106: /**
 1107:  * Remove a reference to a server stored buddy icon.  This will make your
 1108:  * icon stop showing up to other people.
 1109:  *
 1110:  * Really this function just sets the icon to a dummy value.  It's weird...
 1111:  * but I think the dummy value basically means "I don't have an icon!"
 1112:  *
 1113:  * @param od The oscar session.
 1114:  * @return Return 0 if no errors, otherwise return the error number.
 1115:  */
 1116: int aim_ssi_delicon(OscarData *od)
 1117: {
 1118: 	const guint8 csumdata[] = {0x02, 0x01, 0xd2, 0x04, 0x72};
 1119: 
 1120: 	return aim_ssi_seticon(od, csumdata, 5);
 1121: }
 1122: 
 1123: /**
 1124:  * Stores your setting for various SSI settings.  Whether you
 1125:  * should show up as idle or not, etc.
 1126:  *
 1127:  * @param od The oscar odion.
 1128:  * @param presence A bitmask of the first 32 entries [0-31] from
 1129:  *        http://dev.aol.com/aim/oscar/#FEEDBAG__BUDDY_PREFS
 1130:  *        0x00000002 - Hide "eBuddy group" (whatever that is)
 1131:  *        0x00000400 - Allow others to see your idle time
 1132:  *        0x00020000 - Don't show Recent Buddies
 1133:  * @return Return 0 if no errors, otherwise return the error number.
 1134:  */
 1135: int aim_ssi_setpresence(OscarData *od, guint32 presence) {
 1136: 	struct aim_ssi_item *tmp;
 1137: 
 1138: 	if (!od || !od->ssi.received_data)
 1139: 		return -EINVAL;
 1140: 
 1141: 	/* Find the PRESENCEPREFS item, or add it if it does not exist */
 1142: 	if (!(tmp = aim_ssi_itemlist_finditem(od->ssi.local, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) {
 1143: 		/* Make sure the master group exists */
 1144: 		if (aim_ssi_itemlist_find(od->ssi.local, 0x0000, 0x0000) == NULL)
 1145: 			aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0x0000, AIM_SSI_TYPE_GROUP, NULL);
 1146: 
 1147: 		tmp = aim_ssi_itemlist_add(&od->ssi.local, NULL, 0x0000, 0xFFFF, AIM_SSI_TYPE_PRESENCEPREFS, NULL);
 1148: 	}
 1149: 
 1150: 	/* Need to add the x00c9 TLV to the TLV chain */
 1151: 	aim_tlvlist_replace_32(&tmp->data, 0x00c9, presence);
 1152: 
 1153: 	/* Sync our local list with the server list */
 1154: 	return aim_ssi_sync(od);
 1155: }
 1156: 
 1157: /*
 1158:  * Subtype 0x0002 - Request SSI Rights.
 1159:  */
 1160: int aim_ssi_reqrights(OscarData *od)
 1161: {
 1162: 	FlapConnection *conn;
 1163: 
 1164: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
 1165: 		return -EINVAL;
 1166: 
 1167: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQRIGHTS);
 1168: 
 1169: 	return 0;
 1170: }
 1171: 
 1172: /*
 1173:  * Subtype 0x0003 - SSI Rights Information.
 1174:  */
 1175: static int parserights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1176: {
 1177: 	int ret = 0, i;
 1178: 	aim_rxcallback_t userfunc;
 1179: 	GSList *tlvlist;
 1180: 	aim_tlv_t *tlv;
 1181: 	ByteStream bstream;
 1182: 	guint16 *maxitems;
 1183: 
 1184: 	/* This SNAC is made up of a bunch of TLVs */
 1185: 	tlvlist = aim_tlvlist_read(bs);
 1186: 
 1187: 	/* TLV 0x0004 contains the maximum number of each item */
 1188: 	if (!(tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
 1189: 		aim_tlvlist_free(tlvlist);
 1190: 		return 0;
 1191: 	}
 1192: 
 1193: 	byte_stream_init(&bstream, tlv->value, tlv->length);
 1194: 
 1195: 	maxitems = (guint16 *)g_malloc((tlv->length/2)*sizeof(guint16));
 1196: 
 1197: 	for (i=0; i<(tlv->length/2); i++)
 1198: 		maxitems[i] = byte_stream_get16(&bstream);
 1199: 
 1200: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1201: 		ret = userfunc(od, conn, frame, tlv->length/2, maxitems);
 1202: 
 1203: 	aim_tlvlist_free(tlvlist);
 1204: 	g_free(maxitems);
 1205: 
 1206: 	return ret;
 1207: }
 1208: 
 1209: /*
 1210:  * Subtype 0x0004 - Request SSI Data when you don't have a timestamp and
 1211:  * revision number.
 1212:  *
 1213:  */
 1214: int aim_ssi_reqdata(OscarData *od)
 1215: {
 1216: 	FlapConnection *conn;
 1217: 
 1218: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
 1219: 		return -EINVAL;
 1220: 
 1221: 	/* Free any current data, just in case */
 1222: 	aim_ssi_freelist(od);
 1223: 
 1224: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_REQDATA);
 1225: 
 1226: 	return 0;
 1227: }
 1228: 
 1229: /*
 1230:  * Subtype 0x0006 - SSI Data.
 1231:  */
 1232: static int parsedata(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1233: {
 1234: 	int ret = 0;
 1235: 	aim_rxcallback_t userfunc;
 1236: 	guint8 fmtver; /* guess */
 1237: 	guint16 namelen, gid, bid, type;
 1238: 	char *name;
 1239: 	GSList *data;
 1240: 	GString *debugstr = g_string_new("");
 1241: 
 1242: 	fmtver = byte_stream_get8(bs); /* Version of ssi data.  Should be 0x00 */
 1243: 	od->ssi.numitems += byte_stream_get16(bs); /* # of items in this SSI SNAC */
 1244: 
 1245: 	/* Read in the list */
 1246: 	while (byte_stream_bytes_left(bs) > 4) { /* last four bytes are timestamp */
 1247: 		if ((namelen = byte_stream_get16(bs)))
 1248: 			name = byte_stream_getstr(bs, namelen);
 1249: 		else
 1250: 			name = NULL;
 1251: 		gid = byte_stream_get16(bs);
 1252: 		bid = byte_stream_get16(bs);
 1253: 		type = byte_stream_get16(bs);
 1254: 		data = aim_tlvlist_readlen(bs, byte_stream_get16(bs));
 1255: 		aim_ssi_item_debug_append(debugstr, "\t", aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data));
 1256: 		g_free(name);
 1257: 		aim_tlvlist_free(data);
 1258: 	}
 1259: 	purple_debug_misc("oscar", "Reading items from tlvlist for account %s:\n%s",
 1260: 		purple_connection_get_account(od->gc)->username, debugstr->str);
 1261: 	g_string_free(debugstr, TRUE);
 1262: 
 1263: 	/* Read in the timestamp */
 1264: 	od->ssi.timestamp = byte_stream_get32(bs);
 1265: 
 1266: 	if (!(snac->flags & 0x0001)) {
 1267: 		/* Make a copy of the list */
 1268: 		struct aim_ssi_item *cur;
 1269: 		for (cur=od->ssi.official; cur; cur=cur->next)
 1270: 			aim_ssi_itemlist_add(&od->ssi.local, cur->name, cur->gid, cur->bid, cur->type, cur->data);
 1271: 
 1272: 		od->ssi.received_data = TRUE;
 1273: 
 1274: 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1275: 			ret = userfunc(od, conn, frame, fmtver, od->ssi.numitems, od->ssi.timestamp);
 1276: 	}
 1277: 
 1278: 	return ret;
 1279: }
 1280: 
 1281: /*
 1282:  * Subtype 0x0007 - SSI Activate Data.
 1283:  *
 1284:  * Should be sent after receiving 13/6 or 13/f to tell the server you
 1285:  * are ready to begin using the list.  It will promptly give you the
 1286:  * presence information for everyone in your list and put your permit/deny
 1287:  * settings into effect.
 1288:  *
 1289:  */
 1290: int aim_ssi_enable(OscarData *od)
 1291: {
 1292: 	FlapConnection *conn;
 1293: 
 1294: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
 1295: 		return -EINVAL;
 1296: 
 1297: 	aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, 0x0007);
 1298: 
 1299: 	return 0;
 1300: }
 1301: 
 1302: /*
 1303:  * Subtype 0x0008/0x0009/0x000a - SSI Add/Mod/Del Item(s).
 1304:  *
 1305:  * Sends the SNAC to add, modify, or delete items from the server-stored
 1306:  * information.  These 3 SNACs all have an identical structure.  The only
 1307:  * difference is the subtype that is set for the SNAC.
 1308:  *
 1309:  */
 1310: static int aim_ssi_addmoddel(OscarData *od)
 1311: {
 1312: 	FlapConnection *conn;
 1313: 	ByteStream bs;
 1314: 	aim_snacid_t snacid;
 1315: 	int bslen;
 1316: 	struct aim_ssi_tmp *cur;
 1317: 
 1318: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !od->ssi.pending || !od->ssi.pending->item)
 1319: 		return -EINVAL;
 1320: 
 1321: 	/* Calculate total SNAC size */
 1322: 	bslen = 0;
 1323: 	for (cur=od->ssi.pending; cur; cur=cur->next) {
 1324: 		bslen += 10; /* For length, GID, BID, type, and length */
 1325: 		if (cur->item->name)
 1326: 			bslen += strlen(cur->item->name);
 1327: 		if (cur->item->data)
 1328: 			bslen += aim_tlvlist_size(cur->item->data);
 1329: 	}
 1330: 
 1331: 	byte_stream_new(&bs, bslen);
 1332: 
 1333: 	for (cur=od->ssi.pending; cur; cur=cur->next) {
 1334: 		byte_stream_put16(&bs, cur->item->name ? strlen(cur->item->name) : 0);
 1335: 		if (cur->item->name)
 1336: 			byte_stream_putstr(&bs, cur->item->name);
 1337: 		byte_stream_put16(&bs, cur->item->gid);
 1338: 		byte_stream_put16(&bs, cur->item->bid);
 1339: 		byte_stream_put16(&bs, cur->item->type);
 1340: 		byte_stream_put16(&bs, cur->item->data ? aim_tlvlist_size(cur->item->data) : 0);
 1341: 		if (cur->item->data)
 1342: 			aim_tlvlist_write(&bs, &cur->item->data);
 1343: 	}
 1344: 
 1345: 	snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, 0x0000, NULL, 0);
 1346: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, od->ssi.pending->action, snacid, &bs);
 1347: 
 1348: 	byte_stream_destroy(&bs);
 1349: 
 1350: 	return 0;
 1351: }
 1352: 
 1353: /*
 1354:  * Subtype 0x0008 - Incoming SSI add.
 1355:  *
 1356:  * Sent by the server, for example, when someone is added to
 1357:  * your "Recent Buddies" group.
 1358:  */
 1359: static int parseadd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1360: {
 1361: 	int ret = 0;
 1362: 	aim_rxcallback_t userfunc;
 1363: 	char *name;
 1364: 	guint16 len, gid, bid, type;
 1365: 	GSList *data;
 1366: 
 1367: 	while (byte_stream_bytes_left(bs)) {
 1368: 		if ((len = byte_stream_get16(bs)))
 1369: 			name = byte_stream_getstr(bs, len);
 1370: 		else
 1371: 			name = NULL;
 1372: 		gid = byte_stream_get16(bs);
 1373: 		bid = byte_stream_get16(bs);
 1374: 		type = byte_stream_get16(bs);
 1375: 		if ((len = byte_stream_get16(bs)))
 1376: 			data = aim_tlvlist_readlen(bs, len);
 1377: 		else
 1378: 			data = NULL;
 1379: 
 1380: 		aim_ssi_itemlist_add(&od->ssi.local, name, gid, bid, type, data);
 1381: 		aim_ssi_itemlist_add(&od->ssi.official, name, gid, bid, type, data);
 1382: 		aim_tlvlist_free(data);
 1383: 
 1384: 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1385: 			ret = userfunc(od, conn, frame, snac->subtype, type, name);
 1386: 
 1387: 		g_free(name);
 1388: 	}
 1389: 
 1390: 	return ret;
 1391: }
 1392: 
 1393: /*
 1394:  * Subtype 0x0009 - Incoming SSI mod.
 1395:  */
 1396: static int parsemod(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1397: {
 1398: 	int ret = 0;
 1399: 	aim_rxcallback_t userfunc;
 1400: 	char *name;
 1401: 	guint16 len, gid, bid, type;
 1402: 	GSList *data;
 1403: 	struct aim_ssi_item *item;
 1404: 
 1405: 	while (byte_stream_bytes_left(bs)) {
 1406: 		if ((len = byte_stream_get16(bs)))
 1407: 			name = byte_stream_getstr(bs, len);
 1408: 		else
 1409: 			name = NULL;
 1410: 		gid = byte_stream_get16(bs);
 1411: 		bid = byte_stream_get16(bs);
 1412: 		type = byte_stream_get16(bs);
 1413: 		if ((len = byte_stream_get16(bs)))
 1414: 			data = aim_tlvlist_readlen(bs, len);
 1415: 		else
 1416: 			data = NULL;
 1417: 
 1418: 		/* Replace the 2 local items with the given one */
 1419: 		if ((item = aim_ssi_itemlist_find(od->ssi.local, gid, bid))) {
 1420: 			item->type = type;
 1421: 			g_free(item->name);
 1422: 			item->name = g_strdup(name);
 1423: 			aim_tlvlist_free(item->data);
 1424: 			item->data = aim_tlvlist_copy(data);
 1425: 		}
 1426: 
 1427: 		if ((item = aim_ssi_itemlist_find(od->ssi.official, gid, bid))) {
 1428: 			item->type = type;
 1429: 			g_free(item->name);
 1430: 			item->name = g_strdup(name);
 1431: 			aim_tlvlist_free(item->data);
 1432: 			item->data = aim_tlvlist_copy(data);
 1433: 		}
 1434: 
 1435: 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1436: 			ret = userfunc(od, conn, frame, snac->subtype, type, name);
 1437: 
 1438: 		g_free(name);
 1439: 		aim_tlvlist_free(data);
 1440: 	}
 1441: 
 1442: 	return ret;
 1443: }
 1444: 
 1445: /*
 1446:  * Subtype 0x000a - Incoming SSI del.
 1447:  *
 1448:  * XXX - It would probably be good for the client to actually do something when it gets this.
 1449:  */
 1450: static int parsedel(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1451: {
 1452: 	int ret = 0;
 1453: 	aim_rxcallback_t userfunc;
 1454: 	guint16 gid, bid;
 1455: 	struct aim_ssi_item *del;
 1456: 
 1457: 	while (byte_stream_bytes_left(bs)) {
 1458: 		byte_stream_advance(bs, byte_stream_get16(bs));
 1459: 		gid = byte_stream_get16(bs);
 1460: 		bid = byte_stream_get16(bs);
 1461: 		byte_stream_get16(bs);
 1462: 		byte_stream_advance(bs, byte_stream_get16(bs));
 1463: 
 1464: 		if ((del = aim_ssi_itemlist_find(od->ssi.local, gid, bid)))
 1465: 			aim_ssi_itemlist_del(&od->ssi.local, del);
 1466: 		if ((del = aim_ssi_itemlist_find(od->ssi.official, gid, bid)))
 1467: 			aim_ssi_itemlist_del(&od->ssi.official, del);
 1468: 
 1469: 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1470: 			ret = userfunc(od, conn, frame);
 1471: 	}
 1472: 
 1473: 	return ret;
 1474: }
 1475: 
 1476: /*
 1477:  * Subtype 0x000e - SSI Add/Mod/Del Ack.
 1478:  *
 1479:  * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
 1480:  *
 1481:  */
 1482: static int parseack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1483: {
 1484: 	int ret = 0;
 1485: 	aim_rxcallback_t userfunc;
 1486: 	struct aim_ssi_tmp *cur, *del;
 1487: 
 1488: 	/* Read in the success/failure flags from the ack SNAC */
 1489: 	cur = od->ssi.pending;
 1490: 	while (cur && (byte_stream_bytes_left(bs)>0)) {
 1491: 		cur->ack = byte_stream_get16(bs);
 1492: 		cur = cur->next;
 1493: 	}
 1494: 
 1495: 	/*
 1496: 	 * If outcome is 0, then add the item to the item list, or replace the other item,
 1497: 	 * or remove the old item.  If outcome is non-zero, then remove the item from the
 1498: 	 * local list, or unmodify it, or add it.
 1499: 	 */
 1500: 	for (cur=od->ssi.pending; (cur && (cur->ack != 0xffff)); cur=cur->next) {
 1501: 	if (cur->item) {
 1502: 		if (cur->ack) {
 1503: 			/* Our action was unsuccessful, so change the local list back to how it was */
 1504: 			if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
 1505: 				/* Remove the item from the local list */
 1506: 				/* Make sure cur->item is still valid memory */
 1507: 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
 1508: 					cur->name = g_strdup(cur->item->name);
 1509: 					aim_ssi_itemlist_del(&od->ssi.local, cur->item);
 1510: 				}
 1511: 				cur->item = NULL;
 1512: 
 1513: 			} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
 1514: 				/* Replace the local item with the item from the official list */
 1515: 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
 1516: 					struct aim_ssi_item *cur1;
 1517: 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 1518: 						g_free(cur->item->name);
 1519: 						cur->item->name = g_strdup(cur1->name);
 1520: 						aim_tlvlist_free(cur->item->data);
 1521: 						cur->item->data = aim_tlvlist_copy(cur1->data);
 1522: 					}
 1523: 				} else
 1524: 					cur->item = NULL;
 1525: 
 1526: 			} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
 1527: 				/* Add the item back into the local list */
 1528: 				if (aim_ssi_itemlist_valid(od->ssi.official, cur->item)) {
 1529: 					aim_ssi_itemlist_add(&od->ssi.local, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
 1530: 				} else
 1531: 					cur->item = NULL;
 1532: 			}
 1533: 
 1534: 		} else {
 1535: 			/* Do the exact opposite */
 1536: 			if (cur->action == SNAC_SUBTYPE_FEEDBAG_ADD) {
 1537: 			/* Add the local item to the official list */
 1538: 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
 1539: 					aim_ssi_itemlist_add(&od->ssi.official, cur->item->name, cur->item->gid, cur->item->bid, cur->item->type, cur->item->data);
 1540: 				} else
 1541: 					cur->item = NULL;
 1542: 
 1543: 			} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_MOD) {
 1544: 				/* Replace the official item with the item from the local list */
 1545: 				if (aim_ssi_itemlist_valid(od->ssi.local, cur->item)) {
 1546: 					struct aim_ssi_item *cur1;
 1547: 					if ((cur1 = aim_ssi_itemlist_find(od->ssi.official, cur->item->gid, cur->item->bid))) {
 1548: 						g_free(cur1->name);
 1549: 						cur1->name = g_strdup(cur->item->name);
 1550: 						aim_tlvlist_free(cur1->data);
 1551: 						cur1->data = aim_tlvlist_copy(cur->item->data);
 1552: 					}
 1553: 				} else
 1554: 					cur->item = NULL;
 1555: 
 1556: 			} else if (cur->action == SNAC_SUBTYPE_FEEDBAG_DEL) {
 1557: 				/* Remove the item from the official list */
 1558: 				if (aim_ssi_itemlist_valid(od->ssi.official, cur->item))
 1559: 					aim_ssi_itemlist_del(&od->ssi.official, cur->item);
 1560: 				cur->item = NULL;
 1561: 			}
 1562: 
 1563: 		}
 1564: 	} /* End if (cur->item) */
 1565: 	} /* End for loop */
 1566: 
 1567: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1568: 		ret = userfunc(od, conn, frame, od->ssi.pending);
 1569: 
 1570: 	/* Free all aim_ssi_tmp's with an outcome */
 1571: 	cur = od->ssi.pending;
 1572: 	while (cur && (cur->ack != 0xffff)) {
 1573: 		del = cur;
 1574: 		cur = cur->next;
 1575: 		g_free(del->name);
 1576: 		g_free(del);
 1577: 	}
 1578: 	od->ssi.pending = cur;
 1579: 
 1580: 	/* If we're not waiting for any more acks, then send more SNACs */
 1581: 	if (!od->ssi.pending) {
 1582: 		od->ssi.waiting_for_ack = FALSE;
 1583: 		aim_ssi_sync(od);
 1584: 	}
 1585: 
 1586: 	return ret;
 1587: }
 1588: 
 1589: /*
 1590:  * Subtype 0x000f - SSI Data Unchanged.
 1591:  *
 1592:  * Response to aim_ssi_reqifchanged() if the server-side data is not newer than
 1593:  * posted local stamp/revision.
 1594:  *
 1595:  */
 1596: static int parsedataunchanged(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1597: {
 1598: 	int ret = 0;
 1599: 	aim_rxcallback_t userfunc;
 1600: 
 1601: 	od->ssi.received_data = TRUE;
 1602: 
 1603: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1604: 		ret = userfunc(od, conn, frame);
 1605: 
 1606: 	return ret;
 1607: }
 1608: 
 1609: /*
 1610:  * Subtype 0x0011 - SSI Begin Data Modification.
 1611:  *
 1612:  * Tell the server you're going to start modifying data.  This marks
 1613:  * the beginning of a transaction.
 1614:  */
 1615: int aim_ssi_modbegin(OscarData *od)
 1616: {
 1617: 	FlapConnection *conn;
 1618: 
 1619: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
 1620: 		return -EINVAL;
 1621: 
 1622: 	aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTART);
 1623: 
 1624: 	return 0;
 1625: }
 1626: 
 1627: /*
 1628:  * Subtype 0x0012 - SSI End Data Modification.
 1629:  *
 1630:  * Tell the server you're finished modifying data.  The marks the end
 1631:  * of a transaction.
 1632:  */
 1633: int aim_ssi_modend(OscarData *od)
 1634: {
 1635: 	FlapConnection *conn;
 1636: 
 1637: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)))
 1638: 		return -EINVAL;
 1639: 
 1640: 	aim_genericreq_n(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_EDITSTOP);
 1641: 
 1642: 	return 0;
 1643: }
 1644: 
 1645: /*
 1646:  * Subtype 0x0015 - Receive an authorization grant
 1647:  */
 1648: static int receiveauthgrant(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1649: {
 1650: 	int ret = 0;
 1651: 	aim_rxcallback_t userfunc;
 1652: 	guint16 tmp;
 1653: 	char *bn, *msg, *tmpstr;
 1654: 
 1655: 	/* Read buddy name */
 1656: 	tmp = byte_stream_get8(bs);
 1657: 	if (!tmp) {
 1658: 		purple_debug_warning("oscar", "Dropping auth grant SNAC "
 1659: 				"because username was empty\n");
 1660: 		return 0;
 1661: 	}
 1662: 	bn = byte_stream_getstr(bs, tmp);
 1663: 	if (!g_utf8_validate(bn, -1, NULL)) {
 1664: 		purple_debug_warning("oscar", "Dropping auth grant SNAC "
 1665: 				"because the username was not valid UTF-8\n");
 1666: 		g_free(bn);
 1667: 	}
 1668: 
 1669: 	/* Read message */
 1670: 	tmp = byte_stream_get16(bs);
 1671: 	if (tmp) {
 1672: 		msg = byte_stream_getstr(bs, tmp);
 1673: 		if (!g_utf8_validate(msg, -1, NULL)) {
 1674: 			/* Ugh, msg isn't UTF8.  Let's salvage. */
 1675: 			purple_debug_warning("oscar", "Got non-UTF8 message in auth "
 1676: 					"grant from %s\n", bn);
 1677: 			tmpstr = purple_utf8_salvage(msg);
 1678: 			g_free(msg);
 1679: 			msg = tmpstr;
 1680: 		}
 1681: 	} else
 1682: 		msg = NULL;
 1683: 
 1684: 	/* Unknown */
 1685: 	tmp = byte_stream_get16(bs);
 1686: 
 1687: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1688: 		ret = userfunc(od, conn, frame, bn, msg);
 1689: 
 1690: 	g_free(bn);
 1691: 	g_free(msg);
 1692: 
 1693: 	return ret;
 1694: }
 1695: 
 1696: /*
 1697:  * Subtype 0x0018 - Send authorization request
 1698:  *
 1699:  * Sends a request for authorization to the given contact.  The request will either be
 1700:  * granted, denied, or dropped.
 1701:  *
 1702:  */
 1703: int aim_ssi_sendauthrequest(OscarData *od, const char *bn, const char *msg)
 1704: {
 1705: 	FlapConnection *conn;
 1706: 	ByteStream bs;
 1707: 	aim_snacid_t snacid;
 1708: 
 1709: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
 1710: 		return -EINVAL;
 1711: 
 1712: 	byte_stream_new(&bs, 1+strlen(bn) + 2+(msg ? strlen(msg)+1 : 0) + 2);
 1713: 
 1714: 	/* Username */
 1715: 	byte_stream_put8(&bs, strlen(bn));
 1716: 	byte_stream_putstr(&bs, bn);
 1717: 
 1718: 	/* Message (null terminated) */
 1719: 	byte_stream_put16(&bs, msg ? strlen(msg) : 0);
 1720: 	if (msg) {
 1721: 		byte_stream_putstr(&bs, msg);
 1722: 		byte_stream_put8(&bs, 0x00);
 1723: 	}
 1724: 
 1725: 	/* Unknown */
 1726: 	byte_stream_put16(&bs, 0x0000);
 1727: 
 1728: 	snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, 0x0000, NULL, 0);
 1729: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREQ, snacid, &bs);
 1730: 
 1731: 	byte_stream_destroy(&bs);
 1732: 
 1733: 	return 0;
 1734: }
 1735: 
 1736: /*
 1737:  * Subtype 0x0019 - Receive an authorization request
 1738:  */
 1739: static int receiveauthrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1740: {
 1741: 	int ret = 0;
 1742: 	aim_rxcallback_t userfunc;
 1743: 	guint16 tmp;
 1744: 	char *bn, *msg, *tmpstr;
 1745: 
 1746: 	/* Read buddy name */
 1747: 	tmp = byte_stream_get8(bs);
 1748: 	if (!tmp) {
 1749: 		purple_debug_warning("oscar", "Dropping auth request SNAC "
 1750: 				"because username was empty\n");
 1751: 		return 0;
 1752: 	}
 1753: 	bn = byte_stream_getstr(bs, tmp);
 1754: 	if (!g_utf8_validate(bn, -1, NULL)) {
 1755: 		purple_debug_warning("oscar", "Dropping auth request SNAC "
 1756: 				"because the username was not valid UTF-8\n");
 1757: 		g_free(bn);
 1758: 	}
 1759: 
 1760: 	/* Read message */
 1761: 	tmp = byte_stream_get16(bs);
 1762: 	if (tmp) {
 1763: 		msg = byte_stream_getstr(bs, tmp);
 1764: 		if (!g_utf8_validate(msg, -1, NULL)) {
 1765: 			/* Ugh, msg isn't UTF8.  Let's salvage. */
 1766: 			purple_debug_warning("oscar", "Got non-UTF8 message in auth "
 1767: 					"request from %s\n", bn);
 1768: 			tmpstr = purple_utf8_salvage(msg);
 1769: 			g_free(msg);
 1770: 			msg = tmpstr;
 1771: 		}
 1772: 	} else
 1773: 		msg = NULL;
 1774: 
 1775: 	/* Unknown */
 1776: 	tmp = byte_stream_get16(bs);
 1777: 
 1778: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1779: 		ret = userfunc(od, conn, frame, bn, msg);
 1780: 
 1781: 	g_free(bn);
 1782: 	g_free(msg);
 1783: 
 1784: 	return ret;
 1785: }
 1786: 
 1787: /*
 1788:  * Subtype 0x001a - Send authorization reply
 1789:  *
 1790:  * Sends a reply to a request for authorization.  The reply can either
 1791:  * grant authorization or deny authorization.
 1792:  *
 1793:  * if reply=0x00 then deny
 1794:  * if reply=0x01 then grant
 1795:  *
 1796:  */
 1797: int aim_ssi_sendauthreply(OscarData *od, const char *bn, guint8 reply, const char *msg)
 1798: {
 1799: 	FlapConnection *conn;
 1800: 	ByteStream bs;
 1801: 	aim_snacid_t snacid;
 1802: 
 1803: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_FEEDBAG)) || !bn)
 1804: 		return -EINVAL;
 1805: 
 1806: 	byte_stream_new(&bs, 1+strlen(bn) + 1 + 2+(msg ? (strlen(msg)+1) : 0) + 2);
 1807: 
 1808: 	/* Username */
 1809: 	byte_stream_put8(&bs, strlen(bn));
 1810: 	byte_stream_putstr(&bs, bn);
 1811: 
 1812: 	/* Grant or deny */
 1813: 	byte_stream_put8(&bs, reply);
 1814: 
 1815: 	/* Message (null terminated) */
 1816: 	byte_stream_put16(&bs, msg ? (strlen(msg)+1) : 0);
 1817: 	if (msg) {
 1818: 		byte_stream_putstr(&bs, msg);
 1819: 		byte_stream_put8(&bs, 0x00);
 1820: 	}
 1821: 
 1822: 	/* Unknown */
 1823: 	byte_stream_put16(&bs, 0x0000);
 1824: 
 1825: 	snacid = aim_cachesnac(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, 0x0000, NULL, 0);
 1826: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SENDAUTHREP, snacid, &bs);
 1827: 
 1828: 	byte_stream_destroy(&bs);
 1829: 
 1830: 	return 0;
 1831: }
 1832: 
 1833: /*
 1834:  * Subtype 0x001b - Receive an authorization reply
 1835:  *
 1836:  * You get this bad boy when other people respond to the authorization
 1837:  * request that you have previously sent them.
 1838:  */
 1839: static int receiveauthreply(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1840: {
 1841: 	int ret = 0;
 1842: 	aim_rxcallback_t userfunc;
 1843: 	guint16 tmp;
 1844: 	guint8 reply;
 1845: 	char *bn, *msg, *tmpstr;
 1846: 
 1847: 	/* Read buddy name */
 1848: 	tmp = byte_stream_get8(bs);
 1849: 	if (!tmp) {
 1850: 		purple_debug_warning("oscar", "Dropping auth reply SNAC "
 1851: 				"because username was empty\n");
 1852: 		return 0;
 1853: 	}
 1854: 	bn = byte_stream_getstr(bs, tmp);
 1855: 	if (!g_utf8_validate(bn, -1, NULL)) {
 1856: 		purple_debug_warning("oscar", "Dropping auth reply SNAC "
 1857: 				"because the username was not valid UTF-8\n");
 1858: 		g_free(bn);
 1859: 	}
 1860: 
 1861: 	/* Read reply */
 1862: 	reply = byte_stream_get8(bs);
 1863: 
 1864: 	/* Read message */
 1865: 	tmp = byte_stream_get16(bs);
 1866: 	if (tmp) {
 1867: 		msg = byte_stream_getstr(bs, tmp);
 1868: 		if (!g_utf8_validate(msg, -1, NULL)) {
 1869: 			/* Ugh, msg isn't UTF8.  Let's salvage. */
 1870: 			purple_debug_warning("oscar", "Got non-UTF8 message in auth "
 1871: 					"reply from %s\n", bn);
 1872: 			tmpstr = purple_utf8_salvage(msg);
 1873: 			g_free(msg);
 1874: 			msg = tmpstr;
 1875: 		}
 1876: 	} else
 1877: 		msg = NULL;
 1878: 
 1879: 	/* Unknown */
 1880: 	tmp = byte_stream_get16(bs);
 1881: 
 1882: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1883: 		ret = userfunc(od, conn, frame, bn, reply, msg);
 1884: 
 1885: 	g_free(bn);
 1886: 	g_free(msg);
 1887: 
 1888: 	return ret;
 1889: }
 1890: 
 1891: /*
 1892:  * Subtype 0x001c - Receive a message telling you someone added you to their list.
 1893:  */
 1894: static int receiveadded(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1895: {
 1896: 	int ret = 0;
 1897: 	aim_rxcallback_t userfunc;
 1898: 	guint16 tmp;
 1899: 	char *bn;
 1900: 
 1901: 	/* Read buddy name */
 1902: 	tmp = byte_stream_get8(bs);
 1903: 	if (!tmp) {
 1904: 		purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
 1905: 				"because username was empty\n");
 1906: 		return 0;
 1907: 	}
 1908: 	bn = byte_stream_getstr(bs, tmp);
 1909: 	if (!g_utf8_validate(bn, -1, NULL)) {
 1910: 		purple_debug_warning("oscar", "Dropping 'you were added' SNAC "
 1911: 				"because the username was not valid UTF-8\n");
 1912: 		g_free(bn);
 1913: 	}
 1914: 
 1915: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1916: 		ret = userfunc(od, conn, frame, bn);
 1917: 
 1918: 	g_free(bn);
 1919: 
 1920: 	return ret;
 1921: }
 1922: 
 1923: /*
 1924:  * If we're on ICQ, then AIM_SSI_TYPE_DENY is used for the "permanently invisible" list.
 1925:  * AIM_SSI_TYPE_ICQDENY is used for blocking users instead.
 1926:  */
 1927: guint16
 1928: aim_ssi_getdenyentrytype(OscarData* od)
 1929: {
 1930: 	return od->icq ? AIM_SSI_TYPE_ICQDENY : AIM_SSI_TYPE_DENY;
 1931: }
 1932: 
 1933: static int
 1934: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1935: {
 1936: 	if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO)
 1937: 		return parserights(od, conn, mod, frame, snac, bs);
 1938: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_LIST)
 1939: 		return parsedata(od, conn, mod, frame, snac, bs);
 1940: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADD)
 1941: 		return parseadd(od, conn, mod, frame, snac, bs);
 1942: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_MOD)
 1943: 		return parsemod(od, conn, mod, frame, snac, bs);
 1944: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_DEL)
 1945: 		return parsedel(od, conn, mod, frame, snac, bs);
 1946: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_SRVACK)
 1947: 		return parseack(od, conn, mod, frame, snac, bs);
 1948: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_NOLIST)
 1949: 		return parsedataunchanged(od, conn, mod, frame, snac, bs);
 1950: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTH)
 1951: 		return receiveauthgrant(od, conn, mod, frame, snac, bs);
 1952: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ)
 1953: 		return receiveauthrequest(od, conn, mod, frame, snac, bs);
 1954: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP)
 1955: 		return receiveauthreply(od, conn, mod, frame, snac, bs);
 1956: 	else if (snac->subtype == SNAC_SUBTYPE_FEEDBAG_ADDED)
 1957: 		return receiveadded(od, conn, mod, frame, snac, bs);
 1958: 
 1959: 	return 0;
 1960: }
 1961: 
 1962: static void
 1963: ssi_shutdown(OscarData *od, aim_module_t *mod)
 1964: {
 1965: 	aim_ssi_freelist(od);
 1966: }
 1967: 
 1968: int
 1969: ssi_modfirst(OscarData *od, aim_module_t *mod)
 1970: {
 1971: 	mod->family = SNAC_FAMILY_FEEDBAG;
 1972: 	mod->version = 0x0004;
 1973: 	mod->toolid = 0x0110;
 1974: 	mod->toolversion = 0x0629;
 1975: 	mod->flags = 0;
 1976: 	strncpy(mod->name, "feedbag", sizeof(mod->name));
 1977: 	mod->snachandler = snachandler;
 1978: 	mod->shutdown = ssi_shutdown;
 1979: 
 1980: 	return 0;
 1981: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>