+/*
+ * This is sent back as a general response to the login command.
+ * It can be either an error or a success, depending on the
+ * precense of certain TLVs.
+ *
+ * The client should check the value passed as errorcode. If
+ * its nonzero, there was an error.
+ *
+ */
+faim_internal int aim_authparse(struct aim_session_t *sess,
+ struct command_rx_struct *command)
+{
+ struct aim_tlvlist_t *tlvlist;
+ int ret = 1;
+ rxcallback_t userfunc = NULL;
+ char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL;
+ unsigned char *cookie = NULL;
+ int errorcode = 0, regstatus = 0;
+ int latestbuild = 0, latestbetabuild = 0;
+ char *latestrelease = NULL, *latestbeta = NULL;
+ char *latestreleaseurl = NULL, *latestbetaurl = NULL;
+ char *latestreleaseinfo = NULL, *latestbetainfo = NULL;
+
+ /*
+ * Read block of TLVs. All further data is derived
+ * from what is parsed here.
+ *
+ * For SNAC login, there's a 17/3 SNAC header in front.
+ *
+ */
+ if (sess->flags & AIM_SESS_FLAGS_SNACLOGIN)
+ tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
+ else
+ tlvlist = aim_readtlvchain(command->data, command->commandlen);
+
+ /*
+ * No matter what, we should have a screen name.
+ */
+ memset(sess->sn, 0, sizeof(sess->sn));
+ if (aim_gettlv(tlvlist, 0x0001, 1)) {
+ sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+ strncpy(sess->sn, sn, sizeof(sess->sn));
+ }
+
+ /*
+ * Check for an error code. If so, we should also
+ * have an error url.
+ */
+ if (aim_gettlv(tlvlist, 0x0008, 1))
+ errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
+ if (aim_gettlv(tlvlist, 0x0004, 1))
+ errurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+
+ /*
+ * BOS server address.
+ */
+ if (aim_gettlv(tlvlist, 0x0005, 1))
+ bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
+
+ /*
+ * Authorization cookie.
+ */
+ if (aim_gettlv(tlvlist, 0x0006, 1)) {
+ struct aim_tlv_t *tmptlv;
+
+ tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+
+ if ((cookie = malloc(tmptlv->length)))
+ memcpy(cookie, tmptlv->value, tmptlv->length);
+ }
+
+ /*
+ * The email address attached to this account
+ * Not available for ICQ logins.
+ */
+ if (aim_gettlv(tlvlist, 0x0011, 1))
+ email = aim_gettlv_str(tlvlist, 0x0011, 1);
+
+ /*
+ * The registration status. (Not real sure what it means.)
+ * Not available for ICQ logins.
+ *
+ * 1 = No disclosure
+ * 2 = Limited disclosure
+ * 3 = Full disclosure
+ *
+ * This has to do with whether your email address is available
+ * to other users or not. AFAIK, this feature is no longer used.
+ *
+ */
+ if (aim_gettlv(tlvlist, 0x0013, 1))
+ regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
+
+ if (aim_gettlv(tlvlist, 0x0040, 1))
+ latestbetabuild = aim_gettlv32(tlvlist, 0x0040, 1);
+ if (aim_gettlv(tlvlist, 0x0041, 1))
+ latestbetaurl = aim_gettlv_str(tlvlist, 0x0041, 1);
+ if (aim_gettlv(tlvlist, 0x0042, 1))
+ latestbetainfo = aim_gettlv_str(tlvlist, 0x0042, 1);
+ if (aim_gettlv(tlvlist, 0x0043, 1))
+ latestbeta = aim_gettlv_str(tlvlist, 0x0043, 1);
+ if (aim_gettlv(tlvlist, 0x0048, 1))
+ ; /* no idea what this is */
+
+ if (aim_gettlv(tlvlist, 0x0044, 1))
+ latestbuild = aim_gettlv32(tlvlist, 0x0044, 1);
+ if (aim_gettlv(tlvlist, 0x0045, 1))
+ latestreleaseurl = aim_gettlv_str(tlvlist, 0x0045, 1);
+ if (aim_gettlv(tlvlist, 0x0046, 1))
+ latestreleaseinfo = aim_gettlv_str(tlvlist, 0x0046, 1);
+ if (aim_gettlv(tlvlist, 0x0047, 1))
+ latestrelease = aim_gettlv_str(tlvlist, 0x0047, 1);
+ if (aim_gettlv(tlvlist, 0x0049, 1))
+ ; /* no idea what this is */
+
+
+ if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0003)))
+ ret = userfunc(sess, command, sn, errorcode, errurl, regstatus, email, bosip, cookie, latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo, latestbeta, latestbetabuild, latestbetaurl, latestbetainfo);
+
+
+ if (sn)
+ free(sn);
+ if (bosip)
+ free(bosip);
+ if (errurl)
+ free(errurl);
+ if (email)
+ free(email);
+ if (cookie)
+ free(cookie);
+ if (latestrelease)
+ free(latestrelease);
+ if (latestreleaseurl)
+ free(latestreleaseurl);
+ if (latestbeta)
+ free(latestbeta);
+ if (latestbetaurl)
+ free(latestbetaurl);
+ if (latestreleaseinfo)
+ free(latestreleaseinfo);
+ if (latestbetainfo)
+ free(latestbetainfo);
+
+ aim_freetlvchain(&tlvlist);
+
+ return ret;
+}
+
+/*
+ * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed
+ * by only its length in a two byte word.
+ *
+ * Calls the client, which should then use the value to call aim_send_login.
+ *
+ */
+faim_internal int aim_authkeyparse(struct aim_session_t *sess, struct command_rx_struct *command)
+{
+ unsigned char *key;
+ int keylen;
+ int ret = 1;
+ rxcallback_t userfunc;
+
+ keylen = aimutil_get16(command->data+10);
+ if (!(key = malloc(keylen+1)))
+ return ret;
+ memcpy(key, command->data+12, keylen);
+ key[keylen] = '\0';
+
+ if ((userfunc = aim_callhandler(command->conn, 0x0017, 0x0007)))
+ ret = userfunc(sess, command, (char *)key);
+
+ free(key);
+
+ return ret;
+}
+
+/*
+ * Generate an authorization response.
+ *
+ * You probably don't want this unless you're writing an AIM server.
+ *
+ */
+faim_export unsigned long aim_sendauthresp(struct aim_session_t *sess,
+ struct aim_conn_t *conn,
+ char *sn, int errorcode,
+ char *errorurl, char *bosip,
+ char *cookie, char *email,
+ int regstatus)
+{
+ struct command_tx_struct *tx;
+ struct aim_tlvlist_t *tlvlist = NULL;
+
+ if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0004, conn, 1152)))
+ return -1;
+
+ tx->lock = 1;
+
+ if (sn)
+ aim_addtlvtochain_str(&tlvlist, 0x0001, sn, strlen(sn));
+ else
+ aim_addtlvtochain_str(&tlvlist, 0x0001, sess->sn, strlen(sess->sn));
+
+ if (errorcode) {
+ aim_addtlvtochain16(&tlvlist, 0x0008, errorcode);
+ aim_addtlvtochain_str(&tlvlist, 0x0004, errorurl, strlen(errorurl));
+ } else {
+ aim_addtlvtochain_str(&tlvlist, 0x0005, bosip, strlen(bosip));
+ aim_addtlvtochain_str(&tlvlist, 0x0006, cookie, AIM_COOKIELEN);
+ aim_addtlvtochain_str(&tlvlist, 0x0011, email, strlen(email));
+ aim_addtlvtochain16(&tlvlist, 0x0013, (unsigned short)regstatus);
+ }
+
+ tx->commandlen = aim_writetlvchain(tx->data, tx->commandlen, &tlvlist);
+ tx->lock = 0;
+
+ return aim_tx_enqueue(sess, tx);
+}
+
+/*
+ * Generate a random cookie. (Non-client use only)
+ */
+faim_export int aim_gencookie(unsigned char *buf)
+{
+ int i;
+
+ srand(time(NULL));
+
+ for (i=0; i < AIM_COOKIELEN; i++)
+ buf[i] = 1+(int) (256.0*rand()/(RAND_MAX+0.0));
+
+ return i;
+}
+
+/*
+ * Send Server Ready. (Non-client)
+ */
+faim_export int aim_sendserverready(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+ struct command_tx_struct *tx;
+ int i = 0;
+
+ if (!(tx = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+0x22)))
+ return -1;
+
+ tx->lock = 1;
+
+ i += aim_putsnac(tx->data, 0x0001, 0x0003, 0x0000, sess->snac_nextid++);
+
+ i += aimutil_put16(tx->data+i, 0x0001);
+ i += aimutil_put16(tx->data+i, 0x0002);
+ i += aimutil_put16(tx->data+i, 0x0003);
+ i += aimutil_put16(tx->data+i, 0x0004);
+ i += aimutil_put16(tx->data+i, 0x0006);
+ i += aimutil_put16(tx->data+i, 0x0008);
+ i += aimutil_put16(tx->data+i, 0x0009);
+ i += aimutil_put16(tx->data+i, 0x000a);
+ i += aimutil_put16(tx->data+i, 0x000b);
+ i += aimutil_put16(tx->data+i, 0x000c);
+ i += aimutil_put16(tx->data+i, 0x0013);
+ i += aimutil_put16(tx->data+i, 0x0015);
+
+ tx->commandlen = i;
+ tx->lock = 0;
+ return aim_tx_enqueue(sess, tx);
+}
+
+
+/*
+ * Send service redirect. (Non-Client)
+ */
+faim_export unsigned long aim_sendredirect(struct aim_session_t *sess,
+ struct aim_conn_t *conn,
+ unsigned short servid,
+ char *ip,
+ char *cookie)
+{
+ struct command_tx_struct *tx;
+ struct aim_tlvlist_t *tlvlist = NULL;
+ int i = 0;