13234
|
1 /*
|
|
2 * Gaim'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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 /* Subtype 0x0002 - Client Online */
|
|
31 faim_export int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
|
|
32 {
|
|
33 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
|
|
34 struct snacgroup *sg;
|
|
35 aim_frame_t *fr;
|
|
36 aim_snacid_t snacid;
|
|
37
|
|
38 if (!ins)
|
|
39 return -EINVAL;
|
|
40
|
|
41 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
|
|
42 return -ENOMEM;
|
|
43
|
|
44 snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
|
|
45 aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
|
|
46
|
|
47 /*
|
|
48 * Send only the tool versions that the server cares about (that it
|
|
49 * marked as supporting in the server ready SNAC).
|
|
50 */
|
|
51 for (sg = ins->groups; sg; sg = sg->next) {
|
|
52 aim_module_t *mod;
|
|
53
|
|
54 if ((mod = aim__findmodulebygroup(sess, sg->group))) {
|
|
55 aimbs_put16(&fr->data, mod->family);
|
|
56 aimbs_put16(&fr->data, mod->version);
|
|
57 aimbs_put16(&fr->data, mod->toolid);
|
|
58 aimbs_put16(&fr->data, mod->toolversion);
|
|
59 } else
|
|
60 gaim_debug_misc("oscar", "aim_clientready: server supports group 0x%04x but we don't!\n", sg->group);
|
|
61 }
|
|
62
|
|
63 aim_tx_enqueue(sess, fr);
|
|
64
|
|
65 return 0;
|
|
66 }
|
|
67
|
|
68 /*
|
|
69 * Subtype 0x0003 - Host Online
|
|
70 *
|
|
71 * See comments in conn.c about how the group associations are supposed
|
|
72 * to work, and how they really work.
|
|
73 *
|
|
74 * This info probably doesn't even need to make it to the client.
|
|
75 *
|
|
76 * We don't actually call the client here. This starts off the connection
|
|
77 * initialization routine required by all AIM connections. The next time
|
|
78 * the client is called is the CONNINITDONE callback, which should be
|
|
79 * shortly after the rate information is acknowledged.
|
|
80 *
|
|
81 */
|
|
82 static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
83 {
|
|
84 guint16 *families;
|
|
85 int famcount;
|
|
86
|
|
87
|
|
88 if (!(families = malloc(aim_bstream_empty(bs))))
|
|
89 return 0;
|
|
90
|
|
91 for (famcount = 0; aim_bstream_empty(bs); famcount++) {
|
|
92 families[famcount] = aimbs_get16(bs);
|
|
93 aim_conn_addgroup(rx->conn, families[famcount]);
|
|
94 }
|
|
95
|
|
96 free(families);
|
|
97
|
|
98
|
|
99 /*
|
|
100 * Next step is in the Host Versions handler.
|
|
101 *
|
|
102 * Note that we must send this before we request rates, since
|
|
103 * the format of the rate information depends on the versions we
|
|
104 * give it.
|
|
105 *
|
|
106 */
|
|
107 aim_setversions(sess, rx->conn);
|
|
108
|
|
109 return 1;
|
|
110 }
|
|
111
|
|
112 /* Subtype 0x0004 - Service request */
|
|
113 faim_export int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid)
|
|
114 {
|
|
115 return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
|
|
116 }
|
|
117
|
|
118 /*
|
|
119 * Join a room of name roomname. This is the first step to joining an
|
|
120 * already created room. It's basically a Service Request for
|
|
121 * family 0x000e, with a little added on to specify the exchange and room
|
|
122 * name.
|
|
123 */
|
|
124 faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
|
|
125 {
|
|
126 aim_frame_t *fr;
|
|
127 aim_snacid_t snacid;
|
|
128 aim_tlvlist_t *tl = NULL;
|
|
129 struct chatsnacinfo csi;
|
|
130
|
|
131 if (!sess || !conn || !roomname || !strlen(roomname))
|
|
132 return -EINVAL;
|
|
133
|
|
134 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
|
|
135 return -ENOMEM;
|
|
136
|
|
137 memset(&csi, 0, sizeof(csi));
|
|
138 csi.exchange = exchange;
|
|
139 strncpy(csi.name, roomname, sizeof(csi.name));
|
|
140 csi.instance = instance;
|
|
141
|
|
142 snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
|
|
143 aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
|
|
144
|
|
145 /*
|
|
146 * Requesting service chat (0x000e)
|
|
147 */
|
|
148 aimbs_put16(&fr->data, 0x000e);
|
|
149
|
|
150 aim_tlvlist_add_chatroom(&tl, 0x0001, exchange, roomname, instance);
|
|
151 aim_tlvlist_write(&fr->data, &tl);
|
|
152 aim_tlvlist_free(&tl);
|
|
153
|
|
154 aim_tx_enqueue(sess, fr);
|
|
155
|
|
156 return 0;
|
|
157 }
|
|
158
|
|
159 /* Subtype 0x0005 - Redirect */
|
|
160 static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
161 {
|
|
162 struct aim_redirect_data redir;
|
|
163 aim_rxcallback_t userfunc;
|
|
164 aim_tlvlist_t *tlvlist;
|
|
165 aim_snac_t *origsnac = NULL;
|
|
166 int ret = 0;
|
|
167
|
|
168 memset(&redir, 0, sizeof(redir));
|
|
169
|
|
170 tlvlist = aim_tlvlist_read(bs);
|
|
171
|
|
172 if (!aim_tlv_gettlv(tlvlist, 0x000d, 1) ||
|
|
173 !aim_tlv_gettlv(tlvlist, 0x0005, 1) ||
|
|
174 !aim_tlv_gettlv(tlvlist, 0x0006, 1)) {
|
|
175 aim_tlvlist_free(&tlvlist);
|
|
176 return 0;
|
|
177 }
|
|
178
|
|
179 redir.group = aim_tlv_get16(tlvlist, 0x000d, 1);
|
|
180 redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1);
|
|
181 redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length;
|
|
182 redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1);
|
|
183
|
|
184 /* Fetch original SNAC so we can get csi if needed */
|
|
185 origsnac = aim_remsnac(sess, snac->id);
|
|
186
|
|
187 if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
|
|
188 struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
|
|
189
|
|
190 redir.chat.exchange = csi->exchange;
|
|
191 redir.chat.room = csi->name;
|
|
192 redir.chat.instance = csi->instance;
|
|
193 }
|
|
194
|
|
195 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
196 ret = userfunc(sess, rx, &redir);
|
|
197
|
|
198 free((void *)redir.ip);
|
|
199 free((void *)redir.cookie);
|
|
200
|
|
201 if (origsnac)
|
|
202 free(origsnac->data);
|
|
203 free(origsnac);
|
|
204
|
|
205 aim_tlvlist_free(&tlvlist);
|
|
206
|
|
207 return ret;
|
|
208 }
|
|
209
|
|
210 /* Subtype 0x0006 - Request Rate Information. */
|
|
211 faim_internal int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
|
|
212 {
|
|
213 return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x0006);
|
|
214 }
|
|
215
|
|
216 /*
|
|
217 * OSCAR defines several 'rate classes'. Each class has separate
|
|
218 * rate limiting properties (limit level, alert level, disconnect
|
|
219 * level, etc), and a set of SNAC family/type pairs associated with
|
|
220 * it. The rate classes, their limiting properties, and the definitions
|
|
221 * of which SNACs are belong to which class, are defined in the
|
|
222 * Rate Response packet at login to each host.
|
|
223 *
|
|
224 * Logically, all rate offenses within one class count against further
|
|
225 * offenses for other SNACs in the same class (ie, sending messages
|
|
226 * too fast will limit the number of user info requests you can send,
|
|
227 * since those two SNACs are in the same rate class).
|
|
228 *
|
|
229 * Since the rate classes are defined dynamically at login, the values
|
|
230 * below may change. But they seem to be fairly constant.
|
|
231 *
|
|
232 * Currently, BOS defines five rate classes, with the commonly used
|
|
233 * members as follows...
|
|
234 *
|
|
235 * Rate class 0x0001:
|
|
236 * - Everything thats not in any of the other classes
|
|
237 *
|
|
238 * Rate class 0x0002:
|
|
239 * - Buddy list add/remove
|
|
240 * - Permit list add/remove
|
|
241 * - Deny list add/remove
|
|
242 *
|
|
243 * Rate class 0x0003:
|
|
244 * - User information requests
|
|
245 * - Outgoing ICBMs
|
|
246 *
|
|
247 * Rate class 0x0004:
|
|
248 * - A few unknowns: 2/9, 2/b, and f/2
|
|
249 *
|
|
250 * Rate class 0x0005:
|
|
251 * - Chat room create
|
|
252 * - Outgoing chat ICBMs
|
|
253 *
|
|
254 * The only other thing of note is that class 5 (chat) has slightly looser
|
|
255 * limiting properties than class 3 (normal messages). But thats just a
|
|
256 * small bit of trivia for you.
|
|
257 *
|
|
258 * The last thing that needs to be learned about the rate limiting
|
|
259 * system is how the actual numbers relate to the passing of time. This
|
|
260 * seems to be a big mystery.
|
|
261 *
|
|
262 */
|
|
263
|
|
264 static void rc_addclass(struct rateclass **head, struct rateclass *inrc)
|
|
265 {
|
|
266 struct rateclass *rc, *rc2;
|
|
267
|
|
268 if (!(rc = malloc(sizeof(struct rateclass))))
|
|
269 return;
|
|
270
|
|
271 memcpy(rc, inrc, sizeof(struct rateclass));
|
|
272 rc->next = NULL;
|
|
273
|
|
274 for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next)
|
|
275 ;
|
|
276
|
|
277 if (!rc2)
|
|
278 *head = rc;
|
|
279 else
|
|
280 rc2->next = rc;
|
|
281
|
|
282 return;
|
|
283 }
|
|
284
|
|
285 static struct rateclass *rc_findclass(struct rateclass **head, guint16 id)
|
|
286 {
|
|
287 struct rateclass *rc;
|
|
288
|
|
289 for (rc = *head; rc; rc = rc->next) {
|
|
290 if (rc->classid == id)
|
|
291 return rc;
|
|
292 }
|
|
293
|
|
294 return NULL;
|
|
295 }
|
|
296
|
|
297 static void rc_addpair(struct rateclass *rc, guint16 group, guint16 type)
|
|
298 {
|
|
299 struct snacpair *sp, *sp2;
|
|
300
|
|
301 if (!(sp = malloc(sizeof(struct snacpair))))
|
|
302 return;
|
|
303 memset(sp, 0, sizeof(struct snacpair));
|
|
304
|
|
305 sp->group = group;
|
|
306 sp->subtype = type;
|
|
307 sp->next = NULL;
|
|
308
|
|
309 for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next)
|
|
310 ;
|
|
311
|
|
312 if (!sp2)
|
|
313 rc->members = sp;
|
|
314 else
|
|
315 sp2->next = sp;
|
|
316
|
|
317 return;
|
|
318 }
|
|
319
|
|
320 /* Subtype 0x0007 - Rate Parameters */
|
|
321 static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
322 {
|
|
323 aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
|
|
324 guint16 numclasses, i;
|
|
325 aim_rxcallback_t userfunc;
|
|
326
|
|
327
|
|
328 /*
|
|
329 * First are the parameters for each rate class.
|
|
330 */
|
|
331 numclasses = aimbs_get16(bs);
|
|
332 for (i = 0; i < numclasses; i++) {
|
|
333 struct rateclass rc;
|
|
334
|
|
335 memset(&rc, 0, sizeof(struct rateclass));
|
|
336
|
|
337 rc.classid = aimbs_get16(bs);
|
|
338 rc.windowsize = aimbs_get32(bs);
|
|
339 rc.clear = aimbs_get32(bs);
|
|
340 rc.alert = aimbs_get32(bs);
|
|
341 rc.limit = aimbs_get32(bs);
|
|
342 rc.disconnect = aimbs_get32(bs);
|
|
343 rc.current = aimbs_get32(bs);
|
|
344 rc.max = aimbs_get32(bs);
|
|
345
|
|
346 /*
|
|
347 * The server will send an extra five bytes of parameters
|
|
348 * depending on the version we advertised in 1/17. If we
|
|
349 * didn't send 1/17 (evil!), then this will crash and you
|
|
350 * die, as it will default to the old version but we have
|
|
351 * the new version hardcoded here.
|
|
352 */
|
|
353 if (mod->version >= 3)
|
|
354 aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown));
|
|
355
|
|
356 gaim_debug_misc("oscar", "--- Adding rate class %d to connection type %d: window size = %ld, clear = %ld, alert = %ld, limit = %ld, disconnect = %ld, current = %ld, max = %ld\n", rx->conn->type, rc.classid, rc.windowsize, rc.clear, rc.alert, rc.limit, rc.disconnect, rc.current, rc.max);
|
|
357
|
|
358 rc_addclass(&ins->rates, &rc);
|
|
359 }
|
|
360
|
|
361 /*
|
|
362 * Then the members of each class.
|
|
363 */
|
|
364 for (i = 0; i < numclasses; i++) {
|
|
365 guint16 classid, count;
|
|
366 struct rateclass *rc;
|
|
367 int j;
|
|
368
|
|
369 classid = aimbs_get16(bs);
|
|
370 count = aimbs_get16(bs);
|
|
371
|
|
372 rc = rc_findclass(&ins->rates, classid);
|
|
373
|
|
374 for (j = 0; j < count; j++) {
|
|
375 guint16 group, subtype;
|
|
376
|
|
377 group = aimbs_get16(bs);
|
|
378 subtype = aimbs_get16(bs);
|
|
379
|
|
380 if (rc)
|
|
381 rc_addpair(rc, group, subtype);
|
|
382 }
|
|
383 }
|
|
384
|
|
385 /*
|
|
386 * We don't pass the rate information up to the client, as it really
|
|
387 * doesn't care. The information is stored in the connection, however
|
|
388 * so that we can do more fun stuff later (not really).
|
|
389 */
|
|
390
|
|
391 /*
|
|
392 * Last step in the conn init procedure is to acknowledge that we
|
|
393 * agree to these draconian limitations.
|
|
394 */
|
|
395 aim_rates_addparam(sess, rx->conn);
|
|
396
|
|
397 /*
|
|
398 * Finally, tell the client it's ready to go...
|
|
399 */
|
|
400 if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
|
|
401 userfunc(sess, rx);
|
|
402
|
|
403
|
|
404 return 1;
|
|
405 }
|
|
406
|
|
407 /* Subtype 0x0008 - Add Rate Parameter */
|
|
408 faim_internal int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
|
|
409 {
|
|
410 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
|
|
411 aim_frame_t *fr;
|
|
412 aim_snacid_t snacid;
|
|
413 struct rateclass *rc;
|
|
414
|
|
415 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
|
|
416 return -ENOMEM;
|
|
417
|
|
418 snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
|
|
419 aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
|
|
420
|
|
421 for (rc = ins->rates; rc; rc = rc->next)
|
|
422 aimbs_put16(&fr->data, rc->classid);
|
|
423
|
|
424 aim_tx_enqueue(sess, fr);
|
|
425
|
|
426 return 0;
|
|
427 }
|
|
428
|
|
429 /* Subtype 0x0009 - Delete Rate Parameter */
|
|
430 faim_internal int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
|
|
431 {
|
|
432 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
|
|
433 aim_frame_t *fr;
|
|
434 aim_snacid_t snacid;
|
|
435 struct rateclass *rc;
|
|
436
|
|
437 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
|
|
438 return -ENOMEM;
|
|
439
|
|
440 snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0);
|
|
441 aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid);
|
|
442
|
|
443 for (rc = ins->rates; rc; rc = rc->next)
|
|
444 aimbs_put16(&fr->data, rc->classid);
|
|
445
|
|
446 aim_tx_enqueue(sess, fr);
|
|
447
|
|
448 return 0;
|
|
449 }
|
|
450
|
|
451 /* Subtype 0x000a - Rate Change */
|
|
452 static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
453 {
|
|
454 int ret = 0;
|
|
455 aim_rxcallback_t userfunc;
|
|
456 guint16 code, rateclass;
|
|
457 guint32 currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
|
|
458
|
|
459 code = aimbs_get16(bs);
|
|
460 rateclass = aimbs_get16(bs);
|
|
461
|
|
462 windowsize = aimbs_get32(bs);
|
|
463 clear = aimbs_get32(bs);
|
|
464 alert = aimbs_get32(bs);
|
|
465 limit = aimbs_get32(bs);
|
|
466 disconnect = aimbs_get32(bs);
|
|
467 currentavg = aimbs_get32(bs);
|
|
468 maxavg = aimbs_get32(bs);
|
|
469
|
|
470 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
471 ret = userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
|
|
472
|
|
473 return ret;
|
|
474 }
|
|
475
|
|
476 /*
|
|
477 * How Migrations work.
|
|
478 *
|
|
479 * The server sends a Server Pause message, which the client should respond to
|
|
480 * with a Server Pause Ack, which contains the families it needs on this
|
|
481 * connection. The server will send a Migration Notice with an IP address, and
|
|
482 * then disconnect. Next the client should open the connection and send the
|
|
483 * cookie. Repeat the normal login process and pretend this never happened.
|
|
484 *
|
|
485 * The Server Pause contains no data.
|
|
486 *
|
|
487 */
|
|
488
|
|
489 /* Subtype 0x000b - Service Pause */
|
|
490 static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
491 {
|
|
492 int ret = 0;
|
|
493 aim_rxcallback_t userfunc;
|
|
494
|
|
495 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
496 ret = userfunc(sess, rx);
|
|
497
|
|
498 return ret;
|
|
499 }
|
|
500
|
|
501 /*
|
|
502 * Subtype 0x000c - Service Pause Acknowledgement
|
|
503 *
|
|
504 * It is rather important that aim_sendpauseack() gets called for the exact
|
|
505 * same connection that the Server Pause callback was called for, since
|
|
506 * libfaim extracts the data for the SNAC from the connection structure.
|
|
507 *
|
|
508 * Of course, if you don't do that, more bad things happen than just what
|
|
509 * libfaim can cause.
|
|
510 *
|
|
511 */
|
|
512 faim_export int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn)
|
|
513 {
|
|
514 aim_frame_t *fr;
|
|
515 aim_snacid_t snacid;
|
|
516 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
|
|
517 struct snacgroup *sg;
|
|
518
|
|
519 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024)))
|
|
520 return -ENOMEM;
|
|
521
|
|
522 snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0);
|
|
523 aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid);
|
|
524
|
|
525 /*
|
|
526 * This list should have all the groups that the original
|
|
527 * Host Online / Server Ready said this host supports. And
|
|
528 * we want them all back after the migration.
|
|
529 */
|
|
530 for (sg = ins->groups; sg; sg = sg->next)
|
|
531 aimbs_put16(&fr->data, sg->group);
|
|
532
|
|
533 aim_tx_enqueue(sess, fr);
|
|
534
|
|
535 return 0;
|
|
536 }
|
|
537
|
|
538 /* Subtype 0x000d - Service Resume */
|
|
539 static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
540 {
|
|
541 int ret = 0;
|
|
542 aim_rxcallback_t userfunc;
|
|
543
|
|
544 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
545 ret = userfunc(sess, rx);
|
|
546
|
|
547 return ret;
|
|
548 }
|
|
549
|
|
550 /* Subtype 0x000e - Request self-info */
|
|
551 faim_export int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
|
|
552 {
|
|
553 return aim_genericreq_n_snacid(sess, conn, 0x0001, 0x000e);
|
|
554 }
|
|
555
|
|
556 /* Subtype 0x000f - Self User Info */
|
|
557 static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
558 {
|
|
559 int ret = 0;
|
|
560 aim_rxcallback_t userfunc;
|
|
561 aim_userinfo_t userinfo;
|
|
562
|
|
563 aim_info_extract(sess, bs, &userinfo);
|
|
564
|
|
565 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
566 ret = userfunc(sess, rx, &userinfo);
|
|
567
|
|
568 aim_info_free(&userinfo);
|
|
569
|
|
570 return ret;
|
|
571 }
|
|
572
|
|
573 /* Subtype 0x0010 - Evil Notification */
|
|
574 static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
575 {
|
|
576 int ret = 0;
|
|
577 aim_rxcallback_t userfunc;
|
|
578 guint16 newevil;
|
|
579 aim_userinfo_t userinfo;
|
|
580
|
|
581 memset(&userinfo, 0, sizeof(aim_userinfo_t));
|
|
582
|
|
583 newevil = aimbs_get16(bs);
|
|
584
|
|
585 if (aim_bstream_empty(bs))
|
|
586 aim_info_extract(sess, bs, &userinfo);
|
|
587
|
|
588 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
589 ret = userfunc(sess, rx, newevil, &userinfo);
|
|
590
|
|
591 aim_info_free(&userinfo);
|
|
592
|
|
593 return ret;
|
|
594 }
|
|
595
|
|
596 /*
|
|
597 * Subtype 0x0011 - Idle Notification
|
|
598 *
|
|
599 * Should set your current idle time in seconds. Note that this should
|
|
600 * never be called consecutively with a non-zero idle time. That makes
|
|
601 * OSCAR do funny things. Instead, just set it once you go idle, and then
|
|
602 * call it again with zero when you're back.
|
|
603 *
|
|
604 */
|
|
605 faim_export int aim_srv_setidle(aim_session_t *sess, guint32 idletime)
|
|
606 {
|
|
607 aim_conn_t *conn;
|
|
608
|
|
609 if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_BOS)))
|
|
610 return -EINVAL;
|
|
611
|
|
612 return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
|
|
613 }
|
|
614
|
|
615 /*
|
|
616 * Subtype 0x0012 - Service Migrate
|
|
617 *
|
|
618 * This is the final SNAC sent on the original connection during a migration.
|
|
619 * It contains the IP and cookie used to connect to the new server, and
|
|
620 * optionally a list of the SNAC groups being migrated.
|
|
621 *
|
|
622 */
|
|
623 static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
624 {
|
|
625 aim_rxcallback_t userfunc;
|
|
626 int ret = 0;
|
|
627 guint16 groupcount, i;
|
|
628 aim_tlvlist_t *tl;
|
|
629 char *ip = NULL;
|
|
630 aim_tlv_t *cktlv;
|
|
631
|
|
632 /*
|
|
633 * Apparently there's some fun stuff that can happen right here. The
|
|
634 * migration can actually be quite selective about what groups it
|
|
635 * moves to the new server. When not all the groups for a connection
|
|
636 * are migrated, or they are all migrated but some groups are moved
|
|
637 * to a different server than others, it is called a bifurcated
|
|
638 * migration.
|
|
639 *
|
|
640 * Let's play dumb and not support that.
|
|
641 *
|
|
642 */
|
|
643 groupcount = aimbs_get16(bs);
|
|
644 for (i = 0; i < groupcount; i++) {
|
|
645 guint16 group;
|
|
646
|
|
647 group = aimbs_get16(bs);
|
|
648
|
|
649 gaim_debug_misc("oscar", "bifurcated migration unsupported -- group 0x%04x\n", group);
|
|
650 }
|
|
651
|
|
652 tl = aim_tlvlist_read(bs);
|
|
653
|
|
654 if (aim_tlv_gettlv(tl, 0x0005, 1))
|
|
655 ip = aim_tlv_getstr(tl, 0x0005, 1);
|
|
656
|
|
657 cktlv = aim_tlv_gettlv(tl, 0x0006, 1);
|
|
658
|
|
659 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
660 ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL);
|
|
661
|
|
662 aim_tlvlist_free(&tl);
|
|
663 free(ip);
|
|
664
|
|
665 return ret;
|
|
666 }
|
|
667
|
|
668 /* Subtype 0x0013 - Message of the Day */
|
|
669 static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
670 {
|
|
671 aim_rxcallback_t userfunc;
|
|
672 char *msg = NULL;
|
|
673 int ret = 0;
|
|
674 aim_tlvlist_t *tlvlist;
|
|
675 guint16 id;
|
|
676
|
|
677 /*
|
|
678 * Code.
|
|
679 *
|
|
680 * Valid values:
|
|
681 * 1 Mandatory upgrade
|
|
682 * 2 Advisory upgrade
|
|
683 * 3 System bulletin
|
|
684 * 4 Nothing's wrong ("top o the world" -- normal)
|
|
685 * 5 Lets-break-something.
|
|
686 *
|
|
687 */
|
|
688 id = aimbs_get16(bs);
|
|
689
|
|
690 /*
|
|
691 * TLVs follow
|
|
692 */
|
|
693 tlvlist = aim_tlvlist_read(bs);
|
|
694
|
|
695 msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
|
|
696
|
|
697 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
698 ret = userfunc(sess, rx, id, msg);
|
|
699
|
|
700 free(msg);
|
|
701
|
|
702 aim_tlvlist_free(&tlvlist);
|
|
703
|
|
704 return ret;
|
|
705 }
|
|
706
|
|
707 /*
|
|
708 * Subtype 0x0014 - Set privacy flags
|
|
709 *
|
|
710 * Normally 0x03.
|
|
711 *
|
|
712 * Bit 1: Allows other AIM users to see how long you've been idle.
|
|
713 * Bit 2: Allows other AIM users to see how long you've been a member.
|
|
714 *
|
|
715 */
|
|
716 faim_export int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, guint32 flags)
|
|
717 {
|
|
718 return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
|
|
719 }
|
|
720
|
|
721 /*
|
|
722 * Subtype 0x0016 - No-op
|
|
723 *
|
|
724 * WinAIM sends these every 4min or so to keep the connection alive. Its not
|
|
725 * really necessary.
|
|
726 *
|
|
727 * Wha? No? Since when? I think WinAIM sends an empty channel 3
|
|
728 * SNAC as a no-op...
|
|
729 */
|
|
730 faim_export int aim_nop(aim_session_t *sess, aim_conn_t *conn)
|
|
731 {
|
|
732 return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
|
|
733 }
|
|
734
|
|
735 /*
|
|
736 * Subtype 0x0017 - Set client versions
|
|
737 *
|
|
738 * If you've seen the clientonline/clientready SNAC you're probably
|
|
739 * wondering what the point of this one is. And that point seems to be
|
|
740 * that the versions in the client online SNAC are sent too late for the
|
|
741 * server to be able to use them to change the protocol for the earlier
|
|
742 * login packets (client versions are sent right after Host Online is
|
|
743 * received, but client online versions aren't sent until quite a bit later).
|
|
744 * We can see them already making use of this by changing the format of
|
|
745 * the rate information based on what version of group 1 we advertise here.
|
|
746 *
|
|
747 */
|
|
748 faim_internal int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
|
|
749 {
|
|
750 aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
|
|
751 struct snacgroup *sg;
|
|
752 aim_frame_t *fr;
|
|
753 aim_snacid_t snacid;
|
|
754
|
|
755 if (!ins)
|
|
756 return -EINVAL;
|
|
757
|
|
758 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
|
|
759 return -ENOMEM;
|
|
760
|
|
761 snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
|
|
762 aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
|
|
763
|
|
764 /*
|
|
765 * Send only the versions that the server cares about (that it
|
|
766 * marked as supporting in the server ready SNAC).
|
|
767 */
|
|
768 for (sg = ins->groups; sg; sg = sg->next) {
|
|
769 aim_module_t *mod;
|
|
770
|
|
771 if ((mod = aim__findmodulebygroup(sess, sg->group))) {
|
|
772 aimbs_put16(&fr->data, mod->family);
|
|
773 aimbs_put16(&fr->data, mod->version);
|
|
774 } else
|
|
775 gaim_debug_misc("oscar", "aim_setversions: server supports group 0x%04x but we don't!\n", sg->group);
|
|
776 }
|
|
777
|
|
778 aim_tx_enqueue(sess, fr);
|
|
779
|
|
780 return 0;
|
|
781 }
|
|
782
|
|
783 /* Subtype 0x0018 - Host versions */
|
|
784 static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
785 {
|
|
786 int vercount;
|
|
787 guint8 *versions;
|
|
788
|
|
789 /* This is frivolous. (Thank you SmarterChild.) */
|
|
790 vercount = aim_bstream_empty(bs)/4;
|
|
791 versions = aimbs_getraw(bs, aim_bstream_empty(bs));
|
|
792 free(versions);
|
|
793
|
|
794 /*
|
|
795 * Now request rates.
|
|
796 */
|
|
797 aim_reqrates(sess, rx->conn);
|
|
798
|
|
799 return 1;
|
|
800 }
|
|
801
|
|
802 /*
|
|
803 * Subtype 0x001e - Set various account settings (mostly ICQ related).
|
|
804 *
|
|
805 * These settings are transient, not server-stored (i.e. they only
|
|
806 * apply to this session, and must be re-set the next time you sign
|
|
807 * on).
|
|
808 *
|
|
809 * You can set your ICQ status (available, away, do not disturb,
|
|
810 * etc.), or whether your IP address should be hidden or not, or
|
|
811 * if your status is visible on ICQ web sites, and you can set
|
|
812 * your IP address info and what not.
|
|
813 *
|
|
814 * These are the same TLVs seen in user info. You can
|
|
815 * also set 0x0008 and 0x000c.
|
|
816 */
|
|
817 faim_export int aim_setextstatus(aim_session_t *sess, guint32 status)
|
|
818 {
|
|
819 aim_conn_t *conn;
|
|
820 aim_frame_t *fr;
|
|
821 aim_snacid_t snacid;
|
|
822 aim_tlvlist_t *tl = NULL;
|
|
823 guint32 data;
|
|
824
|
|
825 if (!sess || !(conn = aim_conn_findbygroup(sess, OSCAR_FAMILY_ICBM)))
|
|
826 return -EINVAL;
|
|
827
|
|
828 data = AIM_ICQ_STATE_HIDEIP | AIM_ICQ_STATE_DIRECTREQUIREAUTH | status;
|
|
829
|
|
830 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8)))
|
|
831 return -ENOMEM;
|
|
832
|
|
833 snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
|
|
834 aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
|
|
835
|
|
836 aim_tlvlist_add_32(&tl, 0x0006, data);
|
|
837 #if 0
|
|
838 aim_tlvlist_add_raw(&tl, 0x000c, 0x0025, chunk_of_x25_bytes_with_ip_address_etc);
|
|
839 aim_tlvlist_add_raw(&tl, 0x0011, 0x0005, unknown 0x01 61 10 f6 41);
|
|
840 aim_tlvlist_add_16(&tl, 0x0012, unknown 0x00 00);
|
|
841 #endif
|
|
842 aim_tlvlist_write(&fr->data, &tl);
|
|
843 aim_tlvlist_free(&tl);
|
|
844
|
|
845 aim_tx_enqueue(sess, fr);
|
|
846
|
|
847 return 0;
|
|
848 }
|
|
849
|
|
850 /*
|
|
851 * Subtype 0x001e - Extended Status.
|
|
852 *
|
|
853 * Sets your "available" message. This is currently only supported by iChat
|
|
854 * and Gaim.
|
|
855 *
|
|
856 * These are the same TLVs seen in user info. You can
|
|
857 * also set 0x0008 and 0x000c.
|
|
858 */
|
|
859 faim_export int aim_srv_setstatusmsg(aim_session_t *sess, const char *msg)
|
|
860 {
|
|
861 aim_conn_t *conn;
|
|
862 aim_frame_t *fr;
|
|
863 aim_snacid_t snacid;
|
|
864
|
|
865 if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
|
|
866 return -EINVAL;
|
|
867
|
|
868 if ((msg != NULL) && *msg != '\0') {
|
|
869 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + strlen(msg) + 8)))
|
|
870 return -ENOMEM;
|
|
871
|
|
872 snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
|
|
873 aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
|
|
874
|
|
875 aimbs_put16(&fr->data, 0x001d); /* userinfo TLV type */
|
|
876 aimbs_put16(&fr->data, strlen(msg)+8); /* total length of userinfo TLV data */
|
|
877 aimbs_put16(&fr->data, 0x0002);
|
|
878 aimbs_put8(&fr->data, 0x04);
|
|
879 aimbs_put8(&fr->data, strlen(msg)+4);
|
|
880 aimbs_put16(&fr->data, strlen(msg));
|
|
881 aimbs_putstr(&fr->data, msg);
|
|
882 aimbs_put16(&fr->data, 0x0000);
|
|
883 } else {
|
|
884 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + 8)))
|
|
885 return -ENOMEM;
|
|
886
|
|
887 snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
|
|
888 aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
|
|
889
|
|
890 aimbs_put16(&fr->data, 0x001d);
|
|
891 aimbs_put16(&fr->data, 0x0008);
|
|
892 aimbs_put16(&fr->data, 0x0002);
|
|
893 aimbs_put16(&fr->data, 0x0404);
|
|
894 aimbs_put16(&fr->data, 0x0000);
|
|
895 aimbs_put16(&fr->data, 0x0000);
|
|
896 }
|
|
897
|
|
898 aim_tx_enqueue(sess, fr);
|
|
899
|
|
900 return 0;
|
|
901 }
|
|
902
|
|
903 /*
|
|
904 * Starting this past week (26 Mar 2001, say), AOL has started sending
|
|
905 * this nice little extra SNAC. AFAIK, it has never been used until now.
|
|
906 *
|
|
907 * The request contains eight bytes. The first four are an offset, the
|
|
908 * second four are a length.
|
|
909 *
|
|
910 * The offset is an offset into aim.exe when it is mapped during execution
|
|
911 * on Win32. So far, AOL has only been requesting bytes in static regions
|
|
912 * of memory. (I won't put it past them to start requesting data in
|
|
913 * less static regions -- regions that are initialized at run time, but still
|
|
914 * before the client receives this request.)
|
|
915 *
|
|
916 * When the client receives the request, it adds it to the current ds
|
|
917 * (0x00400000) and dereferences it, copying the data into a buffer which
|
|
918 * it then runs directly through the MD5 hasher. The 16 byte output of
|
|
919 * the hash is then sent back to the server.
|
|
920 *
|
|
921 * If the client does not send any data back, or the data does not match
|
|
922 * the data that the specific client should have, the client will get the
|
|
923 * following message from "AOL Instant Messenger":
|
|
924 * "You have been disconnected from the AOL Instant Message Service (SM)
|
|
925 * for accessing the AOL network using unauthorized software. You can
|
|
926 * download a FREE, fully featured, and authorized client, here
|
|
927 * http://www.aol.com/aim/download2.html"
|
|
928 * The connection is then closed, receiving disconnect code 1, URL
|
|
929 * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.
|
|
930 *
|
|
931 * Note, however, that numerous inconsistencies can cause the above error,
|
|
932 * not just sending back a bad hash. Do not immediatly suspect this code
|
|
933 * if you get disconnected. AOL and the open/free software community have
|
|
934 * played this game for a couple years now, generating the above message
|
|
935 * on numerous ocassions.
|
|
936 *
|
|
937 * Anyway, neener. We win again.
|
|
938 *
|
|
939 */
|
|
940 /* Subtype 0x001f - Client verification */
|
|
941 static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
942 {
|
|
943 int ret = 0;
|
|
944 aim_rxcallback_t userfunc;
|
|
945 guint32 offset, len;
|
|
946 aim_tlvlist_t *list;
|
|
947 char *modname;
|
|
948
|
|
949 offset = aimbs_get32(bs);
|
|
950 len = aimbs_get32(bs);
|
|
951 list = aim_tlvlist_read(bs);
|
|
952
|
|
953 modname = aim_tlv_getstr(list, 0x0001, 1);
|
|
954
|
|
955 gaim_debug_info("oscar", "Got memory request for data at 0x%08lx (%d bytes) of requested %s\n", offset, len, modname ? modname : "aim.exe");
|
|
956
|
|
957 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
|
|
958 ret = userfunc(sess, rx, offset, len, modname);
|
|
959
|
|
960 free(modname);
|
|
961 aim_tlvlist_free(&list);
|
|
962
|
|
963 return ret;
|
|
964 }
|
|
965
|
|
966 /* Subtype 0x0020 - Client verification reply */
|
|
967 faim_export int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
|
|
968 {
|
|
969 aim_frame_t *fr;
|
|
970 aim_snacid_t snacid;
|
|
971
|
|
972 if (!sess || !conn)
|
|
973 return -EINVAL;
|
|
974
|
|
975 if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
|
|
976 return -ENOMEM;
|
|
977
|
|
978 snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
|
|
979
|
|
980 aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid);
|
|
981 aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */
|
|
982
|
|
983 if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
|
|
984
|
|
985 aimbs_putraw(&fr->data, buf, 0x10);
|
|
986
|
|
987 } else if (buf && (len > 0)) { /* use input buffer */
|
|
988 GaimCipher *cipher;
|
|
989 GaimCipherContext *context;
|
|
990 guchar digest[16];
|
|
991
|
|
992 cipher = gaim_ciphers_find_cipher("md5");
|
|
993
|
|
994 context = gaim_cipher_context_new(cipher, NULL);
|
|
995 gaim_cipher_context_append(context, buf, len);
|
|
996 gaim_cipher_context_digest(context, 16, digest, NULL);
|
|
997 gaim_cipher_context_destroy(context);
|
|
998
|
|
999 aimbs_putraw(&fr->data, digest, 0x10);
|
|
1000
|
|
1001 } else if (len == 0) { /* no length, just hash NULL (buf is optional) */
|
|
1002 GaimCipher *cipher;
|
|
1003 GaimCipherContext *context;
|
|
1004 guchar digest[16];
|
|
1005 guint8 nil = '\0';
|
|
1006
|
|
1007 /*
|
|
1008 * I'm not sure if we really need the empty append with the
|
|
1009 * new MD5 functions, so I'll leave it in, just in case.
|
|
1010 */
|
|
1011 cipher = gaim_ciphers_find_cipher("md5");
|
|
1012
|
|
1013 context = gaim_cipher_context_new(cipher, NULL);
|
|
1014 gaim_cipher_context_append(context, &nil, 0);
|
|
1015 gaim_cipher_context_digest(context, 16, digest, NULL);
|
|
1016 gaim_cipher_context_destroy(context);
|
|
1017
|
|
1018 aimbs_putraw(&fr->data, digest, 0x10);
|
|
1019
|
|
1020 } else {
|
|
1021
|
|
1022 /*
|
|
1023 * This data is correct for AIM 3.5.1670.
|
|
1024 *
|
|
1025 * Using these blocks is as close to "legal" as you can get
|
|
1026 * without using an AIM binary.
|
|
1027 *
|
|
1028 */
|
|
1029 if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
|
|
1030
|
|
1031 #if 1 /* with "AnrbnrAqhfzcd" */
|
|
1032 aimbs_put32(&fr->data, 0x44a95d26);
|
|
1033 aimbs_put32(&fr->data, 0xd2490423);
|
|
1034 aimbs_put32(&fr->data, 0x93b8821f);
|
|
1035 aimbs_put32(&fr->data, 0x51c54b01);
|
|
1036 #else /* no filename */
|
|
1037 aimbs_put32(&fr->data, 0x1df8cbae);
|
|
1038 aimbs_put32(&fr->data, 0x5523b839);
|
|
1039 aimbs_put32(&fr->data, 0xa0e10db3);
|
|
1040 aimbs_put32(&fr->data, 0xa46d3b39);
|
|
1041 #endif
|
|
1042
|
|
1043 } else if ((offset == 0x00001000) && (len == 0x00000000)) {
|
|
1044
|
|
1045 aimbs_put32(&fr->data, 0xd41d8cd9);
|
|
1046 aimbs_put32(&fr->data, 0x8f00b204);
|
|
1047 aimbs_put32(&fr->data, 0xe9800998);
|
|
1048 aimbs_put32(&fr->data, 0xecf8427e);
|
|
1049
|
|
1050 } else
|
|
1051 gaim_debug_warning("oscar", "sendmemblock: unknown hash request\n");
|
|
1052
|
|
1053 }
|
|
1054
|
|
1055 aim_tx_enqueue(sess, fr);
|
|
1056
|
|
1057 return 0;
|
|
1058 }
|
|
1059
|
|
1060 /*
|
|
1061 * Subtype 0x0021 - Receive our extended status
|
|
1062 *
|
|
1063 * This is used for iChat's "available" messages, and maybe ICQ extended
|
|
1064 * status messages? It's also used to tell the client whether or not it
|
|
1065 * needs to upload an SSI buddy icon... who engineers this stuff, anyway?
|
|
1066 */
|
|
1067 static int aim_parse_extstatus(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1068 {
|
|
1069 int ret = 0;
|
|
1070 aim_rxcallback_t userfunc;
|
|
1071 guint16 type;
|
|
1072 guint8 flags, length;
|
|
1073
|
|
1074 type = aimbs_get16(bs);
|
|
1075 flags = aimbs_get8(bs);
|
|
1076 length = aimbs_get8(bs);
|
|
1077
|
|
1078 if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
|
|
1079 switch (type) {
|
|
1080 case 0x0000:
|
|
1081 case 0x0001: { /* buddy icon checksum */
|
|
1082 /* not sure what the difference between 1 and 0 is */
|
|
1083 guint8 *md5 = aimbs_getraw(bs, length);
|
|
1084 ret = userfunc(sess, rx, type, flags, length, md5);
|
|
1085 free(md5);
|
|
1086 } break;
|
|
1087 case 0x0002: { /* available message */
|
|
1088 /* there is a second length that is just for the message */
|
|
1089 char *msg = aimbs_getstr(bs, aimbs_get16(bs));
|
|
1090 ret = userfunc(sess, rx, msg);
|
|
1091 free(msg);
|
|
1092 } break;
|
|
1093 }
|
|
1094 }
|
|
1095
|
|
1096 return ret;
|
|
1097 }
|
|
1098
|
|
1099 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
|
|
1100 {
|
|
1101
|
|
1102 if (snac->subtype == 0x0003)
|
|
1103 return hostonline(sess, mod, rx, snac, bs);
|
|
1104 else if (snac->subtype == 0x0005)
|
|
1105 return redirect(sess, mod, rx, snac, bs);
|
|
1106 else if (snac->subtype == 0x0007)
|
|
1107 return rateresp(sess, mod, rx, snac, bs);
|
|
1108 else if (snac->subtype == 0x000a)
|
|
1109 return ratechange(sess, mod, rx, snac, bs);
|
|
1110 else if (snac->subtype == 0x000b)
|
|
1111 return serverpause(sess, mod, rx, snac, bs);
|
|
1112 else if (snac->subtype == 0x000d)
|
|
1113 return serverresume(sess, mod, rx, snac, bs);
|
|
1114 else if (snac->subtype == 0x000f)
|
|
1115 return selfinfo(sess, mod, rx, snac, bs);
|
|
1116 else if (snac->subtype == 0x0010)
|
|
1117 return evilnotify(sess, mod, rx, snac, bs);
|
|
1118 else if (snac->subtype == 0x0012)
|
|
1119 return migrate(sess, mod, rx, snac, bs);
|
|
1120 else if (snac->subtype == 0x0013)
|
|
1121 return motd(sess, mod, rx, snac, bs);
|
|
1122 else if (snac->subtype == 0x0018)
|
|
1123 return hostversions(sess, mod, rx, snac, bs);
|
|
1124 else if (snac->subtype == 0x001f)
|
|
1125 return memrequest(sess, mod, rx, snac, bs);
|
|
1126 else if (snac->subtype == 0x0021)
|
|
1127 return aim_parse_extstatus(sess, mod, rx, snac, bs);
|
|
1128
|
|
1129 return 0;
|
|
1130 }
|
|
1131
|
|
1132 faim_internal int service_modfirst(aim_session_t *sess, aim_module_t *mod)
|
|
1133 {
|
|
1134
|
|
1135 mod->family = 0x0001;
|
|
1136 mod->version = 0x0003;
|
|
1137 mod->toolid = 0x0110;
|
|
1138 mod->toolversion = 0x0629;
|
|
1139 mod->flags = 0;
|
|
1140 strncpy(mod->name, "oservice", sizeof(mod->name));
|
|
1141 mod->snachandler = snachandler;
|
|
1142
|
|
1143 return 0;
|
|
1144 }
|