No release numbers
------------------
+ - Sat Sep 2 23:42:37 UTC 2000
+ - Hopefully fix aim_snac.c bugs
+ - Add Buddy List Rights parser (max buddies and max watchers)
+ - Fix rather major problem in rxhandlers caused by missing breaks
+ - Add reason code for msgerr (0004/0001) callback
+ - Add BOS Rights parser (max permit / max deny)
+ - Add locate error (0002/0001) parser
+ - Add parser for missed calls (0004/000a)
+
- Fri Sep 1 23:34:28 UTC 2000
- Switched the read()s in rxqueue to use recv()
- Should fix the big message problem and the big buddy list problem
return( sess->snac_nextid++ );
}
+int aim_parse_buddyrights(struct aim_session_t *sess,
+ struct command_rx_struct *command, ...)
+{
+ rxcallback_t userfunc = NULL;
+ int ret=1;
+ struct aim_tlvlist_t *tlvlist;
+ struct aim_tlv_t *tlv;
+ unsigned short maxbuddies = 0, maxwatchers = 0;
+
+ /*
+ * TLVs follow
+ */
+ if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
+ return ret;
+
+ /*
+ * TLV type 0x0001: Maximum number of buddies.
+ */
+ if ((tlv = aim_gettlv(tlvlist, 0x0001, 1))) {
+ maxbuddies = aimutil_get16(tlv->value);
+ }
+
+ /*
+ * TLV type 0x0002: Maximum number of watchers.
+ *
+ * XXX: what the hell is a watcher?
+ *
+ */
+ if ((tlv = aim_gettlv(tlvlist, 0x0002, 1))) {
+ maxwatchers = aimutil_get16(tlv->value);
+ }
+
+ userfunc = aim_callhandler(command->conn, 0x0003, 0x0003);
+ if (userfunc)
+ ret = userfunc(sess, command, maxbuddies, maxwatchers);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
struct aim_snac_t *snac = NULL;
int ret = 0;
rxcallback_t userfunc = NULL;
+ char *dest;
+ unsigned short reason = 0;
/*
* Get SNAC from packet and look it up
if (!snac) {
printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
- }
+ dest = NULL;
+ } else
+ dest = snac->data;
+
+ reason = aimutil_get16(command->data+10);
/*
* Call client.
*/
userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
if (userfunc)
- ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)");
+ ret = userfunc(sess, command, dest, reason);
else
ret = 0;
}
+int aim_parse_missedcall(struct aim_session_t *sess,
+ struct command_rx_struct *command)
+{
+ int i, ret = 1;
+ rxcallback_t userfunc = NULL;
+ unsigned short channel, nummissed, reason;
+ struct aim_userinfo_s userinfo;
+
+ i = 10; /* Skip SNAC header */
+
+
+ /*
+ * XXX: supposedly, this entire packet can repeat as many times
+ * as necessary. Should implement that.
+ */
+
+ /*
+ * Channel ID.
+ */
+ channel = aimutil_get16(command->data+i);
+ i += 2;
+
+ /*
+ * Extract the standard user info block.
+ */
+ i += aim_extractuserinfo(command->data+i, &userinfo);
+
+ nummissed = aimutil_get16(command->data+i);
+ i += 2;
+
+ reason = aimutil_get16(command->data+i);
+ i += 2;
+
+ /*
+ * Call client.
+ */
+ userfunc = aim_callhandler(command->conn, 0x0004, 0x000a);
+ if (userfunc)
+ ret = userfunc(sess, command, channel, &userinfo, nummissed, reason);
+ else
+ ret = 0;
+
+ return ret;
+}
return (sess->snac_nextid++);
}
+int aim_parse_locateerr(struct aim_session_t *sess,
+ struct command_rx_struct *command)
+{
+ u_long snacid = 0x000000000;
+ struct aim_snac_t *snac = NULL;
+ int ret = 0;
+ rxcallback_t userfunc = NULL;
+ char *dest;
+ unsigned short reason = 0;
+
+ /*
+ * Get SNAC from packet and look it up
+ * the list of unrepliedto/outstanding
+ * SNACs.
+ *
+ */
+ snacid = aimutil_get32(command->data+6);
+ snac = aim_remsnac(sess, snacid);
+
+ if (!snac) {
+ printf("faim: locerr: got an locate-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
+ dest = NULL;
+ } else
+ dest = snac->data;
+
+ reason = aimutil_get16(command->data+10);
+
+ /*
+ * Call client.
+ */
+ userfunc = aim_callhandler(command->conn, 0x0002, 0x0001);
+ if (userfunc)
+ ret = userfunc(sess, command, dest, reason);
+ else
+ ret = 0;
+
+ if (snac) {
+ free(snac->data);
+ free(snac);
+ }
+
+ return ret;
+}
/*
* Capability blocks.
return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
}
+int aim_parse_bosrights(struct aim_session_t *sess,
+ struct command_rx_struct *command, ...)
+{
+ rxcallback_t userfunc = NULL;
+ int ret=1;
+ struct aim_tlvlist_t *tlvlist;
+ struct aim_tlv_t *tlv;
+ unsigned short maxpermits = 0, maxdenies = 0;
+
+ /*
+ * TLVs follow
+ */
+ if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
+ return ret;
+
+ /*
+ * TLV type 0x0001: Maximum number of buddies on permit list.
+ */
+ if ((tlv = aim_gettlv(tlvlist, 0x0001, 1))) {
+ maxpermits = aimutil_get16(tlv->value);
+ }
+
+ /*
+ * TLV type 0x0002: Maximum number of buddies on deny list.
+ *
+ */
+ if ((tlv = aim_gettlv(tlvlist, 0x0002, 1))) {
+ maxdenies = aimutil_get16(tlv->value);
+ }
+
+ userfunc = aim_callhandler(command->conn, 0x0009, 0x0003);
+ if (userfunc)
+ ret = userfunc(sess, command, maxpermits, maxdenies);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
/*
* aim_bos_clientready()
*
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
break;
}
+ break;
case 0x0002: /* Family: Location */
switch (subtype) {
case 0x0001:
- workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0001, workingPtr);
+ workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
break;
case 0x0003:
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
break;
}
+ break;
case 0x0003: /* Family: Buddy List */
switch (subtype) {
case 0x0001:
workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
break;
case 0x0003:
- workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0003, 0x0003, workingPtr);
+ workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
break;
case 0x000b: /* oncoming buddy */
workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
break;
case 0x000a:
- workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x000a, workingPtr);
+ workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
break;
case 0x000c:
workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
if (subtype == 0x0001)
workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
else if (subtype == 0x0003)
- workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0009, 0x0003, workingPtr);
+ workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
else
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
break;
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
}
break;
- case 0x000b:
+ case 0x000b: {
if (subtype == 0x0001)
workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
else if (subtype == 0x0002)
else
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
break;
- case AIM_CB_FAM_SPECIAL:
- workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
- break;
+ }
+ case AIM_CB_FAM_SPECIAL:
+ workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
+ break;
default:
workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
break;
- }
+ } /* switch(family) */
break;
- }
+ } /* AIM_CONN_TYPE_BOS */
case AIM_CONN_TYPE_CHATNAV: {
u_short family;
u_short subtype;
*/
faim_mutex_lock(&conn->active);
if (recv(conn->fd, generic, 6, MSG_WAITALL) < 6){
- printf("faim: flap: read underflow (header)\n");
aim_conn_close(conn);
faim_mutex_unlock(&conn->active);
return -1;
/* read the data portion of the packet */
if (recv(conn->fd, newrx->data, newrx->commandlen, MSG_WAITALL) < newrx->commandlen){
- printf("faim: flap: read underflow (payload)\n");
free(newrx->data);
free(newrx);
aim_conn_close(conn);
u_long aim_newsnac(struct aim_session_t *sess,
struct aim_snac_t *newsnac)
{
- struct aim_snac_t *snac = NULL, *cur = NULL;
+ struct aim_snac_t *snac = NULL;
int index;
if (!newsnac)
sess->snac_hash[index] = snac;
faim_mutex_unlock(&sess->snac_hash_locks[index]);
+ printf("faim: cached snac %lx\n", snac->id);
+
return(snac->id);
}
struct aim_snac_t *aim_remsnac(struct aim_session_t *sess,
u_long id)
{
- struct aim_snac_t *cur;
+ struct aim_snac_t *cur = NULL;
int index;
index = id % FAIM_SNAC_HASH_SIZE;
faim_mutex_lock(&sess->snac_hash_locks[index]);
if (!sess->snac_hash[index])
;
- else if (!sess->snac_hash[index]->next) {
- if (sess->snac_hash[index]->id == id) {
- cur = sess->snac_hash[index];
- sess->snac_hash[index] = NULL;
- }
+ else if (sess->snac_hash[index]->id == id) {
+ cur = sess->snac_hash[index];
+ sess->snac_hash[index] = cur->next;
} else {
cur = sess->snac_hash[index];
while (cur->next) {
int aim_cleansnacs(struct aim_session_t *sess,
int maxage)
{
- struct aim_snac_t *cur;
- struct aim_snac_t *remed = NULL;
+ struct aim_snac_t *cur, *next, *prev = NULL;
time_t curtime;
int i;
for (i = 0; i < FAIM_SNAC_HASH_SIZE; i++) {
faim_mutex_lock(&sess->snac_hash_locks[i]);
- if (!sess->snac_hash[i])
- ;
- else if (!sess->snac_hash[i]->next) {
- if ((sess->snac_hash[i]->issuetime + maxage) >= curtime) {
- remed = sess->snac_hash[i];
- if(remed->data)
- free(remed->data);
- free(remed);
- sess->snac_hash[i] = NULL;
- }
- } else {
- cur = sess->snac_hash[i];
- while(cur && cur->next) {
- if ((cur->next->issuetime + maxage) >= curtime) {
- remed = cur->next;
- cur->next = cur->next->next;
- if (remed->data)
- free(remed->data);
- free(remed);
- }
- cur = cur->next;
+ if (!sess->snac_hash[i]) {
+ faim_mutex_unlock(&sess->snac_hash_locks[i]);
+ continue;
+ }
+
+ curtime = time(NULL); /* done here in case we waited for the lock */
+
+ cur = sess->snac_hash[i];
+ while (cur) {
+ next = cur->next;
+ if ((curtime - cur->issuetime) > maxage) {
+ if (sess->snac_hash[i] == cur)
+ prev = sess->snac_hash[i] = next;
+ else
+ prev->next = next;
+
+ printf("faim: killing ancient snac %lx (%lx)\n", cur->id, curtime - cur->issuetime);
+
+ /* XXX should we have destructors here? */
+ if (cur->data)
+ free(cur->data);
+ free(cur);
+
+ } else {
+ prev = cur;
}
+ cur = next;
}
+
faim_mutex_unlock(&sess->snac_hash_locks[i]);
}
u_long aim_seticbmparam(struct aim_session_t *, struct aim_conn_t *conn);
int aim_parse_msgerror_middle(struct aim_session_t *, struct command_rx_struct *);
int aim_negchan_middle(struct aim_session_t *sess, struct command_rx_struct *command);
+int aim_parse_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+int aim_parse_missedcall(struct aim_session_t *sess, struct command_rx_struct *command);
struct aim_conn_t * aim_directim_initiate(struct aim_session_t *, struct aim_conn_t *, struct aim_directim_priv *, char *);
int aim_send_im_direct(struct aim_session_t *, struct aim_conn_t *, char *);
int aim_putuserinfo(u_char *buf, int buflen, struct aim_userinfo_s *info);
int aim_sendbuddyoncoming(struct aim_session_t *sess, struct aim_conn_t *conn, struct aim_userinfo_s *info);
int aim_sendbuddyoffgoing(struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
-
+int aim_parse_locateerr(struct aim_session_t *sess, struct command_rx_struct *command);
/* aim_auth.c */
int aim_auth_sendcookie(struct aim_session_t *, struct aim_conn_t *, u_char *);
/* aim_buddylist.c */
u_long aim_add_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
u_long aim_remove_buddy(struct aim_session_t *, struct aim_conn_t *, char *);
+int aim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
/* aim_search.c */
u_long aim_usersearch_address(struct aim_session_t *, struct aim_conn_t *, char *);
int faimtest_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...);
int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...);
int faimtest_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+int faimtest_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...);
+
+static char *msgerrreasons[] = {
+ "Invalid error",
+ "Invalid SNAC",
+ "Rate to host",
+ "Rate to client",
+ "Not logged on",
+ "Service unavailable",
+ "Service not defined",
+ "Obsolete SNAC",
+ "Not supported by host",
+ "Not supported by client",
+ "Refused by client",
+ "Reply too big",
+ "Responses lost",
+ "Request denied",
+ "Busted SNAC payload",
+ "Insufficient rights",
+ "In local permit/deny",
+ "Too evil (sender)",
+ "Too evil (receiver)",
+ "User temporarily unavailable",
+ "No match",
+ "List overflow",
+ "Request ambiguous",
+ "Queue full",
+ "Not while on AOL"};
+static int msgerrreasonslen = 25;
int faimtest_reportinterval(struct aim_session_t *sess, struct command_rx_struct *command, ...)
{
return 1;
}
+int faimtest_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ unsigned short maxbuddies, maxwatchers;
+
+ va_start(ap, command);
+ maxbuddies = va_arg(ap, unsigned short);
+ maxwatchers = va_arg(ap, unsigned short);
+ va_end(ap);
+
+ printf("faimtest: buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers);
+
+ return 1;
+}
+
int faimtest_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...)
{
+ unsigned short maxpermits, maxdenies;
+ va_list ap;
+
+ va_start(ap, command);
+ maxpermits = va_arg(ap, unsigned short);
+ maxdenies = va_arg(ap, unsigned short);
+ va_end(ap);
+
+ printf("faimtest: BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies);
+
aim_bos_clientready(sess, command->conn);
printf("faimtest: officially connected to BOS.\n");
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, faimtest_handleredirect, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, faimtest_reportinterval, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, faimtest_parse_buddyrights, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, faimtest_parse_oncoming, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, faimtest_parse_offgoing, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, faimtest_parse_incoming_im, 0);
- aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_misses, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, faimtest_parse_locerr, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, faimtest_parse_misses, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, faimtest_parse_ratechange, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, faimtest_parse_evilnotify, 0);
- aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_misses, 0);
+ aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, faimtest_parse_msgerr, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, faimtest_parse_userinfo, 0);
aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, faimtest_parse_msgack, 0);
struct aim_conn_t *newconn;
newconn = aim_directim_initiate(sess, command->conn, NULL, userinfo->sn);
//aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, faimtest_directim_initiate, 0);
+ } else if (!strncmp(tmpstr, "reqsendmsg", 10)) {
+ aim_send_im(sess, command->conn, "vaxherder", 0, "sendmsg 7986");
} else if (!strncmp(tmpstr, "sendmsg", 7)) {
int i;
i = atoi(tmpstr+8);
int faimtest_parse_motd(struct aim_session_t *sess, struct command_rx_struct *command, ...)
{
+ static char *codes[] = {
+ "Unknown",
+ "Mandatory upgrade",
+ "Advisory upgrade",
+ "System bulletin",
+ "Top o' the world!"};
+ static int codeslen = 5;
+
char *msg;
- u_short id;
+ unsigned short id;
va_list ap;
va_start(ap, command);
- id = va_arg(ap, u_short);
+ id = va_arg(ap, unsigned short);
msg = va_arg(ap, char *);
va_end(ap);
- printf("faimtest: motd: %s (%d)\n", msg, id);
+ printf("faimtest: motd: %s (%d / %s)\n", msg, id,
+ (id < codeslen)?codes[id]:"unknown");
+
+ return 1;
+}
+
+int faimtest_parse_msgerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ char *destsn;
+ unsigned short reason;
+
+ va_start(ap, command);
+ destsn = va_arg(ap, char *);
+ reason = va_arg(ap, unsigned short);
+ va_end(ap);
+
+ printf("faimtest: message to %s bounced (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
+
+ return 1;
+}
+int faimtest_parse_locerr(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ char *destsn;
+ unsigned short reason;
+
+ va_start(ap, command);
+ destsn = va_arg(ap, char *);
+ reason = va_arg(ap, unsigned short);
+ va_end(ap);
+
+ printf("faimtest: user information for %s unavailable (reason 0x%04x: %s)\n", destsn, reason, (reason<msgerrreasonslen)?msgerrreasons[reason]:"unknown");
+
return 1;
}
/*
- * Handles callbacks for: AIM_CB_RATECHANGE, AIM_CB_USERERROR,
- * AIM_CB_MISSED_IM, and AIM_CB_MISSED_CALL.
+ * Handles callbacks for AIM_CB_MISSED_CALL.
*/
int faimtest_parse_misses(struct aim_session_t *sess, struct command_rx_struct *command, ...)
{
- u_short family;
- u_short subtype;
+ static char *missedreasons[] = {
+ "Unknown",
+ "Message too large"};
+ static int missedreasonslen = 2;
- family = aimutil_get16(command->data+0);
- subtype= aimutil_get16(command->data+2);
+ va_list ap;
+ unsigned short chan, nummissed, reason;
+ struct aim_userinfo_s *userinfo;
- switch (family)
- {
- case 0x0001:
- if (subtype == 0x000a) /* or AIM_CB_RATECHANGE */
- printf("\n****STOP SENDING/RECIEVING MESSAGES SO FAST!****\n\n");
- break;
- case 0x0002:
- if (subtype == 0x0001) /* or AIM_CB_USERERROR */
- {
- u_long snacid = 0x00000000;
-
- snacid = aimutil_get32(&command->data[6]);
-
- printf("Received unknown error in SNAC family 0x0002 (snacid = %08lx)\n", snacid);
- }
- break;
- case 0x0004:
- if (subtype == 0x0001) /* or AIM_CB_MISSED_IM */
- printf("\n***LAST IM DIDN\'T MAKE IT BECAUSE THE BUDDY IS NOT ONLINE***\n\n");
- else if (subtype == 0x000a) /* or AIM_CB_MISSED_CALL */
- printf("You missed some messages from %s because they were sent too fast\n", &(command->data[13]));
- break;
- }
+ va_start(ap, command);
+ chan = va_arg(ap, unsigned short);
+ userinfo = va_arg(ap, struct aim_userinfo_s *);
+ nummissed = va_arg(ap, unsigned short);
+ reason = va_arg(ap, unsigned short);
+ va_end(ap);
+ printf("faimtest: missed %d messages from %s (reason %d: %s)\n", nummissed, userinfo->sn, reason, (reason<missedreasonslen)?missedreasons[reason]:"unknown");
+
return 0;
}