File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / family_oservice.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 0x0001 - This is a very special group.  All connections support
   23:  * this group, as it does some particularly good things (like rate limiting).
   24:  */
   25: 
   26: #include "oscar.h"
   27: 
   28: #include "cipher.h"
   29: 
   30: /*
   31:  * Each time we make a FLAP connection to an oscar server the server gives
   32:  * us a list of rate classes.  Each rate class has different properties for
   33:  * how frequently we can send SNACs in that rate class before we become
   34:  * throttled or disconnected.
   35:  *
   36:  * The server also gives us a list of every available SNAC and tells us which
   37:  * rate class it's in.  There are a lot of different SNACs, so this list can be
   38:  * fairly large.  One important characteristic of these rate classes is that
   39:  * currently (and since at least 2004) most SNACs are in the same rate class.
   40:  *
   41:  * One optimization we can do to save memory is to only keep track of SNACs
   42:  * that are in classes other than this default rate class.  So if we try to
   43:  * look up a SNAC and it's not in our hash table then we can assume that it's
   44:  * in the default rate class.
   45:  */
   46: #define OSCAR_DEFAULT_RATECLASS 1
   47: 
   48: /* Subtype 0x0002 - Client Online */
   49: void
   50: aim_srv_clientready(OscarData *od, FlapConnection *conn)
   51: {
   52: 	ByteStream bs;
   53: 	aim_snacid_t snacid;
   54: 	GSList *cur;
   55: 
   56: 	byte_stream_new(&bs, 1142);
   57: 
   58: 	/*
   59: 	 * Send only the tool versions that the server cares about (that it
   60: 	 * marked as supporting in the server ready SNAC).
   61: 	 */
   62: 	for (cur = conn->groups; cur != NULL; cur = cur->next)
   63: 	{
   64: 		aim_module_t *mod;
   65: 
   66: 		if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
   67: 		{
   68: 			byte_stream_put16(&bs, mod->family);
   69: 			byte_stream_put16(&bs, mod->version);
   70: 			byte_stream_put16(&bs, mod->toolid);
   71: 			byte_stream_put16(&bs, mod->toolversion);
   72: 		}
   73: 	}
   74: 
   75: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
   76: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
   77: 
   78: 	byte_stream_destroy(&bs);
   79: }
   80: 
   81: /*
   82:  * Subtype 0x0003 - Host Online
   83:  *
   84:  * See comments in conn.c about how the group associations are supposed
   85:  * to work, and how they really work.
   86:  *
   87:  * This info probably doesn't even need to make it to the client.
   88:  *
   89:  * We don't actually call the client here.  This starts off the connection
   90:  * initialization routine required by all AIM connections.  The next time
   91:  * the client is called is the CONNINITDONE callback, which should be
   92:  * shortly after the rate information is acknowledged.
   93:  *
   94:  */
   95: static int
   96: hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
   97: {
   98: 	int group;
   99: 
  100: 	while (byte_stream_bytes_left(bs))
  101: 	{
  102: 		group = byte_stream_get16(bs);
  103: 		conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
  104: 	}
  105: 
  106: 	/*
  107: 	 * Next step is in the Host Versions handler.
  108: 	 *
  109: 	 * Note that we must send this before we request rates, since
  110: 	 * the format of the rate information depends on the versions we
  111: 	 * give it.
  112: 	 *
  113: 	 */
  114: 	aim_srv_setversions(od, conn);
  115: 
  116: 	return 1;
  117: }
  118: 
  119: /* Subtype 0x0004 - Service request */
  120: void
  121: aim_srv_requestnew(OscarData *od, guint16 serviceid)
  122: {
  123: 	FlapConnection *conn;
  124: 	ByteStream bs;
  125: 	aim_snacid_t snacid;
  126: 	GSList *tlvlist = NULL;
  127: 
  128: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
  129: 	if(!conn)
  130: 		return;
  131: 
  132: 	byte_stream_new(&bs, 6);
  133: 
  134: 	byte_stream_put16(&bs, serviceid);
  135: 
  136: 	if (od->use_ssl)
  137: 		/* Request SSL Connection */
  138: 		aim_tlvlist_add_noval(&tlvlist, 0x008c);
  139: 
  140: 	aim_tlvlist_write(&bs, &tlvlist);
  141: 	aim_tlvlist_free(tlvlist);
  142: 
  143: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
  144: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
  145: 
  146: 	byte_stream_destroy(&bs);
  147: }
  148: 
  149: /*
  150:  * Join a room of name roomname.  This is the first step to joining an
  151:  * already created room.  It's basically a Service Request for
  152:  * family 0x000e, with a little added on to specify the exchange and room
  153:  * name.
  154:  */
  155: int
  156: aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
  157: {
  158: 	FlapConnection *conn;
  159: 	ByteStream bs;
  160: 	aim_snacid_t snacid;
  161: 	GSList *tlvlist = NULL;
  162: 	struct chatsnacinfo csi;
  163: 
  164: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
  165: 	if (!conn || !roomname || roomname[0] == '\0')
  166: 		return -EINVAL;
  167: 
  168: 	byte_stream_new(&bs, 506);
  169: 
  170: 	memset(&csi, 0, sizeof(csi));
  171: 	csi.exchange = exchange;
  172: 	g_strlcpy(csi.name, roomname, sizeof(csi.name));
  173: 	csi.instance = instance;
  174: 
  175: 	/*
  176: 	 * Requesting service chat (0x000e)
  177: 	 */
  178: 	byte_stream_put16(&bs, 0x000e);
  179: 
  180: 	aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
  181: 
  182: 	if (od->use_ssl)
  183: 		/* Request SSL Connection */
  184: 		aim_tlvlist_add_noval(&tlvlist, 0x008c);
  185: 
  186: 	aim_tlvlist_write(&bs, &tlvlist);
  187: 	aim_tlvlist_free(tlvlist);
  188: 
  189: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
  190: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
  191: 
  192: 	byte_stream_destroy(&bs);
  193: 
  194: 	return 0;
  195: }
  196: 
  197: /* Subtype 0x0005 - Redirect */
  198: static int
  199: redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  200: {
  201: 	struct aim_redirect_data redir;
  202: 	aim_rxcallback_t userfunc;
  203: 	GSList *tlvlist;
  204: 	aim_snac_t *origsnac = NULL;
  205: 	int ret = 0;
  206: 
  207: 	memset(&redir, 0, sizeof(redir));
  208: 
  209: 	tlvlist = aim_tlvlist_read(bs);
  210: 
  211: 	if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
  212: 			!aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
  213: 			!aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
  214: 		aim_tlvlist_free(tlvlist);
  215: 		return 0;
  216: 	}
  217: 
  218: 	redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
  219: 	redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
  220: 	redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
  221: 	redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
  222: 	redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
  223: 	redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
  224: 
  225: 	/* Fetch original SNAC so we can get csi if needed */
  226: 	origsnac = aim_remsnac(od, snac->id);
  227: 
  228: 	if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) {
  229: 		struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
  230: 
  231: 		redir.chat.exchange = csi->exchange;
  232: 		redir.chat.room = csi->name;
  233: 		redir.chat.instance = csi->instance;
  234: 	}
  235: 
  236: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  237: 		ret = userfunc(od, conn, frame, &redir);
  238: 
  239: 	g_free((void *)redir.ip);
  240: 	g_free((void *)redir.cookie);
  241: 	g_free((void *)redir.ssl_cert_cn);
  242: 
  243: 	if (origsnac)
  244: 		g_free(origsnac->data);
  245: 	g_free(origsnac);
  246: 
  247: 	aim_tlvlist_free(tlvlist);
  248: 
  249: 	return ret;
  250: }
  251: 
  252: /* Subtype 0x0006 - Request Rate Information. */
  253: void
  254: aim_srv_reqrates(OscarData *od, FlapConnection *conn)
  255: {
  256: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
  257: }
  258: 
  259: /*
  260:  * OSCAR defines several 'rate classes'.  Each class has separate
  261:  * rate limiting properties (limit level, alert level, disconnect
  262:  * level, etc), and a set of SNAC family/type pairs associated with
  263:  * it.  The rate classes, their limiting properties, and the definitions
  264:  * of which SNACs belong to which class are defined in the
  265:  * Rate Response packet at login to each host.
  266:  *
  267:  * Logically, all rate offenses within one class count against further
  268:  * offenses for other SNACs in the same class (ie, sending messages
  269:  * too fast will limit the number of user info requests you can send,
  270:  * since those two SNACs are in the same rate class).
  271:  *
  272:  * Since the rate classes are defined dynamically at login, the values
  273:  * below may change. But they seem to be fairly constant.
  274:  *
  275:  * Currently, BOS defines five rate classes, with the commonly used
  276:  * members as follows...
  277:  *
  278:  *  Rate class 0x0001:
  279:  *	- Everything thats not in any of the other classes
  280:  *
  281:  *  Rate class 0x0002:
  282:  *	- Buddy list add/remove
  283:  *	- Permit list add/remove
  284:  *	- Deny list add/remove
  285:  *
  286:  *  Rate class 0x0003:
  287:  *	- User information requests
  288:  *	- Outgoing ICBMs
  289:  *
  290:  *  Rate class 0x0004:
  291:  *	- A few unknowns: 2/9, 2/b, and f/2
  292:  *
  293:  *  Rate class 0x0005:
  294:  *	- Chat room create
  295:  *	- Outgoing chat ICBMs
  296:  *
  297:  * The only other thing of note is that class 5 (chat) has slightly looser
  298:  * limiting properties than class 3 (normal messages).  But thats just a
  299:  * small bit of trivia for you.
  300:  *
  301:  * The last thing that needs to be learned about the rate limiting
  302:  * system is how the actual numbers relate to the passing of time.  This
  303:  * seems to be a big mystery.
  304:  *
  305:  * See joscar's javadoc for the RateClassInfo class for a great
  306:  * explanation.  You might be able to find it at
  307:  * http://dscoder.com/RateClassInfo.html
  308:  */
  309: 
  310: static struct rateclass *
  311: rateclass_find(GSList *rateclasses, guint16 id)
  312: {
  313: 	GSList *tmp;
  314: 
  315: 	for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
  316: 	{
  317: 		struct rateclass *rateclass;
  318: 		rateclass = tmp->data;
  319: 		if (rateclass->classid == id)
  320: 			return rateclass;
  321: 	}
  322: 
  323: 	return NULL;
  324: }
  325: 
  326: /* Subtype 0x0007 - Rate Parameters */
  327: static int
  328: rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  329: {
  330: 	guint16 numclasses, i;
  331: 	aim_rxcallback_t userfunc;
  332: 
  333: 	/*
  334: 	 * First are the parameters for each rate class.
  335: 	 */
  336: 	numclasses = byte_stream_get16(bs);
  337: 	for (i = 0; i < numclasses; i++)
  338: 	{
  339: 		struct rateclass *rateclass;
  340: 		guint32 delta;
  341: 		struct timeval now;
  342: 
  343: 		gettimeofday(&now, NULL);
  344: 		rateclass = g_new(struct rateclass, 1);
  345: 
  346: 		rateclass->classid = byte_stream_get16(bs);
  347: 		rateclass->windowsize = byte_stream_get32(bs);
  348: 		rateclass->clear = byte_stream_get32(bs);
  349: 		rateclass->alert = byte_stream_get32(bs);
  350: 		rateclass->limit = byte_stream_get32(bs);
  351: 		rateclass->disconnect = byte_stream_get32(bs);
  352: 		rateclass->current = byte_stream_get32(bs);
  353: 		rateclass->max = byte_stream_get32(bs);
  354: 		if (mod->version >= 3) {
  355: 			delta = byte_stream_get32(bs);
  356: 			rateclass->dropping_snacs = byte_stream_get8(bs);
  357: 		} else {
  358: 			delta = 0;
  359: 			rateclass->dropping_snacs = 0;
  360: 		}
  361: 
  362: 		rateclass->last.tv_sec = now.tv_sec - delta / 1000;
  363: 		rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
  364: 
  365: 		conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
  366: 
  367: 		if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
  368: 			conn->default_rateclass = rateclass;
  369: 	}
  370: 	conn->rateclasses = g_slist_reverse(conn->rateclasses);
  371: 
  372: 	/*
  373: 	 * Then the members of each class.
  374: 	 */
  375: 	for (i = 0; i < numclasses; i++)
  376: 	{
  377: 		guint16 classid, count;
  378: 		struct rateclass *rateclass;
  379: 		int j;
  380: 
  381: 		classid = byte_stream_get16(bs);
  382: 		count = byte_stream_get16(bs);
  383: 
  384: 		if (classid == OSCAR_DEFAULT_RATECLASS) {
  385: 			/*
  386: 			 * Don't bother adding these SNACs to the hash table.  See the
  387: 			 * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
  388: 			 */
  389: 			byte_stream_advance(bs, 4 * count);
  390: 			continue;
  391: 		}
  392: 
  393: 		rateclass = rateclass_find(conn->rateclasses, classid);
  394: 
  395: 		for (j = 0; j < count; j++)
  396: 		{
  397: 			guint16 group, subtype;
  398: 
  399: 			group = byte_stream_get16(bs);
  400: 			subtype = byte_stream_get16(bs);
  401: 
  402: 			if (rateclass != NULL)
  403: 				g_hash_table_insert(conn->rateclass_members,
  404: 						GUINT_TO_POINTER((group << 16) + subtype),
  405: 						rateclass);
  406: 		}
  407: 	}
  408: 
  409: 	/*
  410: 	 * We don't pass the rate information up to the client, as it really
  411: 	 * doesn't care.  The information is stored in the connection, however
  412: 	 * so that we can do rate limiting management when sending SNACs.
  413: 	 */
  414: 
  415: 	/*
  416: 	 * Subscribe to rate change information for all rate classes.
  417: 	 */
  418: 	aim_srv_rates_addparam(od, conn);
  419: 
  420: 	/*
  421: 	 * Finally, tell the client it's ready to go...
  422: 	 */
  423: 	if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
  424: 		userfunc(od, conn, frame);
  425: 
  426: 	return 1;
  427: }
  428: 
  429: /* Subtype 0x0008 - Add Rate Parameter */
  430: void
  431: aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
  432: {
  433: 	ByteStream bs;
  434: 	aim_snacid_t snacid;
  435: 	GSList *tmp;
  436: 
  437: 	byte_stream_new(&bs, 502);
  438: 
  439: 	for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
  440: 	{
  441: 		struct rateclass *rateclass;
  442: 		rateclass = tmp->data;
  443: 		byte_stream_put16(&bs, rateclass->classid);
  444: 	}
  445: 
  446: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
  447: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
  448: 
  449: 	byte_stream_destroy(&bs);
  450: }
  451: 
  452: /* Subtype 0x000a - Rate Change */
  453: static int
  454: ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  455: {
  456: 	guint16 code, classid;
  457: 	struct rateclass *rateclass;
  458: 	guint32 delta;
  459: 	struct timeval now;
  460: 	static const char *codes[5] = {
  461: 		"invalid",
  462: 		"change",
  463: 		"warning",
  464: 		"limit",
  465: 		"limit cleared",
  466: 	};
  467: 
  468: 	gettimeofday(&now, NULL);
  469: 	code = byte_stream_get16(bs);
  470: 	classid = byte_stream_get16(bs);
  471: 
  472: 	rateclass = rateclass_find(conn->rateclasses, classid);
  473: 	if (rateclass == NULL)
  474: 		/* This should never really happen */
  475: 		return 0;
  476: 
  477: 	rateclass->windowsize = byte_stream_get32(bs);
  478: 	rateclass->clear = byte_stream_get32(bs);
  479: 	rateclass->alert = byte_stream_get32(bs);
  480: 	rateclass->limit = byte_stream_get32(bs);
  481: 	rateclass->disconnect = byte_stream_get32(bs);
  482: 	rateclass->current = byte_stream_get32(bs);
  483: 	rateclass->max = byte_stream_get32(bs);
  484: 	if (mod->version >= 3) {
  485: 		delta = byte_stream_get32(bs);
  486: 		rateclass->dropping_snacs = byte_stream_get8(bs);
  487: 	} else {
  488: 		delta = 0;
  489: 		rateclass->dropping_snacs = 0;
  490: 	}
  491: 
  492: 	rateclass->last.tv_sec = now.tv_sec - delta / 1000;
  493: 	rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
  494: 
  495: 	purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
  496: 			"maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
  497: 			"disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
  498: 			(code < 5) ? codes[code] : codes[0], rateclass->classid,
  499: 			rateclass->current, rateclass->max, rateclass->alert,
  500: 			rateclass->clear, rateclass->limit, rateclass->disconnect,
  501: 			delta, rateclass->dropping_snacs, rateclass->windowsize);
  502: 
  503: 	if (code == AIM_RATE_CODE_LIMIT) {
  504: 		purple_debug_warning("oscar",  "The last action you attempted "
  505: 				"could not be performed because you are over the rate "
  506: 				"limit. Please wait 10 seconds and try again.\n");
  507: 	}
  508: 
  509: 	return 1;
  510: }
  511: 
  512: /*
  513:  * How Migrations work.
  514:  *
  515:  * The server sends a Server Pause message, which the client should respond to
  516:  * with a Server Pause Ack, which contains the families it needs on this
  517:  * connection. The server will send a Migration Notice with an IP address, and
  518:  * then disconnect. Next the client should open the connection and send the
  519:  * cookie.  Repeat the normal login process and pretend this never happened.
  520:  *
  521:  * The Server Pause contains no data.
  522:  *
  523:  */
  524: 
  525: /* Subtype 0x000b - Service Pause */
  526: static int
  527: serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  528: {
  529: 	int ret = 0;
  530: 	aim_rxcallback_t userfunc;
  531: 
  532: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  533: 		ret = userfunc(od, conn, frame);
  534: 
  535: 	return ret;
  536: }
  537: 
  538: /* Subtype 0x000d - Service Resume */
  539: static int
  540: serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  541: {
  542: 	int ret = 0;
  543: 	aim_rxcallback_t userfunc;
  544: 
  545: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  546: 		ret = userfunc(od, conn, frame);
  547: 
  548: 	return ret;
  549: }
  550: 
  551: /* Subtype 0x000e - Request self-info */
  552: void
  553: aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
  554: {
  555: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
  556: }
  557: 
  558: /* Subtype 0x000f - Self User Info */
  559: static int
  560: selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  561: {
  562: 	int ret = 0;
  563: 	aim_rxcallback_t userfunc;
  564: 	aim_userinfo_t userinfo;
  565: 
  566: 	aim_info_extract(od, bs, &userinfo);
  567: 
  568: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  569: 		ret = userfunc(od, conn, frame, &userinfo);
  570: 
  571: 	aim_info_free(&userinfo);
  572: 
  573: 	return ret;
  574: }
  575: 
  576: /* Subtype 0x0010 - Evil Notification */
  577: static int
  578: evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  579: {
  580: 	int ret = 0;
  581: 	aim_rxcallback_t userfunc;
  582: 	guint16 newevil;
  583: 	aim_userinfo_t userinfo;
  584: 
  585: 	memset(&userinfo, 0, sizeof(aim_userinfo_t));
  586: 
  587: 	newevil = byte_stream_get16(bs);
  588: 
  589: 	if (byte_stream_bytes_left(bs))
  590: 		aim_info_extract(od, bs, &userinfo);
  591: 
  592: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  593: 		ret = userfunc(od, conn, frame, newevil, &userinfo);
  594: 
  595: 	aim_info_free(&userinfo);
  596: 
  597: 	return ret;
  598: }
  599: 
  600: /*
  601:  * Subtype 0x0011 - Idle Notification
  602:  *
  603:  * Should set your current idle time in seconds.  Note that this should
  604:  * never be called consecutively with a non-zero idle time.  That makes
  605:  * OSCAR do funny things.  Instead, just set it once you go idle, and then
  606:  * call it again with zero when you're back.
  607:  *
  608:  */
  609: void
  610: aim_srv_setidle(OscarData *od, guint32 idletime)
  611: {
  612: 	FlapConnection *conn;
  613: 
  614: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
  615: 	if(!conn)
  616: 		return;
  617: 
  618: 	aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
  619: }
  620: 
  621: /*
  622:  * Subtype 0x0012 - Service Migrate
  623:  *
  624:  * This is the final SNAC sent on the original connection during a migration.
  625:  * It contains the IP and cookie used to connect to the new server, and
  626:  * optionally a list of the SNAC groups being migrated.
  627:  *
  628:  */
  629: static int
  630: migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  631: {
  632: 	aim_rxcallback_t userfunc;
  633: 	int ret = 0;
  634: 	guint16 groupcount, i;
  635: 	GSList *tlvlist;
  636: 	char *ip = NULL;
  637: 	aim_tlv_t *cktlv;
  638: 
  639: 	/*
  640: 	 * Apparently there's some fun stuff that can happen right here. The
  641: 	 * migration can actually be quite selective about what groups it
  642: 	 * moves to the new server.  When not all the groups for a connection
  643: 	 * are migrated, or they are all migrated but some groups are moved
  644: 	 * to a different server than others, it is called a bifurcated
  645: 	 * migration.
  646: 	 *
  647: 	 * Let's play dumb and not support that.
  648: 	 *
  649: 	 */
  650: 	groupcount = byte_stream_get16(bs);
  651: 	for (i = 0; i < groupcount; i++) {
  652: 		guint16 group;
  653: 
  654: 		group = byte_stream_get16(bs);
  655: 
  656: 		purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
  657: 	}
  658: 
  659: 	tlvlist = aim_tlvlist_read(bs);
  660: 
  661: 	if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
  662: 		ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
  663: 
  664: 	cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
  665: 
  666: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  667: 		ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
  668: 
  669: 	aim_tlvlist_free(tlvlist);
  670: 	g_free(ip);
  671: 
  672: 	return ret;
  673: }
  674: 
  675: /* Subtype 0x0013 - Message of the Day */
  676: static int
  677: motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  678: {
  679: 	aim_rxcallback_t userfunc;
  680: 	char *msg = NULL;
  681: 	int ret = 0;
  682: 	GSList *tlvlist;
  683: 	guint16 id;
  684: 
  685: 	/*
  686: 	 * Code.
  687: 	 *
  688: 	 * Valid values:
  689: 	 *   1 Mandatory upgrade
  690: 	 *   2 Advisory upgrade
  691: 	 *   3 System bulletin
  692: 	 *   4 Nothing's wrong ("top o the world" -- normal)
  693: 	 *   5 Lets-break-something.
  694: 	 *
  695: 	 */
  696: 	id = byte_stream_get16(bs);
  697: 
  698: 	/*
  699: 	 * TLVs follow
  700: 	 */
  701: 	tlvlist = aim_tlvlist_read(bs);
  702: 
  703: 	msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
  704: 
  705: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  706: 		ret = userfunc(od, conn, frame, id, msg);
  707: 
  708: 	g_free(msg);
  709: 
  710: 	aim_tlvlist_free(tlvlist);
  711: 
  712: 	return ret;
  713: }
  714: 
  715: /*
  716:  * Subtype 0x0017 - Set client versions
  717:  *
  718:  * If you've seen the clientonline/clientready SNAC you're probably
  719:  * wondering what the point of this one is.  And that point seems to be
  720:  * that the versions in the client online SNAC are sent too late for the
  721:  * server to be able to use them to change the protocol for the earlier
  722:  * login packets (client versions are sent right after Host Online is
  723:  * received, but client online versions aren't sent until quite a bit later).
  724:  * We can see them already making use of this by changing the format of
  725:  * the rate information based on what version of group 1 we advertise here.
  726:  *
  727:  */
  728: void
  729: aim_srv_setversions(OscarData *od, FlapConnection *conn)
  730: {
  731: 	ByteStream bs;
  732: 	aim_snacid_t snacid;
  733: 	GSList *cur;
  734: 
  735: 	byte_stream_new(&bs, 1142);
  736: 
  737: 	/*
  738: 	 * Send only the versions that the server cares about (that it
  739: 	 * marked as supporting in the server ready SNAC).
  740: 	 */
  741: 	for (cur = conn->groups; cur != NULL; cur = cur->next)
  742: 	{
  743: 		aim_module_t *mod;
  744: 
  745: 		if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
  746: 		{
  747: 			byte_stream_put16(&bs, mod->family);
  748: 			byte_stream_put16(&bs, mod->version);
  749: 		}
  750: 	}
  751: 
  752: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
  753: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
  754: 
  755: 	byte_stream_destroy(&bs);
  756: }
  757: 
  758: /* Subtype 0x0018 - Host versions */
  759: static int
  760: hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  761: {
  762: 	int vercount;
  763: 	guint8 *versions;
  764: 
  765: 	/* This is frivolous. (Thank you SmarterChild.) */
  766: 	vercount = byte_stream_bytes_left(bs)/4;
  767: 
  768: 	/* XXX: vercount probably should be used for reading versions. */
  769: 	(void)vercount;
  770: 	versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
  771: 	g_free(versions);
  772: 
  773: 	/*
  774: 	 * Now request rates.
  775: 	 */
  776: 	aim_srv_reqrates(od, conn);
  777: 
  778: 	return 1;
  779: }
  780: 
  781: /**
  782:  * Subtype 0x001e - Extended Status/Extra Info.
  783:  *
  784:  * These settings are transient, not server-stored (i.e. they only
  785:  * apply to this session, and must be re-set the next time you sign
  786:  * on).
  787:  *
  788:  * You can set your ICQ status (available, away, do not disturb,
  789:  * etc.), or whether your IP address should be hidden or not, or
  790:  * if your status is visible on ICQ web sites, and you can set
  791:  * your IP address info and what not.
  792:  *
  793:  * You can also set your "available" message.  This is currently
  794:  * only supported by iChat, Purple and other 3rd party clients.
  795:  *
  796:  * These are the same TLVs seen in user info.  You can
  797:  * also set 0x0008 and 0x000c.
  798:  */
  799: int
  800: aim_srv_setextrainfo(OscarData *od,
  801: 		gboolean seticqstatus, guint32 icqstatus,
  802: 		gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
  803: {
  804: 	FlapConnection *conn;
  805: 	ByteStream bs;
  806: 	aim_snacid_t snacid;
  807: 	GSList *tlvlist = NULL;
  808: 
  809: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  810: 		return -EINVAL;
  811: 
  812: 	if (seticqstatus)
  813: 	{
  814: 		aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
  815: 				AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
  816: 	}
  817: 
  818: 	if (setstatusmsg)
  819: 	{
  820: 		size_t statusmsglen, itmsurllen;
  821: 		ByteStream tmpbs;
  822: 
  823: 		statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
  824: 		itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
  825: 
  826: 		byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
  827: 		byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
  828: 		byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
  829: 
  830: 		aim_tlvlist_add_raw(&tlvlist, 0x001d,
  831: 				byte_stream_curpos(&tmpbs), tmpbs.data);
  832: 		byte_stream_destroy(&tmpbs);
  833: 	}
  834: 
  835: 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
  836: 
  837: 	aim_tlvlist_write(&bs, &tlvlist);
  838: 	aim_tlvlist_free(tlvlist);
  839: 
  840: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
  841: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
  842: 
  843: 	byte_stream_destroy(&bs);
  844: 
  845: 	return 0;
  846: }
  847: 
  848: /* Send dummy DC (direct connect) information to the server.
  849:  * Direct connect is ICQ's counterpart for AIM's DirectIM,
  850:  * as far as I can tell. Anyway, we don't support it;
  851:  * the reason to send this packet is that some clients
  852:  * (Miranda, QIP) won't send us channel 2 ICBM messages
  853:  * unless we specify DC version >= 8.
  854:  *
  855:  * See #12044 for more information.
  856:  */
  857: void
  858: aim_srv_set_dc_info(OscarData *od)
  859: {
  860: 	FlapConnection *conn;
  861: 
  862: 	ByteStream bs, tlv0c;
  863: 	aim_snacid_t snacid;
  864: 	GSList *tlvlist = NULL;
  865: 
  866: 	/* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
  867: 	 * Kopete sends a dummy DC info, too, so I just copied the values from them.
  868: 	 */
  869: 	byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
  870: 	byte_stream_put32(&tlv0c, 0x0);
  871: 	byte_stream_put32(&tlv0c, 0x0);
  872: 	byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
  873: 	byte_stream_put16(&tlv0c, 8); /* DC version */
  874: 	byte_stream_put32(&tlv0c, 0x0);
  875: 	byte_stream_put32(&tlv0c, 0x50);
  876: 	byte_stream_put32(&tlv0c, 0x3);
  877: 	byte_stream_put32(&tlv0c, 0x0);
  878: 	byte_stream_put32(&tlv0c, 0x0);
  879: 	byte_stream_put32(&tlv0c, 0x0);
  880: 	byte_stream_put16(&tlv0c, 0x0);
  881: 	aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
  882: 	byte_stream_destroy(&tlv0c);
  883: 
  884: 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
  885: 	aim_tlvlist_write(&bs, &tlvlist);
  886: 	aim_tlvlist_free(tlvlist);
  887: 
  888: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
  889: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  890: 	g_warn_if_fail(conn != NULL);
  891: 	if (conn) {
  892: 		flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
  893: 			0x001e, snacid, &bs);
  894: 	}
  895: 
  896: 	byte_stream_destroy(&bs);
  897: }
  898: 
  899: /**
  900:  * Starting this past week (26 Mar 2001, say), AOL has started sending
  901:  * this nice little extra SNAC.  AFAIK, it has never been used until now.
  902:  *
  903:  * The request contains eight bytes.  The first four are an offset, the
  904:  * second four are a length.
  905:  *
  906:  * The offset is an offset into aim.exe when it is mapped during execution
  907:  * on Win32.  So far, AOL has only been requesting bytes in static regions
  908:  * of memory.  (I won't put it past them to start requesting data in
  909:  * less static regions -- regions that are initialized at run time, but still
  910:  * before the client receives this request.)
  911:  *
  912:  * When the client receives the request, it adds it to the current ds
  913:  * (0x00400000) and dereferences it, copying the data into a buffer which
  914:  * it then runs directly through the MD5 hasher.  The 16 byte output of
  915:  * the hash is then sent back to the server.
  916:  *
  917:  * If the client does not send any data back, or the data does not match
  918:  * the data that the specific client should have, the client will get the
  919:  * following message from "AOL Instant Messenger":
  920:  *    "You have been disconnected from the AOL Instant Message Service (SM)
  921:  *     for accessing the AOL network using unauthorized software.  You can
  922:  *     download a FREE, fully featured, and authorized client, here
  923:  *     http://www.aol.com/aim/download2.html"
  924:  * The connection is then closed, receiving disconnect code 1, URL
  925:  * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
  926:  *
  927:  * Note, however, that numerous inconsistencies can cause the above error,
  928:  * not just sending back a bad hash.  Do not immediatly suspect this code
  929:  * if you get disconnected.  AOL and the open/free software community have
  930:  * played this game for a couple years now, generating the above message
  931:  * on numerous ocassions.
  932:  *
  933:  * Anyway, neener.  We win again.
  934:  *
  935:  */
  936: /* Subtype 0x001f - Client verification */
  937: static int
  938: memrequest(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  939: {
  940: 	int ret = 0;
  941: 	aim_rxcallback_t userfunc;
  942: 	guint32 offset, len;
  943: 	GSList *tlvlist;
  944: 	char *modname;
  945: 
  946: 	offset = byte_stream_get32(bs);
  947: 	len = byte_stream_get32(bs);
  948: 	tlvlist = aim_tlvlist_read(bs);
  949: 
  950: 	modname = aim_tlv_getstr(tlvlist, 0x0001, 1);
  951: 
  952: 	purple_debug_info("oscar", "Got memory request for data at 0x%08x (%u bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
  953: 
  954: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
  955: 		ret = userfunc(od, conn, frame, offset, len, modname);
  956: 
  957: 	g_free(modname);
  958: 	aim_tlvlist_free(tlvlist);
  959: 
  960: 	return ret;
  961: }
  962: 
  963: /* Subtype 0x0020 - Client verification reply */
  964: int
  965: aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
  966: {
  967: 	ByteStream bs;
  968: 	aim_snacid_t snacid;
  969: 
  970: 	if (!od || !conn)
  971: 		return -EINVAL;
  972: 
  973: 	byte_stream_new(&bs, 2+16);
  974: 
  975: 	byte_stream_put16(&bs, 0x0010); /* md5 is always 16 bytes */
  976: 
  977: 	if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
  978: 
  979: 		byte_stream_putraw(&bs, buf, 0x10);
  980: 
  981: 	} else if (buf && (len > 0)) { /* use input buffer */
  982: 		PurpleCipherContext *context;
  983: 		guchar digest[16];
  984: 
  985: 		context = purple_cipher_context_new_by_name("md5", NULL);
  986: 		purple_cipher_context_append(context, buf, len);
  987: 		purple_cipher_context_digest(context, 16, digest, NULL);
  988: 		purple_cipher_context_destroy(context);
  989: 
  990: 		byte_stream_putraw(&bs, digest, 0x10);
  991: 
  992: 	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
  993: 		PurpleCipherContext *context;
  994: 		guchar digest[16];
  995: 		guint8 nil = '\0';
  996: 
  997: 		/*
  998: 		 * I'm not sure if we really need the empty append with the
  999: 		 * new MD5 functions, so I'll leave it in, just in case.
 1000: 		 */
 1001: 		context = purple_cipher_context_new_by_name("md5", NULL);
 1002: 		purple_cipher_context_append(context, &nil, 0);
 1003: 		purple_cipher_context_digest(context, 16, digest, NULL);
 1004: 		purple_cipher_context_destroy(context);
 1005: 
 1006: 		byte_stream_putraw(&bs, digest, 0x10);
 1007: 
 1008: 	} else {
 1009: 
 1010: 		/*
 1011: 		 * This data is correct for AIM 3.5.1670.
 1012: 		 *
 1013: 		 * Using these blocks is as close to "legal" as you can get
 1014: 		 * without using an AIM binary.
 1015: 		 *
 1016: 		 */
 1017: 		if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
 1018: 
 1019: #if 1 /* with "AnrbnrAqhfzcd" */
 1020: 			byte_stream_put32(&bs, 0x44a95d26);
 1021: 			byte_stream_put32(&bs, 0xd2490423);
 1022: 			byte_stream_put32(&bs, 0x93b8821f);
 1023: 			byte_stream_put32(&bs, 0x51c54b01);
 1024: #else /* no filename */
 1025: 			byte_stream_put32(&bs, 0x1df8cbae);
 1026: 			byte_stream_put32(&bs, 0x5523b839);
 1027: 			byte_stream_put32(&bs, 0xa0e10db3);
 1028: 			byte_stream_put32(&bs, 0xa46d3b39);
 1029: #endif
 1030: 
 1031: 		} else
 1032: 			purple_debug_warning("oscar", "sendmemblock: unknown hash request\n");
 1033: 
 1034: 	}
 1035: 
 1036: 	snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
 1037: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, snacid, &bs);
 1038: 
 1039: 	byte_stream_destroy(&bs);
 1040: 
 1041: 	return 0;
 1042: }
 1043: 
 1044: /*
 1045:  * Subtype 0x0021 - Receive our extended status
 1046:  *
 1047:  * This is used for iChat's "available" messages, and maybe ICQ extended
 1048:  * status messages?  It's also used to tell the client whether or not it
 1049:  * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
 1050:  */
 1051: static int
 1052: aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1053: {
 1054: 	guint16 type = byte_stream_get16(bs);
 1055: 	if (type == 0x0000 || type == 0x0001) {
 1056: 		/* buddy icon checksum */
 1057: 		/* not sure what the difference between 1 and 0 is */
 1058: 		guint8 flags = byte_stream_get8(bs);
 1059: 		guint8 length = byte_stream_get8(bs);
 1060: 		guint8 *md5 = byte_stream_getraw(bs, length);
 1061: 
 1062: 		if ((flags == 0x00) || (flags == 0x41)) {
 1063: 			if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
 1064: 				od->iconconnecting = TRUE;
 1065: 				od->set_icon = TRUE;
 1066: 				aim_srv_requestnew(od, SNAC_FAMILY_BART);
 1067: 			} else {
 1068: 				PurpleAccount *account = purple_connection_get_account(od->gc);
 1069: 				PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
 1070: 				if (img == NULL) {
 1071: 					aim_ssi_delicon(od);
 1072: 				} else {
 1073: 
 1074: 					purple_debug_info("oscar",
 1075: 									"Uploading icon to icon server\n");
 1076: 					aim_bart_upload(od, purple_imgstore_get_data(img),
 1077: 							purple_imgstore_get_size(img));
 1078: 					purple_imgstore_unref(img);
 1079: 				}
 1080: 			}
 1081: 		} else if (flags == 0x81) {
 1082: 			PurpleAccount *account = purple_connection_get_account(od->gc);
 1083: 			PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
 1084: 			if (img == NULL)
 1085: 				aim_ssi_delicon(od);
 1086: 			else {
 1087: 				aim_ssi_seticon(od, md5, length);
 1088: 				purple_imgstore_unref(img);
 1089: 			}
 1090: 		}
 1091: 
 1092: 		g_free(md5);
 1093: 	}
 1094: 
 1095: 	return 0;
 1096: }
 1097: 
 1098: static int
 1099: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1100: {
 1101: 	if (snac->subtype == 0x0003)
 1102: 		return hostonline(od, conn, mod, frame, snac, bs);
 1103: 	else if (snac->subtype == 0x0005)
 1104: 		return redirect(od, conn, mod, frame, snac, bs);
 1105: 	else if (snac->subtype == 0x0007)
 1106: 		return rateresp(od, conn, mod, frame, snac, bs);
 1107: 	else if (snac->subtype == 0x000a)
 1108: 		return ratechange(od, conn, mod, frame, snac, bs);
 1109: 	else if (snac->subtype == 0x000b)
 1110: 		return serverpause(od, conn, mod, frame, snac, bs);
 1111: 	else if (snac->subtype == 0x000d)
 1112: 		return serverresume(od, conn, mod, frame, snac, bs);
 1113: 	else if (snac->subtype == 0x000f)
 1114: 		return selfinfo(od, conn, mod, frame, snac, bs);
 1115: 	else if (snac->subtype == 0x0010)
 1116: 		return evilnotify(od, conn, mod, frame, snac, bs);
 1117: 	else if (snac->subtype == 0x0012)
 1118: 		return migrate(od, conn, mod, frame, snac, bs);
 1119: 	else if (snac->subtype == 0x0013)
 1120: 		return motd(od, conn, mod, frame, snac, bs);
 1121: 	else if (snac->subtype == 0x0018)
 1122: 		return hostversions(od, conn, mod, frame, snac, bs);
 1123: 	else if (snac->subtype == 0x001f)
 1124: 		return memrequest(od, conn, mod, frame, snac, bs);
 1125: 	else if (snac->subtype == 0x0021)
 1126: 		return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
 1127: 
 1128: 	return 0;
 1129: }
 1130: 
 1131: int service_modfirst(OscarData *od, aim_module_t *mod)
 1132: {
 1133: 	mod->family = SNAC_FAMILY_OSERVICE;
 1134: 	mod->version = 0x0003;
 1135: 	mod->toolid = 0x0110;
 1136: 	mod->toolversion = 0x0629;
 1137: 	mod->flags = 0;
 1138: 	strncpy(mod->name, "oservice", sizeof(mod->name));
 1139: 	mod->snachandler = snachandler;
 1140: 
 1141: 	return 0;
 1142: }

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