1535
|
1 /*
|
|
2 * aim_rxhandlers.c
|
|
3 *
|
|
4 * This file contains most all of the incoming packet handlers, along
|
|
5 * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is
|
|
6 * actually done in aim_rxqueue.c.
|
|
7 *
|
|
8 */
|
|
9
|
|
10 #define FAIM_INTERNAL
|
|
11 #include <aim.h>
|
|
12
|
|
13 /*
|
|
14 * Bleck functions get called when there's no non-bleck functions
|
|
15 * around to cleanup the mess...
|
|
16 */
|
|
17 faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
|
|
18 {
|
|
19 u_short family;
|
|
20 u_short subtype;
|
|
21
|
|
22 u_short maxf;
|
|
23 u_short maxs;
|
|
24
|
|
25 /* XXX: this is ugly. and big just for debugging. */
|
|
26 char *literals[14][25] = {
|
|
27 {"Invalid",
|
|
28 NULL
|
|
29 },
|
|
30 {"General",
|
|
31 "Invalid",
|
|
32 "Error",
|
|
33 "Client Ready",
|
|
34 "Server Ready",
|
|
35 "Service Request",
|
|
36 "Redirect",
|
|
37 "Rate Information Request",
|
|
38 "Rate Information",
|
|
39 "Rate Information Ack",
|
|
40 NULL,
|
|
41 "Rate Information Change",
|
|
42 "Server Pause",
|
|
43 NULL,
|
|
44 "Server Resume",
|
|
45 "Request Personal User Information",
|
|
46 "Personal User Information",
|
|
47 "Evil Notification",
|
|
48 NULL,
|
|
49 "Migration notice",
|
|
50 "Message of the Day",
|
|
51 "Set Privacy Flags",
|
|
52 "Well Known URL",
|
|
53 "NOP"
|
|
54 },
|
|
55 {"Location",
|
|
56 "Invalid",
|
|
57 "Error",
|
|
58 "Request Rights",
|
|
59 "Rights Information",
|
|
60 "Set user information",
|
|
61 "Request User Information",
|
|
62 "User Information",
|
|
63 "Watcher Sub Request",
|
|
64 "Watcher Notification"
|
|
65 },
|
|
66 {"Buddy List Management",
|
|
67 "Invalid",
|
|
68 "Error",
|
|
69 "Request Rights",
|
|
70 "Rights Information",
|
|
71 "Add Buddy",
|
|
72 "Remove Buddy",
|
|
73 "Watcher List Query",
|
|
74 "Watcher List Response",
|
|
75 "Watcher SubRequest",
|
|
76 "Watcher Notification",
|
|
77 "Reject Notification",
|
|
78 "Oncoming Buddy",
|
|
79 "Offgoing Buddy"
|
|
80 },
|
|
81 {"Messeging",
|
|
82 "Invalid",
|
|
83 "Error",
|
|
84 "Add ICBM Parameter",
|
|
85 "Remove ICBM Parameter",
|
|
86 "Request Parameter Information",
|
|
87 "Parameter Information",
|
|
88 "Outgoing Message",
|
|
89 "Incoming Message",
|
|
90 "Evil Request",
|
|
91 "Evil Reply",
|
|
92 "Missed Calls",
|
|
93 "Message Error",
|
|
94 "Host Ack"
|
|
95 },
|
|
96 {"Advertisements",
|
|
97 "Invalid",
|
|
98 "Error",
|
|
99 "Request Ad",
|
|
100 "Ad Data (GIFs)"
|
|
101 },
|
|
102 {"Invitation / Client-to-Client",
|
|
103 "Invalid",
|
|
104 "Error",
|
|
105 "Invite a Friend",
|
|
106 "Invitation Ack"
|
|
107 },
|
|
108 {"Administrative",
|
|
109 "Invalid",
|
|
110 "Error",
|
|
111 "Information Request",
|
|
112 "Information Reply",
|
|
113 "Information Change Request",
|
|
114 "Information Chat Reply",
|
|
115 "Account Confirm Request",
|
|
116 "Account Confirm Reply",
|
|
117 "Account Delete Request",
|
|
118 "Account Delete Reply"
|
|
119 },
|
|
120 {"Popups",
|
|
121 "Invalid",
|
|
122 "Error",
|
|
123 "Display Popup"
|
|
124 },
|
|
125 {"BOS",
|
|
126 "Invalid",
|
|
127 "Error",
|
|
128 "Request Rights",
|
|
129 "Rights Response",
|
|
130 "Set group permission mask",
|
|
131 "Add permission list entries",
|
|
132 "Delete permission list entries",
|
|
133 "Add deny list entries",
|
|
134 "Delete deny list entries",
|
|
135 "Server Error"
|
|
136 },
|
|
137 {"User Lookup",
|
|
138 "Invalid",
|
|
139 "Error",
|
|
140 "Search Request",
|
|
141 "Search Response"
|
|
142 },
|
|
143 {"Stats",
|
|
144 "Invalid",
|
|
145 "Error",
|
|
146 "Set minimum report interval",
|
|
147 "Report Events"
|
|
148 },
|
|
149 {"Translate",
|
|
150 "Invalid",
|
|
151 "Error",
|
|
152 "Translate Request",
|
|
153 "Translate Reply",
|
|
154 },
|
|
155 {"Chat Navigation",
|
|
156 "Invalid",
|
|
157 "Error",
|
|
158 "Request rights",
|
|
159 "Request Exchange Information",
|
|
160 "Request Room Information",
|
|
161 "Request Occupant List",
|
|
162 "Search for Room",
|
|
163 "Outgoing Message",
|
|
164 "Incoming Message",
|
|
165 "Evil Request",
|
|
166 "Evil Reply",
|
|
167 "Chat Error",
|
|
168 }
|
|
169 };
|
|
170
|
|
171 maxf = sizeof(literals) / sizeof(literals[0]);
|
|
172 maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
|
|
173
|
|
174 family = aimutil_get16(workingPtr->data+0);
|
|
175 subtype= aimutil_get16(workingPtr->data+2);
|
|
176
|
|
177 if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
|
|
178 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
|
|
179 else
|
|
180 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
|
|
181
|
|
182 return 1;
|
|
183 }
|
|
184
|
|
185 faim_export int aim_conn_addhandler(struct aim_session_t *sess,
|
|
186 struct aim_conn_t *conn,
|
|
187 u_short family,
|
|
188 u_short type,
|
|
189 rxcallback_t newhandler,
|
|
190 u_short flags)
|
|
191 {
|
|
192 struct aim_rxcblist_t *newcb;
|
|
193
|
|
194 if (!conn)
|
|
195 return -1;
|
|
196
|
|
197 faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
|
|
198
|
|
199 if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
|
|
200 return -1;
|
|
201 newcb->family = family;
|
|
202 newcb->type = type;
|
|
203 newcb->flags = flags;
|
|
204 if (!newhandler)
|
|
205 newcb->handler = &bleck;
|
|
206 else
|
|
207 newcb->handler = newhandler;
|
|
208 newcb->next = NULL;
|
|
209
|
|
210 if (!conn->handlerlist)
|
|
211 conn->handlerlist = newcb;
|
|
212 else {
|
|
213 struct aim_rxcblist_t *cur;
|
|
214
|
|
215 cur = conn->handlerlist;
|
|
216
|
|
217 while (cur->next)
|
|
218 cur = cur->next;
|
|
219 cur->next = newcb;
|
|
220 }
|
|
221
|
|
222 return 0;
|
|
223 }
|
|
224
|
|
225 faim_export int aim_clearhandlers(struct aim_conn_t *conn)
|
|
226 {
|
|
227 struct aim_rxcblist_t *cur;
|
|
228
|
|
229 if (!conn)
|
|
230 return -1;
|
|
231
|
|
232 for (cur = conn->handlerlist; cur; ) {
|
|
233 struct aim_rxcblist_t *tmp;
|
|
234
|
|
235 tmp = cur->next;
|
|
236 free(cur);
|
|
237 cur = tmp;
|
|
238 }
|
|
239 conn->handlerlist = NULL;
|
|
240
|
|
241 return 0;
|
|
242 }
|
|
243
|
|
244 faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess,
|
|
245 struct aim_conn_t *conn,
|
|
246 unsigned short family,
|
|
247 unsigned short type)
|
|
248 {
|
|
249 struct aim_rxcblist_t *cur;
|
|
250
|
|
251 if (!conn)
|
|
252 return NULL;
|
|
253
|
|
254 faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
|
|
255
|
|
256 for (cur = conn->handlerlist; cur; cur = cur->next) {
|
|
257 if ((cur->family == family) && (cur->type == type))
|
|
258 return cur->handler;
|
|
259 }
|
|
260
|
|
261 if (type == AIM_CB_SPECIAL_DEFAULT) {
|
|
262 faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
|
|
263 return NULL; /* prevent infinite recursion */
|
|
264 }
|
|
265
|
|
266 faimdprintf(sess, 1, "aim_callhandler: no handler for 0x%04x/0x%04x\n", family, type);
|
|
267
|
|
268 return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
|
|
269 }
|
|
270
|
|
271 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
|
|
272 struct aim_conn_t *conn,
|
|
273 u_short family,
|
|
274 u_short type,
|
|
275 struct command_rx_struct *ptr)
|
|
276 {
|
|
277 rxcallback_t userfunc = NULL;
|
|
278 userfunc = aim_callhandler(sess, conn, family, type);
|
|
279 if (userfunc)
|
|
280 return userfunc(sess, ptr);
|
|
281 return 1; /* XXX */
|
|
282 }
|
|
283
|
|
284 /*
|
|
285 aim_rxdispatch()
|
|
286
|
|
287 Basically, heres what this should do:
|
|
288 1) Determine correct packet handler for this packet
|
|
289 2) Mark the packet handled (so it can be dequeued in purge_queue())
|
|
290 3) Send the packet to the packet handler
|
|
291 4) Go to next packet in the queue and start over
|
|
292 5) When done, run purge_queue() to purge handled commands
|
|
293
|
|
294 Note that any unhandlable packets should probably be left in the
|
|
295 queue. This is the best way to prevent data loss. This means
|
|
296 that a single packet may get looked at by this function multiple
|
|
297 times. This is more good than bad! This behavior may change.
|
|
298
|
|
299 Aren't queue's fun?
|
|
300
|
|
301 TODO: Get rid of all the ugly if's.
|
|
302 TODO: Clean up.
|
|
303 TODO: More support for mid-level handlers.
|
|
304 TODO: Allow for NULL handlers.
|
|
305
|
|
306 */
|
|
307 faim_export int aim_rxdispatch(struct aim_session_t *sess)
|
|
308 {
|
|
309 int i = 0;
|
|
310 struct command_rx_struct *workingPtr = NULL;
|
|
311
|
|
312 if (sess->queue_incoming == NULL) {
|
|
313 faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
|
|
314 return 0;
|
|
315 } else {
|
|
316 workingPtr = sess->queue_incoming;
|
|
317 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
|
|
318 /*
|
|
319 * XXX: This is still fairly ugly.
|
|
320 */
|
|
321 if (workingPtr->handled)
|
|
322 continue;
|
|
323
|
|
324 /*
|
|
325 * This is a debugging/sanity check only and probably could/should be removed
|
|
326 * for stable code.
|
|
327 */
|
|
328 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
|
|
329 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
|
|
330 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
|
|
331 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
|
|
332 faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
|
|
333 workingPtr->handled = 1;
|
|
334 continue;
|
|
335 }
|
|
336
|
|
337 switch(workingPtr->conn->type) {
|
|
338 case -1:
|
|
339 /*
|
|
340 * This can happen if we have a queued command
|
|
341 * that was recieved after a connection has
|
|
342 * been terminated. In which case, the handler
|
|
343 * list has been cleared, and there's nothing we
|
|
344 * can do for it. We can only cancel it.
|
|
345 */
|
|
346 workingPtr->handled = 1;
|
|
347 break;
|
|
348 case AIM_CONN_TYPE_AUTH: {
|
|
349 unsigned long head;
|
|
350
|
|
351 head = aimutil_get32(workingPtr->data);
|
|
352 if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
|
|
353 faimdprintf(sess, 1, "got connection ack on auth line\n");
|
|
354 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
355 } else if (workingPtr->hdr.oscar.type == 0x04) {
|
|
356 /* Used only by the older login protocol */
|
|
357 workingPtr->handled = aim_authparse(sess, workingPtr);
|
|
358 } else {
|
|
359 unsigned short family,subtype;
|
|
360
|
|
361 family = aimutil_get16(workingPtr->data);
|
|
362 subtype = aimutil_get16(workingPtr->data+2);
|
|
363
|
|
364 switch (family) {
|
|
365 /* New login protocol */
|
|
366 case 0x0017:
|
|
367 if (subtype == 0x0001)
|
|
368 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
|
|
369 else if (subtype == 0x0003)
|
|
370 workingPtr->handled = aim_authparse(sess, workingPtr);
|
|
371 else if (subtype == 0x0007)
|
|
372 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
|
|
373 else
|
|
374 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
|
|
375 break;
|
|
376
|
|
377 case 0x0001:
|
|
378 if (subtype == 0x0003)
|
|
379 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
|
|
380 else if (subtype == 0x0007)
|
|
381 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
|
|
382 else if (subtype == 0x0018)
|
|
383 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
|
|
384 else
|
|
385 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
|
|
386 break;
|
|
387
|
|
388 case 0x0007:
|
|
389 if (subtype == 0x0003)
|
|
390 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
|
|
391 else if (subtype == 0x0005)
|
|
392 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
|
|
393 else if (subtype == 0x0007)
|
|
394 workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
|
|
395 break;
|
|
396
|
|
397 case AIM_CB_FAM_SPECIAL:
|
|
398 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
|
|
399 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
|
|
400 break;
|
|
401 } else
|
|
402 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
|
|
403 break;
|
|
404
|
|
405 default:
|
|
406 break;
|
|
407 }
|
|
408 }
|
|
409 break;
|
|
410 }
|
|
411 case AIM_CONN_TYPE_BOS: {
|
|
412 u_short family;
|
|
413 u_short subtype;
|
|
414
|
|
415 if (workingPtr->hdr.oscar.type == 0x04) {
|
|
416 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
|
|
417 break;
|
|
418 }
|
|
419
|
|
420 family = aimutil_get16(workingPtr->data);
|
|
421 subtype = aimutil_get16(workingPtr->data+2);
|
|
422
|
|
423 switch (family) {
|
|
424 case 0x0000: /* not really a family, but it works */
|
|
425 if (subtype == 0x0001)
|
|
426 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
427 else
|
|
428 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
|
|
429 break;
|
|
430 case 0x0001: /* Family: General */
|
|
431 switch (subtype) {
|
|
432 case 0x0001:
|
|
433 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
|
|
434 break;
|
|
435 case 0x0003:
|
|
436 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
|
|
437 break;
|
|
438 case 0x0005:
|
|
439 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
|
|
440 break;
|
|
441 case 0x0007:
|
|
442 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
|
|
443 break;
|
|
444 case 0x000a:
|
|
445 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
|
|
446 break;
|
|
447 case 0x000f:
|
|
448 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
|
|
449 break;
|
|
450 case 0x0010:
|
|
451 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
|
|
452 break;
|
|
453 case 0x0013:
|
|
454 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
|
|
455 break;
|
|
456 case 0x0018:
|
|
457 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
|
|
458 break;
|
|
459 default:
|
|
460 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
|
|
461 break;
|
|
462 }
|
|
463 break;
|
|
464 case 0x0002: /* Family: Location */
|
|
465 switch (subtype) {
|
|
466 case 0x0001:
|
|
467 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
|
|
468 break;
|
|
469 case 0x0003:
|
|
470 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
|
|
471 break;
|
|
472 case 0x0006:
|
|
473 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
|
|
474 break;
|
|
475 default:
|
|
476 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
|
|
477 break;
|
|
478 }
|
|
479 break;
|
|
480 case 0x0003: /* Family: Buddy List */
|
|
481 switch (subtype) {
|
|
482 case 0x0001:
|
|
483 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
|
|
484 break;
|
|
485 case 0x0003:
|
|
486 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
|
|
487 break;
|
|
488 case 0x000b: /* oncoming buddy */
|
|
489 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
|
|
490 break;
|
|
491 case 0x000c: /* offgoing buddy */
|
|
492 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
|
|
493 break;
|
|
494 default:
|
|
495 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
|
|
496 }
|
|
497 break;
|
|
498 case 0x0004: /* Family: Messaging */
|
|
499 switch (subtype) {
|
|
500 case 0x0001:
|
|
501 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
|
|
502 break;
|
|
503 case 0x0005:
|
|
504 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
|
|
505 break;
|
|
506 case 0x0006:
|
|
507 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
|
|
508 break;
|
|
509 case 0x0007:
|
|
510 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
|
|
511 break;
|
|
512 case 0x000a:
|
|
513 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
|
|
514 break;
|
|
515 case 0x000c:
|
|
516 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
|
|
517 break;
|
|
518 default:
|
|
519 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
|
|
520 }
|
|
521 break;
|
|
522 case 0x0009:
|
|
523 if (subtype == 0x0001)
|
|
524 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
|
|
525 else if (subtype == 0x0003)
|
|
526 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
|
|
527 else
|
|
528 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
|
|
529 break;
|
|
530 case 0x000a: /* Family: User lookup */
|
|
531 switch (subtype) {
|
|
532 case 0x0001:
|
|
533 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
|
|
534 break;
|
|
535 case 0x0003:
|
|
536 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
|
|
537 break;
|
|
538 default:
|
|
539 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
|
|
540 }
|
|
541 break;
|
|
542 case 0x000b: {
|
|
543 if (subtype == 0x0001)
|
|
544 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
|
|
545 else if (subtype == 0x0002)
|
|
546 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
|
|
547 else
|
|
548 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
|
|
549 break;
|
|
550 }
|
|
551 case 0x0013: {
|
|
552 faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
|
|
553 break;
|
|
554 }
|
|
555 case AIM_CB_FAM_SPECIAL:
|
|
556 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
|
|
557 break;
|
|
558 default:
|
|
559 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
|
|
560 break;
|
|
561 } /* switch(family) */
|
|
562 break;
|
|
563 } /* AIM_CONN_TYPE_BOS */
|
|
564 case AIM_CONN_TYPE_ADS: {
|
|
565 unsigned short family;
|
|
566 unsigned short subtype;
|
|
567
|
|
568 family = aimutil_get16(workingPtr->data);
|
|
569 subtype= aimutil_get16(workingPtr->data+2);
|
|
570
|
|
571 if ((family == 0x0000) && (subtype == 0x00001)) {
|
|
572 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
573 } else if ((family == 0x0001) && (subtype == 0x0003)) {
|
|
574 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
|
|
575 } else {
|
|
576 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
|
|
577 }
|
|
578 break;
|
|
579 }
|
|
580 case AIM_CONN_TYPE_CHATNAV: {
|
|
581 u_short family;
|
|
582 u_short subtype;
|
|
583 family = aimutil_get16(workingPtr->data);
|
|
584 subtype= aimutil_get16(workingPtr->data+2);
|
|
585
|
|
586 if ((family == 0x0000) && (subtype == 0x00001)) {
|
|
587 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
588 } else if ((family == 0x0001) && (subtype == 0x0003)) {
|
|
589 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
|
|
590 } else if ((family == 0x000d) && (subtype == 0x0009)) {
|
|
591 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
|
|
592 } else {
|
|
593 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
|
|
594 }
|
|
595 break;
|
|
596 }
|
|
597 case AIM_CONN_TYPE_CHAT: {
|
|
598 u_short family, subtype;
|
|
599
|
|
600 family = aimutil_get16(workingPtr->data);
|
|
601 subtype= aimutil_get16(workingPtr->data+2);
|
|
602
|
|
603 if ((family == 0x0000) && (subtype == 0x00001)) {
|
|
604 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
605 } else if (family == 0x0001) {
|
|
606 if (subtype == 0x0001)
|
|
607 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
|
|
608 else if (subtype == 0x0003)
|
|
609 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
|
|
610 else if (subtype == 0x0007)
|
|
611 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
|
|
612 else if (subtype == 0x000a)
|
|
613 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
|
|
614 else
|
|
615 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
|
|
616 } else if (family == 0x000e) {
|
|
617 if (subtype == 0x0002)
|
|
618 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
|
|
619 else if (subtype == 0x0003)
|
|
620 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
|
|
621 else if (subtype == 0x0004)
|
|
622 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
|
|
623 else if (subtype == 0x0006)
|
|
624 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
|
|
625 else
|
|
626 faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype);
|
|
627 } else {
|
|
628 faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype);
|
|
629 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
|
|
630 }
|
|
631 break;
|
|
632 }
|
|
633 case AIM_CONN_TYPE_RENDEZVOUS: {
|
|
634 /* make sure that we only get OFT frames on these connections */
|
|
635 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
|
|
636 faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
|
|
637 workingPtr->handled = 1; /* get rid of it */
|
|
638 break;
|
|
639 }
|
|
640
|
|
641 /* XXX: implement this */
|
|
642 faimdprintf(sess, 0, "faim: OFT frame!\n");
|
|
643
|
|
644 break;
|
|
645 }
|
|
646 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
|
|
647 /* not possible */
|
|
648 break;
|
|
649 }
|
|
650 default:
|
|
651 faimdprintf(sess, 0, "internal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
|
|
652 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
|
|
653 break;
|
|
654 }
|
|
655 }
|
|
656 }
|
|
657
|
|
658 /*
|
|
659 * This doesn't have to be called here. It could easily be done
|
|
660 * by a seperate thread or something. It's an administrative operation,
|
|
661 * and can take a while. Though the less you call it the less memory
|
|
662 * you'll have :)
|
|
663 */
|
|
664 aim_purge_rxqueue(sess);
|
|
665
|
|
666 return 0;
|
|
667 }
|
|
668
|
|
669 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
|
|
670 {
|
|
671 rxcallback_t userfunc = NULL;
|
|
672 char sn[MAXSNLEN];
|
|
673 unsigned short type;
|
|
674 int i = 10+8; /* skip SNAC and cookie */
|
|
675 int ret = 1;
|
|
676 unsigned char snlen;
|
|
677
|
|
678 type = aimutil_get16(command->data+i);
|
|
679 i += 2;
|
|
680
|
|
681 snlen = aimutil_get8(command->data+i);
|
|
682 i++;
|
|
683
|
|
684 memset(sn, 0, sizeof(sn));
|
|
685 strncpy(sn, (char *)command->data+i, snlen);
|
|
686
|
|
687 if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
|
|
688 ret = userfunc(sess, command, type, sn);
|
|
689
|
|
690 return ret;
|
|
691 }
|
|
692
|
|
693 /*
|
|
694 * The Rate Limiting System, An Abridged Guide to Nonsense.
|
|
695 *
|
|
696 * OSCAR defines several 'rate classes'. Each class has seperate
|
|
697 * rate limiting properties (limit level, alert level, disconnect
|
|
698 * level, etc), and a set of SNAC family/type pairs associated with
|
|
699 * it. The rate classes, their limiting properties, and the definitions
|
|
700 * of which SNACs are belong to which class, are defined in the
|
|
701 * Rate Response packet at login to each host.
|
|
702 *
|
|
703 * Logically, all rate offenses within one class count against further
|
|
704 * offenses for other SNACs in the same class (ie, sending messages
|
|
705 * too fast will limit the number of user info requests you can send,
|
|
706 * since those two SNACs are in the same rate class).
|
|
707 *
|
|
708 * Since the rate classes are defined dynamically at login, the values
|
|
709 * below may change. But they seem to be fairly constant.
|
|
710 *
|
|
711 * Currently, BOS defines five rate classes, with the commonly used
|
|
712 * members as follows...
|
|
713 *
|
|
714 * Rate class 0x0001:
|
|
715 * - Everything thats not in any of the other classes
|
|
716 *
|
|
717 * Rate class 0x0002:
|
|
718 * - Buddy list add/remove
|
|
719 * - Permit list add/remove
|
|
720 * - Deny list add/remove
|
|
721 *
|
|
722 * Rate class 0x0003:
|
|
723 * - User information requests
|
|
724 * - Outgoing ICBMs
|
|
725 *
|
|
726 * Rate class 0x0004:
|
|
727 * - A few unknowns: 2/9, 2/b, and f/2
|
|
728 *
|
|
729 * Rate class 0x0005:
|
|
730 * - Chat room create
|
|
731 * - Outgoing chat ICBMs
|
|
732 *
|
|
733 * The only other thing of note is that class 5 (chat) has slightly looser
|
|
734 * limiting properties than class 3 (normal messages). But thats just a
|
|
735 * small bit of trivia for you.
|
|
736 *
|
|
737 * The last thing that needs to be learned about the rate limiting
|
|
738 * system is how the actual numbers relate to the passing of time. This
|
|
739 * seems to be a big mystery.
|
|
740 *
|
|
741 */
|
|
742 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
|
|
743 {
|
|
744 rxcallback_t userfunc = NULL;
|
|
745 int ret = 1;
|
|
746 int i;
|
|
747 int code;
|
|
748 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
|
|
749 unsigned long currentavg, maxavg;
|
|
750
|
|
751 i = 10;
|
|
752
|
|
753 code = aimutil_get16(command->data+i);
|
|
754 i += 2;
|
|
755
|
|
756 rateclass = aimutil_get16(command->data+i);
|
|
757 i += 2;
|
|
758
|
|
759 windowsize = aimutil_get32(command->data+i);
|
|
760 i += 4;
|
|
761 clear = aimutil_get32(command->data+i);
|
|
762 i += 4;
|
|
763 alert = aimutil_get32(command->data+i);
|
|
764 i += 4;
|
|
765 limit = aimutil_get32(command->data+i);
|
|
766 i += 4;
|
|
767 disconnect = aimutil_get32(command->data+i);
|
|
768 i += 4;
|
|
769 currentavg = aimutil_get32(command->data+i);
|
|
770 i += 4;
|
|
771 maxavg = aimutil_get32(command->data+i);
|
|
772 i += 4;
|
|
773
|
|
774 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
|
|
775 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
|
|
776
|
|
777 return ret;
|
|
778 }
|
|
779
|
|
780 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
|
|
781 {
|
|
782 rxcallback_t userfunc = NULL;
|
|
783 int ret = 1;
|
|
784 int i;
|
|
785 unsigned short newevil;
|
|
786 struct aim_userinfo_s userinfo;
|
|
787
|
|
788 i = 10;
|
|
789 newevil = aimutil_get16(command->data+10);
|
|
790 i += 2;
|
|
791
|
|
792 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
|
|
793 if (command->commandlen-i)
|
|
794 i += aim_extractuserinfo(sess, command->data+i, &userinfo);
|
|
795
|
|
796 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
|
|
797 ret = userfunc(sess, command, newevil, &userinfo);
|
|
798
|
|
799 return ret;
|
|
800 }
|
|
801
|
|
802 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
|
|
803 struct command_rx_struct *command, ...)
|
|
804 {
|
|
805 rxcallback_t userfunc = NULL;
|
|
806 char *msg;
|
|
807 int ret=1;
|
|
808 struct aim_tlvlist_t *tlvlist;
|
|
809 u_short id;
|
|
810
|
|
811 /*
|
|
812 * Code.
|
|
813 *
|
|
814 * Valid values:
|
|
815 * 1 Mandatory upgrade
|
|
816 * 2 Advisory upgrade
|
|
817 * 3 System bulletin
|
|
818 * 4 Nothing's wrong ("top o the world" -- normal)
|
|
819 *
|
|
820 */
|
|
821 id = aimutil_get16(command->data+10);
|
|
822
|
|
823 /*
|
|
824 * TLVs follow
|
|
825 */
|
|
826 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
|
|
827 return ret;
|
|
828
|
|
829 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
|
|
830 aim_freetlvchain(&tlvlist);
|
|
831 return ret;
|
|
832 }
|
|
833
|
|
834 userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
|
|
835 if (userfunc)
|
|
836 ret = userfunc(sess, command, id, msg);
|
|
837
|
|
838 aim_freetlvchain(&tlvlist);
|
|
839 free(msg);
|
|
840
|
|
841 return ret;
|
|
842 }
|
|
843
|
|
844 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
|
|
845 struct command_rx_struct *command, ...)
|
|
846 {
|
|
847 rxcallback_t userfunc = NULL;
|
|
848 int ret = 1;
|
|
849 unsigned short *families = NULL;
|
|
850 int famcount = 0, i;
|
|
851
|
|
852 famcount = (command->commandlen-10)/2;
|
|
853 if (!(families = malloc(command->commandlen-10)))
|
|
854 return ret;
|
|
855
|
|
856 for (i = 0; i < famcount; i++)
|
|
857 families[i] = aimutil_get16(command->data+((i*2)+10));
|
|
858
|
|
859 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
|
|
860 ret = userfunc(sess, command, famcount, families);
|
|
861
|
|
862 free(families);
|
|
863
|
|
864 return ret;
|
|
865 }
|
|
866
|
|
867 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
|
|
868 struct command_rx_struct *command)
|
|
869 {
|
|
870 rxcallback_t userfunc = NULL;
|
|
871 int ret = 1;
|
|
872 int status = -1;
|
|
873
|
|
874 status = aimutil_get16(command->data+10);
|
|
875
|
|
876 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
|
|
877 ret = userfunc(sess, command, status);
|
|
878
|
|
879 return ret;
|
|
880 }
|
|
881
|
|
882 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
|
|
883 struct command_rx_struct *command)
|
|
884 {
|
|
885 unsigned short subtype; /* called for both reply and change-reply */
|
|
886 int i;
|
|
887
|
|
888 subtype = aimutil_get16(command->data+2);
|
|
889
|
|
890 /*
|
|
891 * struct {
|
|
892 * unsigned short perms;
|
|
893 * unsigned short tlvcount;
|
|
894 * aim_tlv_t tlvs[tlvcount];
|
|
895 * } admin_info[n];
|
|
896 */
|
|
897 for (i = 10; i < command->commandlen; ) {
|
|
898 int perms, tlvcount;
|
|
899
|
|
900 perms = aimutil_get16(command->data+i);
|
|
901 i += 2;
|
|
902
|
|
903 tlvcount = aimutil_get16(command->data+i);
|
|
904 i += 2;
|
|
905
|
|
906 while (tlvcount) {
|
|
907 rxcallback_t userfunc;
|
|
908 struct aim_tlv_t *tlv;
|
|
909 int str = 0;
|
|
910
|
|
911 if ((aimutil_get16(command->data+i) == 0x0011) ||
|
|
912 (aimutil_get16(command->data+i) == 0x0004))
|
|
913 str = 1;
|
|
914
|
|
915 if (str)
|
|
916 tlv = aim_grabtlvstr(command->data+i);
|
|
917 else
|
|
918 tlv = aim_grabtlv(command->data+i);
|
|
919
|
|
920 /* XXX fix so its only called once for the entire packet */
|
|
921 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
|
|
922 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
|
|
923
|
|
924 if (tlv)
|
|
925 i += 2+2+tlv->length;
|
|
926
|
|
927 if (tlv && tlv->value)
|
|
928 free(tlv->value);
|
|
929 if (tlv)
|
|
930 free(tlv);
|
|
931
|
|
932 tlvcount--;
|
|
933 }
|
|
934 }
|
|
935
|
|
936 return 1;
|
|
937 }
|
|
938
|
|
939 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
|
|
940 struct command_rx_struct *command, ...)
|
|
941 {
|
|
942 rxcallback_t userfunc = NULL;
|
|
943 int ret = 1;
|
|
944 int vercount;
|
|
945
|
|
946 vercount = (command->commandlen-10)/4;
|
|
947
|
|
948 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
|
|
949 ret = userfunc(sess, command, vercount, command->data+10);
|
|
950
|
|
951 return ret;
|
|
952 }
|
|
953
|
|
954 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
|
|
955 struct command_rx_struct *command, ...)
|
|
956 {
|
|
957 int serviceid = 0;
|
|
958 unsigned char *cookie = NULL;
|
|
959 char *ip = NULL;
|
|
960 rxcallback_t userfunc = NULL;
|
|
961 struct aim_tlvlist_t *tlvlist;
|
|
962 int ret = 1;
|
|
963
|
|
964 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
|
|
965
|
|
966 if (aim_gettlv(tlvlist, 0x000d, 1))
|
|
967 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
|
|
968 if (aim_gettlv(tlvlist, 0x0005, 1))
|
|
969 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
|
|
970 if (aim_gettlv(tlvlist, 0x0006, 1))
|
|
971 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
|
|
972
|
|
973 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
|
|
974
|
|
975 /*
|
|
976 * Chat hack.
|
|
977 *
|
|
978 */
|
|
979 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
|
|
980 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
|
|
981 free(sess->pendingjoin);
|
|
982 sess->pendingjoin = NULL;
|
|
983 sess->pendingjoinexchange = 0;
|
|
984 } else if (!serviceid || !ip || !cookie) { /* yeep! */
|
|
985 ret = 1;
|
|
986 } else {
|
|
987 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
|
|
988 ret = userfunc(sess, command, serviceid, ip, cookie);
|
|
989 }
|
|
990
|
|
991 if (ip)
|
|
992 free(ip);
|
|
993 if (cookie)
|
|
994 free(cookie);
|
|
995
|
|
996 aim_freetlvchain(&tlvlist);
|
|
997
|
|
998 return ret;
|
|
999 }
|
|
1000
|
|
1001 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
|
|
1002 struct command_rx_struct *command, ...)
|
|
1003 {
|
|
1004 u_int i = 0;
|
|
1005
|
|
1006 if (!sess || !command)
|
|
1007 return 1;
|
|
1008
|
|
1009 faimdprintf(sess, 1, "\nRecieved unknown packet:");
|
|
1010
|
|
1011 for (i = 0; i < command->commandlen; i++)
|
|
1012 {
|
|
1013 if ((i % 8) == 0)
|
|
1014 faimdprintf(sess, 1, "\n\t");
|
|
1015
|
|
1016 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
|
|
1017 }
|
|
1018
|
|
1019 faimdprintf(sess, 1, "\n\n");
|
|
1020
|
|
1021 return 1;
|
|
1022 }
|
|
1023
|
|
1024
|
|
1025 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
|
|
1026 struct command_rx_struct *command)
|
|
1027 {
|
|
1028 struct aim_tlvlist_t *tlvlist;
|
|
1029 char *msg = NULL;
|
|
1030 unsigned short code = 0;
|
|
1031 rxcallback_t userfunc = NULL;
|
|
1032 int ret = 1;
|
|
1033
|
|
1034 tlvlist = aim_readtlvchain(command->data, command->commandlen);
|
|
1035
|
|
1036 if (aim_gettlv(tlvlist, 0x0009, 1))
|
|
1037 code = aim_gettlv16(tlvlist, 0x0009, 1);
|
|
1038
|
|
1039 if (aim_gettlv(tlvlist, 0x000b, 1))
|
|
1040 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
|
|
1041
|
|
1042 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
|
|
1043 ret = userfunc(sess, command, code, msg);
|
|
1044
|
|
1045 aim_freetlvchain(&tlvlist);
|
|
1046
|
|
1047 if (msg)
|
|
1048 free(msg);
|
|
1049
|
|
1050 return ret;
|
|
1051 }
|
|
1052
|
|
1053 /*
|
|
1054 * aim_parse_generalerrs()
|
|
1055 *
|
|
1056 * Middle handler for 0x0001 snac of each family.
|
|
1057 *
|
|
1058 */
|
|
1059 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
|
|
1060 struct command_rx_struct *command, ...)
|
|
1061 {
|
|
1062 unsigned short family;
|
|
1063 unsigned short subtype;
|
|
1064 int ret = 1;
|
|
1065 int error = 0;
|
|
1066 rxcallback_t userfunc = NULL;
|
|
1067
|
|
1068 family = aimutil_get16(command->data+0);
|
|
1069 subtype= aimutil_get16(command->data+2);
|
|
1070
|
|
1071 if (command->commandlen > 10)
|
|
1072 error = aimutil_get16(command->data+10);
|
|
1073
|
|
1074 if ((userfunc = aim_callhandler(sess, command->conn, family, subtype)))
|
|
1075 ret = userfunc(sess, command, error);
|
|
1076
|
|
1077 return ret;
|
|
1078 }
|
|
1079
|
|
1080
|
|
1081
|