changeset 331:f3c8d79688db

[gaim-migrate @ 341] GAIM DOES ICQ!!!! CATCHING UP TO EVERYBUDDY! :) committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sat, 03 Jun 2000 07:25:42 +0000
parents 02da800b7805
children b573f33c5e0e
files ChangeLog libfaim/CHANGES.gaim libfaim/README.gaim libfaim/aim_login.c libfaim/aim_rxhandlers.c libfaim/faim/aim.h libfaim/faim/aim_cbtypes.h src/oscar.c src/server.c
diffstat 9 files changed, 182 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Jun 03 00:53:28 2000 +0000
+++ b/ChangeLog	Sat Jun 03 07:25:42 2000 +0000
@@ -1,6 +1,7 @@
 GAIM: The Pimpin' Penguin IM Clone thats good for the soul! 
 
 version 0.9.19:
+	* Oscar can sign in to ICQ now
 
 version 0.9.18 (06/02/2000):
 	* Logging in works better for oscar
--- a/libfaim/CHANGES.gaim	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/CHANGES.gaim	Sat Jun 03 07:25:42 2000 +0000
@@ -1,3 +1,9 @@
+
+Sat Jun  3 07:22:50 UTC 2000 EWarmenhoven
+	- Adam is by far the coolest person I know. He figured out how to get
+	  libfaim not only to use ICQ, but how to make it so that there aren't
+	  any client changes that are required - just use your UIN instead of
+	  your SN.
 
 Fri Jun  2 19:39:13 UTC 2000 EWarmenhoven
 	- Buddy lists no longer get sent individually, but all together (as
--- a/libfaim/README.gaim	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/README.gaim	Sat Jun 03 07:25:42 2000 +0000
@@ -51,6 +51,7 @@
  - inviting someone
  - getting invited
  - refreshing the chatlist in the preferences dialog
+ICQ!!! (Use your UIN instead of your SN to sign on)
 
 CURRENTLY UNSUPPORTED FEATURES
 ==============================
--- a/libfaim/aim_login.c	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/aim_login.c	Sat Jun 03 07:25:42 2000 +0000
@@ -80,7 +80,8 @@
 {
   u_char *password_encoded = NULL;  /* to store encoded password */
   int curbyte=0;
-
+  int icqmode = 0;
+  
   struct command_tx_struct *newpacket;
 
   if (!clientinfo || !sn || !password)
@@ -89,6 +90,22 @@
   if (!(newpacket = aim_tx_new(0x0002, conn, 1152)))
     return -1;
 
+  /*
+   * For ICQ logins, the client version must be at
+   * least as high as ICQ2000a.
+   */
+  if ((sn[0] >= '0') && (sn[0] <= '9')) {
+    icqmode = 1; /* needs a different password encoding */
+    if (clientinfo && (clientinfo->major < 4)) {
+      printf("faim: icq: version must be at least 4.30.3141 for ICQ OSCAR login\n");
+      return -1;
+    }
+    if (strlen(password) > 8) {
+      printf("faim: icq: password too long (8 char max)\n");
+      return -1;
+    }
+  }
+
 #ifdef SNACLOGIN 
   newpacket->commandlen = 10;
   newpacket->commandlen += 2 + 2 + strlen(sn);
@@ -118,8 +135,10 @@
   curbyte+= aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
 #else
   
-  newpacket->commandlen = 4 + 4+strlen(sn) + 4+strlen(password) + 6;
+  newpacket->commandlen = 4 + 4 + strlen(sn) + 4+strlen(password) + 6;
  
+  newpacket->commandlen += 8; /* tlv 0x0014 */
+
   if (clientinfo) {
     if (strlen(clientinfo->clientstring))
       newpacket->commandlen += 4+strlen(clientinfo->clientstring);
@@ -129,7 +148,6 @@
     if (strlen(clientinfo->lang))
       newpacket->commandlen += 4+strlen(clientinfo->lang);
   }
-  newpacket->commandlen += 6;
 
   newpacket->lock = 1;
   newpacket->type = 0x01;
@@ -143,35 +161,38 @@
   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
   curbyte += aimutil_put16(newpacket->data+curbyte, strlen(password));
   password_encoded = (char *) malloc(strlen(password));
-  aim_encode_password(password, password_encoded);
+  if (icqmode)
+    aimicq_encode_password(password, password_encoded);
+  else
+    aim_encode_password(password, password_encoded);
   curbyte += aimutil_putstr(newpacket->data+curbyte, password_encoded, strlen(password));
   free(password_encoded);
   
-  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x0004);
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0016, 0x010a /*0x0004*/);
   
-  if (clientinfo) {
-    if (strlen(clientinfo->clientstring)) {
-      curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
-      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->clientstring));
-      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->clientstring, strlen(clientinfo->clientstring));
-    }
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, clientinfo->major /*0x0001*/);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, clientinfo->minor /*0x0001*/);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0000);
-    curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, clientinfo->build /*0x0013*/);
-    if (strlen(clientinfo->country)) {
-      curbyte += aimutil_put16(newpacket->data+curbyte, 0x000e);
-      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->country));
-      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->country, strlen(clientinfo->country));
-    }
-    if (strlen(clientinfo->lang)) {
-      curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
-      curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->lang));
-      curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->lang, strlen(clientinfo->lang));
-    }
+  if (strlen(clientinfo->clientstring)) {
+    curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+    curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->clientstring));
+    curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->clientstring, strlen(clientinfo->clientstring));
+  }
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0017, clientinfo->major /*0x0001*/);
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0018, clientinfo->minor /*0x0001*/);
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0019, 0x0001);
+  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x001a, clientinfo->build /*0x0013*/);
+
+  curbyte += aim_puttlv_32(newpacket->data+curbyte, 0x0014, 0x00000055);
+
+  if (strlen(clientinfo->country)) {
+    curbyte += aimutil_put16(newpacket->data+curbyte, 0x000e);
+    curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->country));
+    curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->country, strlen(clientinfo->country));
+  }
+  if (strlen(clientinfo->lang)) {
+    curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+    curbyte += aimutil_put16(newpacket->data+curbyte, strlen(clientinfo->lang));
+    curbyte += aimutil_putstr(newpacket->data+curbyte, clientinfo->lang, strlen(clientinfo->lang));
   }
 
-  curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0009, 0x0015);
 #endif
 
   newpacket->lock = 0;
@@ -212,6 +233,31 @@
 }
 
 /*
+ * They changed the hash slightly for ICQ. 
+ *   This new hash may work for AIM too (though
+ *   the max password length for ICQ is only 
+ *   eight characters, where its 16 with AIM).
+ *
+ */
+int aimicq_encode_password(const char *password, u_char *encoded)
+{
+  u_char encoding_table[] = {
+    0xf3, 0x26, 0x81, 0xc4,
+    0x39, 0x86, 0xdb, 0x92
+  };
+
+  int i;
+
+  if (strlen(password) > 8)
+    return -1;
+
+  for (i = 0; i < strlen(password); i++)
+      encoded[i] = (password[i] ^ encoding_table[i]);
+
+  return 0;
+}
+
+/*
  * 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.  
@@ -250,44 +296,45 @@
    * Check for an error code.  If so, we should also
    * have an error url.
    */
-  if (aim_gettlv(tlvlist, 0x0008, 1))
-    {
-      struct aim_tlv_t *errtlv;
-      errtlv = aim_gettlv(tlvlist, 0x0008, 1);
-      sess->logininfo.errorcode = aimutil_get16(errtlv->value);
-      sess->logininfo.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1);
-    }
+  if (aim_gettlv(tlvlist, 0x0008, 1)) {
+    struct aim_tlv_t *errtlv;
+    errtlv = aim_gettlv(tlvlist, 0x0008, 1);
+    sess->logininfo.errorcode = aimutil_get16(errtlv->value);
+    sess->logininfo.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+  }
   /* 
    * If we have both an IP number (0x0005) and a cookie (0x0006),
    * then the login was successful.
    */
-  else if (aim_gettlv(tlvlist, 0x0005, 1) && aim_gettlv(tlvlist, 0x0006, 1))
-    {
-      struct aim_tlv_t *tmptlv;
-
-      /*
-       * IP address of BOS server.
-       */
-      sess->logininfo.BOSIP = aim_gettlv_str(tlvlist, 0x0005, 1);
+  else if (aim_gettlv(tlvlist, 0x0005, 1) && aim_gettlv(tlvlist, 0x0006, 1)) {
+    struct aim_tlv_t *tmptlv;
 
-      /*
-       * Authorization Cookie
-       */
-      tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
-      memcpy(sess->logininfo.cookie, tmptlv->value, AIM_COOKIELEN);
-
-      /*
-       * The email address attached to this account
-       */
+    /*
+     * IP address of BOS server.
+     */
+    sess->logininfo.BOSIP = aim_gettlv_str(tlvlist, 0x0005, 1);
+    
+    /*
+     * Authorization Cookie
+     */
+    tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+    memcpy(sess->logininfo.cookie, tmptlv->value, AIM_COOKIELEN);
+    
+    /*
+     * The email address attached to this account
+     *   Not available for ICQ logins.
+     */
+    if (aim_gettlv(tlvlist, 0x0011, 1))
       sess->logininfo.email = aim_gettlv_str(tlvlist, 0x0011, 1);
-
-      /*
-       * The registration status.  (Not real sure what it means.)
-       */
-      tmptlv = aim_gettlv(tlvlist, 0x0013, 1);
+    
+    /*
+     * The registration status.  (Not real sure what it means.)
+     *   Not available for ICQ logins.
+     */
+    if ((tmptlv = aim_gettlv(tlvlist, 0x0013, 1)))
       sess->logininfo.regstatus = aimutil_get16(tmptlv->value);
       
-    }
+  }
 
 #ifdef SNACLOGIN
   userfunc = aim_callhandler(command->conn, 0x0017, 0x0003);
--- a/libfaim/aim_rxhandlers.c	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/aim_rxhandlers.c	Sat Jun 03 07:25:42 2000 +0000
@@ -375,7 +375,12 @@
       case AIM_CONN_TYPE_BOS: {
 	u_short family;
 	u_short subtype;
-	
+
+	if (workingPtr->type == 0x04) {
+	  workingPtr->handled = aim_negchan_middle(sess, workingPtr);
+	  break;
+	}
+
 	family = aimutil_get16(workingPtr->data);
 	subtype = aimutil_get16(workingPtr->data+2);
 	
@@ -686,6 +691,35 @@
 }
 
 
+int aim_negchan_middle(struct aim_session_t *sess,
+		       struct command_rx_struct *command)
+{
+  struct aim_tlvlist_t *tlvlist;
+  char *msg = NULL;
+  unsigned short code = 0;
+  struct aim_tlv_t *tmptlv;
+  rxcallback_t userfunc = NULL;
+  int ret = 1;
+
+  tlvlist = aim_readtlvchain(command->data, command->commandlen);
+
+  if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
+    code = aimutil_get16(tmptlv->value);
+
+  if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
+    msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+  userfunc = aim_callhandler(command->conn, 
+			     AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
+  if (userfunc)
+    ret =  userfunc(sess, command, code, msg);
+
+  aim_freetlvchain(&tlvlist);
+  free(msg);
+
+  return ret;
+}
+
 /*
  * aim_parse_generalerrs()
  *
--- a/libfaim/faim/aim.h	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/faim/aim.h	Sat Jun 03 07:25:42 2000 +0000
@@ -10,8 +10,8 @@
 #define FAIM_VERSION_MINOR 99
 #define FAIM_VERSION_MINORMINOR 0
 
-#include <faimconfig.h>
-#include <aim_cbtypes.h>
+#include <faim/faimconfig.h>
+#include <faim/aim_cbtypes.h>
 
 #ifndef FAIM_USEPTHREADS
 #error pthreads are currently required.
@@ -71,7 +71,7 @@
  * Note that although we can send this much, its impossible
  * for WinAIM clients (up through the latest (4.0.1957)) to
  * send any more than 1kb.  Amaze all your windows friends
- * with uterrly oversized instant messages!
+ * with utterly oversized instant messages!
  * 
  */
 #define MAXMSGLEN 7988
@@ -329,6 +329,7 @@
 int aim_request_login (struct aim_session_t *sess, struct aim_conn_t *conn, char *sn);
 int aim_send_login (struct aim_session_t *, struct aim_conn_t *, char *, char *, struct client_info_s *);
 int aim_encode_password(const char *, u_char *);
+int aimicq_encode_password(const char *password, u_char *encoded);
 unsigned long aim_sendauthresp(struct aim_session_t *sess, 
 			       struct aim_conn_t *conn, 
 			       char *sn, char *bosip, 
@@ -442,6 +443,7 @@
 int aim_parse_incoming_im_middle(struct aim_session_t *, struct command_rx_struct *);
 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);
 
 /* aim_info.c */
 #define AIM_CAPS_BUDDYICON 0x01
--- a/libfaim/faim/aim_cbtypes.h	Sat Jun 03 00:53:28 2000 +0000
+++ b/libfaim/faim/aim_cbtypes.h	Sat Jun 03 07:25:42 2000 +0000
@@ -190,6 +190,7 @@
  */ 
 #define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
 #define AIM_CB_SPECIAL_AUTHOTHER 0x0002
+#define AIM_CB_SPECIAL_CONNERR 0x0003
 #define AIM_CB_SPECIAL_UNKNOWN 0xffff
 #define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
 
--- a/src/oscar.c	Sat Jun 03 00:53:28 2000 +0000
+++ b/src/oscar.c	Sat Jun 03 07:25:42 2000 +0000
@@ -39,6 +39,7 @@
 #include "gnome_applet_mgr.h"
 
 static int inpa = -1;
+static int paspa = -1;
 struct aim_session_t *gaim_sess;
 struct aim_conn_t    *gaim_conn;
 int gaim_caps = AIM_CAPS_CHAT | AIM_CAPS_SENDFILE | AIM_CAPS_GETFILE;
@@ -104,7 +105,7 @@
 int oscar_login(char *username, char *password) {
 	struct aim_session_t *sess;
 	struct aim_conn_t *conn;
-	struct client_info_s info = {"Gaim/Faim", 3, 5, 1670, "us", "en"};
+	struct client_info_s info = {"Gaim/Faim", 4, 30, 3141, "us", "en"};
 	struct aim_user *u;
 	char buf[256];
 
@@ -279,9 +280,22 @@
 	return 1;
 }
 
+gboolean change_password = FALSE;
+char *old_password;
+char *new_password;
+
 int gaim_auth_server_ready(struct aim_session_t *sess,
 			   struct command_rx_struct *command, ...) {
+	debug_print("Authorization server is ready.\n");
 	aim_auth_clientready(sess, command->conn);
+	if (change_password) {
+		debug_print("Changing passwords...\n");
+		aim_auth_changepasswd(sess, command->conn, old_password,
+							   new_password);
+		g_free(old_password);
+		g_free(new_password);
+		change_password = FALSE;
+	}
 	return 1;
 }
 
@@ -389,9 +403,13 @@
 		struct aim_conn_t *tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, ip);
 		if (tstconn == NULL || tstconn->status >= AIM_CONN_STATUS_RESOLVERR)
 			debug_print("unable to reconnect with authorizer\n");
-		else
+		else {
+			paspa = gdk_input_add(tstconn->fd,
+					GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+					oscar_callback, tstconn);
 			aim_auth_sendcookie(sess, tstconn, cookie);
 		}
+		}
 		break;
 	case 0xd: /* ChatNav */
 		{
--- a/src/server.c	Sat Jun 03 00:53:28 2000 +0000
+++ b/src/server.c	Sat Jun 03 07:25:42 2000 +0000
@@ -260,12 +260,22 @@
 #endif
 }
 
+extern gboolean change_password;
+extern char *old_password;
+extern char *new_password;
+
 void serv_change_passwd(char *orig, char *new) {
 #ifndef USE_OSCAR
 	char *buf = g_malloc(BUF_LONG); 
 	g_snprintf(buf, BUF_LONG, "toc_change_passwd %s %s", orig, new);
 	sflap_send(buf, strlen(buf), TYPE_DATA);
 	g_free(buf);
+#else
+	if (change_password) return;
+	change_password = TRUE;
+	old_password = g_strdup(orig);
+	new_password = g_strdup(new);
+	aim_bos_reqservice(gaim_sess, gaim_conn, AIM_CONN_TYPE_AUTH);
 #endif
 }