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