rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / oscar / family_oservice.c
blobd2ef0fd5ade5ebf28803e8213f0da29164e609c3
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.
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.
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.
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
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).
26 #include "oscar.h"
29 * Each time we make a FLAP connection to an oscar server the server gives
30 * us a list of rate classes. Each rate class has different properties for
31 * how frequently we can send SNACs in that rate class before we become
32 * throttled or disconnected.
34 * The server also gives us a list of every available SNAC and tells us which
35 * rate class it's in. There are a lot of different SNACs, so this list can be
36 * fairly large. One important characteristic of these rate classes is that
37 * currently (and since at least 2004) most SNACs are in the same rate class.
39 * One optimization we can do to save memory is to only keep track of SNACs
40 * that are in classes other than this default rate class. So if we try to
41 * look up a SNAC and it's not in our hash table then we can assume that it's
42 * in the default rate class.
44 #define OSCAR_DEFAULT_RATECLASS 1
46 /* Subtype 0x0002 - Client Online */
47 void
48 aim_srv_clientready(OscarData *od, FlapConnection *conn)
50 ByteStream bs;
51 aim_snacid_t snacid;
52 GSList *cur;
54 byte_stream_new(&bs, 1142);
57 * Send only the tool versions that the server cares about (that it
58 * marked as supporting in the server ready SNAC).
60 for (cur = conn->groups; cur != NULL; cur = cur->next)
62 aim_module_t *mod;
64 if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
66 byte_stream_put16(&bs, mod->family);
67 byte_stream_put16(&bs, mod->version);
68 byte_stream_put16(&bs, mod->toolid);
69 byte_stream_put16(&bs, mod->toolversion);
73 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
74 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, snacid, &bs);
76 byte_stream_destroy(&bs);
80 * Subtype 0x0003 - Host Online
82 * See comments in conn.c about how the group associations are supposed
83 * to work, and how they really work.
85 * This info probably doesn't even need to make it to the client.
87 * We don't actually call the client here. This starts off the connection
88 * initialization routine required by all AIM connections. The next time
89 * the client is called is the CONNINITDONE callback, which should be
90 * shortly after the rate information is acknowledged.
93 static int
94 hostonline(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
96 int group;
98 while (byte_stream_bytes_left(bs))
100 group = byte_stream_get16(bs);
101 conn->groups = g_slist_prepend(conn->groups, GUINT_TO_POINTER(group));
105 * Next step is in the Host Versions handler.
107 * Note that we must send this before we request rates, since
108 * the format of the rate information depends on the versions we
109 * give it.
112 aim_srv_setversions(od, conn);
114 return 1;
117 /* Subtype 0x0004 - Service request */
118 void
119 aim_srv_requestnew(OscarData *od, guint16 serviceid)
121 FlapConnection *conn;
122 ByteStream bs;
123 aim_snacid_t snacid;
124 GSList *tlvlist = NULL;
126 conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
127 if(!conn)
128 return;
130 byte_stream_new(&bs, 6);
132 byte_stream_put16(&bs, serviceid);
134 if (od->use_ssl)
135 /* Request SSL Connection */
136 aim_tlvlist_add_noval(&tlvlist, 0x008c);
138 aim_tlvlist_write(&bs, &tlvlist);
139 aim_tlvlist_free(tlvlist);
141 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0);
142 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
144 byte_stream_destroy(&bs);
148 * Join a room of name roomname. This is the first step to joining an
149 * already created room. It's basically a Service Request for
150 * family 0x000e, with a little added on to specify the exchange and room
151 * name.
154 aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 instance)
156 FlapConnection *conn;
157 ByteStream bs;
158 aim_snacid_t snacid;
159 GSList *tlvlist = NULL;
160 struct chatsnacinfo csi;
162 conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
163 if (!conn || !roomname || roomname[0] == '\0')
164 return -EINVAL;
166 byte_stream_new(&bs, 506);
168 memset(&csi, 0, sizeof(csi));
169 csi.exchange = exchange;
170 g_strlcpy(csi.name, roomname, sizeof(csi.name));
171 csi.instance = instance;
174 * Requesting service chat (0x000e)
176 byte_stream_put16(&bs, 0x000e);
178 aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance);
180 if (od->use_ssl)
181 /* Request SSL Connection */
182 aim_tlvlist_add_noval(&tlvlist, 0x008c);
184 aim_tlvlist_write(&bs, &tlvlist);
185 aim_tlvlist_free(tlvlist);
187 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
188 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, snacid, &bs);
190 byte_stream_destroy(&bs);
192 return 0;
195 /* Subtype 0x0005 - Redirect */
196 static int
197 redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
199 struct aim_redirect_data redir;
200 aim_rxcallback_t userfunc;
201 GSList *tlvlist;
202 aim_snac_t *origsnac = NULL;
203 int ret = 0;
205 memset(&redir, 0, sizeof(redir));
207 tlvlist = aim_tlvlist_read(bs);
209 if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
210 !aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
211 !aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
212 aim_tlvlist_free(tlvlist);
213 return 0;
216 redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
217 redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
218 redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
219 redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
220 redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1);
221 redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1);
223 /* Fetch original SNAC so we can get csi if needed */
224 origsnac = aim_remsnac(od, snac->id);
226 if ((redir.group == SNAC_FAMILY_CHAT) && origsnac) {
227 struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
229 redir.chat.exchange = csi->exchange;
230 redir.chat.room = csi->name;
231 redir.chat.instance = csi->instance;
234 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
235 ret = userfunc(od, conn, frame, &redir);
237 g_free((void *)redir.ip);
238 g_free((void *)redir.cookie);
239 g_free((void *)redir.ssl_cert_cn);
241 if (origsnac)
242 g_free(origsnac->data);
243 g_free(origsnac);
245 aim_tlvlist_free(tlvlist);
247 return ret;
250 /* Subtype 0x0006 - Request Rate Information. */
251 void
252 aim_srv_reqrates(OscarData *od, FlapConnection *conn)
254 aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
258 * OSCAR defines several 'rate classes'. Each class has separate
259 * rate limiting properties (limit level, alert level, disconnect
260 * level, etc), and a set of SNAC family/type pairs associated with
261 * it. The rate classes, their limiting properties, and the definitions
262 * of which SNACs belong to which class are defined in the
263 * Rate Response packet at login to each host.
265 * Logically, all rate offenses within one class count against further
266 * offenses for other SNACs in the same class (ie, sending messages
267 * too fast will limit the number of user info requests you can send,
268 * since those two SNACs are in the same rate class).
270 * Since the rate classes are defined dynamically at login, the values
271 * below may change. But they seem to be fairly constant.
273 * Currently, BOS defines five rate classes, with the commonly used
274 * members as follows...
276 * Rate class 0x0001:
277 * - Everything thats not in any of the other classes
279 * Rate class 0x0002:
280 * - Buddy list add/remove
281 * - Permit list add/remove
282 * - Deny list add/remove
284 * Rate class 0x0003:
285 * - User information requests
286 * - Outgoing ICBMs
288 * Rate class 0x0004:
289 * - A few unknowns: 2/9, 2/b, and f/2
291 * Rate class 0x0005:
292 * - Chat room create
293 * - Outgoing chat ICBMs
295 * The only other thing of note is that class 5 (chat) has slightly looser
296 * limiting properties than class 3 (normal messages). But thats just a
297 * small bit of trivia for you.
299 * The last thing that needs to be learned about the rate limiting
300 * system is how the actual numbers relate to the passing of time. This
301 * seems to be a big mystery.
303 * See joscar's javadoc for the RateClassInfo class for a great
304 * explanation. You might be able to find it at
305 * http://dscoder.com/RateClassInfo.html
308 static struct rateclass *
309 rateclass_find(GSList *rateclasses, guint16 id)
311 GSList *tmp;
313 for (tmp = rateclasses; tmp != NULL; tmp = tmp->next)
315 struct rateclass *rateclass;
316 rateclass = tmp->data;
317 if (rateclass->classid == id)
318 return rateclass;
321 return NULL;
324 /* Subtype 0x0007 - Rate Parameters */
325 static int
326 rateresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
328 guint16 numclasses, i;
329 aim_rxcallback_t userfunc;
332 * First are the parameters for each rate class.
334 numclasses = byte_stream_get16(bs);
335 for (i = 0; i < numclasses; i++)
337 struct rateclass *rateclass;
338 guint32 delta;
339 struct timeval now;
341 gettimeofday(&now, NULL);
342 rateclass = g_new(struct rateclass, 1);
344 rateclass->classid = byte_stream_get16(bs);
345 rateclass->windowsize = byte_stream_get32(bs);
346 rateclass->clear = byte_stream_get32(bs);
347 rateclass->alert = byte_stream_get32(bs);
348 rateclass->limit = byte_stream_get32(bs);
349 rateclass->disconnect = byte_stream_get32(bs);
350 rateclass->current = byte_stream_get32(bs);
351 rateclass->max = byte_stream_get32(bs);
352 if (mod->version >= 3) {
353 delta = byte_stream_get32(bs);
354 rateclass->dropping_snacs = byte_stream_get8(bs);
355 } else {
356 delta = 0;
357 rateclass->dropping_snacs = 0;
360 rateclass->last.tv_sec = now.tv_sec - delta / 1000;
361 rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
363 conn->rateclasses = g_slist_prepend(conn->rateclasses, rateclass);
365 if (rateclass->classid == OSCAR_DEFAULT_RATECLASS)
366 conn->default_rateclass = rateclass;
368 conn->rateclasses = g_slist_reverse(conn->rateclasses);
371 * Then the members of each class.
373 for (i = 0; i < numclasses; i++)
375 guint16 classid, count;
376 struct rateclass *rateclass;
377 int j;
379 classid = byte_stream_get16(bs);
380 count = byte_stream_get16(bs);
382 if (classid == OSCAR_DEFAULT_RATECLASS) {
384 * Don't bother adding these SNACs to the hash table. See the
385 * comment for OSCAR_DEFAULT_RATECLASS at the top of this file.
387 byte_stream_advance(bs, 4 * count);
388 continue;
391 rateclass = rateclass_find(conn->rateclasses, classid);
393 for (j = 0; j < count; j++)
395 guint16 group, subtype;
397 group = byte_stream_get16(bs);
398 subtype = byte_stream_get16(bs);
400 if (rateclass != NULL)
401 g_hash_table_insert(conn->rateclass_members,
402 GUINT_TO_POINTER((group << 16) + subtype),
403 rateclass);
408 * We don't pass the rate information up to the client, as it really
409 * doesn't care. The information is stored in the connection, however
410 * so that we can do rate limiting management when sending SNACs.
414 * Subscribe to rate change information for all rate classes.
416 aim_srv_rates_addparam(od, conn);
419 * Finally, tell the client it's ready to go...
421 if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
422 userfunc(od, conn, frame);
424 return 1;
427 /* Subtype 0x0008 - Add Rate Parameter */
428 void
429 aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
431 ByteStream bs;
432 aim_snacid_t snacid;
433 GSList *tmp;
435 byte_stream_new(&bs, 502);
437 for (tmp = conn->rateclasses; tmp != NULL; tmp = tmp->next)
439 struct rateclass *rateclass;
440 rateclass = tmp->data;
441 byte_stream_put16(&bs, rateclass->classid);
444 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
445 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, snacid, &bs);
447 byte_stream_destroy(&bs);
450 /* Subtype 0x000a - Rate Change */
451 static int
452 ratechange(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
454 guint16 code, classid;
455 struct rateclass *rateclass;
456 guint32 delta;
457 struct timeval now;
458 static const char *codes[5] = {
459 "invalid",
460 "change",
461 "warning",
462 "limit",
463 "limit cleared",
466 gettimeofday(&now, NULL);
467 code = byte_stream_get16(bs);
468 classid = byte_stream_get16(bs);
470 rateclass = rateclass_find(conn->rateclasses, classid);
471 if (rateclass == NULL)
472 /* This should never really happen */
473 return 0;
475 rateclass->windowsize = byte_stream_get32(bs);
476 rateclass->clear = byte_stream_get32(bs);
477 rateclass->alert = byte_stream_get32(bs);
478 rateclass->limit = byte_stream_get32(bs);
479 rateclass->disconnect = byte_stream_get32(bs);
480 rateclass->current = byte_stream_get32(bs);
481 rateclass->max = byte_stream_get32(bs);
482 if (mod->version >= 3) {
483 delta = byte_stream_get32(bs);
484 rateclass->dropping_snacs = byte_stream_get8(bs);
485 } else {
486 delta = 0;
487 rateclass->dropping_snacs = 0;
490 rateclass->last.tv_sec = now.tv_sec - delta / 1000;
491 rateclass->last.tv_usec = now.tv_usec - (delta % 1000) * 1000;
493 purple_debug_misc("oscar", "rate %s (param ID 0x%04hx): curavg = %u, "
494 "maxavg = %u, alert at %u, clear warning at %u, limit at %u, "
495 "disconnect at %u, delta is %u, dropping is %u (window size = %u)\n",
496 (code < 5) ? codes[code] : codes[0], rateclass->classid,
497 rateclass->current, rateclass->max, rateclass->alert,
498 rateclass->clear, rateclass->limit, rateclass->disconnect,
499 delta, rateclass->dropping_snacs, rateclass->windowsize);
501 if (code == AIM_RATE_CODE_LIMIT) {
502 purple_debug_warning("oscar", "The last action you attempted "
503 "could not be performed because you are over the rate "
504 "limit. Please wait 10 seconds and try again.\n");
507 return 1;
511 * How Migrations work.
513 * The server sends a Server Pause message, which the client should respond to
514 * with a Server Pause Ack, which contains the families it needs on this
515 * connection. The server will send a Migration Notice with an IP address, and
516 * then disconnect. Next the client should open the connection and send the
517 * cookie. Repeat the normal login process and pretend this never happened.
519 * The Server Pause contains no data.
523 /* Subtype 0x000b - Service Pause */
524 static int
525 serverpause(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
527 int ret = 0;
528 aim_rxcallback_t userfunc;
530 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
531 ret = userfunc(od, conn, frame);
533 return ret;
536 /* Subtype 0x000d - Service Resume */
537 static int
538 serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
540 int ret = 0;
541 aim_rxcallback_t userfunc;
543 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
544 ret = userfunc(od, conn, frame);
546 return ret;
549 /* Subtype 0x000e - Request self-info */
550 void
551 aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
553 aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
556 /* Subtype 0x000f - Self User Info */
557 static int
558 selfinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
560 int ret = 0;
561 aim_rxcallback_t userfunc;
562 aim_userinfo_t userinfo;
564 aim_info_extract(od, bs, &userinfo);
566 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
567 ret = userfunc(od, conn, frame, &userinfo);
569 aim_info_free(&userinfo);
571 return ret;
574 /* Subtype 0x0010 - Evil Notification */
575 static int
576 evilnotify(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
578 int ret = 0;
579 aim_rxcallback_t userfunc;
580 guint16 newevil;
581 aim_userinfo_t userinfo;
583 memset(&userinfo, 0, sizeof(aim_userinfo_t));
585 newevil = byte_stream_get16(bs);
587 if (byte_stream_bytes_left(bs))
588 aim_info_extract(od, bs, &userinfo);
590 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
591 ret = userfunc(od, conn, frame, newevil, &userinfo);
593 aim_info_free(&userinfo);
595 return ret;
599 * Subtype 0x0011 - Idle Notification
601 * Should set your current idle time in seconds. Note that this should
602 * never be called consecutively with a non-zero idle time. That makes
603 * OSCAR do funny things. Instead, just set it once you go idle, and then
604 * call it again with zero when you're back.
607 void
608 aim_srv_setidle(OscarData *od, guint32 idletime)
610 FlapConnection *conn;
612 conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS);
613 if(!conn)
614 return;
616 aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
620 * Subtype 0x0012 - Service Migrate
622 * This is the final SNAC sent on the original connection during a migration.
623 * It contains the IP and cookie used to connect to the new server, and
624 * optionally a list of the SNAC groups being migrated.
627 static int
628 migrate(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
630 aim_rxcallback_t userfunc;
631 int ret = 0;
632 guint16 groupcount, i;
633 GSList *tlvlist;
634 char *ip = NULL;
635 aim_tlv_t *cktlv;
638 * Apparently there's some fun stuff that can happen right here. The
639 * migration can actually be quite selective about what groups it
640 * moves to the new server. When not all the groups for a connection
641 * are migrated, or they are all migrated but some groups are moved
642 * to a different server than others, it is called a bifurcated
643 * migration.
645 * Let's play dumb and not support that.
648 groupcount = byte_stream_get16(bs);
649 for (i = 0; i < groupcount; i++) {
650 guint16 group;
652 group = byte_stream_get16(bs);
654 purple_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
657 tlvlist = aim_tlvlist_read(bs);
659 if (aim_tlv_gettlv(tlvlist, 0x0005, 1))
660 ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
662 cktlv = aim_tlv_gettlv(tlvlist, 0x0006, 1);
664 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
665 ret = userfunc(od, conn, frame, ip, cktlv ? cktlv->value : NULL);
667 aim_tlvlist_free(tlvlist);
668 g_free(ip);
670 return ret;
673 /* Subtype 0x0013 - Message of the Day */
674 static int
675 motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
677 aim_rxcallback_t userfunc;
678 char *msg = NULL;
679 int ret = 0;
680 GSList *tlvlist;
681 guint16 id;
684 * Code.
686 * Valid values:
687 * 1 Mandatory upgrade
688 * 2 Advisory upgrade
689 * 3 System bulletin
690 * 4 Nothing's wrong ("top o the world" -- normal)
691 * 5 Lets-break-something.
694 id = byte_stream_get16(bs);
697 * TLVs follow
699 tlvlist = aim_tlvlist_read(bs);
701 msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
703 if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
704 ret = userfunc(od, conn, frame, id, msg);
706 g_free(msg);
708 aim_tlvlist_free(tlvlist);
710 return ret;
714 * Subtype 0x0017 - Set client versions
716 * If you've seen the clientonline/clientready SNAC you're probably
717 * wondering what the point of this one is. And that point seems to be
718 * that the versions in the client online SNAC are sent too late for the
719 * server to be able to use them to change the protocol for the earlier
720 * login packets (client versions are sent right after Host Online is
721 * received, but client online versions aren't sent until quite a bit later).
722 * We can see them already making use of this by changing the format of
723 * the rate information based on what version of group 1 we advertise here.
726 void
727 aim_srv_setversions(OscarData *od, FlapConnection *conn)
729 ByteStream bs;
730 aim_snacid_t snacid;
731 GSList *cur;
733 byte_stream_new(&bs, 1142);
736 * Send only the versions that the server cares about (that it
737 * marked as supporting in the server ready SNAC).
739 for (cur = conn->groups; cur != NULL; cur = cur->next)
741 aim_module_t *mod;
743 if ((mod = aim__findmodulebygroup(od, GPOINTER_TO_UINT(cur->data))))
745 byte_stream_put16(&bs, mod->family);
746 byte_stream_put16(&bs, mod->version);
750 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
751 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, snacid, &bs);
753 byte_stream_destroy(&bs);
756 /* Subtype 0x0018 - Host versions */
757 static int
758 hostversions(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
760 int vercount;
761 guint8 *versions;
763 /* This is frivolous. (Thank you SmarterChild.) */
764 vercount = byte_stream_bytes_left(bs)/4;
766 /* XXX: vercount probably should be used for reading versions. */
767 (void)vercount;
768 versions = byte_stream_getraw(bs, byte_stream_bytes_left(bs));
769 g_free(versions);
772 * Now request rates.
774 aim_srv_reqrates(od, conn);
776 return 1;
780 * Subtype 0x001e - Extended Status/Extra Info.
782 * These settings are transient, not server-stored (i.e. they only
783 * apply to this session, and must be re-set the next time you sign
784 * on).
786 * You can set your ICQ status (available, away, do not disturb,
787 * etc.), or whether your IP address should be hidden or not, or
788 * if your status is visible on ICQ web sites, and you can set
789 * your IP address info and what not.
791 * You can also set your "available" message. This is currently
792 * only supported by iChat, Purple and other 3rd party clients.
794 * These are the same TLVs seen in user info. You can
795 * also set 0x0008 and 0x000c.
798 aim_srv_setextrainfo(OscarData *od,
799 gboolean seticqstatus, guint32 icqstatus,
800 gboolean setstatusmsg, const char *statusmsg, const char *itmsurl)
802 FlapConnection *conn;
803 ByteStream bs;
804 aim_snacid_t snacid;
805 GSList *tlvlist = NULL;
807 if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
808 return -EINVAL;
810 if (seticqstatus)
812 aim_tlvlist_add_32(&tlvlist, 0x0006, icqstatus |
813 AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH);
816 if (setstatusmsg)
818 size_t statusmsglen, itmsurllen;
819 ByteStream tmpbs;
821 statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0;
822 itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0;
824 byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8);
825 byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg);
826 byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl);
828 aim_tlvlist_add_raw(&tlvlist, 0x001d,
829 byte_stream_curpos(&tmpbs), tmpbs.data);
830 byte_stream_destroy(&tmpbs);
833 byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
835 aim_tlvlist_write(&bs, &tlvlist);
836 aim_tlvlist_free(tlvlist);
838 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
839 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, snacid, &bs);
841 byte_stream_destroy(&bs);
843 return 0;
846 /* Send dummy DC (direct connect) information to the server.
847 * Direct connect is ICQ's counterpart for AIM's DirectIM,
848 * as far as I can tell. Anyway, we don't support it;
849 * the reason to send this packet is that some clients
850 * (Miranda, QIP) won't send us channel 2 ICBM messages
851 * unless we specify DC version >= 8.
853 * See #12044 for more information.
855 void
856 aim_srv_set_dc_info(OscarData *od)
858 FlapConnection *conn;
860 ByteStream bs, tlv0c;
861 aim_snacid_t snacid;
862 GSList *tlvlist = NULL;
864 /* http://iserverd.khstu.ru/oscar/snac_01_1e.html has a nice analysis of what goes in 0xc tlv.
865 * Kopete sends a dummy DC info, too, so I just copied the values from them.
867 byte_stream_new(&tlv0c, 4*2 + 1 + 2 + 4*6 + 2);
868 byte_stream_put32(&tlv0c, 0x0);
869 byte_stream_put32(&tlv0c, 0x0);
870 byte_stream_put8(&tlv0c, 0x0); /* We don't support DC */
871 byte_stream_put16(&tlv0c, 8); /* DC version */
872 byte_stream_put32(&tlv0c, 0x0);
873 byte_stream_put32(&tlv0c, 0x50);
874 byte_stream_put32(&tlv0c, 0x3);
875 byte_stream_put32(&tlv0c, 0x0);
876 byte_stream_put32(&tlv0c, 0x0);
877 byte_stream_put32(&tlv0c, 0x0);
878 byte_stream_put16(&tlv0c, 0x0);
879 aim_tlvlist_add_raw(&tlvlist, 0x000c, byte_stream_curpos(&tlv0c), tlv0c.data);
880 byte_stream_destroy(&tlv0c);
882 byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
883 aim_tlvlist_write(&bs, &tlvlist);
884 aim_tlvlist_free(tlvlist);
886 snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
887 conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
888 g_warn_if_fail(conn != NULL);
889 if (conn) {
890 flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE,
891 0x001e, snacid, &bs);
894 byte_stream_destroy(&bs);
898 * Subtype 0x0021 - Receive our extended status
900 * This is used for iChat's "available" messages, and maybe ICQ extended
901 * status messages? It's also used to tell the client whether or not it
902 * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
904 static int
905 aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
907 guint16 type = byte_stream_get16(bs);
908 if (type == 0x0000 || type == 0x0001) {
909 /* buddy icon checksum */
910 /* not sure what the difference between 1 and 0 is */
911 guint8 flags = byte_stream_get8(bs);
912 guint8 length = byte_stream_get8(bs);
913 guint8 *md5 = byte_stream_getraw(bs, length);
915 if ((flags == 0x00) || (flags == 0x41)) {
916 if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) {
917 od->iconconnecting = TRUE;
918 od->set_icon = TRUE;
919 aim_srv_requestnew(od, SNAC_FAMILY_BART);
920 } else {
921 PurpleAccount *account = purple_connection_get_account(od->gc);
922 PurpleImage *img = purple_buddy_icons_find_account_icon(account);
923 if (img == NULL) {
924 aim_ssi_delicon(od);
925 } else {
927 purple_debug_info("oscar",
928 "Uploading icon to icon server\n");
929 aim_bart_upload(od,
930 purple_image_get_data(img),
931 purple_image_get_data_size(img));
932 g_object_unref(img);
935 } else if (flags == 0x81) {
936 PurpleAccount *account = purple_connection_get_account(od->gc);
937 PurpleImage *img = purple_buddy_icons_find_account_icon(account);
938 if (img == NULL)
939 aim_ssi_delicon(od);
940 else {
941 aim_ssi_seticon(od, md5, length);
942 g_object_unref(img);
946 g_free(md5);
949 return 0;
952 static int
953 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
955 if (snac->subtype == 0x0003)
956 return hostonline(od, conn, mod, frame, snac, bs);
957 else if (snac->subtype == 0x0005)
958 return redirect(od, conn, mod, frame, snac, bs);
959 else if (snac->subtype == 0x0007)
960 return rateresp(od, conn, mod, frame, snac, bs);
961 else if (snac->subtype == 0x000a)
962 return ratechange(od, conn, mod, frame, snac, bs);
963 else if (snac->subtype == 0x000b)
964 return serverpause(od, conn, mod, frame, snac, bs);
965 else if (snac->subtype == 0x000d)
966 return serverresume(od, conn, mod, frame, snac, bs);
967 else if (snac->subtype == 0x000f)
968 return selfinfo(od, conn, mod, frame, snac, bs);
969 else if (snac->subtype == 0x0010)
970 return evilnotify(od, conn, mod, frame, snac, bs);
971 else if (snac->subtype == 0x0012)
972 return migrate(od, conn, mod, frame, snac, bs);
973 else if (snac->subtype == 0x0013)
974 return motd(od, conn, mod, frame, snac, bs);
975 else if (snac->subtype == 0x0018)
976 return hostversions(od, conn, mod, frame, snac, bs);
977 else if (snac->subtype == 0x0021)
978 return aim_parse_extstatus(od, conn, mod, frame, snac, bs);
980 return 0;
983 int service_modfirst(OscarData *od, aim_module_t *mod)
985 mod->family = SNAC_FAMILY_OSERVICE;
986 mod->version = 0x0003;
987 mod->toolid = 0x0110;
988 mod->toolversion = 0x0629;
989 mod->flags = 0;
990 strncpy(mod->name, "oservice", sizeof(mod->name));
991 mod->snachandler = snachandler;
993 return 0;