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).
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 */
48 aim_srv_clientready(OscarData
*od
, FlapConnection
*conn
)
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
)
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.
94 hostonline(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
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
112 aim_srv_setversions(od
, conn
);
117 /* Subtype 0x0004 - Service request */
119 aim_srv_requestnew(OscarData
*od
, guint16 serviceid
)
121 FlapConnection
*conn
;
124 GSList
*tlvlist
= NULL
;
126 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_BOS
);
130 byte_stream_new(&bs
, 6);
132 byte_stream_put16(&bs
, serviceid
);
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
154 aim_chat_join(OscarData
*od
, guint16 exchange
, const char *roomname
, guint16 instance
)
156 FlapConnection
*conn
;
159 GSList
*tlvlist
= NULL
;
160 struct chatsnacinfo csi
;
162 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_BOS
);
163 if (!conn
|| !roomname
|| roomname
[0] == '\0')
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
);
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
);
195 /* Subtype 0x0005 - Redirect */
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
;
202 aim_snac_t
*origsnac
= NULL
;
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
);
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
);
242 g_free(origsnac
->data
);
245 aim_tlvlist_free(tlvlist
);
250 /* Subtype 0x0006 - Request Rate Information. */
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...
277 * - Everything thats not in any of the other classes
280 * - Buddy list add/remove
281 * - Permit list add/remove
282 * - Deny list add/remove
285 * - User information requests
289 * - A few unknowns: 2/9, 2/b, and f/2
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
)
313 for (tmp
= rateclasses
; tmp
!= NULL
; tmp
= tmp
->next
)
315 struct rateclass
*rateclass
;
316 rateclass
= tmp
->data
;
317 if (rateclass
->classid
== id
)
324 /* Subtype 0x0007 - Rate Parameters */
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
;
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
);
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
;
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
);
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
),
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
);
427 /* Subtype 0x0008 - Add Rate Parameter */
429 aim_srv_rates_addparam(OscarData
*od
, FlapConnection
*conn
)
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 */
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
;
458 static const char *codes
[5] = {
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 */
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
);
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");
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 */
525 serverpause(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
528 aim_rxcallback_t userfunc
;
530 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
531 ret
= userfunc(od
, conn
, frame
);
536 /* Subtype 0x000d - Service Resume */
538 serverresume(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
541 aim_rxcallback_t userfunc
;
543 if ((userfunc
= aim_callhandler(od
, snac
->family
, snac
->subtype
)))
544 ret
= userfunc(od
, conn
, frame
);
549 /* Subtype 0x000e - Request self-info */
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 */
558 selfinfo(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
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
);
574 /* Subtype 0x0010 - Evil Notification */
576 evilnotify(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
579 aim_rxcallback_t userfunc
;
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
);
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.
608 aim_srv_setidle(OscarData
*od
, guint32 idletime
)
610 FlapConnection
*conn
;
612 conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_BOS
);
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.
628 migrate(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
630 aim_rxcallback_t userfunc
;
632 guint16 groupcount
, i
;
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
645 * Let's play dumb and not support that.
648 groupcount
= byte_stream_get16(bs
);
649 for (i
= 0; i
< groupcount
; i
++) {
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
);
673 /* Subtype 0x0013 - Message of the Day */
675 motd(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
677 aim_rxcallback_t userfunc
;
687 * 1 Mandatory upgrade
690 * 4 Nothing's wrong ("top o the world" -- normal)
691 * 5 Lets-break-something.
694 id
= byte_stream_get16(bs
);
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
);
708 aim_tlvlist_free(tlvlist
);
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.
727 aim_srv_setversions(OscarData
*od
, FlapConnection
*conn
)
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
)
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 */
758 hostversions(OscarData
*od
, FlapConnection
*conn
, aim_module_t
*mod
, FlapFrame
*frame
, aim_modsnac_t
*snac
, ByteStream
*bs
)
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. */
768 versions
= byte_stream_getraw(bs
, byte_stream_bytes_left(bs
));
774 aim_srv_reqrates(od
, conn
);
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
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
;
805 GSList
*tlvlist
= NULL
;
807 if (!od
|| !(conn
= flap_connection_findbygroup(od
, SNAC_FAMILY_ICBM
)))
812 aim_tlvlist_add_32(&tlvlist
, 0x0006, icqstatus
|
813 AIM_ICQ_STATE_HIDEIP
| AIM_ICQ_STATE_DIRECTREQUIREAUTH
);
818 size_t statusmsglen
, itmsurllen
;
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
);
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.
856 aim_srv_set_dc_info(OscarData
*od
)
858 FlapConnection
*conn
;
860 ByteStream bs
, tlv0c
;
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
);
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?
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
;
919 aim_srv_requestnew(od
, SNAC_FAMILY_BART
);
921 PurpleAccount
*account
= purple_connection_get_account(od
->gc
);
922 PurpleImage
*img
= purple_buddy_icons_find_account_icon(account
);
927 purple_debug_info("oscar",
928 "Uploading icon to icon server\n");
930 purple_image_get_data(img
),
931 purple_image_get_data_size(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
);
941 aim_ssi_seticon(od
, md5
, length
);
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
);
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;
990 strncpy(mod
->name
, "oservice", sizeof(mod
->name
));
991 mod
->snachandler
= snachandler
;