Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_oservice.c, revision 1.1
1.1 ! snw 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>