]> andersk Git - libfaim.git/blob - aim_rxhandlers.c
Random Changes.
[libfaim.git] / aim_rxhandlers.c
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 #include <faim/aim.h>
11
12 /*
13  * Bleck functions get called when there's no non-bleck functions
14  * around to cleanup the mess...
15  */
16 int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
17 {
18   u_short family;
19   u_short subtype;
20
21   u_short maxf;
22   u_short maxs;
23
24   /* XXX: this is ugly. and big just for debugging. */
25   char *literals[14][25] = {
26     {"Invalid", 
27      NULL
28     },
29     {"General", 
30      "Invalid",
31      "Error",
32      "Client Ready",
33      "Server Ready",
34      "Service Request",
35      "Redirect",
36      "Rate Information Request",
37      "Rate Information",
38      "Rate Information Ack",
39      NULL,
40      "Rate Information Change",
41      "Server Pause",
42      NULL,
43      "Server Resume",
44      "Request Personal User Information",
45      "Personal User Information",
46      "Evil Notification",
47      NULL,
48      "Migration notice",
49      "Message of the Day",
50      "Set Privacy Flags",
51      "Well Known URL",
52      "NOP"
53     },
54     {"Location", 
55       "Invalid",
56       "Error",
57       "Request Rights",
58       "Rights Information", 
59       "Set user information", 
60       "Request User Information", 
61       "User Information", 
62       "Watcher Sub Request",
63       "Watcher Notification"
64     },
65     {"Buddy List Management", 
66       "Invalid", 
67       "Error", 
68       "Request Rights",
69       "Rights Information",
70       "Add Buddy", 
71       "Remove Buddy", 
72       "Watcher List Query", 
73       "Watcher List Response", 
74       "Watcher SubRequest", 
75       "Watcher Notification", 
76       "Reject Notification", 
77       "Oncoming Buddy", 
78       "Offgoing Buddy"
79     },
80     {"Messeging", 
81       "Invalid",
82       "Error", 
83       "Add ICBM Parameter",
84       "Remove ICBM Parameter", 
85       "Request Parameter Information",
86       "Parameter Information",
87       "Outgoing Message", 
88       "Incoming Message",
89       "Evil Request",
90       "Evil Reply", 
91       "Missed Calls",
92       "Message Error", 
93       "Host Ack"
94     },
95     {"Advertisements", 
96       "Invalid", 
97       "Error", 
98       "Request Ad",
99       "Ad Data (GIFs)"
100     },
101     {"Invitation / Client-to-Client", 
102      "Invalid",
103      "Error",
104      "Invite a Friend",
105      "Invitation Ack"
106     },
107     {"Administrative", 
108       "Invalid",
109       "Error",
110       "Information Request",
111       "Information Reply",
112       "Information Change Request",
113       "Information Chat Reply",
114       "Account Confirm Request",
115       "Account Confirm Reply",
116       "Account Delete Request",
117       "Account Delete Reply"
118     },
119     {"Popups", 
120       "Invalid",
121       "Error",
122       "Display Popup"
123     },
124     {"BOS", 
125       "Invalid",
126       "Error",
127       "Request Rights",
128       "Rights Response",
129       "Set group permission mask",
130       "Add permission list entries",
131       "Delete permission list entries",
132       "Add deny list entries",
133       "Delete deny list entries",
134       "Server Error"
135     },
136     {"User Lookup", 
137       "Invalid",
138       "Error",
139       "Search Request",
140       "Search Response"
141     },
142     {"Stats", 
143       "Invalid",
144       "Error",
145       "Set minimum report interval",
146       "Report Events"
147     },
148     {"Translate", 
149       "Invalid",
150       "Error",
151       "Translate Request",
152       "Translate Reply",
153     },
154     {"Chat Navigation", 
155       "Invalid",
156       "Error",
157       "Request rights",
158       "Request Exchange Information",
159       "Request Room Information",
160       "Request Occupant List",
161       "Search for Room",
162       "Outgoing Message", 
163       "Incoming Message",
164       "Evil Request", 
165       "Evil Reply", 
166       "Chat Error",
167     }
168   };
169
170   maxf = sizeof(literals) / sizeof(literals[0]);
171   maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
172
173   family = aimutil_get16(workingPtr->data+0);
174   subtype= aimutil_get16(workingPtr->data+2);
175
176   if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
177     printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
178   else
179     printf("bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
180
181   return 1;
182 }
183
184 int aim_conn_addhandler(struct aim_session_t *sess,
185                         struct aim_conn_t *conn,
186                         u_short family,
187                         u_short type,
188                         rxcallback_t newhandler,
189                         u_short flags)
190 {
191   struct aim_rxcblist_t *new,*cur;
192
193   if (!conn)
194     return -1;
195
196   faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
197
198   new = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
199   new->family = family;
200   new->type = type;
201   new->flags = flags;
202   if (!newhandler)
203     new->handler = &bleck;
204   else
205     new->handler = newhandler;
206   new->next = NULL;
207   
208   cur = conn->handlerlist;
209   if (!cur)
210     conn->handlerlist = new;
211   else 
212     {
213       while (cur->next)
214         cur = cur->next;
215       cur->next = new;
216     }
217
218   return 0;
219 }
220
221 int aim_clearhandlers(struct aim_conn_t *conn)
222 {
223  struct aim_rxcblist_t *cur,*tmp;
224  if (!conn)
225    return -1;
226
227  cur = conn->handlerlist;
228  while(cur)
229    {
230      tmp = cur->next;
231      free(cur);
232      cur = tmp;
233    }
234  return 0;
235 }
236
237 rxcallback_t aim_callhandler(struct aim_conn_t *conn,
238                     u_short family,
239                     u_short type)
240 {
241   struct aim_rxcblist_t *cur;
242
243   if (!conn)
244     return NULL;
245
246   faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
247   
248   cur = conn->handlerlist;
249   while(cur)
250     {
251       if ( (cur->family == family) && (cur->type == type) )
252         return cur->handler;
253       cur = cur->next;
254     }
255
256   if (type==0xffff)
257     return NULL;
258   return aim_callhandler(conn, family, 0xffff);
259 }
260
261 int aim_callhandler_noparam(struct aim_session_t *sess,
262                             struct aim_conn_t *conn,
263                             u_short family,
264                             u_short type,
265                             struct command_rx_struct *ptr)
266 {
267   rxcallback_t userfunc = NULL;
268   userfunc = aim_callhandler(conn, family, type);
269   if (userfunc)
270     return userfunc(sess, ptr);
271   return 1; /* XXX */
272 }
273
274 /*
275   aim_rxdispatch()
276
277   Basically, heres what this should do:
278     1) Determine correct packet handler for this packet
279     2) Mark the packet handled (so it can be dequeued in purge_queue())
280     3) Send the packet to the packet handler
281     4) Go to next packet in the queue and start over
282     5) When done, run purge_queue() to purge handled commands
283
284   Note that any unhandlable packets should probably be left in the
285   queue.  This is the best way to prevent data loss.  This means
286   that a single packet may get looked at by this function multiple
287   times.  This is more good than bad!  This behavior may change.
288
289   Aren't queue's fun? 
290
291   TODO: Get rid of all the ugly if's.
292   TODO: Clean up.
293   TODO: More support for mid-level handlers.
294   TODO: Allow for NULL handlers.
295   
296  */
297 int aim_rxdispatch(struct aim_session_t *sess)
298 {
299   int i = 0;
300   struct command_rx_struct *workingPtr = NULL;
301   
302   if (sess->queue_incoming == NULL) {
303     faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
304     return 0;
305   } else {
306     workingPtr = sess->queue_incoming;
307     for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
308       /*
309        * XXX: This is still fairly ugly.
310        */
311       if (workingPtr->handled)
312         continue;
313
314       switch(workingPtr->conn->type) {
315       case -1:
316         /*
317          * This can happen if we have a queued command
318          * that was recieved after a connection has 
319          * been terminated.  In which case, the handler
320          * list has been cleared, and there's nothing we
321          * can do for it.  We can only cancel it.
322          */
323         workingPtr->handled = 1;
324         break;
325       case AIM_CONN_TYPE_AUTH: {
326         u_long head;
327         
328         head = aimutil_get32(workingPtr->data);
329         if (head == 0x00000001) {
330           faimdprintf(1, "got connection ack on auth line\n");
331           workingPtr->handled = 1;
332         } else {
333           u_short family,subtype;
334           
335           family = aimutil_get16(workingPtr->data);
336           subtype = aimutil_get16(workingPtr->data+2);
337           
338           switch (family) {
339             /* New login protocol */
340 #ifdef SNACLOGIN
341           case 0x0017:
342             if (subtype == 0x0001)
343               workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
344             else if (subtype == 0x0003)
345               workingPtr->handled = aim_authparse(sess, workingPtr);
346             else if (subtype == 0x0007)
347               workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0007, workingPtr);
348             else
349               workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
350             break;
351 #else   
352             /* XXX: this isnt foolproof */
353           case 0x0001:
354             if (subtype == 0x0003)
355               workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, workingPtr);
356             else
357               workingPtr->handled = aim_authparse(sess, workingPtr);
358             break;
359           case 0x0007:
360             if (subtype == 0x0005)
361               workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
362             break;
363           default:
364             /* Old login protocol */
365             /* any user callbacks will be called from here */
366             workingPtr->handled = aim_authparse(sess, workingPtr);
367 #endif
368           }
369         }
370         break;
371       }
372       case AIM_CONN_TYPE_BOS: {
373         u_short family;
374         u_short subtype;
375         
376         family = aimutil_get16(workingPtr->data);
377         subtype = aimutil_get16(workingPtr->data+2);
378         
379         switch (family) {
380         case 0x0000: /* not really a family, but it works */
381           if (subtype == 0x0001)
382             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
383           else
384             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
385           break;
386         case 0x0001: /* Family: General */
387           switch (subtype) {
388           case 0x0001:
389             workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
390             break;
391           case 0x0003:
392             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
393             break;
394           case 0x0005:
395             workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
396             break;
397           case 0x0007:
398             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
399             break;
400           case 0x000a:
401             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000a, workingPtr);
402             break;
403           case 0x000f:
404             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
405             break;
406           case 0x0013:
407             workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
408             break;
409           default:
410             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
411             break;
412           }
413         case 0x0002: /* Family: Location */
414           switch (subtype) {
415           case 0x0001:
416             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0001, workingPtr);
417             break;
418           case 0x0003:
419             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
420             break;
421           case 0x0006:
422             workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
423             break;
424           default:
425             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
426             break;
427           }
428         case 0x0003: /* Family: Buddy List */
429           switch (subtype) {
430           case 0x0001:
431             workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
432             break;
433           case 0x0003:
434             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0003, 0x0003, workingPtr);
435             break;
436           case 0x000b: /* oncoming buddy */
437             workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
438             break;
439           case 0x000c: /* offgoing buddy */
440             workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
441             break;
442           default:
443             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
444           }
445           break;
446         case 0x0004: /* Family: Messeging */
447           switch (subtype) {
448           case 0x0001:
449             workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
450             break;
451           case 0x0005:
452             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
453             break;
454           case 0x0007:
455             workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
456             break;
457           case 0x000a:
458             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x000a, workingPtr);
459             break;
460           default:
461             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
462           }
463           break;
464         case 0x0009:
465           if (subtype == 0x0001)
466             workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
467           else if (subtype == 0x0003)
468             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0009, 0x0003, workingPtr);
469           else
470             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
471           break;
472         case 0x000a:  /* Family: User lookup */
473           switch (subtype) {
474           case 0x0001:
475             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
476             break;
477           case 0x0003:
478             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
479             break;
480           default:
481             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
482           }
483           break;
484         case 0x000b:
485           if (subtype == 0x0001)
486             workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
487           else if (subtype == 0x0002)
488             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
489           else
490             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
491           break;
492         default:
493           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
494           break;
495         }
496         break;
497       }
498       case AIM_CONN_TYPE_CHATNAV: {
499         u_short family;
500         u_short subtype;
501         family = aimutil_get16(workingPtr->data);
502         subtype= aimutil_get16(workingPtr->data+2);
503         
504         if ((family == 0x0002) && (subtype == 0x0006)) {
505           workingPtr->handled = 1;
506           aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
507         } else if ((family == 0x000d) && (subtype == 0x0009)) {
508           workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
509         } else {
510           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
511         }
512         break;
513       }
514       case AIM_CONN_TYPE_CHAT: {
515         u_short family, subtype;
516         
517         family = aimutil_get16(workingPtr->data);
518         subtype= aimutil_get16(workingPtr->data+2);
519         
520         if ((family == 0x0000) && (subtype == 0x00001))
521           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
522         else if (family == 0x0001) {
523           if (subtype == 0x0001)
524             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
525           else if (subtype == 0x0003)
526             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
527           else if (subtype == 0x0007)
528             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
529           else
530             printf("Chat: unknown snac %04x/%04x\n", family, subtype);
531         } else if (family == 0x000e) {
532           if (subtype == 0x0002)
533             workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
534           else if (subtype == 0x0003)
535             workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);      
536           else if (subtype == 0x0004)
537             workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);       
538           else if (subtype == 0x0006)
539             workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
540           else  
541             printf("Chat: unknown snac %04x/%04x\n", family, subtype); 
542         } else {
543           printf("Chat: unknown snac %04x/%04x\n", family, subtype);
544           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
545         }
546         break;
547       }
548       default:
549         printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, channel = %02x, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->type, workingPtr->commandlen);
550         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
551         break;
552       } 
553     }
554   }
555
556   /* 
557    * This doesn't have to be called here.  It could easily be done
558    * by a seperate thread or something. It's an administrative operation,
559    * and can take a while. Though the less you call it the less memory
560    * you'll have :)
561    */
562   aim_purge_rxqueue(sess);
563   
564   return 0;
565 }
566
567 int aim_parsemotd_middle(struct aim_session_t *sess,
568                               struct command_rx_struct *command, ...)
569 {
570   rxcallback_t userfunc = NULL;
571   char *msg;
572   int ret=1;
573   struct aim_tlvlist_t *tlvlist;
574   u_short id;
575
576   /*
577    * Dunno.
578    */
579   id = aimutil_get16(command->data+10);
580
581   /* 
582    * TLVs follow 
583    */
584   tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12);
585   
586   msg = aim_gettlv_str(tlvlist, 0x000b, 1);
587   
588   userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
589   if (userfunc)
590     ret =  userfunc(sess, command, id, msg);
591
592   aim_freetlvchain(&tlvlist);
593
594   return ret;
595   
596 }
597
598 int aim_handleredirect_middle(struct aim_session_t *sess,
599                               struct command_rx_struct *command, ...)
600 {
601   struct aim_tlv_t *tmptlv = NULL;
602   int serviceid = 0x00;
603   char cookie[AIM_COOKIELEN];
604   char *ip = NULL;
605   rxcallback_t userfunc = NULL;
606   struct aim_tlvlist_t *tlvlist;
607   int ret = 1;
608   
609   if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
610     {
611       printf("libfaim: major bug: unable to read tlvchain from redirect\n");
612       return ret;
613     }
614   
615   if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1))) 
616     {
617       printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
618       aim_freetlvchain(&tlvlist);
619       return ret;
620     }
621   serviceid = aimutil_get16(tmptlv->value);
622
623   if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1))) 
624     {
625       printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
626       aim_freetlvchain(&tlvlist);
627       return ret;
628     }
629   
630   if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
631     {
632       printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
633       aim_freetlvchain(&tlvlist);
634       return ret;
635     }
636   memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
637
638   if (serviceid == AIM_CONN_TYPE_CHAT)
639     {
640       /*
641        * Chat hack.
642        *
643        */
644       userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
645       if (userfunc)
646         ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
647       free(sess->pendingjoin);
648       sess->pendingjoin = NULL;
649     }
650   else
651     {
652       userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
653       if (userfunc)
654         ret =  userfunc(sess, command, serviceid, ip, cookie);
655     }
656
657   /*
658    * XXX: Is there a leak here?  Where does IP get freed?
659    */
660   aim_freetlvchain(&tlvlist);
661
662   return ret;
663 }
664
665 int aim_parse_unknown(struct aim_session_t *sess,
666                       struct command_rx_struct *command, ...)
667 {
668   u_int i = 0;
669
670   faimdprintf(1, "\nRecieved unknown packet:");
671
672   for (i = 0; i < command->commandlen; i++)
673     {
674       if ((i % 8) == 0)
675         printf("\n\t");
676
677       printf("0x%2x ", command->data[i]);
678     }
679   
680   printf("\n\n");
681
682   return 1;
683 }
684
685
686 /*
687  * aim_parse_generalerrs()
688  *
689  * Middle handler for 0x0001 snac of each family.
690  *
691  */
692 int aim_parse_generalerrs(struct aim_session_t *sess,
693                           struct command_rx_struct *command, ...)
694 {
695   u_short family;
696   u_short subtype;
697   
698   family = aimutil_get16(command->data+0);
699   subtype= aimutil_get16(command->data+2);
700   
701   switch(family)
702     {
703     default:
704       /* Unknown family */
705       return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);
706     }
707
708   return 1;
709 }
710
711
712
This page took 0.106096 seconds and 5 git commands to generate.