File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / family_locate.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 0x0002 - Locate.
   23:  *
   24:  * The functions here are responsible for requesting and parsing information-
   25:  * gathering SNACs.  Or something like that.  This family contains the SNACs
   26:  * for getting and setting info, away messages, directory profile thingy, etc.
   27:  */
   28: 
   29: #include "oscar.h"
   30: #ifdef _WIN32
   31: #include "win32dep.h"
   32: #endif
   33: 
   34: /* Define to log unknown TLVs */
   35: /* #define LOG_UNKNOWN_TLV */
   36: 
   37: /*
   38:  * Capability blocks.
   39:  *
   40:  * These are CLSIDs. They should actually be of the form:
   41:  *
   42:  * {0x0946134b, 0x4c7f, 0x11d1,
   43:  *  {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
   44:  *
   45:  * But, eh.
   46:  */
   47: static const struct {
   48: 	guint64 flag;
   49: 	guint8 data[16];
   50: } aim_caps[] = {
   51: 
   52: 	/*
   53: 	 * These are in ascending numerical order.
   54: 	 */
   55: 
   56: 	/* Client understands short caps, a UUID of the form
   57: 	 * 0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short cap. */
   58: 	{OSCAR_CAPABILITY_SHORTCAPS,
   59: 	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
   60: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   61: 
   62: 	{OSCAR_CAPABILITY_SECUREIM,
   63: 	 {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
   64: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   65: 
   66: 	/* OSCAR_CAPABILITY_XHTML_IM */
   67: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
   68: 	 {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
   69: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   70: 
   71: 	{OSCAR_CAPABILITY_VIDEO,
   72: 	 {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
   73: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   74: 
   75: 	/* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */
   76: 	{OSCAR_CAPABILITY_LIVEVIDEO,
   77: 	 {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1,
   78: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   79: 
   80: 	/* "Camera" support in Windows AIM 5.5.3501 and newer */
   81: 	{OSCAR_CAPABILITY_CAMERA,
   82: 	 {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1,
   83: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   84: 
   85: 	/* "Microphone" support in Windows AIM 5.5.3501 and newer */
   86: 	/* OSCAR_CAPABILITY_MICROPHONE */
   87: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
   88: 	 {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
   89: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   90: 
   91: 	/* Supports RTC Audio */
   92: 	/* OSCAR_CAPABILITY_RTCAUDIO */
   93: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
   94: 	 {0x09, 0x46, 0x01, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
   95: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
   96: 
   97: 	/* In iChatAV (version numbers...?) */
   98: 	{OSCAR_CAPABILITY_ICHATAV,
   99: 	 {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
  100: 	  0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}},
  101: 
  102: 	/* Supports "new status message features" (Who advertises this one?) */
  103: 	/* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */
  104: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  105: 	 {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1,
  106: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  107: 
  108: 	/* Supports "see as I type" (Who advertises this one?) */
  109: 	/* OSCAR_CAPABILITY_SEE_AS_I_TYPE */
  110: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  111: 	 {0x09, 0x46, 0x01, 0x0b, 0x4c, 0x7f, 0x11, 0xd1,
  112: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  113: 
  114: 	/* Client only asserts caps for services in which it is participating */
  115: 	/* OSCAR_CAPABILITY_SMARTCAPS */
  116: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  117: 	 {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1,
  118: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  119: 
  120: 	{OSCAR_CAPABILITY_HIPTOP,
  121: 	 {0x09, 0x46, 0x13, 0x23, 0x4c, 0x7f, 0x11, 0xd1,
  122: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  123: 
  124: 	{OSCAR_CAPABILITY_TALK,
  125: 	 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
  126: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  127: 
  128: 	{OSCAR_CAPABILITY_SENDFILE,
  129: 	 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
  130: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  131: 
  132: 	{OSCAR_CAPABILITY_ICQ_DIRECT,
  133: 	 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
  134: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  135: 
  136: 	{OSCAR_CAPABILITY_DIRECTIM,
  137: 	 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
  138: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  139: 
  140: 	{OSCAR_CAPABILITY_BUDDYICON,
  141: 	 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
  142: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  143: 
  144: 	{OSCAR_CAPABILITY_ADDINS,
  145: 	 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
  146: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  147: 
  148: 	{OSCAR_CAPABILITY_GETFILE,
  149: 	 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
  150: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  151: 
  152: 	{OSCAR_CAPABILITY_ICQSERVERRELAY,
  153: 	 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
  154: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  155: 
  156: 	/*
  157: 	 * Indeed, there are two of these.  The former appears to be correct,
  158: 	 * but in some versions of winaim, the second one is set.  Either they
  159: 	 * forgot to fix endianness, or they made a typo. It really doesn't
  160: 	 * matter which.
  161: 	 */
  162: 	{OSCAR_CAPABILITY_GAMES,
  163: 	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
  164: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  165: 	{OSCAR_CAPABILITY_GAMES2,
  166: 	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
  167: 	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  168: 
  169: 	/* New format of caps (xtraz icons) */
  170: 	{OSCAR_CAPABILITY_NEWCAPS,
  171: 	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
  172: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  173: 
  174: 	/* Support xtraz statuses */
  175: 	{OSCAR_CAPABILITY_XTRAZ,
  176: 	 {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5,
  177: 	  0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}},
  178: 
  179: 	{OSCAR_CAPABILITY_SENDBUDDYLIST,
  180: 	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
  181: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  182: 
  183: 	/*
  184: 	 * Setting this lets AIM users receive messages from ICQ users, and ICQ
  185: 	 * users receive messages from AIM users.  It also lets ICQ users show
  186: 	 * up in buddy lists for AIM users, and AIM users show up in buddy lists
  187: 	 * for ICQ users.  And ICQ privacy/invisibility acts like AIM privacy,
  188: 	 * in that if you add a user to your deny list, you will not be able to
  189: 	 * see them as online (previous you could still see them, but they
  190: 	 * couldn't see you.
  191: 	 */
  192: 	{OSCAR_CAPABILITY_INTEROPERATE,
  193: 	 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
  194: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  195: 
  196: 	{OSCAR_CAPABILITY_UNICODE,
  197: 	 {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1,
  198: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  199: 
  200: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  201: 	 {0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
  202: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  203: 
  204: 	{OSCAR_CAPABILITY_ICHAT_SCREENSHARE,
  205: 	 {0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
  206: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  207: 
  208: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  209: 	 {0x09, 0x46, 0xf0, 0x05, 0x4c, 0x7f, 0x11, 0xd1,
  210: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  211: 
  212: 	{OSCAR_CAPABILITY_UNICODEOLD,
  213: 	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
  214: 	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
  215: 
  216: 	{OSCAR_CAPABILITY_TYPING,
  217: 	 {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd,
  218: 	  0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}},
  219: 
  220: 	/*
  221: 	 * Chat is oddball.
  222: 	 */
  223: 	{OSCAR_CAPABILITY_CHAT,
  224: 	 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
  225: 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
  226: 
  227: 	/* This is added by the servers and it only shows up for ourselves... */
  228: 	{OSCAR_CAPABILITY_GENERICUNKNOWN,
  229: 	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
  230: 	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09}},
  231: 
  232: 	{OSCAR_CAPABILITY_ICQRTF,
  233: 	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
  234: 	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
  235: 
  236: 	{OSCAR_CAPABILITY_APINFO,
  237: 	 {0xaa, 0x4a, 0x32, 0xb5, 0xf8, 0x84, 0x48, 0xc6,
  238: 	  0xa3, 0xd7, 0x8c, 0x50, 0x97, 0x19, 0xfd, 0x5b}},
  239: 
  240: 	{OSCAR_CAPABILITY_TRILLIANCRYPT,
  241: 	 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
  242: 	  0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
  243: 
  244: 	{OSCAR_CAPABILITY_EMPTY,
  245: 	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  246: 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
  247: 
  248: 	{OSCAR_CAPABILITY_HTML_MSGS,
  249: 	 {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15,
  250: 	  0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}},
  251: 
  252: 	{OSCAR_CAPABILITY_LAST,
  253: 	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  254: 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
  255: };
  256: 
  257: /* Keep this array synchronized with icq_purple_moods. */
  258: static const struct {
  259: 	const char *mood;
  260: 	guint8 data[16];
  261: } icq_custom_icons[] = {
  262: 
  263: 	{"thinking",
  264: 	 {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
  265: 	  0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
  266: 
  267: 	{"busy",
  268: 	 {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
  269: 	  0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
  270: 
  271: 	{"shopping",
  272: 	 {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff,
  273: 	  0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}},
  274: 
  275: 	/* This was in the original patch, but isn't what the official client
  276: 	 * (ICQ 6) sets when you choose its typewriter icon. */
  277: 	{"typing",
  278: 	 {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
  279: 	  0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
  280: 
  281: 	{"question",
  282: 	 {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
  283: 	  0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
  284: 
  285: 	{"angry",
  286: 	 {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
  287: 	  0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
  288: 
  289: 	{"plate",
  290: 	 {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
  291: 	  0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
  292: 
  293: 	{"cinema",
  294: 	 {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
  295: 	  0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
  296: 
  297: 	{"sick",
  298: 	 {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
  299: 	  0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
  300: 
  301: 	{"typing",
  302: 	 {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
  303: 	  0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
  304: 
  305: 	{"suit",
  306: 	 {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
  307: 	  0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
  308: 
  309: 	{"bathing",
  310: 	 {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
  311: 	  0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
  312: 
  313: 	{"tv",
  314: 	 {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
  315: 	  0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
  316: 
  317: 	{"excited",
  318: 	 {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
  319: 	  0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
  320: 
  321: 	{"sleeping",
  322: 	 {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
  323: 	  0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
  324: 
  325: 	{"hiptop",
  326: 	 {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
  327: 	  0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
  328: 
  329: 	{"in_love",
  330: 	 {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
  331: 	  0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
  332: 
  333: 	{"sleepy",
  334: 	 {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
  335: 	  0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
  336: 
  337: 	{"meeting",
  338: 	 {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
  339: 	  0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
  340: 
  341: 	{"phone",
  342: 	 {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
  343: 	  0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
  344: 
  345: 	{"surfing",
  346: 	 {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
  347: 	  0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
  348: 
  349: 	{"mobile",
  350: 	 {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
  351: 	  0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
  352: 
  353: 	{"search",
  354: 	 {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
  355: 	  0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
  356: 
  357: 	{"party",
  358: 	 {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
  359: 	  0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
  360: 
  361: 	{"coffee",
  362: 	 {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
  363: 	  0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
  364: 
  365: 	{"console",
  366: 	 {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
  367: 	  0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
  368: 
  369: 	{"internet",
  370: 	 {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
  371: 	  0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
  372: 
  373: 	{"cigarette",
  374: 	 {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
  375: 	  0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
  376: 
  377: 	{"writing",
  378: 	 {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
  379: 	  0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
  380: 
  381: 	{"beer",
  382: 	 {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
  383: 	  0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
  384: 
  385: 	{"music",
  386: 	 {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
  387: 	  0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
  388: 
  389: 	{"studying",
  390: 	 {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
  391: 	  0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
  392: 
  393: 	{"working",
  394: 	 {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
  395: 	  0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
  396: 
  397: 	{"restroom",
  398: 	 {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
  399: 	  0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}},
  400: 
  401: 	{NULL,
  402: 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  403: 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
  404: };
  405: 
  406: /* Keep this array synchronized with icq_custom_icons. */
  407: static PurpleMood icq_purple_moods[] = {
  408: 	{"thinking", N_("Thinking"), NULL},
  409: 	{"busy", N_("Busy"), NULL},
  410: 	{"shopping", N_("Shopping"), NULL},
  411: 	/* This was in the original patch, but isn't what the official client
  412: 	 * (ICQ 6) sets when you choose its typewriter icon. */
  413: 	{"typing", NULL, NULL},
  414: 	{"question", N_("Questioning"), NULL},
  415: 	{"angry", N_("Angry"), NULL},
  416: 	{"plate", N_("Eating"), NULL},
  417: 	{"cinema", N_("Watching a movie"), NULL},
  418: 	{"sick", N_("Sick"), NULL},
  419: 	{"typing", N_("Typing"), NULL},
  420: 	{"suit", N_("At the office"), NULL},
  421: 	{"bathing", N_("Taking a bath"), NULL},
  422: 	{"tv", N_("Watching TV"), NULL},
  423: 	{"excited", N_("Having fun"), NULL},
  424: 	{"sleeping", N_("Sleeping"), NULL},
  425: 	{"hiptop", N_("Using a PDA"), NULL},
  426: 	{"in_love", N_("In love"), NULL},
  427: 	/* Sleepy / Tired */
  428: 	{"sleepy", N_("Sleepy"), NULL},
  429: 	{"meeting", N_("Meeting friends"), NULL},
  430: 	{"phone", N_("On the phone"), NULL},
  431: 	{"surfing", N_("Surfing"), NULL},
  432: 	/* "I am mobile." / "John is mobile." */
  433: 	{"mobile", N_("Mobile"), NULL},
  434: 	{"search", N_("Searching the web"), NULL},
  435: 	{"party", N_("At a party"), NULL},
  436: 	{"coffee", N_("Having Coffee"), NULL},
  437: 	/* Playing video games */
  438: 	{"console", N_("Gaming"), NULL},
  439: 	{"internet", N_("Browsing the web"), NULL},
  440: 	{"cigarette", N_("Smoking"), NULL},
  441: 	{"writing", N_("Writing"), NULL},
  442: 	/* Drinking [Alcohol] */
  443: 	{"beer", N_("Drinking"), NULL},
  444: 	{"music", N_("Listening to music"), NULL},
  445: 	{"studying", N_("Studying"), NULL},
  446: 	{"working", N_("Working"), NULL},
  447: 	{"restroom", N_("In the restroom"), NULL},
  448: 	/* Mark the last record. */
  449: 	{NULL, NULL, NULL},
  450: };
  451: 
  452: 
  453: /*
  454:  * Add the userinfo to our linked list.  If we already have userinfo
  455:  * for this buddy, then just overwrite parts of the old data.
  456:  *
  457:  * @param userinfo Contains the new information for the buddy.
  458:  */
  459: static void
  460: aim_locate_adduserinfo(OscarData *od, aim_userinfo_t *userinfo)
  461: {
  462: 	aim_userinfo_t *cur;
  463: 
  464: 	cur = aim_locate_finduserinfo(od, userinfo->bn);
  465: 
  466: 	if (cur == NULL) {
  467: 		cur = (aim_userinfo_t *)g_new0(aim_userinfo_t, 1);
  468: 		cur->bn = g_strdup(userinfo->bn);
  469: 		cur->next = od->locate.userinfo;
  470: 		od->locate.userinfo = cur;
  471: 	}
  472: 
  473: 	cur->warnlevel = userinfo->warnlevel;
  474: 	cur->idletime = userinfo->idletime;
  475: 	if (userinfo->flags != 0)
  476: 		cur->flags = userinfo->flags;
  477: 	if (userinfo->createtime != 0)
  478: 		cur->createtime = userinfo->createtime;
  479: 	if (userinfo->membersince != 0)
  480: 		cur->membersince = userinfo->membersince;
  481: 	if (userinfo->onlinesince != 0)
  482: 		cur->onlinesince = userinfo->onlinesince;
  483: 	if (userinfo->sessionlen != 0)
  484: 		cur->sessionlen = userinfo->sessionlen;
  485: 	if (userinfo->capabilities != 0)
  486: 		cur->capabilities = userinfo->capabilities;
  487: 
  488: 	cur->present |= userinfo->present;
  489: 
  490: 	if (userinfo->iconcsumlen > 0) {
  491: 		g_free(cur->iconcsum);
  492: 		cur->iconcsum = (guint8 *)g_malloc(userinfo->iconcsumlen);
  493: 		memcpy(cur->iconcsum, userinfo->iconcsum, userinfo->iconcsumlen);
  494: 		cur->iconcsumlen = userinfo->iconcsumlen;
  495: 	}
  496: 
  497: 	if (userinfo->info != NULL) {
  498: 		g_free(cur->info);
  499: 		g_free(cur->info_encoding);
  500: 		if (userinfo->info_len > 0) {
  501: 			cur->info = (char *)g_malloc(userinfo->info_len);
  502: 			memcpy(cur->info, userinfo->info, userinfo->info_len);
  503: 		} else
  504: 			cur->info = NULL;
  505: 		cur->info_encoding = g_strdup(userinfo->info_encoding);
  506: 		cur->info_len = userinfo->info_len;
  507: 	}
  508: 
  509: 	if (userinfo->status != NULL) {
  510: 		g_free(cur->status);
  511: 		g_free(cur->status_encoding);
  512: 		if (userinfo->status_len > 0) {
  513: 			cur->status = (char *)g_malloc(userinfo->status_len);
  514: 			memcpy(cur->status, userinfo->status, userinfo->status_len);
  515: 		} else
  516: 			cur->status = NULL;
  517: 		if (userinfo->status_encoding != NULL)
  518: 			cur->status_encoding = g_strdup(userinfo->status_encoding);
  519: 		else
  520: 			cur->status_encoding = NULL;
  521: 		cur->status_len = userinfo->status_len;
  522: 	}
  523: 
  524: 	if (userinfo->itmsurl != NULL) {
  525: 		g_free(cur->itmsurl);
  526: 		g_free(cur->itmsurl_encoding);
  527: 		if (userinfo->itmsurl_len > 0) {
  528: 			cur->itmsurl = (char *)g_malloc(userinfo->itmsurl_len);
  529: 			memcpy(cur->itmsurl, userinfo->itmsurl, userinfo->itmsurl_len);
  530: 		} else
  531: 			cur->itmsurl = NULL;
  532: 		if (userinfo->itmsurl_encoding != NULL)
  533: 			cur->itmsurl_encoding = g_strdup(userinfo->itmsurl_encoding);
  534: 		else
  535: 			cur->itmsurl_encoding = NULL;
  536: 		cur->itmsurl_len = userinfo->itmsurl_len;
  537: 	}
  538: 
  539: 	if (userinfo->away != NULL) {
  540: 		g_free(cur->away);
  541: 		g_free(cur->away_encoding);
  542: 		if (userinfo->away_len > 0) {
  543: 			cur->away = (char *)g_malloc(userinfo->away_len);
  544: 			memcpy(cur->away, userinfo->away, userinfo->away_len);
  545: 		} else
  546: 			cur->away = NULL;
  547: 		cur->away_encoding = g_strdup(userinfo->away_encoding);
  548: 		cur->away_len = userinfo->away_len;
  549: 
  550: 	} else {
  551: 		/*
  552: 		 * We don't have an away message specified in this user_info
  553: 		 * block, so clear any cached away message now.
  554: 		 */
  555: 		if (cur->away) {
  556: 			g_free(cur->away);
  557: 			cur->away = NULL;
  558: 		}
  559: 		if (cur->away_encoding) {
  560: 			g_free(cur->away_encoding);
  561: 			cur->away_encoding = NULL;
  562: 		}
  563: 		cur->away_len = 0;
  564: 	}
  565: }
  566: 
  567: aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *bn) {
  568: 	aim_userinfo_t *cur = NULL;
  569: 
  570: 	if (bn == NULL)
  571: 		return NULL;
  572: 
  573: 	cur = od->locate.userinfo;
  574: 
  575: 	while (cur != NULL) {
  576: 		if (oscar_util_name_compare(cur->bn, bn) == 0)
  577: 			return cur;
  578: 		cur = cur->next;
  579: 	}
  580: 
  581: 	return NULL;
  582: }
  583: 
  584: guint64
  585: aim_locate_getcaps(OscarData *od, ByteStream *bs, int len)
  586: {
  587: 	guint64 flags = 0;
  588: 	int offset;
  589: 
  590: 	for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
  591: 		guint8 *cap;
  592: 		int i, identified;
  593: 
  594: 		cap = byte_stream_getraw(bs, 0x10);
  595: 
  596: 		for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
  597: 			if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
  598: 				flags |= aim_caps[i].flag;
  599: 				identified++;
  600: 				break; /* should only match once... */
  601: 			}
  602: 		}
  603: 
  604: 		if (!identified)
  605: 			purple_debug_misc("oscar", "unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  606: 					cap[0], cap[1], cap[2], cap[3],
  607: 					cap[4], cap[5],
  608: 					cap[6], cap[7],
  609: 					cap[8], cap[9],
  610: 					cap[10], cap[11], cap[12], cap[13],
  611: 					cap[14], cap[15]);
  612: 		g_free(cap);
  613: 	}
  614: 
  615: 	return flags;
  616: }
  617: 
  618: static const char *
  619: aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
  620: {
  621: 	int offset;
  622: 	const char *result = NULL;
  623: 
  624: 	for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x10) {
  625: 		/* check wheather this capability is a custom user icon */
  626: 		guint8 *cap;
  627: 		int i;
  628: 
  629: 		cap = byte_stream_getraw(bs, 0x10);
  630: 
  631: 		for (i = 0; icq_custom_icons[i].mood; i++) {
  632: 			if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) {
  633: 				purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description);
  634: 				result = icq_custom_icons[i].mood;
  635: 				break; /* should only match once... */
  636: 			}
  637: 		}
  638: 		g_free(cap);
  639: 	}
  640: 
  641: 	return result;
  642: }
  643: 
  644: guint64
  645: aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
  646: {
  647: 	guint64 flags = 0;
  648: 	int offset;
  649: 
  650: 	for (offset = 0; byte_stream_bytes_left(bs) && (offset < len); offset += 0x02) {
  651: 		guint8 *cap;
  652: 		int i, identified;
  653: 
  654: 		cap = byte_stream_getraw(bs, 0x02);
  655: 
  656: 		for (i = 0, identified = 0; !(aim_caps[i].flag & OSCAR_CAPABILITY_LAST); i++) {
  657: 			if (memcmp(&aim_caps[i].data[2], cap, 0x02) == 0) {
  658: 				flags |= aim_caps[i].flag;
  659: 				identified++;
  660: 				break; /* should only match once... */
  661: 			}
  662: 		}
  663: 
  664: 		if (!identified)
  665: 			purple_debug_misc("oscar", "unknown short capability: {%02x%02x}\n", cap[0], cap[1]);
  666: 
  667: 		g_free(cap);
  668: 	}
  669: 
  670: 	return flags;
  671: }
  672: 
  673: int
  674: byte_stream_putcaps(ByteStream *bs, guint64 caps)
  675: {
  676: 	int i;
  677: 
  678: 	if (!bs)
  679: 		return -EINVAL;
  680: 
  681: 	for (i = 0; byte_stream_bytes_left(bs); i++) {
  682: 		if (aim_caps[i].flag == OSCAR_CAPABILITY_LAST)
  683: 			break;
  684: 
  685: 		if (caps & aim_caps[i].flag)
  686: 			byte_stream_putraw(bs, aim_caps[i].data, 0x10);
  687: 	}
  688: 	return 0;
  689: }
  690: 
  691: #ifdef LOG_UNKNOWN_TLV
  692: static void
  693: dumptlv(OscarData *od, guint16 type, ByteStream *bs, guint8 len)
  694: {
  695: 	int i;
  696: 
  697: 	if (!od || !bs || !len)
  698: 		return;
  699: 
  700: 	purple_debug_misc("oscar", "userinfo:   type  =0x%04x\n", type);
  701: 	purple_debug_misc("oscar", "userinfo:   length=0x%04x\n", len);
  702: 	purple_debug_misc("oscar", "userinfo:   value:\n");
  703: 
  704: 	for (i = 0; i < len; i++) {
  705: 		if ((i % 8) == 0)
  706: 			purple_debug_misc("oscar", "\nuserinfo:        ");
  707: 		purple_debug_misc("oscar", "0x%2x ", byte_stream_get8(bs));
  708: 	}
  709: 
  710: 	purple_debug_misc("oscar", "\n");
  711: 
  712: 	return;
  713: }
  714: #endif
  715: 
  716: void
  717: aim_info_free(aim_userinfo_t *info)
  718: {
  719: 	g_free(info->bn);
  720: 	g_free(info->iconcsum);
  721: 	g_free(info->info);
  722: 	g_free(info->info_encoding);
  723: 	g_free(info->status);
  724: 	g_free(info->status_encoding);
  725: 	g_free(info->itmsurl);
  726: 	g_free(info->itmsurl_encoding);
  727: 	g_free(info->away);
  728: 	g_free(info->away_encoding);
  729: }
  730: 
  731: static const struct {
  732: 	char *icqmood;
  733: 	const char *mood;
  734: } icqmoods[] = {
  735: 	{"icqmood0",  "shopping"},
  736: 	{"icqmood1",  "bathing"},
  737: 	{"icqmood2",  "sleepy"},
  738: 	{"icqmood3",  "party"},
  739: 	{"icqmood4",  "beer"},
  740: 	{"icqmood5",  "thinking"},
  741: 	{"icqmood6",  "plate"},
  742: 	{"icqmood7",  "tv"},
  743: 	{"icqmood8",  "meeting"},
  744: 	{"icqmood9",  "coffee"},
  745: 	{"icqmood10", "music"},
  746: 	{"icqmood11", "suit"},
  747: 	{"icqmood12", "cinema"},
  748: 	{"icqmood13", "smile-big"},
  749: 	{"icqmood14", "phone"},
  750: 	{"icqmood15", "console"},
  751: 	{"icqmood16", "studying"},
  752: 	{"icqmood17", "sick"},
  753: 	{"icqmood18", "sleeping"},
  754: 	{"icqmood19", "surfing"},
  755: 	{"icqmood20", "internet"},
  756: 	{"icqmood21", "working"},
  757: 	{"icqmood22", "typing"},
  758: 	{"icqmood23", "angry"},
  759: 	{NULL, 0}
  760: 
  761: };
  762: 
  763: /*
  764:  * AIM is fairly regular about providing user info.  This is a generic
  765:  * routine to extract it in its standard form.
  766:  */
  767: int
  768: aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *outinfo)
  769: {
  770: 	int curtlv, tlvcnt;
  771: 	guint8 bnlen;
  772: 
  773: 	if (!bs || !outinfo)
  774: 		return -EINVAL;
  775: 
  776: 	/* Clear out old data first */
  777: 	memset(outinfo, 0x00, sizeof(aim_userinfo_t));
  778: 
  779: 	/*
  780: 	 * Username.  Stored as an unterminated string prepended with a
  781: 	 * byte containing its length.
  782: 	 */
  783: 	bnlen = byte_stream_get8(bs);
  784: 	outinfo->bn = byte_stream_getstr(bs, bnlen);
  785: 
  786: 	/*
  787: 	 * Warning Level.  Stored as an unsigned short.
  788: 	 */
  789: 	outinfo->warnlevel = byte_stream_get16(bs);
  790: 
  791: 	/*
  792: 	 * TLV Count. Unsigned short representing the number of
  793: 	 * Type-Length-Value triples that follow.
  794: 	 */
  795: 	tlvcnt = byte_stream_get16(bs);
  796: 
  797: 	/*
  798: 	 * Parse out the Type-Length-Value triples as they're found.
  799: 	 */
  800: 	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
  801: 		guint16 type, length;
  802: 		int endpos;
  803: 		int curpos;
  804: 
  805: 		type = byte_stream_get16(bs);
  806: 		length = byte_stream_get16(bs);
  807: 		curpos = byte_stream_curpos(bs);
  808: 		endpos = curpos + MIN(length, byte_stream_bytes_left(bs));
  809: 
  810: 		if (type == 0x0001) {
  811: 			/*
  812: 			 * User flags
  813: 			 *
  814: 			 * Specified as any of the following ORed together:
  815: 			 *      0x0001  Unconfirmed account
  816: 			 *      0x0002  Unknown bit 2
  817: 			 *      0x0004  AOL Main Service user
  818: 			 *      0x0008  Unknown bit 4
  819: 			 *      0x0010  Free (AIM) user
  820: 			 *      0x0020  Away
  821: 			 *      0x0040  ICQ user (AIM bit also set)
  822: 			 *      0x0080  Mobile device
  823: 			 *      0x0400  Bot (like ActiveBuddy)
  824: 			 */
  825: 			outinfo->flags = byte_stream_get16(bs);
  826: 			outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
  827: 
  828: 		} else if (type == 0x0002) {
  829: 			/*
  830: 			 * Account creation time
  831: 			 *
  832: 			 * The time/date that the user originally registered for
  833: 			 * the service, stored in time_t format.
  834: 			 *
  835: 			 * I'm not sure how this differs from type 5 ("member
  836: 			 * since").
  837: 			 *
  838: 			 * Note: This is the field formerly known as "member
  839: 			 * since".  All these years and I finally found out
  840: 			 * that I got the name wrong.
  841: 			 */
  842: 			outinfo->createtime = byte_stream_get32(bs);
  843: 			outinfo->present |= AIM_USERINFO_PRESENT_CREATETIME;
  844: 
  845: 		} else if (type == 0x0003) {
  846: 			/*
  847: 			 * On-Since date
  848: 			 *
  849: 			 * The time/date that the user started their current
  850: 			 * session, stored in time_t format.
  851: 			 */
  852: 			outinfo->onlinesince = byte_stream_get32(bs);
  853: 			outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
  854: 
  855: 		} else if (type == 0x0004) {
  856: 			/*
  857: 			 * Idle time
  858: 			 *
  859: 			 * Number of minutes since the user actively used the
  860: 			 * service.
  861: 			 *
  862: 			 * Note that the client tells the server when to start
  863: 			 * counting idle times, so this may or may not be
  864: 			 * related to reality.
  865: 			 */
  866: 			outinfo->idletime = byte_stream_get16(bs);
  867: 			outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
  868: 
  869: 		} else if (type == 0x0005) {
  870: 			/*
  871: 			 * Member since date
  872: 			 *
  873: 			 * The time/date that the user originally registered for
  874: 			 * the service, stored in time_t format.
  875: 			 *
  876: 			 * This is sometimes sent instead of type 2 ("account
  877: 			 * creation time"), particularly in the self-info.
  878: 			 * And particularly for ICQ?
  879: 			 */
  880: 			outinfo->membersince = byte_stream_get32(bs);
  881: 			outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
  882: 
  883: 		} else if (type == 0x0006) {
  884: 			/*
  885: 			 * ICQ Online Status
  886: 			 *
  887: 			 * ICQ's Away/DND/etc "enriched" status. Some decoding
  888: 			 * of values done by Scott <darkagl@pcnet.com>
  889: 			 */
  890: 			byte_stream_get16(bs);
  891: 			outinfo->icqinfo.status = byte_stream_get16(bs);
  892: 			outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
  893: 
  894: 		} else if (type == 0x0008) {
  895: 			/*
  896: 			 * Client type, or some such.
  897: 			 */
  898: 
  899: 		} else if (type == 0x000a) {
  900: 			/*
  901: 			 * ICQ User IP Address
  902: 			 *
  903: 			 * Ahh, the joy of ICQ security.
  904: 			 */
  905: 			outinfo->icqinfo.ipaddr = byte_stream_get32(bs);
  906: 			outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
  907: 
  908: 		} else if (type == 0x000c) {
  909: 			/*
  910: 			 * Random crap containing the IP address,
  911: 			 * apparently a port number, and some Other Stuff.
  912: 			 *
  913: 			 * Format is:
  914: 			 * 4 bytes - Our IP address, 0xc0 a8 01 2b for 192.168.1.43
  915: 			 */
  916: 			byte_stream_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
  917: 			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
  918: 
  919: 		} else if (type == 0x000d) {
  920: 			PurpleAccount *account = purple_connection_get_account(od->gc);
  921: 			const char *mood;
  922: 
  923: 			/*
  924: 			 * OSCAR Capability information
  925: 			 */
  926: 			outinfo->capabilities |= aim_locate_getcaps(od, bs, length);
  927: 			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
  928: 			byte_stream_setpos(bs, curpos);
  929: 
  930: 			mood = aim_receive_custom_icon(od, bs, length);
  931: 			if (mood)
  932: 				purple_prpl_got_user_status(account, outinfo->bn, "mood",
  933: 						PURPLE_MOOD_NAME, mood,
  934: 						NULL);
  935: 			else
  936: 				purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
  937: 
  938: 		} else if (type == 0x000e) {
  939: 			/*
  940: 			 * AOL capability information
  941: 			 */
  942: 
  943: 		} else if ((type == 0x000f) || (type == 0x0010)) {
  944: 			/*
  945: 			 * Type = 0x000f: Session Length. (AIM)
  946: 			 * Type = 0x0010: Session Length. (AOL)
  947: 			 *
  948: 			 * The duration, in seconds, of the user's current
  949: 			 * session.
  950: 			 *
  951: 			 * Which TLV type this comes in depends on the
  952: 			 * service the user is using (AIM or AOL).
  953: 			 */
  954: 			outinfo->sessionlen = byte_stream_get32(bs);
  955: 			outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
  956: 
  957: 		} else if (type == 0x0014) {
  958: 			/*
  959: 			 * My instance number.
  960: 			 */
  961: 			byte_stream_get8(bs);
  962: 
  963: 		} else if (type == 0x0019) {
  964: 			/*
  965: 			 * OSCAR short capability information.  A shortened
  966: 			 * form of the normal capabilities.
  967: 			 */
  968: 			outinfo->capabilities |= aim_locate_getcaps_short(od, bs, length);
  969: 			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
  970: 
  971: 		} else if (type == 0x001a) {
  972: 			/*
  973: 			 * Type = 0x001a
  974: 			 *
  975: 			 * AOL short capability information.  A shortened
  976: 			 * form of the normal capabilities.
  977: 			 */
  978: 
  979: 		} else if (type == 0x001b) {
  980: 			/*
  981: 			 * Encryption certification MD5 checksum.
  982: 			 */
  983: 
  984: 		} else if (type == 0x001d) {
  985: 			/*
  986: 			 * Buddy icon information and status/available messages.
  987: 			 *
  988: 			 * This almost seems like the AIM protocol guys gave
  989: 			 * the iChat guys a Type, and the iChat guys tried to
  990: 			 * cram as much cool shit into it as possible.  Then
  991: 			 * the Windows AIM guys were like, "hey, that's
  992: 			 * pretty neat, let's copy those prawns."
  993: 			 *
  994: 			 * In that spirit, this can contain a custom message,
  995: 			 * kind of like an away message, but you're not away
  996: 			 * (it's called an "available" message).  Or it can
  997: 			 * contain information about the buddy icon the user
  998: 			 * has stored on the server.
  999: 			 */
 1000: 			guint16 type2;
 1001: 			guint8 number2, length2;
 1002: 			int endpos2;
 1003: 
 1004: 			/*
 1005: 			 * Continue looping as long as we're able to read type2,
 1006: 			 * number2, and length2.
 1007: 			 */
 1008: 			while (byte_stream_curpos(bs) + 4 <= endpos) {
 1009: 				type2 = byte_stream_get16(bs);
 1010: 				number2 = byte_stream_get8(bs);
 1011: 				length2 = byte_stream_get8(bs);
 1012: 
 1013: 				endpos2 = byte_stream_curpos(bs) + MIN(length2, byte_stream_bytes_left(bs));
 1014: 
 1015: 				switch (type2) {
 1016: 					case 0x0000: { /* This is an official buddy icon? */
 1017: 						/* This is always 5 bytes of "0x02 01 d2 04 72"? */
 1018: 					} break;
 1019: 
 1020: 					case 0x0001: { /* A buddy icon checksum */
 1021: 						if ((length2 > 0) && ((number2 == 0x00) || (number2 == 0x01))) {
 1022: 							g_free(outinfo->iconcsum);
 1023: 							outinfo->iconcsumtype = number2;
 1024: 							outinfo->iconcsum = byte_stream_getraw(bs, length2);
 1025: 							outinfo->iconcsumlen = length2;
 1026: 						}
 1027: 					} break;
 1028: 
 1029: 					case 0x0002: { /* A status/available message */
 1030: 						g_free(outinfo->status);
 1031: 						g_free(outinfo->status_encoding);
 1032: 						if (length2 >= 4) {
 1033: 							outinfo->status_len = byte_stream_get16(bs);
 1034: 							outinfo->status = byte_stream_getstr(bs, outinfo->status_len);
 1035: 							if (byte_stream_get16(bs) == 0x0001) { /* We have an encoding */
 1036: 								byte_stream_get16(bs);
 1037: 								outinfo->status_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
 1038: 							} else {
 1039: 								/* No explicit encoding, client should use UTF-8 */
 1040: 								outinfo->status_encoding = NULL;
 1041: 							}
 1042: 						} else {
 1043: 							byte_stream_advance(bs, length2);
 1044: 							outinfo->status_len = 0;
 1045: 							outinfo->status = g_strdup("");
 1046: 							outinfo->status_encoding = NULL;
 1047: 						}
 1048: 					} break;
 1049: 
 1050: 					case 0x0009: { /* An iTunes Music Store link */
 1051: 						g_free(outinfo->itmsurl);
 1052: 						g_free(outinfo->itmsurl_encoding);
 1053: 						if (length2 >= 4) {
 1054: 							outinfo->itmsurl_len = byte_stream_get16(bs);
 1055: 							outinfo->itmsurl = byte_stream_getstr(bs, outinfo->itmsurl_len);
 1056: 							if (byte_stream_get16(bs) == 0x0001) {
 1057: 								/* We have an encoding */
 1058: 								byte_stream_get16(bs);
 1059: 								outinfo->itmsurl_encoding = byte_stream_getstr(bs, byte_stream_get16(bs));
 1060: 							} else {
 1061: 								/* No explicit encoding, client should use UTF-8 */
 1062: 								outinfo->itmsurl_encoding = NULL;
 1063: 							}
 1064: 						} else {
 1065: 							byte_stream_advance(bs, length2);
 1066: 							outinfo->itmsurl_len = 0;
 1067: 							outinfo->itmsurl = g_strdup("");
 1068: 							outinfo->itmsurl_encoding = NULL;
 1069: 						}
 1070: 					} break;
 1071: 
 1072: 					case 0x000e: { /* ICQ mood */
 1073: 						PurpleAccount *account = purple_connection_get_account(od->gc);
 1074: 						char *icqmood;
 1075: 						gint32 i;
 1076: 						const char *mood = NULL;
 1077: 
 1078: 						icqmood = byte_stream_getstr(bs, length2);
 1079: 
 1080: 						/* icqmood = "" means X-Status
 1081: 						 * with no mood icon. */
 1082: 						if (*icqmood) {
 1083: 							for (i = 0; icqmoods[i].icqmood; i++) {
 1084: 								if (purple_strequal(icqmood, icqmoods[i].icqmood)) {
 1085: 									mood = icqmoods[i].mood;
 1086: 									break; /* should only match once... */
 1087: 								}
 1088: 							}
 1089: 
 1090: 							if (!mood)
 1091: 								purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
 1092: 						}
 1093: 						g_free(icqmood);
 1094: 
 1095: 						if (mood)
 1096: 							purple_prpl_got_user_status(account, outinfo->bn, "mood",
 1097: 									PURPLE_MOOD_NAME, mood,
 1098: 									NULL);
 1099: 						else
 1100: 							purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
 1101: 					} break;
 1102: 				}
 1103: 
 1104: 				/* Save ourselves. */
 1105: 				byte_stream_setpos(bs, endpos2);
 1106: 			}
 1107: 
 1108: 		} else if (type == 0x001e) {
 1109: 			/*
 1110: 			 * Always four bytes, but it doesn't look like an int.
 1111: 			 */
 1112: 
 1113: 		} else if (type == 0x001f) {
 1114: 			/*
 1115: 			 * Upper bytes of user flags.  Can be any size
 1116: 			 *
 1117: 			 * Seen on a buddy using DeadAIM.  Data was 4 bytes:
 1118: 			 * 0x00 00 00 10
 1119: 			 */
 1120: 
 1121: 		} else if (type == 0x0023) {
 1122: 			/*
 1123: 			 * Last Buddy Feed update time, in seconds since the epoch.
 1124: 			 */
 1125: 
 1126: 		} else if (type == 0x0026) {
 1127: 			/*
 1128: 			 * Time that the profile was set, in seconds since the epoch.
 1129: 			 */
 1130: 
 1131: 		} else if (type == 0x0027) {
 1132: 			/*
 1133: 			 * Time that the away message was set, in seconds since the epoch.
 1134: 			 */
 1135: 
 1136: 		} else if (type == 0x002a) {
 1137: 			/*
 1138: 			 * Country code based on GeoIP data.
 1139: 			 */
 1140: 
 1141: 		} else {
 1142: 
 1143: 			/*
 1144: 			 * Reaching here indicates that either AOL has
 1145: 			 * added yet another TLV for us to deal with,
 1146: 			 * or the parsing has gone Terribly Wrong.
 1147: 			 *
 1148: 			 * Either way, inform the owner and attempt
 1149: 			 * recovery.
 1150: 			 *
 1151: 			 */
 1152: #ifdef LOG_UNKNOWN_TLV
 1153: 			purple_debug_misc("oscar", "userinfo: **warning: unexpected TLV:\n");
 1154: 			purple_debug_misc("oscar", "userinfo:   bn    =%s\n", outinfo->bn);
 1155: 			dumptlv(od, type, bs, length);
 1156: #endif
 1157: 		}
 1158: 
 1159: 		/* Save ourselves. */
 1160: 		byte_stream_setpos(bs, endpos);
 1161: 	}
 1162: 
 1163: 	aim_locate_adduserinfo(od, outinfo);
 1164: 
 1165: 	return 0;
 1166: }
 1167: 
 1168: /*
 1169:  * Subtype 0x0001
 1170:  */
 1171: static int
 1172: error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1173: {
 1174: 	aim_snac_t *snac2;
 1175: 	guint16 reason;
 1176: 	char *bn;
 1177: 
 1178: 	snac2 = aim_remsnac(od, snac->id);
 1179: 	if (!snac2) {
 1180: 		purple_debug_misc("oscar", "locate error: received response from unknown request!\n");
 1181: 		return 0;
 1182: 	}
 1183: 
 1184: 	if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
 1185: 		purple_debug_misc("oscar", "locate error: received response from invalid request! %d\n", snac2->family);
 1186: 		g_free(snac2->data);
 1187: 		g_free(snac2);
 1188: 		return 0;
 1189: 	}
 1190: 
 1191: 	bn = snac2->data;
 1192: 	if (!bn) {
 1193: 		purple_debug_misc("oscar", "locate error: received response from request without a buddy name!\n");
 1194: 		g_free(snac2);
 1195: 		return 0;
 1196: 	}
 1197: 
 1198: 	reason = byte_stream_get16(bs);
 1199: 
 1200: 	oscar_user_info_display_error(od, reason, bn);
 1201: 
 1202: 	g_free(snac2->data);
 1203: 	g_free(snac2);
 1204: 
 1205: 	return 1;
 1206: }
 1207: 
 1208: /*
 1209:  * Subtype 0x0002
 1210:  *
 1211:  * Request Location services rights.
 1212:  *
 1213:  */
 1214: int
 1215: aim_locate_reqrights(OscarData *od)
 1216: {
 1217: 	FlapConnection *conn;
 1218: 
 1219: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 1220: 		return -EINVAL;
 1221: 
 1222: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_REQRIGHTS);
 1223: 
 1224: 	return 0;
 1225: }
 1226: 
 1227: /*
 1228:  * Subtype 0x0003
 1229:  *
 1230:  * Normally contains:
 1231:  *   t(0001)  - short containing max profile length (value = 1024)
 1232:  *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
 1233:  *   t(0003)  - short - unknown (value = 10)
 1234:  *   t(0004)  - short - unknown (value = 2048) [ICQ only?]
 1235:  */
 1236: static int
 1237: rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1238: {
 1239: 	GSList *tlvlist;
 1240: 	aim_rxcallback_t userfunc;
 1241: 	int ret = 0;
 1242: 	guint16 maxsiglen = 0;
 1243: 
 1244: 	tlvlist = aim_tlvlist_read(bs);
 1245: 
 1246: 	if (aim_tlv_gettlv(tlvlist, 0x0001, 1))
 1247: 		maxsiglen = aim_tlv_get16(tlvlist, 0x0001, 1);
 1248: 
 1249: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1250: 		ret = userfunc(od, conn, frame, maxsiglen);
 1251: 
 1252: 	aim_tlvlist_free(tlvlist);
 1253: 
 1254: 	return ret;
 1255: }
 1256: 
 1257: /*
 1258:  * Subtype 0x0004
 1259:  *
 1260:  * Gives BOS your profile.
 1261:  *
 1262:  * profile_encoding and awaymsg_encoding MUST be set if profile or
 1263:  * away are set, respectively, and their value may or may not be
 1264:  * restricted to a few choices.  I am currently aware of:
 1265:  *
 1266:  * us-ascii		Just that
 1267:  * unicode-2-0		UTF-16BE
 1268:  *
 1269:  * profile_len and awaymsg_len MUST be set similarly, and they MUST
 1270:  * be the length of their respective strings in bytes.
 1271:  *
 1272:  * To get the previous behavior of awaymsg == "" un-setting the away
 1273:  * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the
 1274:  * obvious equivalent).
 1275:  *
 1276:  */
 1277: int
 1278: aim_locate_setprofile(OscarData *od,
 1279: 				  const char *profile_encoding, const gchar *profile, const int profile_len,
 1280: 				  const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len)
 1281: {
 1282: 	FlapConnection *conn;
 1283: 	ByteStream bs;
 1284: 	aim_snacid_t snacid;
 1285: 	GSList *tlvlist = NULL;
 1286: 	char *encoding;
 1287: 	static const char defencoding[] = {"text/aolrtf; charset=\"%s\""};
 1288: 
 1289: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 1290: 		return -EINVAL;
 1291: 
 1292: 	if (!profile && !awaymsg)
 1293: 		return -EINVAL;
 1294: 
 1295: 	if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) {
 1296: 		return -EINVAL;
 1297: 	}
 1298: 
 1299: 	/* Build the packet first to get real length */
 1300: 	if (profile) {
 1301: 		/* no + 1 here because of %s */
 1302: 		encoding = g_malloc(strlen(defencoding) + strlen(profile_encoding));
 1303: 		snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
 1304: 		aim_tlvlist_add_str(&tlvlist, 0x0001, encoding);
 1305: 		aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile);
 1306: 		g_free(encoding);
 1307: 	}
 1308: 
 1309: 	/*
 1310: 	 * So here's how this works:
 1311: 	 *   - You are away when you have a non-zero-length type 4 TLV stored.
 1312: 	 *   - You become unaway when you clear the TLV with a zero-length
 1313: 	 *       type 4 TLV.
 1314: 	 *   - If you do not send the type 4 TLV, your status does not change
 1315: 	 *       (that is, if you were away, you'll remain away).
 1316: 	 */
 1317: 	if (awaymsg) {
 1318: 		if (awaymsg_len) {
 1319: 			encoding = g_malloc(strlen(defencoding) + strlen(awaymsg_encoding));
 1320: 			snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
 1321: 			aim_tlvlist_add_str(&tlvlist, 0x0003, encoding);
 1322: 			aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg);
 1323: 			g_free(encoding);
 1324: 		} else
 1325: 			aim_tlvlist_add_noval(&tlvlist, 0x0004);
 1326: 	}
 1327: 
 1328: 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 1329: 
 1330: 	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
 1331: 
 1332: 	aim_tlvlist_write(&bs, &tlvlist);
 1333: 	aim_tlvlist_free(tlvlist);
 1334: 
 1335: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
 1336: 
 1337: 	byte_stream_destroy(&bs);
 1338: 
 1339: 	return 0;
 1340: }
 1341: 
 1342: /*
 1343:  * Subtype 0x0004 - Set your client's capabilities.
 1344:  */
 1345: int
 1346: aim_locate_setcaps(OscarData *od, guint64 caps)
 1347: {
 1348: 	FlapConnection *conn;
 1349: 	PurpleAccount *account = purple_connection_get_account(od->gc);
 1350: 	PurplePresence *presence = purple_account_get_presence(account);
 1351: 	PurpleStatus *status = purple_presence_get_status(presence, "mood");
 1352: 	const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
 1353: 	ByteStream bs;
 1354: 	aim_snacid_t snacid;
 1355: 	GSList *tlvlist = NULL;
 1356: 
 1357: 	if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 1358: 		return -EINVAL;
 1359: 
 1360: 	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood);
 1361: 
 1362: 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 1363: 
 1364: 	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
 1365: 
 1366: 	aim_tlvlist_write(&bs, &tlvlist);
 1367: 	aim_tlvlist_free(tlvlist);
 1368: 
 1369: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs);
 1370: 
 1371: 	byte_stream_destroy(&bs);
 1372: 
 1373: 	return 0;
 1374: }
 1375: 
 1376: /* Subtype 0x0006 */
 1377: static int
 1378: userinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1379: {
 1380: 	int ret = 0;
 1381: 	aim_userinfo_t *userinfo, *userinfo2;
 1382: 	GSList *tlvlist;
 1383: 	aim_tlv_t *tlv = NULL;
 1384: 
 1385: 	userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t));
 1386: 	aim_info_extract(od, bs, userinfo);
 1387: 	tlvlist = aim_tlvlist_read(bs);
 1388: 
 1389: 	/* Profile will be 1 and 2 */
 1390: 	userinfo->info_encoding = aim_tlv_getstr(tlvlist, 0x0001, 1);
 1391: 	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0002, 1))) {
 1392: 		userinfo->info = (char *)g_malloc(tlv->length);
 1393: 		memcpy(userinfo->info, tlv->value, tlv->length);
 1394: 		userinfo->info_len = tlv->length;
 1395: 	}
 1396: 
 1397: 	/* Away message will be 3 and 4 */
 1398: 	userinfo->away_encoding = aim_tlv_getstr(tlvlist, 0x0003, 1);
 1399: 	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0004, 1))) {
 1400: 		userinfo->away = (char *)g_malloc(tlv->length);
 1401: 		memcpy(userinfo->away, tlv->value, tlv->length);
 1402: 		userinfo->away_len = tlv->length;
 1403: 	}
 1404: 
 1405: 	/* Caps will be 5 */
 1406: 	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
 1407: 		ByteStream cbs;
 1408: 		PurpleAccount *account = purple_connection_get_account(od->gc);
 1409: 		const char *mood;
 1410: 
 1411: 		byte_stream_init(&cbs, tlv->value, tlv->length);
 1412: 		userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
 1413: 		byte_stream_rewind(&cbs);
 1414: 		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
 1415: 
 1416: 		mood = aim_receive_custom_icon(od, &cbs, tlv->length);
 1417: 		if (mood)
 1418: 			purple_prpl_got_user_status(account, userinfo->bn, "mood",
 1419: 					PURPLE_MOOD_NAME, mood,
 1420: 					NULL);
 1421: 		else
 1422: 			purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood");
 1423: 	}
 1424: 	aim_tlvlist_free(tlvlist);
 1425: 
 1426: 	aim_locate_adduserinfo(od, userinfo);
 1427: 	userinfo2 = aim_locate_finduserinfo(od, userinfo->bn);
 1428: 	aim_info_free(userinfo);
 1429: 	g_free(userinfo);
 1430: 
 1431: 	/* Show the info to the user */
 1432: 	oscar_user_info_display_aim(od, userinfo2);
 1433: 
 1434: 	return ret;
 1435: }
 1436: 
 1437: /*
 1438:  * Subtype 0x0015 - Request the info of a user using the short method.  This is
 1439:  * what iChat uses.  It normally is VERY leniently rate limited.
 1440:  *
 1441:  * @param bn The buddy name whose info you wish to request.
 1442:  * @param flags The bitmask which specifies the type of info you wish to request.
 1443:  *        0x00000001 - Info/profile.
 1444:  *        0x00000002 - Away message.
 1445:  *        0x00000004 - Capabilities.
 1446:  *        0x00000008 - Certification.
 1447:  * @return Return 0 if no errors, otherwise return the error number.
 1448:  */
 1449: int
 1450: aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags)
 1451: {
 1452: 	FlapConnection *conn;
 1453: 	ByteStream bs;
 1454: 	aim_snacid_t snacid;
 1455: 
 1456: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn)
 1457: 		return -EINVAL;
 1458: 
 1459: 	byte_stream_new(&bs, 4 + 1 + strlen(bn));
 1460: 	byte_stream_put32(&bs, flags);
 1461: 	byte_stream_put8(&bs, strlen(bn));
 1462: 	byte_stream_putstr(&bs, bn);
 1463: 
 1464: 	snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1);
 1465: 	flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, snacid, &bs, FALSE);
 1466: 
 1467: 	byte_stream_destroy(&bs);
 1468: 
 1469: 	return 0;
 1470: }
 1471: 
 1472: static int
 1473: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1474: {
 1475: 	if (snac->subtype == 0x0001)
 1476: 		return error(od, conn, mod, frame, snac, bs);
 1477: 	else if (snac->subtype == 0x0003)
 1478: 		return rights(od, conn, mod, frame, snac, bs);
 1479: 	else if (snac->subtype == 0x0006)
 1480: 		return userinfo(od, conn, mod, frame, snac, bs);
 1481: 
 1482: 	return 0;
 1483: }
 1484: 
 1485: static void
 1486: locate_shutdown(OscarData *od, aim_module_t *mod)
 1487: {
 1488: 	aim_userinfo_t *del;
 1489: 
 1490: 	while (od->locate.userinfo) {
 1491: 		del = od->locate.userinfo;
 1492: 		od->locate.userinfo = od->locate.userinfo->next;
 1493: 		aim_info_free(del);
 1494: 		g_free(del);
 1495: 	}
 1496: }
 1497: 
 1498: int
 1499: locate_modfirst(OscarData *od, aim_module_t *mod)
 1500: {
 1501: 	mod->family = SNAC_FAMILY_LOCATE;
 1502: 	mod->version = 0x0001;
 1503: 	mod->toolid = 0x0110;
 1504: 	mod->toolversion = 0x0629;
 1505: 	mod->flags = 0;
 1506: 	strncpy(mod->name, "locate", sizeof(mod->name));
 1507: 	mod->snachandler = snachandler;
 1508: 	mod->shutdown = locate_shutdown;
 1509: 
 1510: 	return 0;
 1511: }
 1512: 
 1513: const char*
 1514: icq_get_custom_icon_description(const char *mood)
 1515: {
 1516: 	int i;
 1517: 
 1518: 	if (!(mood && *mood))
 1519: 		return NULL;
 1520: 
 1521: 	for (i = 0; icq_custom_icons[i].mood; i++) {
 1522: 		/* We check that description is not NULL to exclude
 1523: 		 * duplicates, like the typing duplicate. */
 1524: 		if (icq_purple_moods[i].description &&
 1525: 		    purple_strequal(mood, icq_custom_icons[i].mood)) {
 1526: 			return icq_purple_moods[i].description;
 1527: 		}
 1528: 	}
 1529: 
 1530: 	return NULL;
 1531: }
 1532: 
 1533: guint8*
 1534: icq_get_custom_icon_data(const char *mood)
 1535: {
 1536: 	int i;
 1537: 
 1538: 	if (!(mood && *mood))
 1539: 		return NULL;
 1540: 
 1541: 	for (i = 0; icq_custom_icons[i].mood; i++) {
 1542: 		/* We check that description is not NULL to exclude
 1543: 		 * duplicates, like the typing duplicate. */
 1544: 		if (icq_purple_moods[i].description &&
 1545: 		    purple_strequal(mood, icq_custom_icons[i].mood)) {
 1546: 			return (guint8 *)icq_custom_icons[i].data;
 1547: 		}
 1548: 	}
 1549: 	return NULL;
 1550: }
 1551: 
 1552: PurpleMood*
 1553: icq_get_purple_moods(PurpleAccount *account)
 1554: {
 1555: 	return icq_purple_moods;
 1556: }

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