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