changeset 1432:4c510ca3563f

[gaim-migrate @ 1442] icqlib 1.1.5 committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sun, 28 Jan 2001 01:52:27 +0000
parents 0137bacd63c8
children f2f45486d656
files plugins/icq/.cvsignore plugins/icq/CHANGES_SINCE_1.0 plugins/icq/ChangeLog plugins/icq/Makefile.am plugins/icq/VERSION plugins/icq/chatsession.c plugins/icq/chatsession.h plugins/icq/contacts.c plugins/icq/contacts.h plugins/icq/cyrillic.c plugins/icq/eventhandle.c plugins/icq/eventhandle.h plugins/icq/filesession.c plugins/icq/gaim_icq.c plugins/icq/icq.h plugins/icq/icqbyteorder.c plugins/icq/icqbyteorder.h plugins/icq/icqevent.c plugins/icq/icqevent.h plugins/icq/icqlib.c plugins/icq/icqlib.h plugins/icq/icqpacket.c plugins/icq/list.c plugins/icq/list.h plugins/icq/proxy.c plugins/icq/queue.c plugins/icq/socketmanager.c plugins/icq/socketmanager.h plugins/icq/stdpackets.c plugins/icq/stdpackets.h plugins/icq/tcp.c plugins/icq/tcpchathandle.c plugins/icq/tcpfilehandle.c plugins/icq/tcphandle.c plugins/icq/tcplink.c plugins/icq/tcplink.h plugins/icq/udp.c plugins/icq/udphandle.c plugins/icq/util.c
diffstat 39 files changed, 1156 insertions(+), 613 deletions(-) [+]
line wrap: on
line diff
--- a/plugins/icq/.cvsignore	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/.cvsignore	Sun Jan 28 01:52:27 2001 +0000
@@ -14,6 +14,7 @@
 list.lo
 proxy.lo
 queue.lo
+socketmanager.lo
 stdpackets.lo
 tcp.lo
 tcpchathandle.lo
--- a/plugins/icq/CHANGES_SINCE_1.0	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/CHANGES_SINCE_1.0	Sun Jan 28 01:52:27 2001 +0000
@@ -1,6 +1,6 @@
 
-This file is intendended to be a list of source incompatable changes have 
-been made since icqlib-1.0.0.
+This file is intendended to be a list of source incompatable changes 
+that have been made since icqlib-1.0.0.
 
 2000-12-19: ICQLINKs have been reworked.  Members that provide no useful 
 information to the library client have been moved to a private structure 
@@ -17,7 +17,60 @@
 and icq_ICQLINKDelete instead.  In addition, icq_ICQLINKNew has gained an
 additional parameter: a flag to turn TCP on or off.
 
+2001-01-16: The chat and file request/session interface has been
+significantly reworked.  icq_RequestNotify now will return
+ICQ_NOTIFY_SUCCESS as soon as the file or chat *request* is complete, but
+not before returning a handle to an icq_ChatSession or icq_FileSession.  
+See ICQ_NOTIFY_CHATSESSION and ICQ_NOTIFY_FILESESSION.  All future
+operations on the chat or file session should be performed using this
+handle and the appropriate icq_ChatSession* or icq_FileSession* functions.
 
+Old function             New function
+
+icq_TCPCloseChat         icq_ChatSessionClose
+icq_TCPSendChatData      icq_ChatSessionSendData
+icq_TCPSendChatData_n    icq_ChatSessionSendData_n
+
+In addition, notification messages for chat and file sessions come back 
+through two new callbacks: icq_ChatNotify and icq_FileNotify.  
+icq_ChatSession has also been exposed in the public interface, as well as 
+a number of chat status constants.
+
+Old notification                       New notification
+
+icq_RequestNotify/ICQ_NOTIFY_CHAT      icq_ChatNotify/CHAT_NOTIFY_STATUS
+icq_RequestNotify/ICQ_NOTIFY_CHATDATA  icq_ChatNotify/CHAT_NOTIFY_DATA
+icq_RequestNotify/ICQ_NOTIFY_SUCCESS   icq_ChatNotify/CHAT_NOTIFY_CLOSE
+
+icq_RequestNotify/ICQ_NOTIFY_FILE      icq_FileNotify/FILE_NOTIFY_STATUS
+icq_RequestNotify/ICQ_NOTIFY_FILEDATA  icq_FileNotify/FILE_NOTIFY_DATAPACKET
+icq_RequestNotify/ICQ_NOTIFY_SUCCESS   icq_FileNotify/FILE_NOTIFY_CLOSE
 
+Finally, some fake file status values now come through seperate 
+FILE_NOTIFY events, and one has been renamed:
 
+Old status                         New Notification/Status
 
+FILE_STATUS_NEW_SPEED              icq_FileNotify/FILE_NOTIFY_NEW_SPEED
+FILE_STATUS_STOP_FILE              icq_FileNotify/FILE_NOTIFY_STOP_FILE
+FILE_STATUS_INITIALIZED            FILE_STATUS_INITIALIZING
+
+2001-01-16: icqlib now reports when it needs read and write ready 
+notifications for sockets through icq_SocketNotify.  Install a callback 
+for the icq_SocketNotify function, which takes the following parameters:
+
+   void (*icq_SocketNotify)(int socket, int type, int status);
+
+socket will be the socket number, type will be either ICQ_SOCKET_READ or 
+ICQ_SOCKET_WRITE, and status will be either true or false, true indicating 
+icqlib now needs notification, and false indicating icqlib no longer needs 
+notification.  Your application should use this to install some sort of 
+socket notification object (e.g., in Kicq, we use a QSocketNotifier).  
+When the socket is ready, you should invoke the new 
+icq_HandleReadySocket(socket, type) function.
+
+This mechanism is completely optional - you can simply set up a timer to 
+call icq_Main every 1/10 of a second or so.  This has the disadvantage of 
+wasting CPU cycles and poor TCP file transfer performance, though.
+
+ 
--- a/plugins/icq/ChangeLog	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/ChangeLog	Sun Jan 28 01:52:27 2001 +0000
@@ -1,3 +1,81 @@
+2001-01-27  Bill Soudan  <soudan@kde.org>
+
+	* icqlib/socketmanager.c, icqlib/socketmanager.h, 
+	icqlib/tcplink.c: fix bugs related to TCP and new socket manager:
+	implemented accepting TCP sockets, fixed crashes when sending
+	TCP messages.
+
+2001-01-24  Bill Soudan  <soudan@kde.org>
+
+	* icqlib/stdpackets.c, icqlib/stdpackets.h, icqlib/tcphandle.c,
+	icqlib/icq.h.in: applied patch from Robin Ericsson 
+	(lobbin@localhost.nu) which implements receiving contact lists.  
+	See new icq_RecvContactList callback.
+	
+2001-01-20  Bill Soudan  <soudan@kde.org>
+
+	* icqlib/chatsession.c, icqlib/icq.h.in: added documentation
+
+	* icqlib/icqbyteorder.h: applied patch from Eric Warmenhoven
+	to fix byte order on HP/UX.
+
+	* icqlib/tcplink.h: applied patch from Eric Warmenhoven to
+	fix compilation on FreeBSD.
+
+2001-01-16  Bill Soudan  <soudan@kde.org>
+
+	Phase 2 of my interface cleanups.  See CHANGES_SINCE_1.0 for
+	more details.  icqlib developers be sure to look at 
+	socketmanager.c - all future socket create/delete/handling needs 
+	to go through this code so the new icq_SocketNotify callback
+	works properly.
+
+	* icqlib/socketmanager.c, icqlib/socketmanager.h, 
+	icqlib/Makefile.am: added socketmanager.c & socketmanager.h
+
+	* icqlib/contacts.h, icqlib/list.h, icqlib/proxy.c: cleanup
+
+	* icqlib/chatsession.c, icqlib/chatsession.h, icqlib/filesession.c,
+	icqlib/icq.h.in, icqlib/icqevent.c, icqlib/icqevent.h, 
+	icqlib/icqlib.c, icqlib/icqlib.h, icqlib/tcp.c, 
+	icqlib/tcpchathandle.c, icqlib/tcpfilehandle.c, 
+	icqlib/tcphandle.c, icqlib/tcplink.c, icqlib/tcplink.h:
+	Rework chat and file interfaces; implement socket notifications.
+
+	* VERSION: bumped to 1.1.5
+
+2001-01-16  Denis V. Dmitrienko  <denis@null.net>
+
+	* icqlib/contacts.c, icqlib/contacts.h, icqlib/icq.h.in, icqlib/udp.c:
+	Invisible list has been finished.
+
+2001-01-15  Denis V. Dmitrienko  <denis@null.net>
+
+	* icqlib/tcplink.c:
+	Applied patch from Ilya Melamed <ilya@ort.org.il> which fixes random
+	icq_TCPLinkAccept() fails.
+
+	* icqlib/udphandle.c:
+	Applied patch from Andrey Chernomyrdin <andrey@excom.spb.su> to
+	handle icq2000 specific "You've been added" packet.
+
+	* icqlib/icq.h.in:
+	Prototype for icq_SendInvisibleList() has been added.
+
+	* .cvsignore, icqlib.spec.in:
+	Applied patch from Ryan Weaver <ryanw@infohwy.com> for .spec file
+	generation.
+
+	* configure.in, Makefile.am, Makefile.cvs, admin/acinclude.m4.in,
+	admin/config.guess, admin/config.sub, admin/icqlib.m4.in,
+	admin/install-sh, admin/libtool.m4.in, admin/ltconfig,
+	admin/ltmain.sh, admin/missing, admin/mkinstalldirs,
+	acinclude.m4.in, am_edit, config.guess, config.sub, icqlib.m4.in,
+	install-sh, libtool.m4.in, ltconfig, ltmain.sh, missing,
+	mkinstalldirs:
+	autoconf/automake files have been moved to admin directory.
+
+	* TODO, icqlib/icqlib.c: Cleanup.
 
 2000-12-19  Bill Soudan  <wes0472@rit.edu>
 
--- a/plugins/icq/Makefile.am	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/Makefile.am	Sun Jan 28 01:52:27 2001 +0000
@@ -1,4 +1,4 @@
-CFLAGS += -I\$(top_srcdir)/src
+CFLAGS += -I\$(top_srcdir)/src $(DEBUG_CFLAGS)
 LIBS = $(GTK_LIBS)
 
 EXTRA_DIST =	AUTHORS \
@@ -35,6 +35,8 @@
 			proxy.c \
 			queue.c \
 			queue.h \
+			socketmanager.c \
+			socketmanager.h \
 			stdpackets.c \
 			stdpackets.h \
 			tcp.c \
--- a/plugins/icq/VERSION	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/VERSION	Sun Jan 28 01:52:27 2001 +0000
@@ -1,1 +1,1 @@
-1.1.0
+1.1.5
--- a/plugins/icq/chatsession.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/chatsession.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,7 +1,9 @@
 
 #include <stdlib.h>
 
+#include "icq.h"
 #include "icqlib.h"
+#include "tcplink.h"
 #include "chatsession.h"
 #include "list.h"
 
@@ -11,10 +13,10 @@
 
   if (p)
   {
-    p->remote_handle=0L;
     p->status=0;
     p->id=0L;
     p->icqlink=icqlink;
+    p->tcplink=NULL;
     list_insert(icqlink->d->icq_ChatSessions, 0, p);
   }
 	
@@ -23,15 +25,13 @@
 
 void icq_ChatSessionDelete(void *p)
 {
+  icq_ChatSession *pchat = (icq_ChatSession *)p;
+  invoke_callback(pchat->icqlink, icq_ChatNotify)(pchat, CHAT_NOTIFY_CLOSE,
+    0, NULL);
+  
   free(p);
 }
 
-void icq_ChatSessionClose(icq_ChatSession *p)
-{
-  list_remove(p->icqlink->d->icq_ChatSessions, p);
-  icq_ChatSessionDelete(p);
-}
-
 int _icq_FindChatSession(void *p, va_list data)
 {
   DWORD uin=va_arg(data, DWORD);
@@ -48,6 +48,77 @@
 {
   p->status=status;
   if(p->id)
-    if(p->icqlink->icq_RequestNotify)
-      (*p->icqlink->icq_RequestNotify)(p->icqlink, p->id, ICQ_NOTIFY_CHAT, status, 0);
+    invoke_callback(p->icqlink, icq_ChatNotify)(p, CHAT_NOTIFY_STATUS,
+      status, NULL);
+}
+
+/* public */
+
+/** Closes a chat session. 
+ * @param session desired icq_ChatSession
+ * @ingroup ChatSession
+ */
+void icq_ChatSessionClose(icq_ChatSession *session)
+{
+  icq_TCPLink *plink = session->tcplink;
+
+  /* if we're attached to a tcplink, unattach so the link doesn't try
+   * to close us, and then close the tcplink */
+  if (plink)
+  {
+    plink->session=NULL;
+    icq_TCPLinkClose(plink);
+  }
+  
+  icq_ChatSessionDelete(session);
+
+  list_remove(session->icqlink->d->icq_ChatSessions, session);
 }
+
+/** Sends chat data to the remote uin.
+ * @param session desired icq_ChatSession
+ * @param data pointer to data buffer, null-terminated
+ * @ingroup ChatSession
+ */
+void icq_ChatSessionSendData(icq_ChatSession *session, const char *data)
+{
+  icq_TCPLink *plink = session->tcplink;
+  int data_length = strlen(data);
+  char *buffer;
+
+  if(!plink)
+    return;
+
+  buffer = (char *)malloc(data_length);
+  strcpy(buffer, data);
+  icq_ChatRusConv_n("kw", buffer, data_length);
+  
+  send(plink->socket, buffer, data_length, 0);  
+  
+  free(buffer);
+}
+
+/** Sends chat data to the remote uin.
+ * @param session desired icq_ChatSession
+ * @param data pointer to data buffer
+ * @param length length of data
+ * @ingroup ChatSession
+ */
+void icq_ChatSessionSendData_n(icq_ChatSession *session, const char *data,
+  int length)
+{
+  icq_TCPLink *plink = session->tcplink;
+  char *buffer;
+
+  if(!plink)
+    return;
+  
+  buffer = (char *)malloc(length);
+  memcpy(buffer, data, length);
+  icq_ChatRusConv_n("kw", buffer, length);
+    
+  send(plink->socket, buffer, length, 0);
+
+  free(buffer);
+}
+
--- a/plugins/icq/chatsession.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/chatsession.h	Sun Jan 28 01:52:27 2001 +0000
@@ -5,19 +5,6 @@
 #include "icq.h"
 #include "icqtypes.h"
 
-/* chat session statuses- request receiver */
-#define CHAT_STATUS_LISTENING    1
-#define CHAT_STATUS_CONNECTED    3
-#define CHAT_STATUS_WAIT_NAME    4
-#define CHAT_STATUS_WAIT_FONT    6
-
-/* chat session statuses- request sender */
-#define CHAT_STATUS_CONNECTING   2
-#define CHAT_STATUS_WAIT_ALLINFO 5
-
-/* once negotiation is complete, both sides enter ready state */
-#define CHAT_STATUS_READY        7
-
 /* chat session states:
 
    accepting chat request 
@@ -60,20 +47,8 @@
 			ICQ_NOTIFY_SUCCESS
 */
 
-typedef struct icq_ChatSession_s {
-
-  DWORD id;
-  int status;
-  ICQLINK *icqlink;
-
-  DWORD remote_uin;
-  char *remote_handle;
-
-} icq_ChatSession;
-
 icq_ChatSession *icq_ChatSessionNew(ICQLINK *);
 void icq_ChatSessionDelete(void *);
-void icq_ChatSessionClose(icq_ChatSession *);
 void icq_ChatSessionSetStatus(icq_ChatSession *, int);
 icq_ChatSession *icq_FindChatSession(ICQLINK *, DWORD);
 
--- a/plugins/icq/contacts.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/contacts.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: contacts.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: contacts.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.6  2001/01/16 00:10:13  denis
+Invisible list has been finished.
 
 Revision 1.5  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -96,6 +99,13 @@
     p->vis_list = vu;
 }
 
+void icq_ContactSetInvis(ICQLINK *link, DWORD cuin, BYTE vu)
+{
+  icq_ContactItem *p = icq_ContactFind(link, cuin);
+  if(p)
+    p->invis_list = vu;
+}
+
 icq_ContactItem *icq_ContactGetFirst(ICQLINK *link)
 {
   return list_first(link->d->icq_ContactList);
@@ -109,5 +119,4 @@
     return p->next->item;
   else
     return 0L;
-
 }
--- a/plugins/icq/contacts.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/contacts.h	Sun Jan 28 01:52:27 2001 +0000
@@ -5,11 +5,12 @@
 #include "icq.h"
 #include "icqtypes.h"
 
-typedef struct icq_ContItem
+typedef struct icq_ContactItem_s
 {
   ICQLINK *icqlink;
   unsigned long uin;
   int vis_list;
+  int invis_list;
   unsigned long remote_ip;
   unsigned long remote_real_ip;
   unsigned long remote_port;
--- a/plugins/icq/cyrillic.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/cyrillic.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: cyrillic.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: cyrillic.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
 
 Revision 1.7  2000/05/21 17:41:14  denis
 Applied patch for russian letters IO and io by
--- a/plugins/icq/eventhandle.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/eventhandle.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,11 +1,11 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
 /* 
- * $Id: eventhandle.c 1319 2000-12-19 10:08:29Z warmenhoven $
+ * $Id: eventhandle.c 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * $Log$
- * Revision 1.1  2000/12/19 10:08:29  warmenhoven
- * Yay, new icqlib
+ * Revision 1.2  2001/01/28 01:52:27  warmenhoven
+ * icqlib 1.1.5
  *
  * Revision 1.3  2000/12/19 06:00:07  bills
  * moved members from ICQLINK to ICQLINK_private struct
--- a/plugins/icq/eventhandle.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/eventhandle.h	Sun Jan 28 01:52:27 2001 +0000
@@ -1,11 +1,11 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
 /*
- * $Id: eventhandle.h 1319 2000-12-19 10:08:29Z warmenhoven $
+ * $Id: eventhandle.h 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * $Log$
- * Revision 1.1  2000/12/19 10:08:29  warmenhoven
- * Yay, new icqlib
+ * Revision 1.2  2001/01/28 01:52:27  warmenhoven
+ * icqlib 1.1.5
  *
  * Revision 1.1  2000/06/15 18:50:03  bills
  * committed for safekeeping - this code will soon replace tcphandle.c
--- a/plugins/icq/filesession.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/filesession.c	Sun Jan 28 01:52:27 2001 +0000
@@ -47,6 +47,9 @@
 {
   icq_FileSession *p=(icq_FileSession *)pv;
 
+  invoke_callback(p->icqlink, icq_FileNotify)(p, FILE_NOTIFY_CLOSE, 0, 
+    NULL);
+
   if(p->files) {
     char **p2=p->files;
     while(*p2)
@@ -84,9 +87,14 @@
   if(status!=p->status)
   {
     p->status=status;
-    if(p->id && p->icqlink->icq_RequestNotify)
-      (*p->icqlink->icq_RequestNotify)(p->icqlink, p->id, ICQ_NOTIFY_FILE,
-       status, 0);
+    if(p->id)
+      invoke_callback(p->icqlink, icq_FileNotify)(p, FILE_NOTIFY_STATUS,
+        status, NULL);
+    if (status == FILE_STATUS_SENDING)
+      icq_SocketSetHandler(p->tcplink->socket, ICQ_SOCKET_WRITE,
+        icq_FileSessionSendData, p);
+    else
+      icq_SocketSetHandler(p->tcplink->socket, ICQ_SOCKET_WRITE, NULL, NULL);
   }
 }
 
@@ -181,10 +189,9 @@
       p->total_transferred_bytes+=count;
       p->current_file_progress+=count;
       icq_FileSessionSetStatus(p, FILE_STATUS_SENDING);
-      
-      if (p->icqlink->icq_RequestNotify)
-        (*p->icqlink->icq_RequestNotify)(p->icqlink, p->id,
-          ICQ_NOTIFY_FILEDATA, count, NULL); 
+
+      invoke_callback(p->icqlink, icq_FileNotify)(p, FILE_NOTIFY_DATAPACKET,
+        count, buffer);
   }
 
   /* done transmitting if read returns less that 2048 bytes */
--- a/plugins/icq/gaim_icq.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/gaim_icq.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,5 +1,6 @@
 #include <gtk/gtk.h>
 #include <string.h>
+#include <stdlib.h>
 #include "icq.h"   /* well, we're doing ICQ, right? */
 #include "multi.h" /* needed for gaim_connection */
 #include "prpl.h"  /* needed for prpl */
@@ -17,7 +18,6 @@
 struct icq_data {
 	ICQLINK *link;
 	int cur_status;
-	int tcp_timer;
 	int ack_timer;
 };
 
@@ -48,17 +48,45 @@
 	debug_printf("ICQ debug %d: %s", level, log);
 }
 
-static gint icq_tcp_timer(ICQLINK *link) {
-	icq_TCPMain(link);
-	return TRUE;
+GList *sockets = NULL;
+struct gaim_sock {
+	int socket;
+	gint inpa;
+};
+
+static void gaim_icq_handler(gpointer data, gint source, GdkInputCondition cond) {
+	if ((cond & GDK_INPUT_READ) || (cond & GDK_INPUT_EXCEPTION))
+		icq_HandleReadySocket(source, ICQ_SOCKET_READ);
+	if (cond & GDK_INPUT_WRITE)
+		icq_HandleReadySocket(source, ICQ_SOCKET_WRITE);
 }
 
-static void icq_callback(gpointer data, gint source, GdkInputCondition condition) {
-	struct gaim_connection *gc = (struct gaim_connection *)data;
-	struct icq_data *id = (struct icq_data *)gc->proto_data;
-	debug_printf("ICQ Callback handler\n");
-
-	icq_HandleServerResponse(id->link);
+static void icq_sock_notify(int socket, int type, int status) {
+	struct gaim_sock *gs;
+	if (status) {
+		GdkInputCondition cond;
+		if (type == ICQ_SOCKET_READ)
+			cond = GDK_INPUT_READ | GDK_INPUT_EXCEPTION;
+		else
+			cond = GDK_INPUT_WRITE | GDK_INPUT_EXCEPTION;
+		gs = g_new0(struct gaim_sock, 1);
+		gs->socket = socket;
+		gs->inpa = gdk_input_add(socket, cond, gaim_icq_handler, NULL);
+		sockets = g_list_append(sockets, gs);
+	} else {
+		GList *m = sockets;
+		while (m) {
+			gs = m->data;
+			if (gs->socket == socket)
+				break;
+			m = g_list_next(m);
+		}
+		if (m) {
+			gdk_input_remove(gs->inpa);
+			sockets = g_list_remove(sockets, gs);
+			g_free(gs);
+		}
+	}
 }
 
 static void icq_online(ICQLINK *link) {
@@ -78,10 +106,6 @@
 static void icq_logged_off(ICQLINK *link) {
 	struct gaim_connection *gc = find_gaim_conn_by_icq_link(link);
 	struct icq_data *id = (struct icq_data *)gc->proto_data;
-	int icqSocket;
-
-	gtk_timeout_remove(id->tcp_timer);
-	gdk_input_remove(gc->inpa);
 
 	if (icq_Connect(link, "icq.mirabilis.com", 4000) < 1) {
 		hide_login_progress(gc, "Unable to connect");
@@ -89,13 +113,8 @@
 		return;
 	}
 
-	icqSocket = icq_GetSok(link);
-	gc->inpa = gdk_input_add(icqSocket, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, icq_callback, gc);
-
 	icq_Login(link, STATUS_ONLINE);
 	id->cur_status = STATUS_ONLINE;
-
-	id->tcp_timer = gtk_timeout_add(100, (GtkFunction)icq_tcp_timer, link);
 }
 
 static void icq_msg_incoming(ICQLINK *link, unsigned long uin, unsigned char hour, unsigned char minute,
@@ -196,10 +215,6 @@
 	g_show_info_text(buf);
 }
 
-static void icq_req_notify(struct icq_link *link, unsigned long id, int result,
-				unsigned int length, void *data) {
-}
-
 static void icq_web_pager(struct icq_link *link, unsigned char hour, unsigned char minute,
 		unsigned char day, unsigned char month, unsigned short year, const char *nick,
 		const char *email, const char *msg) {
@@ -225,13 +240,17 @@
 static void icq_login(struct aim_user *user) {
 	struct gaim_connection *gc = new_gaim_conn(user);
 	struct icq_data *id = gc->proto_data = g_new0(struct icq_data, 1);
-	ICQLINK *link = id->link = icq_ICQLINKNew(atol(user->username), user->password,
-			user->proto_opt[USEROPT_NICK][0] ? user->proto_opt[USEROPT_NICK] : "gaim user",
-			TRUE);
-	int icqSocket;
+	ICQLINK *link;
+
+	if (!icq_SocketNotify)
+		icq_SocketNotify = icq_sock_notify;
 
 	icq_LogLevel = ICQ_LOG_MESSAGE;
 
+	link = id->link = icq_ICQLINKNew(atol(user->username), user->password,
+			  user->proto_opt[USEROPT_NICK][0] ? user->proto_opt[USEROPT_NICK] : "gaim user",
+			  TRUE);
+
 	link->icq_Logged = icq_online;
 	link->icq_Disconnected = icq_logged_off;
 	link->icq_RecvMessage = icq_msg_incoming;
@@ -245,7 +264,6 @@
 	link->icq_WrongPassword = icq_wrong_passwd;
 	link->icq_InvalidUIN = icq_invalid_uin;
 	link->icq_Log = icq_do_log;
-	link->icq_RequestNotify = icq_req_notify;
 	link->icq_SetTimeout = icq_set_timeout;
 
 	icq_UnsetProxy(link);
@@ -256,24 +274,15 @@
 		return;
 	}
 
-	icqSocket = icq_GetSok(link);
-	gc->inpa = gdk_input_add(icqSocket, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, icq_callback, gc);
-
 	icq_Login(link, STATUS_ONLINE);
 	id->cur_status = STATUS_ONLINE;
 
-	id->tcp_timer = gtk_timeout_add(100, (GtkFunction)icq_tcp_timer, link);
-
 	set_login_progress(gc, 0, "Connecting...");
 }
 
 static void icq_close(struct gaim_connection *gc) {
 	struct icq_data *id = (struct icq_data *)gc->proto_data;
 
-	if (id->tcp_timer > 0)
-		gtk_timeout_remove(id->tcp_timer);
-	if (gc->inpa > 0)
-		gdk_input_remove(gc->inpa);
 	icq_Logout(id->link);
 	icq_Disconnect(id->link);
 	icq_ICQLINKDelete(id->link);
@@ -297,7 +306,6 @@
 	struct icq_data *id = (struct icq_data *)gc->proto_data;
 	icq_ContactAdd(id->link, atol(who));
 	icq_ContactSetVis(id->link, atol(who), TRUE);
-	icq_SendNewUser(id->link, atol(who));
 }
 
 static void icq_add_buddies(struct gaim_connection *gc, GList *whos) {
@@ -448,6 +456,8 @@
 	m = g_list_append(m, "Occupied");
 	m = g_list_append(m, "Free For Chat");
 	m = g_list_append(m, "Invisible");
+
+	return m;
 }
 
 static void icq_init(struct prpl *ret) {
--- a/plugins/icq/icq.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icq.h	Sun Jan 28 01:52:27 2001 +0000
@@ -6,22 +6,22 @@
 #include <config.h>
 #endif
 
-#define ICQLIBVER   0x010100
-#define ICQLIBMAJOR 1
-#define ICQLIBMINOR 1
-#define ICQLIBMICRO 0
-
-#include <time.h>
-
 #ifndef _WIN32
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
-#endif
+#else
+#include <winsock.h>
+#endif /* _WIN32 */
+
+#include <time.h>
 
-#ifdef _WIN32
-#include <winsock.h>
-#endif
+/* ICQLIB version defines */
+#define ICQLIBVER   0x010105
+#define ICQLIBMAJOR 1
+#define ICQLIBMINOR 1
+#define ICQLIBMICRO 5
+
 
 #define ICQ_LOG_OFF     0
 #define ICQ_LOG_FATAL   1
@@ -49,12 +49,8 @@
 #define ICQ_NOTIFY_SENT           4
 #define ICQ_NOTIFY_ACK            5
 
-#define ICQ_NOTIFY_CHAT           6
-#define ICQ_NOTIFY_CHATDATA       7
-
-#define ICQ_NOTIFY_FILE           10
-#define ICQ_NOTIFY_FILESESSION    11
-#define ICQ_NOTIFY_FILEDATA       12
+#define ICQ_NOTIFY_CHATSESSION    7
+#define ICQ_NOTIFY_FILESESSION    8
 
 #ifdef __cplusplus
 extern "C" {
@@ -66,7 +62,11 @@
   unsigned short code;
 } icq_ArrayType;
 
+/* dummy forward declarations */
 struct icq_link_private;
+typedef struct icq_TCPLink_s icq_TCPLink;
+typedef struct icq_FileSession_s icq_FileSession;
+typedef struct icq_ChatSession_s icq_ChatSession;
 
 /**
  * The ICQLINK structure represents a single connection to the ICQ  servers.
@@ -164,6 +164,8 @@
        unsigned char hour, unsigned char minute, unsigned char day,
        unsigned char month, unsigned short year, const char *url,
        const char *descr);
+  void (*icq_RecvContactList)(struct icq_link *link, unsigned long uin,
+       int nr, const char **contact_uin, const char **contact_nick);
   void (*icq_RecvWebPager)(struct icq_link *link,unsigned char hour,
        unsigned char minute, unsigned char day, unsigned char month,
        unsigned short year, const char *nick, const char *email,
@@ -211,8 +213,12 @@
   void (*icq_Log)(struct icq_link *link, time_t time, unsigned char level,
        const char *str);
   void (*icq_SrvAck)(struct icq_link *link, unsigned short seq);
-  void (*icq_RequestNotify)(struct icq_link *link, unsigned long id, int result,
-       unsigned int length, void *data);
+  void (*icq_RequestNotify)(struct icq_link *link, unsigned long id, 
+       int type, int arg, void *data);
+  void (*icq_FileNotify)(icq_FileSession *session, int type, int arg,
+       void *data);
+  void (*icq_ChatNotify)(icq_ChatSession *session, int type, int arg,
+       void *data);
   void (*icq_NewUIN)(struct icq_link *link, unsigned long uin);
   void (*icq_SetTimeout)(struct icq_link *link, long interval);
   void (*icq_MetaUserFound)(struct icq_link *link, unsigned short seq2,
@@ -269,9 +275,6 @@
   const char *nick, unsigned char useTCP);
 void icq_ICQLINKDelete(ICQLINK *link);
   
-void icq_Init(ICQLINK *link, unsigned long uin, const char *password,
-     const char *nick, unsigned char useTCP);
-void icq_Done(ICQLINK *link);
 int icq_Connect(ICQLINK *link, const char *hostname, int port);
 void icq_Disconnect(ICQLINK *link);
 int icq_GetSok(ICQLINK *link);
@@ -285,6 +288,7 @@
 void icq_Logout(ICQLINK *link);
 void icq_SendContactList(ICQLINK *link);
 void icq_SendVisibleList(ICQLINK *link);
+void icq_SendInvisibleList(ICQLINK *link);
 void icq_SendNewUser(ICQLINK * link, unsigned long uin);
 unsigned long icq_SendMessage(ICQLINK *link, unsigned long uin,
      const char *text, unsigned char thruSrv);
@@ -330,6 +334,7 @@
 void icq_ContactRemove(ICQLINK *link, unsigned long cuin);
 void icq_ContactClear(ICQLINK *link );
 void icq_ContactSetVis(ICQLINK *link, unsigned long cuin, unsigned char vu);
+void icq_ContactSetInvis(ICQLINK *link, unsigned long cuin, unsigned char vu);
 
 /*** TCP ***/
 void icq_TCPMain(ICQLINK *link);
@@ -344,10 +349,6 @@
      const char *message);
 void icq_AcceptChatRequest(ICQLINK *link, unsigned long uin, unsigned long seq);
 
-void icq_TCPSendChatData(ICQLINK *link, unsigned long uin, const char *data);
-void icq_TCPSendChatData_n(ICQLINK *link, unsigned long uin, const char *data, int len);
-void icq_TCPCloseChat(ICQLINK *link, unsigned long uin);
-
 void icq_CancelChatRequest(ICQLINK *link, unsigned long uin, 
      unsigned long sequence);
 void icq_RefuseChatRequest(ICQLINK *link, unsigned long uin,
@@ -355,6 +356,181 @@
 
 /*** TCP ***/
 
+/** \defgroup ChatSession Chat Session Documentation
+ * icqlib's 'Chat Session' abstraction represents ICQ's 'chat' function
+ * between two participants.  Multi-party chat is not yet supported.
+ *
+ * An icq_ChatSession is instantiated when a 'Chat Request' event is
+ * accepted.  Upon receipt of a 'Chat Accept' event or a call to
+ * icq_AcceptChatRequest, icqlib will create a new chat session and pass the
+ * new chat session pointer back to the library client through the
+ * icq_RequestNotify / ICQ_NOTIFY_CHATSESSION callback.  This pointer should
+ * be stored by the library client, as multiple chat sessions may be in
+ * progress at any given time.  The icq_ChatSession pointer is used as a key
+ * for all future communication between the library and the library client to
+ * indicate which icq_ChatSession is currently being dealt with.
+ *
+ * icqlib communicates chat session events through use of the icq_ChatNotify
+ * callback, such as the CHAT_NOTIFY_DATA event.  The library client
+ * can perform operations on a chat session by use of the icq_ChatSession*
+ * functions, such as sending data to the remote uin by using the
+ * icq_ChatSessionSendData function.  
+ *
+ * A new chat session must first undergo an initialization sequence before is
+ * ready to transmit and receive data.  As this initialization is in progress
+ * the chat session will transition through various statuses depending on
+ * whether icqlib sent the accept event or it received the accept event.
+ * Each change in chat session status will be reported to the library
+ * client through use of the icq_ChatNotify callback, with a @type parameter
+ * of CHAT_NOTIFY_STATUS and an @a arg parameter of the status value.
+ *
+ * Once the chat session initialization is complete, both sides will enter
+ * the CHAT_STATUS_READY state, indicating that the chat session is
+ * ready to send and receive data.  Received data is reported through the
+ * icq_ChatNotify callback, with a @type of CHAT_NOTIFY_DATA.  The library
+ * client can send data using icq_ChatSessionSendData or
+ * icq_ChatSessionSendData_n.
+ *
+ * Chat sessions may be terminated at any time, by either side.  The library
+ * client may terminate a chat session by using icq_ChatSessionClose, or 
+ * the remote uin may terminate a chat session.  In either instance, a
+ * CHAT_STATUS_CLOSE event will be reported through the icq_ChatNotify
+ * callback.  Once this callback is complete (e.g. your application's
+ * callback handler returns), the icq_ChatSession will be deleted by icqlib 
+ * and the session pointer becomes invalid.
+ */
+
+/** @name Type Constants
+ * @ingroup ChatSession
+ * These values are used as the @a type parameter in the icq_ChatNotify
+ * callback to indicate the type of chat session event that has occured.
+ * The remaining @a arg and @a data parameters passed by the callback
+ * are specific to each event;  see the documentation for each type
+ * constant.
+ */
+
+/*@{*/
+
+/** Status has changed.
+ * @param arg new session status - one of the CHAT_STATUS_* defines
+ * @param data unused.
+ * @ingroup ChatSession
+ */
+#define CHAT_NOTIFY_STATUS       1
+
+/** Data has been received from a chat participant.
+ * @param arg length of data received
+ * @param data pointer to buffer containing received data
+ * @ingroup ChatSession
+ */
+#define CHAT_NOTIFY_DATA         2
+
+/** Session has been closed, either automatically by icqlib or
+ * explicitly by a call to icq_ChatSessionClose.
+ * @param arg unused
+ * @param data unused
+ * @ingroup ChatSession
+ */ 
+#define CHAT_NOTIFY_CLOSE        3
+
+/*@}*/
+
+/** @name Status Constants
+ * @ingroup ChatSession
+ * These constants are used as the @a arg parameter during in the
+ * icq_ChatNotify/CHAT_NOTIFY_STATUS callback to indicate the
+ * new status of the chat session.
+ */
+
+/*@{*/
+
+/** icqlib is listening for a chat connection from the remote uin.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_LISTENING    1
+
+/** A connection has been established with the remote uin.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_CONNECTED    3
+
+/** icqlib is currently waiting for the remote uin to send the chat
+ * initialization packet which contains the remote uin's chat handle.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_WAIT_NAME    4
+
+/** icqlib is currently waiting for the remote uin to send the chat
+ * initialization packet which contains the remote uin's font information.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_WAIT_FONT    6
+
+/** A connection to the chat session port of the remote uin is in 
+ * progress.
+ * @ingroup ChatSession
+ */
+/* chat session statuses - request sender */
+#define CHAT_STATUS_CONNECTING   2
+
+/** icqlib is currently waiting for the remote uin to send the chat
+ * initialization packet which contains the remote uin's chat handle
+ * and font information.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_WAIT_ALLINFO 5
+
+/** Chat session initialization has completed successfully.  The session
+ * is now fully established - both sides can begin to send data and
+ * should be prepared to accept data.
+ * @ingroup ChatSession
+ */
+#define CHAT_STATUS_READY        7
+
+/*@}*/
+
+/** Chat Session state structure.  This structure is used internally by
+ * icqlib to maintain state information about each chat session.  All
+ * members should be considered read-only!  Use the appropriate 
+ * icq_ChatSession* function to change the state of a chat session,
+ * results are undefined if your application attempts to manipulate this
+ * structure itself.
+ */
+struct icq_ChatSession_s {        
+
+  /** For internal icqlib use only. */
+  unsigned long id;
+  
+  /** Current status of the chat session.  See 'Status Constants' group. */
+  int status;
+  
+  /** ICQLINK that spawned this chat session. */
+  ICQLINK *icqlink;
+  
+  /** For internal icqlib use only. */
+  icq_TCPLink *tcplink;
+
+  /** Remote uin number. */
+  unsigned long remote_uin;
+  
+  /** Remote uin's chat handle. */
+  char remote_handle[64];
+
+};
+
+void icq_ChatSessionClose(icq_ChatSession *session);
+void icq_ChatSessionSendData(icq_ChatSession *session, const char *data);
+void icq_ChatSessionSendData_n(icq_ChatSession *session, const char *data,
+  int length);
+
+
+/* FileNotify constants */
+#define FILE_NOTIFY_DATAPACKET   1
+#define FILE_NOTIFY_STATUS       2
+#define FILE_NOTIFY_CLOSE        3
+#define FILE_NOTIFY_NEW_SPEED    4
+#define FILE_NOTIFY_STOP_FILE    5
+
 /* file session statuses- request receiver */
 #define FILE_STATUS_LISTENING    1
 #define FILE_STATUS_CONNECTED    3
@@ -362,24 +538,20 @@
 /* file session statuses- request sender */
 #define FILE_STATUS_CONNECTING   2
 
-#define FILE_STATUS_INITIALIZED  4
+#define FILE_STATUS_INITIALIZING 4
+
 #define FILE_STATUS_NEXT_FILE    5
-#define FILE_STATUS_STOP_FILE    6
-#define FILE_STATUS_NEW_SPEED    7
 
 /* once negotiation is complete, file session enters proper state */
-#define FILE_STATUS_READY        8
-#define FILE_STATUS_SENDING      8
-#define FILE_STATUS_RECEIVING    9  
+#define FILE_STATUS_SENDING      6
+#define FILE_STATUS_RECEIVING    7  
 
-struct icq_TCPLink_s;
-
-typedef struct icq_FileSession_s {
+struct icq_FileSession_s {
 
   unsigned long id;
   int status;
   ICQLINK *icqlink;
-  struct icq_TCPLink_s *tcplink;
+  icq_TCPLink *tcplink;
 
   int direction;
 
@@ -400,7 +572,7 @@
 
   int current_speed;
 
-} icq_FileSession;
+};
           
 icq_FileSession *icq_AcceptFileRequest(ICQLINK *link, unsigned long uin,
                  unsigned long sequence);
@@ -416,6 +588,16 @@
 void icq_FileSessionSetWorkingDir(icq_FileSession *p, const char *dir);
 void icq_FileSessionSetFiles(icq_FileSession *p, char **files);
 
+/* Socket Manager */
+
+#define ICQ_SOCKET_READ  0
+#define ICQ_SOCKET_WRITE 1
+#define ICQ_SOCKET_MAX   2
+
+extern void (*icq_SocketNotify)(int socket, int type, int status);
+
+void icq_HandleReadySocket(int socket, int type);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
--- a/plugins/icq/icqbyteorder.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqbyteorder.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,5 +1,5 @@
 /*
- * $Id: icqbyteorder.c 1319 2000-12-19 10:08:29Z warmenhoven $
+ * $Id: icqbyteorder.c 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * This header defines macros to handle ICQ protocol byte order conversion.
  *
--- a/plugins/icq/icqbyteorder.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqbyteorder.h	Sun Jan 28 01:52:27 2001 +0000
@@ -1,5 +1,5 @@
 /*
- * $Id: icqbyteorder.h 1385 2001-01-04 22:46:19Z warmenhoven $
+ * $Id: icqbyteorder.h 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * This header defines macros to handle ICQ protocol byte order conversion.
  *
@@ -54,6 +54,11 @@
 # include <machine/endian.h>
 #endif
 
+/* hpux way */
+#ifdef hpux
+#include <arpa/nameser.h>
+#endif
+
 /*
  * Kind of portable way. this common header, at least I found it on BSD and Solaris.
  * On Solaris this is only place where endiannes is defined.
@@ -62,12 +67,6 @@
 # include <arpa/nameser_compat.h>
 #endif
 
-/* put in by EWarmenhoven; jlsantiago told me what to do. I should send this on to the
- * icqlib guys. */
-#ifdef hpux
-#include <arpa/nameser.h>
-#endif
-
 /*
  * I am really trying to use builtin optimized byte swap routines.
  * they are highly optimised on some platforms.
--- a/plugins/icq/icqevent.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqevent.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,11 +1,11 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
 /*
- * $Id: icqevent.c 1319 2000-12-19 10:08:29Z warmenhoven $
+ * $Id: icqevent.c 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * $Log$
- * Revision 1.1  2000/12/19 10:08:29  warmenhoven
- * Yay, new icqlib
+ * Revision 1.2  2001/01/28 01:52:27  warmenhoven
+ * icqlib 1.1.5
  *
  * Revision 1.2  2000/06/15 18:54:09  bills
  * added time attribute and handleEvent function pointer to icq_Event,
--- a/plugins/icq/icqevent.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqevent.h	Sun Jan 28 01:52:27 2001 +0000
@@ -1,11 +1,11 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
 /*
- * $Id: icqevent.h 1319 2000-12-19 10:08:29Z warmenhoven $
+ * $Id: icqevent.h 1442 2001-01-28 01:52:27Z warmenhoven $
  *
  * $Log$
- * Revision 1.1  2000/12/19 10:08:29  warmenhoven
- * Yay, new icqlib
+ * Revision 1.2  2001/01/28 01:52:27  warmenhoven
+ * icqlib 1.1.5
  *
  * Revision 1.2  2000/06/15 18:54:09  bills
  * added time attribute and handleEvent function pointer to icq_Event,
--- a/plugins/icq/icqlib.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqlib.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,18 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: icqlib.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: icqlib.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.49  2001/01/17 01:29:17  bills
+Rework chat and file session interfaces; implement socket notifications.
+
+Revision 1.48  2001/01/15 06:20:24  denis
+Cleanup.
+
+Revision 1.47  2000/12/19 21:29:51  bills
+actually return the link in icq_ICQLINKNew
 
 Revision 1.46  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -17,121 +26,7 @@
 
 Revision 1.43  2000/07/09 22:05:11  bills
 removed unnecessary functions
-
-Revision 1.42  2000/07/09 18:28:07  denis
-Initial memset() in icq_Init() replaced by callback's clearance.
-
-Revision 1.41  2000/06/15 01:50:39  bills
-removed *Seq functions
-
-Revision 1.40  2000/05/10 19:06:59  denis
-UDP outgoing packet queue was implemented.
-
-Revision 1.39  2000/05/03 18:12:36  denis
-Unfinished UDP queue was commented out.
-
-Revision 1.38  2000/04/10 16:36:04  denis
-Some more Win32 compatibility from Guillaume Rosanis <grs@mail.com>
-
-Revision 1.37  2000/04/06 16:38:04  denis
-icq_*Send*Seq() functions with specified sequence number were added.
-
-Revision 1.36  2000/04/05 14:37:02  denis
-Applied patch from "Guillaume R." <grs@mail.com> for basic Win32
-compatibility.
-
-Revision 1.35  2000/01/16 03:59:10  bills
-reworked list code so list_nodes don't need to be inside item structures,
-removed strlist code and replaced with generic list calls
-
-Revision 1.34  1999/12/27 16:06:32  bills
-cleanups
-
-Revision 1.33  1999/10/03 21:35:55  tim
-Fixed "url" and "descr" parameters order when sending a URL via TCP.
-
-Revision 1.32  1999/09/29 16:49:43  denis
-Host/network/icq byteorder systemized.
-icq_Init() cleaned up.
-
-Revision 1.31  1999/07/18 20:15:55  bills
-changed to use new byte-order functions & contact list functions
-
-Revision 1.30  1999/07/16 12:27:06  denis
-Other global variables moved to ICQLINK structure.
-Initialization of random number generator added in icq_Init()
-Cleaned up.
-
-Revision 1.29  1999/07/12 15:13:31  cproch
-- added definition of ICQLINK to hold session-specific global variabled
-  applications which have more than one connection are now possible
-- changed nearly every function defintion to support ICQLINK parameter
-
-Revision 1.28  1999/07/03 02:26:02  bills
-added new code to support thruSrv arg to SendMessage and SendURL
-
-Revision 1.27  1999/04/17 19:21:37  bills
-modified Send* Functions to return DWORD instead of WORD
-
-Revision 1.26  1999/04/14 14:48:18  denis
-Switched from icq_Log callback to icq_Fmt function.
-Cleanups for "strict" compiling (-ansi -pedantic)
-
-Revision 1.25  1999/04/05 13:14:57  denis
-Send messages and URLs to 'not in list' users fixed.
-
-Revision 1.24  1999/03/31 01:43:40  bills
-added TCP support to SendURL
-
-Revision 1.23  1999/03/30 22:47:44  lord
-list of countries now sorted.
-
-Revision 1.22  1999/03/28 03:18:22  bills
-enable tcp messaging in icq_SendMessage, uncommented icq_OurPort and
-icq_OurIp and fixed function names so icqlib compiles
-
-Revision 1.21  1999/03/25 22:16:43  bills
-added #include "util.h"
-
-Revision 1.20  1999/03/24 11:37:36  denis
-Underscored files with TCP stuff renamed.
-TCP stuff cleaned up
-Function names changed to corresponding names.
-icqlib.c splitted to many small files by subject.
-C++ comments changed to ANSI C comments.
-
-Revision 1.19  1999/03/22 20:51:28  bills
-added code in icq_HandleUserOnline to set/clear new struct entries in
-icq_ContactItem; added cleanup code in icq_HandleUserOffline for same
-
-Revision 1.18  1999/03/09 13:14:05  denis
-Cyrillic recoding removed from URLs
-
-Revision 1.17  1999/03/05 13:57:54  denis
-Some cosmetic changes...
-
-Revision 1.16  1998/12/08 16:00:59  denis
-Cleaned up a little before releasing
-
-Revision 1.15  1998/11/25 19:18:16  denis
-Added close icq_ProxySok in icq_Disconnect
-
-Revision 1.14  1998/11/25 09:48:49  denis
-icq_GetProxySok and icq_HandleProxyResponse methods added
-Connection terminated support added
-
-Revision 1.13  1998/11/19 12:22:48  denis
-SOCKS support cleaned a little
-icq_RecvUrl renamed to icq_RecvURL
-icq_ProxyAuth added for Username/Password Authentication
-URL/Description order inverted
-icq_Quit splitted to icq_Logout and icq_Disconnect
-icq_ProxyName and icq_ProxyPass range checking added
-
-Revision 1.12  1998/11/18 16:21:29  denis
-Fixed SOCKS5 proxy support
-
- */
+*/
 
 #include "icqlib.h"
 
@@ -163,10 +58,13 @@
 #include "udp.h"
 #include "tcp.h"
 #include "queue.h"
+#include "socketmanager.h"
 
 int icq_Russian = FALSE;
 BYTE icq_LogLevel = 0;
 
+void (*icq_SocketNotify)(int socket, int type, int status);
+
 DWORD icq_SendMessage(ICQLINK *link, DWORD uin, const char *text, BYTE thruSrv)
 {
   if(thruSrv==ICQ_SEND_THRUSERVER)
@@ -216,14 +114,15 @@
 }
 
 ICQLINK *icq_ICQLINKNew(DWORD uin, const char *password, const char *nick,
-  unsigned char useTCP)
+                        unsigned char useTCP)
 {
-  ICQLINK *link = (ICQLINK *)malloc(sizeof(ICQLINK));
-  link->d = (ICQLINK_private *)malloc(sizeof(ICQLINK_private));
+  ICQLINK *link = (ICQLINK*)malloc(sizeof(ICQLINK));
+  link->d = (ICQLINK_private*)malloc(sizeof(ICQLINK_private));
 
   srand(time(0L));
-
-/*   memset(link, 0, sizeof(ICQLINK)); */
+  /* initialize icq_SocketList on first call */
+  if (!icq_SocketList)
+    icq_SocketList = list_new();
 
   /* Initialize all callbacks */
   link->icq_Logged = 0L;
@@ -276,6 +175,7 @@
   link->d->icq_UDPSession = 0;
   icq_UDPQueueNew(link);
 
+  /* TCP stuff */
   icq_TCPInit(link);
   link->icq_UseTCP = useTCP;
 
@@ -316,17 +216,7 @@
 *******************************/
 void icq_Main(ICQLINK *link)
 {
-  struct timeval tv;
-  fd_set readfds;
-
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-  FD_ZERO(&readfds);
-  FD_SET(link->icq_UDPSok, &readfds);
-  select(link->icq_UDPSok+1, &readfds, 0L, 0L, &tv);
-  if(FD_ISSET(link->icq_UDPSok, &readfds))
-    icq_HandleServerResponse(link);
-  icq_TCPMain(link);
+  icq_SocketPoll();
 }
 
 /**********************************
@@ -343,7 +233,9 @@
   struct sockaddr_in sin, prsin;  /* used to store inet addr stuff */
   struct hostent *host_struct; /* used in DNS llokup */
 
-  link->icq_UDPSok = socket(AF_INET, SOCK_DGRAM, 0);/* create the unconnected socket*/
+  /* create the unconnected socket*/
+  link->icq_UDPSok = icq_SocketNew(AF_INET, SOCK_DGRAM, 0);
+
   if(link->icq_UDPSok == -1)
   {
     icq_FmtLog(link, ICQ_LOG_FATAL, "Socket creation failed\n");
@@ -356,6 +248,8 @@
   if(bind(link->icq_UDPSok, (struct sockaddr*)&sin, sizeof(struct sockaddr))<0)
   {
     icq_FmtLog(link, ICQ_LOG_FATAL, "Can't bind socket to free port\n");
+    icq_SocketDelete(link->icq_UDPSok);
+    link->icq_UDPSok = -1;
     return -1;
   }
   length = sizeof(sin);
@@ -378,7 +272,10 @@
     link->icq_ProxyIP = ntohl(prsin.sin_addr.s_addr);
     prsin.sin_family = AF_INET; /* we're using the inet not appletalk*/
     prsin.sin_port = htons(link->icq_ProxyPort); /* port */
-    link->icq_ProxySok = socket(AF_INET, SOCK_STREAM, 0);/* create the unconnected socket*/
+
+    /* create the unconnected socket*/
+    link->icq_ProxySok = icq_SocketNew(AF_INET, SOCK_STREAM, 0);
+
     if(link->icq_ProxySok == -1)
     {
       icq_FmtLog(link, ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
@@ -409,11 +306,7 @@
       if(res != 2 || buf[0] != 5 || buf[1] != 2) /* username/password authentication*/
       {
         icq_FmtLog(link, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
-#ifdef _WIN32
-        closesocket(link->icq_ProxySok);
-#else
-        close(link->icq_ProxySok);
-#endif
+        icq_SocketDelete(link->icq_ProxySok);
         return -1;
       }
       buf[0] = 1; /* version of subnegotiation */
@@ -431,11 +324,7 @@
       if(res != 2 || buf[0] != 1 || buf[1] != 0)
       {
         icq_FmtLog(link, ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
-#ifdef _WIN32
-        closesocket(link->icq_ProxySok);
-#else
-        close(link->icq_ProxySok);
-#endif
+        icq_SocketDelete(link->icq_ProxySok);
         return -1;
       }
     }
@@ -444,11 +333,7 @@
       if(res != 2 || buf[0] != 5 || buf[1] != 0) /* no authentication required */
       {
         icq_FmtLog(link, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
-#ifdef _WIN32
-        closesocket(link->icq_ProxySok);
-#else
-        close(link->icq_ProxySok);
-#endif
+        icq_SocketDelete(link->icq_ProxySok);
         return -1;
       }
     }
@@ -501,11 +386,8 @@
           icq_FmtLog(link, ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
           break;
       }
-#ifdef _WIN32
-      closesocket(link->icq_ProxySok);
-#else
-      close(link->icq_ProxySok);
-#endif
+      icq_SocketDelete(link->icq_ProxySok);
+      link->icq_ProxySok = -1;
       return -1;
     }
   }
@@ -518,11 +400,7 @@
       icq_FmtLog(link, ICQ_LOG_FATAL, "Can't find hostname: %s\n", hostname);
       if(link->icq_UseProxy)
       {
-#ifdef _WIN32
-        closesocket(link->icq_ProxySok);
-#else
-        close(link->icq_ProxySok);
-#endif
+        icq_SocketDelete(link->icq_ProxySok);
       }
       return -1;
     }
@@ -546,11 +424,7 @@
     icq_FmtLog(link, ICQ_LOG_FATAL, "Connection refused\n");
     if(link->icq_UseProxy)
     {
-#ifdef _WIN32
-      closesocket(link->icq_ProxySok);
-#else
-      close(link->icq_ProxySok);
-#endif
+      icq_SocketDelete(link->icq_ProxySok);
     }
     return -1;
   }
@@ -558,23 +432,22 @@
   getsockname(link->icq_UDPSok, (struct sockaddr*)&sin, &length);
   link->icq_OurIP = ntohl(sin.sin_addr.s_addr);
   link->icq_OurPort = ntohs(sin.sin_port);
+
+  /* sockets are ready to receive data - install handlers */
+  icq_SocketSetHandler(link->icq_UDPSok, ICQ_SOCKET_READ,
+    icq_HandleServerResponse, link);
+  if (link->icq_UseProxy)
+    icq_SocketSetHandler(link->icq_ProxySok, ICQ_SOCKET_READ,
+      icq_HandleProxyResponse, link);
   return link->icq_UDPSok;
 }
 
 void icq_Disconnect(ICQLINK *link)
 {
-#ifdef _WIN32
-  closesocket(link->icq_UDPSok);
-#else
-  close(link->icq_UDPSok);
-#endif
+  icq_SocketDelete(link->icq_UDPSok);
   if(link->icq_UseProxy)
   {
-#ifdef _WIN32
-    closesocket(link->icq_ProxySok);
-#else
-    close(link->icq_ProxySok);
-#endif
+    icq_SocketDelete(link->icq_ProxySok);
   }
   icq_UDPQueueFree(link);
 }
--- a/plugins/icq/icqlib.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqlib.h	Sun Jan 28 01:52:27 2001 +0000
@@ -62,10 +62,10 @@
   void *icq_TCPLinks;
   void *icq_ChatSessions;
   void *icq_FileSessions;
-  int TCP_maxfd;
-  fd_set TCP_readfds;
-  fd_set TCP_writefds;
 
 } ICQLINK_private;
 
+#define invoke_callback(plink, callback) \
+   if (plink->callback) (*(plink->callback))
+
 #endif /* _ICQLIB_H_ */
--- a/plugins/icq/icqpacket.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/icqpacket.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: icqpacket.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: icqpacket.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
 
 Revision 1.10  2000/07/07 15:26:35  denis
 "icq_Packet data overflow" log message temporarily commented out.
--- a/plugins/icq/list.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/list.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: list.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: list.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
 
 Revision 1.14  2000/07/10 01:44:20  bills
 i really don't learn - removed LIST_TRACE define
--- a/plugins/icq/list.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/list.h	Sun Jan 28 01:52:27 2001 +0000
@@ -14,19 +14,22 @@
 #define list_dequeue(plist) \
   list_remove_node(plist, plist->head)
 
-typedef struct list_node_s
+typedef struct list_node_s list_node;
+typedef struct list_s list;
+
+struct list_node_s
 {
-  struct list_node_s *next;
-  struct list_node_s *previous;
+  list_node *next;
+  list_node *previous;
   void *item;
-} list_node;
+};
 
-typedef struct list_s
+struct list_s
 {
   list_node *head;
   list_node *tail;
   int count;
-} list;
+};
 
 list *list_new(void);
 void list_delete(list *plist, void (*item_free_f)(void *));
--- a/plugins/icq/proxy.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/proxy.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: proxy.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: proxy.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.9  2001/01/17 01:19:49  bills
+cleanup
 
 Revision 1.8  2000/05/10 18:51:23  denis
 icq_Disconnect() now called before icq_Disconnected callback to
@@ -42,9 +45,7 @@
 
 #ifndef _WIN32
 #include <unistd.h>
-#endif
-
-#ifdef _WIN32
+#else
 #include <winsock.h>
 #endif
 
--- a/plugins/icq/queue.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/queue.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: queue.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: queue.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.3  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.4  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
 
 Revision 1.12  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/icq/socketmanager.c	Sun Jan 28 01:52:27 2001 +0000
@@ -0,0 +1,234 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*
+  $Id: socketmanager.c 1442 2001-01-28 01:52:27Z warmenhoven $
+*/
+
+#include "socketmanager.h"
+
+/**
+ * The icqlib socket manager is a simple socket abstraction layer, which
+ * supports opening and closing sockets as well as installing handler
+ * functions for read ready and write ready events.  Its purpose is to
+ * both unify socket handling in icqlib and expose icqlib's socket
+ * requirements so the library client can assist with socket housekeeping.
+ *
+ * Library clients have two options to support icqlib:
+ *
+ * 1. Periodically call icq_Main.  This will handle all select logic
+ * internally.  Advantage is implementation ease, disadvantage is wasted 
+ * CPU cycles because of polling and poor TCP file transfer performance.
+ * 
+ * 2. Install a icq_SocketNotify callback, perform your own socket
+ * management, and notify icqlib using the icq_SocketReady method when
+ * a socket is ready for reading or writing.  Advantage is efficiency,
+ * disadvantage is extra code.
+ *
+ */
+
+/* need to track:
+ *   socket wants read notification
+ *   socket no longer wants read notification
+ *   socket wants write notification
+ *   socket no longer wants write notification
+ */
+
+#include <sys/types.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#else
+#include <winsock.h>
+#endif
+
+list *icq_SocketList = NULL;
+fd_set icq_FdSets[ICQ_SOCKET_MAX];
+int icq_MaxSocket;
+
+/**
+ * Creates a new socket using the operating system's socket creation
+ * facitily.
+ */
+int icq_SocketNew(int domain, int type, int protocol)
+{
+  int s = socket(domain, type, protocol);
+
+  icq_SocketAlloc(s);
+
+  return s;
+}
+
+
+/**
+ * Creates a new socket by accepting a connection from a listening
+ * socket.
+ */
+int icq_SocketAccept(int listens, struct sockaddr *addr, socklen_t *addrlen)
+{
+  int s = accept(listens, addr, addrlen);
+
+  icq_SocketAlloc(s);
+
+  return s;
+}
+
+/**
+ * Creates a new icq_Socket structure, and appends it to the 
+ * socketmanager's global socket list.
+ */
+void icq_SocketAlloc(int s)
+{
+  if (s != -1)
+  {
+    icq_Socket *psocket = (icq_Socket *)malloc(sizeof(icq_Socket));
+    int i;
+    psocket->socket = s;
+
+    for (i=0; i<ICQ_SOCKET_MAX; i++)
+      psocket->handlers[i] = NULL;
+
+    list_enqueue(icq_SocketList, psocket);
+  }
+}  
+
+/**
+ * Closes a socket.  This function will notify the library client
+ * through the icq_SocketNotify callback if the socket had an installed
+ * read or write handler.
+ */
+int icq_SocketDelete(int socket)
+{
+#ifdef _WIN32
+  int result = closesocket(socket);
+#else
+  int result = close(socket);
+#endif
+
+  if (result != -1)
+  {
+    icq_Socket *s = icq_FindSocket(socket);
+    int i;
+
+    /* uninstall all handlers - this will take care of notifing library
+     * client */
+    for (i=0; i<ICQ_SOCKET_MAX; i++)
+    {
+      if (s->handlers[i])
+        icq_SocketSetHandler(s->socket, i, NULL, NULL);
+    }
+
+    list_remove(icq_SocketList, s);
+    free(s);
+  }
+
+  return result;
+}
+
+/**
+ * Installs a socket event handler.  The handler will be called when
+ * the socket is ready for reading or writing, depending on the type
+ * which should be either ICQ_SOCKET_READ or ICQ_SOCKET_WRITE.  In 
+ * addition, user data can be passed to the callback function through
+ * the data member.
+ */
+void icq_SocketSetHandler(int socket, int type, icq_SocketHandler handler, 
+  void *data)
+{
+  icq_Socket *s = icq_FindSocket(socket);
+  if (s)
+  {
+    s->data[type] = data;
+    s->handlers[type] = handler;
+    if (icq_SocketNotify)
+      (*icq_SocketNotify)(socket, type, handler ? 1 : 0);
+    icq_SocketBuildFdSets();
+  }
+}
+
+/**
+ * Handles a socket ready event by calling the installed callback 
+ * function, if any.
+ */
+void icq_SocketReady(icq_Socket *s, int type)
+{
+  if (s && s->handlers[type])
+  {
+    (*s->handlers[type])(s->data[type]);
+  }
+}
+
+void icq_HandleReadySocket(int socket, int type)
+{
+  icq_SocketReady(icq_FindSocket(socket), type);
+}
+  
+int _icq_SocketBuildFdSets(void *p, va_list data)
+{
+  icq_Socket *s = p;
+  int i;
+
+  for (i=0; i<ICQ_SOCKET_MAX; i++)
+    if (s->handlers[i])
+      FD_SET(s->socket, &icq_FdSets[i]);
+
+  if (s->socket > icq_MaxSocket)
+    icq_MaxSocket = s->socket;
+
+  return 0; /* traverse entire list */
+}
+
+void icq_SocketBuildFdSets()
+{
+  int i;
+  
+  /* clear fdsets */
+  for (i=0; i<ICQ_SOCKET_MAX; i++)
+    FD_ZERO(&icq_FdSets[i]);
+
+  icq_MaxSocket = 0;
+  
+  /* build fd lists for open sockets */
+  (void)list_traverse(icq_SocketList, _icq_SocketBuildFdSets);
+}
+
+int _icq_SocketHandleReady(void *p, va_list data)
+{
+  icq_Socket *s = p;
+  int i;
+
+  for (i=0; i<ICQ_SOCKET_MAX; i++)
+    if (FD_ISSET(s->socket, &icq_FdSets[i]))
+      icq_SocketReady(s, i);
+
+  return 0; /* traverse entire list */
+}
+      
+void icq_SocketPoll()
+{
+  struct timeval tv;
+  int max_socket = 0;
+  int i;
+
+  tv.tv_sec = 0; tv.tv_usec = 0;
+  
+  /* determine which sockets require maintenance */
+  select(icq_MaxSocket+1, &icq_FdSets[0], &icq_FdSets[1], NULL, &tv);
+
+  /* handle ready sockets */
+  (void)list_traverse(icq_SocketList, _icq_SocketHandleReady);
+}
+
+int _icq_FindSocket(void *p, va_list data)
+{
+  int socket = va_arg(data, int);
+  return (((icq_Socket *)p)->socket == socket);
+}
+
+icq_Socket *icq_FindSocket(int socket)
+{
+  return list_traverse(icq_SocketList, _icq_FindSocket, socket);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/icq/socketmanager.h	Sun Jan 28 01:52:27 2001 +0000
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#ifndef _SOCKETMANAGER_H
+#define _SOCKETMANAGER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "icq.h"
+#include "list.h"
+
+typedef struct icq_Socket_s icq_Socket;
+typedef void (*icq_SocketHandler)(void *data);
+
+struct icq_Socket_s
+{
+  int socket;
+
+  icq_SocketHandler handlers[ICQ_SOCKET_MAX];
+  void *data[ICQ_SOCKET_MAX];
+};
+
+int icq_SocketNew(int domain, int type, int protocol);
+int icq_SocketAccept(int listens, struct sockaddr *addr, socklen_t *addrlen);
+void icq_SocketAlloc(int s);
+int icq_SocketDelete(int socket);
+void icq_SocketSetHandler(int socket, int type, icq_SocketHandler handler,
+  void *data);
+void icq_SocketReady(icq_Socket *s, int type);
+void icq_SocketBuildFdSets(void);
+void icq_SocketPoll();
+icq_Socket *icq_FindSocket(int socket);
+
+extern list *icq_SocketList;
+
+#endif /* _SOCKETMANAGER_H */
--- a/plugins/icq/stdpackets.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/stdpackets.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,13 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: stdpackets.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: stdpackets.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.12  2001/01/24 05:11:14  bills
+applied patch from Robin Ericsson <lobbin@localhost.nu> which implements
+receiving contact lists.  See new icq_RecvContactList callback.
 
 Revision 1.11  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -422,6 +426,18 @@
    return p;
 }
 
+icq_Packet *icq_TCPCreateContactListAck(icq_TCPLink *plink, const unsigned char *message)
+{
+  icq_Packet *p=icq_TCPCreateStdPacket(
+    plink,
+    ICQ_TCP_ACK,
+    ICQ_TCP_MSG_CONTACTLIST,
+    message,
+    0, /* status */
+    ICQ_TCP_MSG_ACK);
+
+  return p;
+}
 
 icq_Packet *icq_TCPCreateFile00Packet(DWORD num_files, DWORD total_bytes,
   DWORD speed, const char *name)
--- a/plugins/icq/stdpackets.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/stdpackets.h	Sun Jan 28 01:52:27 2001 +0000
@@ -20,6 +20,7 @@
 #define ICQ_TCP_MSG_CHAT         0x0002
 #define ICQ_TCP_MSG_FILE         0x0003
 #define ICQ_TCP_MSG_URL          0x0004
+#define ICQ_TCP_MSG_CONTACTLIST  0x0013
 #define ICQ_TCP_MSG_READAWAY     0x03E8
 #define ICQ_TCP_MSG_READOCCUPIED 0x03E9
 #define ICQ_TCP_MSG_READNA       0x03EA
@@ -70,6 +71,7 @@
 
 icq_Packet *icq_TCPCreateMessageAck(icq_TCPLink *plink, const unsigned char *message);
 icq_Packet *icq_TCPCreateURLAck(icq_TCPLink *plink, const unsigned char *message);
+icq_Packet *icq_TCPCreateContactListAck(icq_TCPLink *plink, const unsigned char *message);
 icq_Packet *icq_TCPCreateWebPagerAck(icq_TCPLink *plink, const unsigned char *message);
 icq_Packet *icq_TCPCreateChatReqAck(icq_TCPLink *plink, WORD port);
 icq_Packet *icq_TCPCreateChatReqCancel(icq_TCPLink *plink, WORD port);
--- a/plugins/icq/tcp.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcp.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: tcp.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: tcp.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.38  2001/01/17 01:29:17  bills
+Rework chat and file session interfaces; implement socket notifications.
 
 Revision 1.37  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -126,10 +129,6 @@
 
 #include <stdlib.h>
 
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
 #include <fcntl.h>
 #include <stdarg.h>
 #include <errno.h>
@@ -140,14 +139,12 @@
 #include <winsock.h>
 #else
 #include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
 #endif
 
 #include <sys/stat.h>
 
-#ifndef _WIN32
-#include <sys/time.h>
-#endif
-
 #include "icqtypes.h"
 #include "icqlib.h"
 
@@ -195,53 +192,7 @@
   list_delete(link->d->icq_FileSessions, icq_FileSessionDelete);
 }
 
-/* helper function for icq_TCPMain */
-int _generate_fds(void *p, va_list data)
-{
-  icq_TCPLink *plink=(icq_TCPLink *)p;
-  ICQLINK *icqlink = plink->icqlink;
-
-  (void)data;
-
-  if(plink->socket>-1)
-  {
-    int socket=plink->socket;
-
-    FD_SET(socket, &icqlink->d->TCP_readfds);
-
-    /* we only care about writing if socket is trying to connect */
-    if(plink->mode & TCP_LINK_MODE_CONNECTING)
-    {
-      if(plink->mode & (TCP_LINK_SOCKS_AUTHORIZATION | TCP_LINK_SOCKS_NOAUTHSTATUS | TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_CONNSTATUS))
-        FD_SET(socket, &icqlink->d->TCP_readfds);
-      else
-        FD_SET(socket, &icqlink->d->TCP_writefds);
-    }
-
-    if(socket+1>icqlink->d->TCP_maxfd)
-      icqlink->d->TCP_maxfd=socket+1;
-  }
-
-  return 0; /* traverse the entire list */
-}
-
-/* helper function for icq_TCPMain */
-int _handle_ready_sockets(void *p, va_list data)
-{
-  icq_TCPLink *plink=(icq_TCPLink *)p;
-  ICQLINK *icqlink = plink->icqlink;
-  int socket=plink->socket;
-
-  (void)data;
-
-  /* handle connecting sockets */
-  if (plink->mode & TCP_LINK_MODE_CONNECTING)
-  {
-    if(socket>-1 && (FD_ISSET(socket, &icqlink->d->TCP_writefds) || FD_ISSET(socket, &icqlink->d->TCP_readfds)))
-    {
-      icq_TCPLinkOnConnect(plink);
-      return 0; 
-    }
+/* need to do this somehow...
 
     if((time(0L) - plink->connect_time) > TCP_LINK_CONNECT_TIMEOUT)
     {
@@ -249,73 +200,7 @@
       return 0;
     }
   }
-
-  /* handle ready for read sockets- either a connection is waiting on *
-   * the listen sockets or data is ready to be read */
-  if(socket>-1 && FD_ISSET(socket, &icqlink->d->TCP_readfds))
-  {
-    if(plink->mode & TCP_LINK_MODE_LISTEN)
-      (void)icq_TCPLinkAccept(plink);
-    else {
-
-      int result=icq_TCPLinkOnDataReceived(plink);
-
-      /* close the link if there was a receive error or if *
-       * the remote end has closed the connection */
-      if (result < 1) 
-        icq_TCPLinkClose(plink);
-
-    }
-  }
-
-  return 0; /* traverse the entire list */
-}
-
-/* helper function for icq_TCPMain */
-int _process_links(void *p, va_list data)
-{
-  icq_TCPLink *plink=(icq_TCPLink *)p;
-
-  (void)data;
-
-  /* receive any packets watiting on the link */
-  icq_TCPLinkProcessReceived(plink);
-
-  /* if this a currently sending file link, send data! */
-  if(plink->type==TCP_LINK_FILE) {
-    icq_FileSession *psession=plink->session;
-    if(psession && psession->status==FILE_STATUS_SENDING)
-      icq_FileSessionSendData(psession);
-  }
-
-  return 0; /* traverse entire list */
-}
-
-void icq_TCPMain(ICQLINK *link)
-{
-  struct timeval tv;
-
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  link->d->TCP_maxfd = 0;
-  FD_ZERO(&link->d->TCP_readfds);
-  FD_ZERO(&link->d->TCP_writefds);
-
-  /* generate the fd sets for all open tcp links */
-  (void)list_traverse(link->d->icq_TCPLinks, _generate_fds);
-
-  /* determine which sockets require maintenance */
-  select(link->d->TCP_maxfd, &link->d->TCP_readfds, &link->d->TCP_writefds, 0, &tv);
-
-  /* call icq_TCPLinkOnDataReceived for any sockets with ready data,
-   * send all packets on send queue if socket has connected, and
-   * accept() from any listening sockets with pending connections */ 
-  (void)list_traverse(link->d->icq_TCPLinks, _handle_ready_sockets, 0, 0);
-
-  /* process all packets waiting for each TCPLink */
-  (void)list_traverse(link->d->icq_TCPLinks, _process_links, 0, 0);
-}
+*/
 
 icq_TCPLink *icq_TCPCheckLink(ICQLINK *link, DWORD uin, int type)
 {
@@ -519,15 +404,6 @@
 
 }
 
-void icq_TCPCloseChat(ICQLINK *link, unsigned long uin)
-{
-  icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
-
-  if(plink)
-    icq_TCPLinkClose(plink);
-
-}
-
 icq_FileSession *icq_AcceptFileRequest(ICQLINK *link, DWORD uin,
   unsigned long sequence)
 {
--- a/plugins/icq/tcpchathandle.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcpchathandle.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: tcpchathandle.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: tcpchathandle.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.9  2001/01/17 01:29:17  bills
+Rework chat and file session interfaces; implement socket notifications.
 
 Revision 1.8  2000/07/24 03:10:08  bills
 added support for real nickname during TCP transactions like file and
@@ -57,15 +60,25 @@
   icq_ChatSession *pchat;
   icq_Packet *p2;
 
-  if(plink->icqlink->icq_RequestNotify)
-    (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_ACK, 0, 0);
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink, 
+    p->id, ICQ_NOTIFY_ACK, 0, NULL);
+
   pchatlink=icq_TCPLinkNew(plink->icqlink);
   pchatlink->type=TCP_LINK_CHAT;
   pchatlink->id=p->id;
-  
+
+  /* once the ack packet has been processed, create a new chat session */
   pchat=icq_ChatSessionNew(plink->icqlink);
+
   pchat->id=p->id;
   pchat->remote_uin=plink->remote_uin;
+  pchat->tcplink=pchatlink;
+
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink, p->id,
+    ICQ_NOTIFY_CHATSESSION, sizeof(icq_ChatSession), pchat);
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink, p->id,
+    ICQ_NOTIFY_SUCCESS, 0, NULL);
+
   icq_ChatSessionSetStatus(pchat, CHAT_STATUS_CONNECTING);
   icq_TCPLinkConnect(pchatlink, plink->remote_uin, port);
 
@@ -123,13 +136,15 @@
   }
 }
 
-void icq_TCPChatUpdateFont(icq_TCPLink *plink, const char *font, WORD encoding, DWORD style, DWORD size)
+void icq_TCPChatUpdateFont(icq_ChatSession *psession, const char *font, 
+  WORD encoding, DWORD style, DWORD size)
 {
+  ICQLINK *icqlink = psession->icqlink;
   int packet_len, fontlen;
   char *buffer;
-  if(!plink->icqlink->icq_RequestNotify)
-    return;
-  buffer = malloc(packet_len = (2 + (fontlen = strlen(font) + 1)) + 2 + 1 + (4+1)*2);
+
+  buffer = malloc(packet_len = (2 + (fontlen = strlen(font) + 1)) + 
+    2 + 1 + (4+1)*2);
   buffer[0] = '\x11';
   *((DWORD *)&buffer[1]) = style;
   buffer[5] = '\x12';
@@ -137,26 +152,30 @@
   buffer[10] = '\x10';
   *((WORD *)&buffer[11]) = fontlen;
   strcpy(&buffer[13], font);
+
   icq_RusConv("wk", &buffer[13]);
+
   *((WORD *)&buffer[13 + fontlen]) = encoding;
-  if(plink->icqlink->icq_RequestNotify)
-    (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id, ICQ_NOTIFY_CHATDATA, packet_len, buffer);
+
+  invoke_callback(icqlink, icq_ChatNotify)(psession, CHAT_NOTIFY_DATA,
+    packet_len, buffer);
+
   free(buffer);
 }
 
-void icq_TCPChatUpdateColors(icq_TCPLink *plink, DWORD foreground, DWORD background)
+void icq_TCPChatUpdateColors(icq_ChatSession *psession, DWORD foreground, 
+  DWORD background)
 {
+  ICQLINK *icqlink = psession->icqlink;
   char buffer[10];
 
-  if(!plink->icqlink->icq_RequestNotify)
-    return;  
   buffer[0] = '\x00';
   *((DWORD *)&buffer[1]) = foreground;
   buffer[5] = '\x01';
   *((DWORD *)&buffer[6]) = background;
-  if(plink->icqlink->icq_RequestNotify)
-    (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id, ICQ_NOTIFY_CHATDATA,
-                                         sizeof(buffer), buffer);
+
+  invoke_callback(icqlink, icq_ChatNotify)(psession, 
+    CHAT_NOTIFY_DATA, sizeof(buffer), buffer);
 }
 
 void icq_TCPProcessChatPacket(icq_Packet *p, icq_TCPLink *plink)
@@ -204,7 +223,7 @@
       encoding = icq_PacketRead16(p);
     }
     if(font)
-      icq_TCPChatUpdateFont(plink, font, encoding, fontstyle, fontsize);
+      icq_TCPChatUpdateFont(pchat, font, encoding, fontstyle, fontsize);
     icq_ChatSessionSetStatus(pchat, CHAT_STATUS_READY);
     plink->mode|=TCP_LINK_MODE_RAW;
   }
@@ -216,7 +235,7 @@
       icq_PacketRead16(p); /* Unknown */;
       fg = icq_PacketRead32(p);
       bg = icq_PacketRead32(p);
-      icq_TCPChatUpdateColors(plink, fg, bg);
+      icq_TCPChatUpdateColors(pchat, fg, bg);
 
       presponse=icq_TCPCreateChatInfo2Packet(plink, plink->icqlink->icq_Nick,
         0x00ffffff, 0x00000000);
@@ -229,7 +248,7 @@
       user = icq_PacketReadString(p);
       fg = icq_PacketRead32(p);
       bg = icq_PacketRead32(p);
-      icq_TCPChatUpdateColors(plink, fg, bg);
+      icq_TCPChatUpdateColors(pchat, fg, bg);
       font = (char *)NULL;
       encoding = 0;
       fontstyle = 0;
@@ -259,7 +278,7 @@
         encoding = icq_PacketRead16(p);
       }
       if(font)
-        icq_TCPChatUpdateFont(plink, font, encoding, fontstyle, fontsize);
+        icq_TCPChatUpdateFont(pchat, font, encoding, fontstyle, fontsize);
       presponse=icq_TCPCreateChatFontInfoPacket(plink);
       icq_PacketSend(presponse, plink->socket);
       icq_PacketDelete(presponse);
--- a/plugins/icq/tcpfilehandle.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcpfilehandle.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: tcpfilehandle.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: tcpfilehandle.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.16  2001/01/17 01:29:17  bills
+Rework chat and file session interfaces; implement socket notifications.
 
 Revision 1.15  2000/07/24 03:10:08  bills
 added support for real nickname during TCP transactions like file and
@@ -110,6 +113,7 @@
 void icq_TCPProcessFilePacket(icq_Packet *p, icq_TCPLink *plink)
 {
   icq_FileSession *psession=(icq_FileSession *)plink->session;
+  ICQLINK *icqlink = plink->icqlink;
   BYTE type;
   DWORD num_files;
   DWORD total_bytes;
@@ -136,10 +140,10 @@
       psession->total_bytes=total_bytes;
       psession->current_speed=speed;
       icq_FileSessionSetHandle(psession, name);
-      icq_FileSessionSetStatus(psession, FILE_STATUS_INITIALIZED);
+      icq_FileSessionSetStatus(psession, FILE_STATUS_INITIALIZING);
 
       /* respond */
-      presponse=icq_TCPCreateFile01Packet(speed, plink->icqlink->icq_Nick);
+      presponse=icq_TCPCreateFile01Packet(speed, icqlink->icq_Nick);
 
       icq_TCPLinkSend(plink, presponse);
 #ifdef TCP_PACKET_TRACE
@@ -153,7 +157,7 @@
       name=icq_PacketReadString(p);
       psession->current_speed=speed;
       icq_FileSessionSetHandle(psession, name);
-      icq_FileSessionSetStatus(psession, FILE_STATUS_INITIALIZED);
+      icq_FileSessionSetStatus(psession, FILE_STATUS_INITIALIZING);
 
       /* respond */
       icq_FileSessionPrepareNextFile(psession);
@@ -193,7 +197,7 @@
       printf("file 03 packet sent to uin %lu\n", plink->remote_uin);
 #endif       
       break;
-
+	
     case 0x03:
       filesize=icq_PacketRead32(p);
       (void)icq_PacketRead32(p);       
@@ -208,29 +212,33 @@
 
     case 0x04:
       (void)icq_PacketRead32(p);
-      icq_FileSessionSetStatus(psession, FILE_STATUS_STOP_FILE);
+      invoke_callback(icqlink, icq_FileNotify)(psession, 
+        FILE_NOTIFY_STOP_FILE, 0, NULL);
       break;
 
     case 0x05:
       speed=icq_PacketRead32(p);
       psession->current_speed=speed;
-      if(plink->icqlink->icq_RequestNotify)
-        (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id,
-          ICQ_NOTIFY_FILE, FILE_STATUS_NEW_SPEED, 0);   
+      invoke_callback(icqlink, icq_FileNotify)(psession,
+        FILE_NOTIFY_NEW_SPEED, speed, NULL);
       break;
 
     case 0x06:
-      if(plink->icqlink->icq_RequestNotify)
-        (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id, ICQ_NOTIFY_FILEDATA,
-                           p->length-sizeof(BYTE), p->data+sizeof(BYTE));
+    {
+      void *data = p->data+sizeof(BYTE);
+      int length = p->length-sizeof(BYTE);
+
+      invoke_callback(icqlink, icq_FileNotify)(psession,
+        FILE_NOTIFY_DATAPACKET, length, data);
       icq_FileSessionSetStatus(psession, FILE_STATUS_RECEIVING);
-      result=write(psession->current_fd, p->data+sizeof(BYTE), p->length-sizeof(BYTE));
-      psession->current_file_progress+=p->length-sizeof(BYTE);
-      psession->total_transferred_bytes+=p->length-sizeof(BYTE);
+      result=write(psession->current_fd, data, length);
+      psession->current_file_progress+=length;
+      psession->total_transferred_bytes+=length;
       break;
+    }
 
     default:
-      icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "unknown file packet type %d!\n", type);
+      icq_FmtLog(icqlink, ICQ_LOG_WARNING, "unknown file packet type %d!\n", type);
 
   }
 }
@@ -266,8 +274,9 @@
   icq_FileSession *pfile;
   icq_Packet *p2;
 
-  if(plink->icqlink->icq_RequestNotify)
-    (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_ACK, 0, 0);
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink, 
+    p->id, ICQ_NOTIFY_ACK, 0, NULL);
+
   pfilelink=icq_TCPLinkNew(plink->icqlink);
   pfilelink->type=TCP_LINK_FILE;
   pfilelink->id=p->id;
@@ -278,9 +287,10 @@
   pfile->tcplink=pfilelink;
   pfilelink->id=pfile->id;
 
-  if (plink->icqlink->icq_RequestNotify)
-    (*plink->icqlink->icq_RequestNotify)(plink->icqlink, pfile->id, 
-      ICQ_NOTIFY_FILESESSION, sizeof(icq_FileSession), pfile);
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink,
+    pfile->id, ICQ_NOTIFY_FILESESSION, sizeof(icq_FileSession), pfile);
+  invoke_callback(plink->icqlink, icq_RequestNotify)(plink->icqlink,
+    pfile->id, ICQ_NOTIFY_SUCCESS, 0, NULL);
   
   icq_FileSessionSetStatus(pfile, FILE_STATUS_CONNECTING);
   icq_TCPLinkConnect(pfilelink, plink->remote_uin, port);
--- a/plugins/icq/tcphandle.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcphandle.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,16 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: tcphandle.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: tcphandle.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.16  2001/01/24 05:11:14  bills
+applied patch from Robin Ericsson <lobbin@localhost.nu> which implements
+receiving contact lists.  See new icq_RecvContactList callback.
+
+Revision 1.15  2001/01/17 01:31:47  bills
+Rework chat and file interfaces; implement socket notifications.
 
 Revision 1.14  2000/12/06 05:15:45  denis
 Handling for mass TCP messages has been added based on patch by
@@ -85,6 +92,7 @@
 
 */
 
+#include <stdlib.h>
 #include <time.h>
 
 #ifndef _WIN32
@@ -101,6 +109,7 @@
 
 void icq_TCPOnMessageReceived(ICQLINK *link, DWORD uin, const char *message, DWORD id, icq_TCPLink *plink);
 void icq_TCPOnURLReceived(ICQLINK *link, DWORD uin, const char *message, DWORD id);
+void icq_TCPOnContactListReceived(ICQLINK *link, DWORD uin, const char *message, DWORD id);
 void icq_TCPOnChatReqReceived(ICQLINK *link, DWORD uin, const char *message, DWORD id);
 void icq_TCPOnFileReqReceived(ICQLINK *link, DWORD uin, const char *message, 
    const char *filename, unsigned long filesize, DWORD id);
@@ -144,6 +153,7 @@
   {
     case ICQ_TCP_MSG_MSG:
     case ICQ_TCP_MSG_URL:
+    case ICQ_TCP_MSG_CONTACTLIST:
       p->id=icq_PacketRead32(p);
       break;
 
@@ -197,6 +207,10 @@
           icq_TCPOnFileReqReceived(plink->icqlink, uin, message, filename, filesize, p->id);
           break;
 
+        case ICQ_TCP_MSG_CONTACTLIST:
+          icq_TCPOnContactListReceived(plink->icqlink, uin, message, p->id);
+          break;
+
         default:
           icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "unknown message type %d!\n", type);
           break;
@@ -219,9 +233,10 @@
           if(plink->icqlink->icq_RequestNotify)
           {
             icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "received ack %d\n", p->id);
-            (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_ACK, status,
-                                               (void *)message);
-            (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_SUCCESS, 0, 0);
+            invoke_callback(plink->icqlink, icq_RequestNotify)
+              (plink->icqlink, p->id, ICQ_NOTIFY_ACK, status, (void *)message);
+            invoke_callback(plink->icqlink, icq_RequestNotify)
+              (plink->icqlink, p->id, ICQ_NOTIFY_SUCCESS, 0, NULL);
           }
           break;
       }
@@ -372,3 +387,42 @@
     icq_PacketDelete(pack);
   }
 }
+
+void icq_TCPOnContactListReceived(ICQLINK *link, DWORD uin, const char *message, DWORD id)
+{
+#ifdef TCP_PACKET_TRACE
+  printf("tcp contactlist packet received from %lu { sequence=%lx }\n", uin, id);
+#endif /* TCP_PACKET_TRACE */
+
+  if (link->icq_RecvContactList) {
+    /* use the current system time for time received */
+    time_t t=time(0);
+    struct tm *ptime=localtime(&t);
+    icq_Packet *pack;
+    list *strList = list_new();
+    int i, k, nr = icq_SplitFields(strList, message);
+    char *contact_uin[(nr - 2) /2], *contact_nick[(nr - 2) /2];
+    icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_MESSAGE);
+
+    /* split message */
+    for (i = 1, k = 0; i < (nr - 1); k++)
+    {
+      contact_uin[k]  = list_at(strList, i);
+      contact_nick[k] = list_at(strList, i + 1);
+      i += 2;
+    }
+
+    (*link->icq_RecvContactList)(link, uin, k, (const char **) 
+      contact_uin, (const char **) contact_nick);
+    /* send an acknowledement to the remote client */
+    pack=icq_TCPCreateContactListAck(plink, 0);
+    icq_PacketAppend32(pack, id);
+    icq_PacketSend(pack, plink->socket);
+#ifdef TCP_PACKET_TRACE
+    printf("tcp message ack sent to %lu { sequence=%lx }\n", uin, id);
+#endif /* TCP_PACKE_TRACE */
+    icq_PacketDelete(pack);
+
+    list_delete(strList, free);
+  }
+}
--- a/plugins/icq/tcplink.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcplink.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,20 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: tcplink.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: tcplink.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.43  2001/01/27 22:48:01  bills
+fix bugs related to TCP and new socket manager: implemented accepting TCP
+sockets, fixed crashes when sending TCP messages.
+
+Revision 1.42  2001/01/17 01:29:17  bills
+Rework chat and file session interfaces; implement socket notifications.
+
+Revision 1.41  2001/01/15 06:19:12  denis
+Applied patch from Ilya Melamed <ilya@ort.org.il> which fixes random
+icq_TCPLinkAccept() fails.
 
 Revision 1.40  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -233,12 +244,6 @@
   list_delete(p->send_queue, icq_PacketDelete);
   list_delete(p->received_queue, icq_PacketDelete);
 
-  /* notify app if this was a chat or file xfer session and there is an
-   * id assigned */
-  if((p->type==TCP_LINK_CHAT || p->type==TCP_LINK_FILE) && p->id)
-    if(p->icqlink->icq_RequestNotify)
-      (*p->icqlink->icq_RequestNotify)(p->icqlink, p->id, ICQ_NOTIFY_SUCCESS, 0, 0);
-
   /* if this is a chat or file link, delete the associated session as
    * well, but make sure we unassociate ourself first so the session
    * doesn't try to close us */
@@ -247,13 +252,13 @@
     if(p->type==TCP_LINK_CHAT)
     {
       icq_ChatSession *psession=p->session;
-      /*psession->tcplink=0L;*/
+      psession->tcplink=NULL;
       icq_ChatSessionClose(psession);
     }
 
     if(p->type==TCP_LINK_FILE) {
       icq_FileSession *psession=p->session;
-      psession->tcplink=0L;
+      psession->tcplink=NULL;
       icq_FileSessionClose(psession);
     }
   }
@@ -261,11 +266,7 @@
   /* close the socket after we notify app so app can read errno if necessary */
   if (p->socket > -1)
   {
-#ifdef _WIN32
-    closesocket(p->socket);
-#else
-    close(p->socket);
-#endif
+    icq_SocketDelete(p->socket);
   }
 
   free(p);
@@ -353,7 +354,9 @@
   int res;
   char buf[1024];
 
-  plink->mode = (plink->mode & (~TCP_LINK_SOCKS_AUTHORIZATION)) | TCP_LINK_SOCKS_AUTHSTATUS;
+  plink->mode &= ~TCP_LINK_SOCKS_AUTHORIZATION;
+  plink->mode |= TCP_LINK_SOCKS_AUTHSTATUS;
+
 #ifdef _WIN32
   res = recv(plink->socket, buf, 2, 0);
 #else
@@ -362,11 +365,7 @@
   if(res != 2 || buf[0] != 5 || buf[1] != 2) /* username/password authentication*/
   {
     icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
-#ifdef _WIN32
-    closesocket(plink->socket);
-#else
-    close(plink->socket);
-#endif
+    icq_SocketDelete(plink->socket);
     return -1;
   }
   buf[0] = 1; /* version of subnegotiation */
@@ -398,11 +397,7 @@
   if(res != 2 || buf[0] != 1 || buf[1] != 0)
   {
     icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
-#ifdef _WIN32
-    closesocket(plink->socket);
-#else
-    close(plink->socket);
-#endif
+    icq_SocketDelete(plink->socket);
     return -1;
   }
   return 0;
@@ -422,11 +417,7 @@
   if(res != 2 || buf[0] != 5 || buf[1] != 0) /* no authentication required */
   {
     icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
-#ifdef _WIN32
-    closesocket(plink->socket);
-#else
-    close(plink->socket);
-#endif
+    icq_SocketDelete(plink->socket);
     return -1;
   }
   return 0;
@@ -505,11 +496,7 @@
         res = EFAULT;
         break;
     }
-#ifdef _WIN32
-    closesocket(plink->socket);
-#else
-    close(plink->socket);
-#endif
+    icq_SocketDelete(plink->socket);
     return res;
   }
   return 0;
@@ -532,7 +519,7 @@
   if(!pcontact)
     return -2;
 
-  if((plink->socket=socket(AF_INET, SOCK_STREAM, 0)) < 0)
+  if((plink->socket=icq_SocketNew(AF_INET, SOCK_STREAM, 0)) < 0)
     return -3;
 
 /*   bzero(&(plink->remote_address), sizeof(plink->remote_address));   Win32 incompatible... */
@@ -606,6 +593,9 @@
   printf("hello packet queued for %lu\n", uin);
 #endif /* TCP_PACKET_TRACE */
 
+  icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE,
+    icq_TCPLinkOnConnect, plink);
+  
   return 1;
 }
 
@@ -622,21 +612,26 @@
   
   if(pnewlink)
   {
-		socket=accept(plink->socket, (struct sockaddr *)&(plink->remote_address),
-                  &remote_length);
+    remote_length = sizeof(struct sockaddr_in);
+    socket=icq_SocketAccept(plink->socket,
+      (struct sockaddr *)&(plink->remote_address), &remote_length);
 
-		icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE,
-			"accepting tcp connection from %s:%d\n",
-			inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))),
-			ntohs(plink->remote_address.sin_port));
+    icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE,
+      "accepting tcp connection from %s:%d\n",
+      inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))),
+      ntohs(plink->remote_address.sin_port));
 
-		/* FIXME: make sure accept succeeded */
+    /* FIXME: make sure accept succeeded */
 
     pnewlink->type=plink->type;
     pnewlink->socket=socket;
 
     /* first packet sent on an icq tcp link is always the hello packet */
     pnewlink->mode|=TCP_LINK_MODE_HELLOWAIT;
+
+    /* install socket handler for new socket */
+    icq_SocketSetHandler(socket, ICQ_SOCKET_READ, icq_TCPLinkOnDataReceived,
+      pnewlink);
   }
 
   /* set the socket to non-blocking */
@@ -659,7 +654,7 @@
   plink->remote_uin=0;
 
   /* create tcp listen socket */
-  if((plink->socket=socket(AF_INET, SOCK_STREAM, 0)) < 0)
+  if((plink->socket=icq_SocketNew(AF_INET, SOCK_STREAM, 0)) < 0)
     return -1;
 
   /* must use memset, no bzero for Win32! */
@@ -685,7 +680,10 @@
              ntohs(plink->socket_address.sin_port));
 
   plink->mode|=TCP_LINK_MODE_LISTEN;
-  
+
+  icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, icq_TCPLinkAccept,
+    plink);
+
   return 0;
 }
 
@@ -729,7 +727,7 @@
     icq_RusConv_n(to, &t_in[j], i - j);
 }
 
-int icq_TCPLinkOnDataReceived(icq_TCPLink *plink)
+void icq_TCPLinkOnDataReceived(icq_TCPLink *plink)
 {
   int process_count=0, recv_result=0;
   char *buffer=plink->buffer;
@@ -763,9 +761,8 @@
       /* notify the app with the new data */
       if(plink->type == TCP_LINK_CHAT)
         icq_ChatRusConv_n("wk", plink->buffer, plink->buffer_count);
-      if(plink->icqlink->icq_RequestNotify)
-        (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id, ICQ_NOTIFY_CHATDATA,
-                           plink->buffer_count, plink->buffer);
+      invoke_callback(plink->icqlink, icq_ChatNotify)(plink->session,
+        CHAT_NOTIFY_DATA, plink->buffer_count, plink->buffer);
       plink->buffer_count=0;
       continue;
     }
@@ -782,7 +779,7 @@
         icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "tcplink buffer "
           "overflow, packet size = %d, buffer size = %d, closing link\n",
           packet_size, icq_TCPLinkBufferSize);
-        return 0;
+        return;
       }
 
       if(packet_size+sizeof(WORD) <= (unsigned)plink->buffer_count)
@@ -815,9 +812,14 @@
     icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "recv failed from %d (%d-%s),"
       " closing link\n", plink->remote_uin, errno, strerror(errno));
 
+    icq_TCPLinkClose(plink);
+
+  } else {
+
+    icq_TCPLinkProcessReceived(plink);
+
   }
 
-  return process_count;
 }
 
 void icq_TCPLinkOnPacketReceived(icq_TCPLink *plink, icq_Packet *p)
@@ -881,7 +883,12 @@
   }
 
   if(plink->mode & (TCP_LINK_SOCKS_CONNECTING | TCP_LINK_SOCKS_AUTHORIZATION | TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_NOAUTHSTATUS | TCP_LINK_SOCKS_CROSSCONNECT | TCP_LINK_SOCKS_CONNSTATUS))
+  {
+    icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE, NULL, NULL);
+    icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, 
+      icq_TCPLinkOnConnect, plink);
     return;
+  }
 
   len=sizeof(plink->socket_address);
   getsockname(plink->socket, (struct sockaddr *)&plink->socket_address, &len);
@@ -896,6 +903,10 @@
 
   plink->mode&= ~TCP_LINK_MODE_CONNECTING;
 
+  icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, 
+    icq_TCPLinkOnDataReceived, plink);
+  icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE, NULL, NULL);
+
   /* socket is now connected, notify each request that connection
    * has been established and send pending data */
   while(plink->send_queue->count>0)
--- a/plugins/icq/tcplink.h	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/tcplink.h	Sun Jan 28 01:52:27 2001 +0000
@@ -40,8 +40,8 @@
 #define icq_TCPLinkBufferSize 4096
 #define TCP_LINK_CONNECT_TIMEOUT 30
 
-typedef struct icq_TCPLink_s
-{
+struct icq_TCPLink_s {
+
    /* icq_TCPLink ICQLINK, type, mode, and session */
    ICQLINK *icqlink;
    int type;
@@ -71,7 +71,7 @@
    /* connect timer */
    time_t connect_time;
 
-} icq_TCPLink;
+};
 
 icq_TCPLink *icq_TCPLinkNew(ICQLINK *link);
 void icq_TCPLinkDelete(void *p);
@@ -82,7 +82,7 @@
 icq_TCPLink *icq_TCPLinkAccept(icq_TCPLink *plink);
 int icq_TCPLinkListen(icq_TCPLink *plink);
 
-int icq_TCPLinkOnDataReceived(icq_TCPLink *plink);
+void icq_TCPLinkOnDataReceived(icq_TCPLink *plink);
 void icq_TCPLinkOnPacketReceived(icq_TCPLink *plink, icq_Packet *p);
 void icq_TCPLinkOnConnect(icq_TCPLink *plink);
 
--- a/plugins/icq/udp.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/udp.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,12 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: udp.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: udp.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.27  2001/01/16 00:10:13  denis
+Invisible list has been finished.
 
 Revision 1.26  2000/12/19 06:00:07  bills
 moved members from ICQLINK to ICQLINK_private struct
@@ -439,7 +442,7 @@
   icq_PacketAdvance(p,1);
   while(ptr)
   {
-    if(ptr->vis_list)
+    if(ptr->invis_list)
     {
       icq_PacketAppend32(p, ptr->uin);
       num_used++;
--- a/plugins/icq/udphandle.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/udphandle.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,13 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: udphandle.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: udphandle.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
+
+Revision 1.30  2001/01/15 06:17:35  denis
+Applied patch from Andrey Chernomyrdin <andrey@excom.spb.su> to
+handle icq2000 specific "You've been added" packet.
 
 Revision 1.29  2000/11/02 07:28:30  denis
 Do not ack unhandled protocol version.
@@ -155,15 +159,18 @@
                BYTE minute, BYTE day, BYTE month, WORD year)
 {
   list *strList;
+  int fieldCount;
 
   strList = list_new();
   switch(type)
   {
     case TYPE_ADDED:
       /* Format: Nick, 0xFE, FName, 0xFE, LName, 0xFE, EMail */
-      if(icq_SplitFields(strList, data)!=4)
+      fieldCount = icq_SplitFields(strList, data);
+      if(fieldCount != 4 && fieldCount != 5)
       {
-        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad packet!\n");
+        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad TYPE_ADDED packet (expected 4/5 args, received %i)!\n",
+                   fieldCount);
         return;
       }
       icq_RusConv("wk", list_at(strList, 0)); /* Nick */
@@ -181,9 +188,11 @@
       break;
     case TYPE_AUTH_REQ:
       /* Format: Nick, 0xFE, FName, 0xFE, LName, 0xFE, EMail, 0xFE, 0, 0xFE, Reason */
-      if(icq_SplitFields(strList, data)!=6)
+      fieldCount = icq_SplitFields(strList, data);
+      if(fieldCount != 6)
       {
-        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad packet!\n");
+        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad TYPE_AUTH_REQ packet (expected 6 args, received %i)!\n",
+                   fieldCount);
         return;
       }
       icq_RusConv("wk", list_at(strList, 0)); /* Nick */
@@ -202,9 +211,10 @@
       break;
     case TYPE_URL:
       /* Format: Description, 0xFE, URL */
-      if(icq_SplitFields(strList, data)!=2)
+      fieldCount = icq_SplitFields(strList, data);
+      if(fieldCount != 2)
       {
-        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad packet!\n");
+        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad TYPE_URL packet (expected 2 args, recived %i)!\n", fieldCount);
         return;
       }
       icq_RusConv("wk", list_at(strList, 0)); /* Description */
@@ -217,9 +227,11 @@
     case TYPE_WEBPAGER:
       /* Format: Nick, 0xFE, Empty-FName, 0xFE, Empty-LName, 0xFE, EMail, 0xFE,
        *         Reason(3), 0xFE, Message with IP & Subject */
-      if(icq_SplitFields(strList, data)!=6)
+      fieldCount = icq_SplitFields(strList, data);
+      if(fieldCount != 6)
       {
-        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad packet!\n");
+        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad TYPE_WEBPAGER packet (expected 6 args, received %i)!\n",
+                   fieldCount);
         return;
       }
       icq_RusConv("wk", list_at(strList, 0)); /* Nick */
@@ -234,9 +246,11 @@
     case TYPE_EXPRESS:
       /* Format: Nick, 0xFE, Empty-FName, 0xFE, Empty-LName, 0xFE, EMail, 0xFE,
        *         Reason(3), 0xFE, Message Subject */
-      if(icq_SplitFields(strList, data)!=6)
+      fieldCount = icq_SplitFields(strList, data);
+      if(fieldCount != 6)
       {
-        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad packet!\n");
+        icq_FmtLog(link, ICQ_LOG_ERROR, "Bad TYPE_EXPRESS packet (expected 6 args, received %i)!\n",
+                   fieldCount);
         return;
       }
       icq_RusConv("wk", list_at(strList, 0)); /* Nick */
--- a/plugins/icq/util.c	Sat Jan 27 11:18:17 2001 +0000
+++ b/plugins/icq/util.c	Sun Jan 28 01:52:27 2001 +0000
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
-$Id: util.c 1319 2000-12-19 10:08:29Z warmenhoven $
+$Id: util.c 1442 2001-01-28 01:52:27Z warmenhoven $
 $Log$
-Revision 1.2  2000/12/19 10:08:29  warmenhoven
-Yay, new icqlib
+Revision 1.3  2001/01/28 01:52:27  warmenhoven
+icqlib 1.1.5
 
 Revision 1.33  2000/08/13 19:26:50  denis
 icq_Genders[] array have been added.