[Licq-devel] SSBI for Licq, new version

Sergey Kononenko <[email protected]>
Newsgroups gmane.network.licq.devel
Message-ID <[email protected]>
Hello,

This is new version of patch that add SSBI (server side buddy icon (or
user picture)) support to licq. Changes from prior versions:
1. Fixed handling of icq direct client to client communication which
was completely broken in previous versions of patch.
2. Fixed crash on exit when UseSSBI is off.

With best regards,
Sergey Kononenko.
ssbi-licq.patch (text/x-patch, 68.2 KB)
diff -urN licq/include/licq_buffer.h licq-ssbi/include/licq_buffer.h
--- licq/include/licq_buffer.h	2006-08-10 21:17:07.000000000 +0300
+++ licq-ssbi/include/licq_buffer.h	2008-01-22 01:11:26.000000000 +0200
@@ -85,6 +85,7 @@
    CBuffer& operator >> (unsigned short &in);
    CBuffer& operator >> (unsigned long &in);
    char *UnpackRaw(char *, unsigned short);
+   char *UnpackBinBlock(char *, unsigned short);
    char *UnpackString(char *, unsigned short);
    char *UnpackString();                // Need to delete[] returned string
    char *UnpackStringBE(char *, unsigned short);
diff -urN licq/include/licq_events.h licq-ssbi/include/licq_events.h
--- licq/include/licq_events.h	2006-11-11 21:24:51.000000000 +0200
+++ licq-ssbi/include/licq_events.h	2008-01-22 01:11:26.000000000 +0200
@@ -249,6 +249,12 @@
   // Daemon only
   unsigned short SubType()     { return m_nSubType; }
   unsigned short ExtraInfo()   { return m_nExtraInfo; }
+  void SetSubType(unsigned short nSubType) { m_nSubType = nSubType; }
+  bool NoAck()                 { return m_NoAck; }
+  void SetNoAck(bool NoAck)    { m_NoAck = NoAck; }
+  bool IsCancelled()           { return m_bCancelled; }
+
+  void AttachPacket(CPacket *p);
 
   // Compare this event to another one
   bool CompareEvent(int, unsigned short) const;
@@ -290,9 +296,11 @@
   static unsigned long s_nNextEventId;
 
 friend class CICQDaemon;
+friend class COscarService;
 friend class CMSN;
 friend void *ProcessRunningEvent_Client_tep(void *p);
 friend void *ProcessRunningEvent_Server_tep(void *p);
+friend void *OscarServiceSendQueue_tep(void *p);
 friend void *MonitorSockets_tep(void *p);
 };
 
@@ -476,6 +484,8 @@
   PROTOxREQUESTxINFO,
   //! The user has requested to update the owner's profile/information.
   PROTOxUPDATExINFO,
+  //! The user has requested the user's picture/icon/avatar/etc..
+  PROTOxREQUESTxPICTURE,
   //! The user has requested this user be added to the Invisible/Block list.
   PROTOxBLOCKxUSER,
   //! The user has requested this user be removed from the Invisible/Block
@@ -684,6 +694,13 @@
        *m_szZipCode;
 };
 
+class CRequestPicture : public CSignal
+{
+public:
+  CRequestPicture(const char *);
+  virtual ~CRequestPicture();
+};
+
 class CBlockUserSignal : public CSignal
 {
 public:
diff -urN licq/include/licq_icqd.h licq-ssbi/include/licq_icqd.h
--- licq/include/licq_icqd.h	2007-08-21 14:51:52.000000000 +0300
+++ licq-ssbi/include/licq_icqd.h	2008-01-22 01:11:26.000000000 +0200
@@ -42,6 +42,7 @@
 class SrvSocket;
 class INetSocket;
 class ProxyServer;
+class COscarService;
 class CReverseConnectToUserData;
 class CMSN;
 
@@ -300,6 +301,8 @@
     const char *szFaxNumber, const char *szAddress, const char *szCellularNumber,
     const char *szZipCode, unsigned short nCountryCode, bool bHideEmail);
 
+  unsigned long ProtoRequestPicture(const char *szId, unsigned long nPPID);
+
   unsigned long ProtoOpenSecureChannel(const char *szId, unsigned long nPPID);
   unsigned long ProtoCloseSecureChannel(const char *szId, unsigned long nPPID);
   void ProtoOpenSecureChannelCancel(const char *szId, unsigned long nPPID,
@@ -474,6 +477,7 @@
   void icqUpdateContactList();
   void icqTypingNotification(const char *_szId, bool _bActive);
   void icqCheckInvisible(const char *_szId);
+  void icqRequestService(unsigned short nFam);
 
   // Visible/Invisible/Ignore list functions
   void ProtoToggleInvisibleList(const char *_szId, unsigned long _nPPID);
@@ -514,6 +518,8 @@
   void UnregisterProtoPlugin();
   char *ProtoPluginName(unsigned long);
 
+  EDaemonStatus Status() { return m_eStatus; }
+
   void PluginUIViewEvent(const char *szId, unsigned long nPPID ) {
     PushPluginSignal(new CICQSignal(SIGNAL_UI_VIEWEVENT, 0, szId, nPPID, 0));
   }
@@ -567,7 +573,9 @@
 
   // Proxy options
   void InitProxy();
+  ProxyServer *CreateProxy();
   bool ProxyEnabled() {  return m_bProxyEnabled;  }
+  ProxyServer *GetProxy() {  return m_xProxy;  }
   void SetProxyEnabled(bool b) {  m_bProxyEnabled = b;  }
   unsigned short ProxyType() {  return m_nProxyType;  }
   void SetProxyType(unsigned short t) {  m_nProxyType = t;  }
@@ -609,10 +617,12 @@
 
   // ICQ options
   bool UseServerContactList()         { return m_bUseSS; }
+  bool UseServerSideBuddyIcons()      { return m_bUseSSBI; }
   bool SendTypingNotification()       { return m_bSendTN; }
 
-  void SetUseServerContactList(bool b)   { m_bUseSS = b; }
-  void SetSendTypingNotification(bool b) { m_bSendTN = b; }
+  void SetUseServerContactList(bool b)    { m_bUseSS = b; }
+  void SetUseServerSideBuddyIcons(bool b);
+  void SetSendTypingNotification(bool b)  { m_bSendTN = b; }
 
   // Misc functions
   bool ReconnectAfterUinClash()              { return m_bReconnectAfterUinClash; }
@@ -700,8 +710,12 @@
   char *m_szProxyPasswd;
   ProxyServer *m_xProxy;
 
+  // Services
+  COscarService *m_xSSBIService;
+
   // Misc
   bool m_bUseSS; // server side list
+  bool m_bUseSSBI; // server side buddy icons
   bool m_bSendTN; // Send typing notifications
   bool m_bReconnectAfterUinClash; // reconnect after uin has been used from another location?
 
@@ -729,6 +743,7 @@
   pthread_t thread_monitorsockets,
             thread_ping,
             thread_updateusers,
+            thread_ssbiservice,
             thread_shutdown;
 
   pthread_cond_t cond_serverack;
@@ -762,6 +777,7 @@
   ICQEvent *DoneExtendedEvent(unsigned long tag, EventResult _eResult);
   bool hasServerEvent(unsigned long);
   void ProcessDoneEvent(ICQEvent *);
+  void PushEvent(ICQEvent *);
   void PushExtendedEvent(ICQEvent *);
   void PushPluginSignal(CICQSignal *);
   void PushPluginEvent(ICQEvent *);
@@ -830,10 +846,12 @@
   friend void *ReverseConnectToUser_tep(void *p);
   friend void *ProcessRunningEvent_Client_tep(void *p);
   friend void *ProcessRunningEvent_Server_tep(void *p);
+  friend void *OscarServiceSendQueue_tep(void *p);
   friend void *Shutdown_tep(void *p);
   friend void *ConnectToServer_tep(void *s);
   friend class ICQUser;
   friend class CSocketManager;
+  friend class COscarService;
   friend class CChatManager;
   friend class CFileTransferManager;
   friend class COnEventManager;
diff -urN licq/include/licq_icq.h licq-ssbi/include/licq_icq.h
--- licq/include/licq_icq.h	2008-01-03 16:09:22.000000000 +0200
+++ licq-ssbi/include/licq_icq.h	2008-01-22 01:11:26.000000000 +0200
@@ -34,6 +34,7 @@
 const unsigned short ICQ_SNACxFAM_BUDDY            = 0x0003;
 const unsigned short ICQ_SNACxFAM_MESSAGE          = 0x0004;
 const unsigned short ICQ_SNACxFAM_BOS              = 0x0009;
+const unsigned short ICQ_SNACxFAM_SSBI             = 0x0010;
 const unsigned short ICQ_SNACxFAM_LIST             = 0x0013;
 const unsigned short ICQ_SNACxFAM_VARIOUS          = 0x0015;
 const unsigned short ICQ_SNACxFAM_NEWUIN	   = 0x0017;
@@ -104,6 +105,13 @@
 const unsigned short ICQ_SNACxREQUEST_IMAGE        = 0x000C; // client
 const unsigned short ICQ_SNACxSEND_IMAGE           = 0x000D; // server
 
+// Subtypes for SSBI family
+const unsigned short ICQ_SNACxSSBI_ERROR           = 0x0001; // server
+const unsigned short ICQ_SNACxSSBI_UPLOAD          = 0x0002; // client
+const unsigned short ICQ_SNACxSSBI_UPLOADxACK      = 0x0003; // server
+const unsigned short ICQ_SNACxSSBI_ICONxREQUEST    = 0x0006; // client
+const unsigned short ICQ_SNACxSSBI_ICONxREPLY      = 0x0007; // server
+
 // Subtypes for list family
 const unsigned short ICQ_SNACxLIST_REQUESTxRIGHTS  = 0x0002; // client
 const unsigned short ICQ_SNACxLIST_RIGHTSxGRANTED  = 0x0003; // server
@@ -544,6 +552,10 @@
                                           0x4E, 0xC5, 0x9D, 0x51, 0xA6, 0x47,
                                           0x4E, 0x34, 0xF5, 0xA0 };
 
+const char ICQ_CAPABILITY_SSBI[]      = { 0x09, 0x46, 0x13, 0x46, 0x4C, 0x7F,
+                                          0x11, 0xD1, 0x82, 0x22, 0x44, 0x45,
+                                          0x53, 0x54, 0x00, 0x00 };
+
 const char ICQ_CAPABILITY_LICQxVER[]  = { 'L',  'i',  'c',  'q',  ' ',  'c',
                                           'l',  'i',  'e',  'n',  't',  ' ',
                                           0x00, 0x00, 0x00, 0x00 };
diff -urN licq/include/licq_oscarservice.h licq-ssbi/include/licq_oscarservice.h
--- licq/include/licq_oscarservice.h	1970-01-01 03:00:00.000000000 +0300
+++ licq-ssbi/include/licq_oscarservice.h	2008-01-22 01:11:26.000000000 +0200
@@ -0,0 +1,61 @@
+#ifndef OSCARSERVICE_H
+#define OSCARSERVICE_H
+
+#include <list>
+
+class CICQDaemon;
+class ICQEvent;
+class CBuffer;
+class CPacket;
+class ProxyServer;
+
+enum EOscarServiceStatus {STATUS_UNINITIALIZED, STATUS_SERVICE_REQ_SENT,
+                          STATUS_SERVICE_REQ_ACKED, STATUS_CONNECTED,
+                          STATUS_SRV_READY_RECV, STATUS_SRV_VER_RECV,
+                          STATUS_SRV_RATE_RECV, STATUS_READY};
+
+void *OscarServiceSendQueue_tep(void *p);
+
+class COscarService
+{
+public:
+  COscarService(CICQDaemon *Daemon, unsigned short Fam);
+  ~COscarService();
+  bool Initialize();
+  bool ProcessPacket(CBuffer &packet);
+  unsigned long SendEvent(const char *Id, unsigned short SubType, bool Request);
+  void ClearQueue();
+
+  void SetConnectCredential(char *Server, unsigned short Port,
+                            char *Cookie, unsigned short CookieLen);
+  void ChangeStatus(EOscarServiceStatus s);
+  int GetSocketDesc() { return mySocketDesc; }
+  void ResetSocket() { mySocketDesc = -1; }
+  unsigned short GetFam() { return myFam; }
+
+protected:
+  CICQDaemon *myDaemon;
+  unsigned short myFam;
+  int mySocketDesc;
+  ProxyServer *myProxy;
+  EOscarServiceStatus myStatus;
+  char *myServer, *myCookie;
+  unsigned short myPort, myCookieLen;
+  std::list <ICQEvent *> mySendQueue;
+  pthread_mutex_t mutex_sendqueue;
+  pthread_cond_t cond_sendqueue;
+  pthread_mutex_t mutex_status;
+  pthread_cond_t cond_status;
+
+  bool SendPacket(CPacket *packet);
+  bool WaitForStatus(EOscarServiceStatus s);
+  bool SendSSBIFam(ICQEvent *e);
+  void ProcessNewChannel(CBuffer &packet);
+  void ProcessDataChannel(CBuffer &packet);
+  void ProcessServiceFam(CBuffer &packet, unsigned short SubType, unsigned long RequestId);
+  void ProcessSSBIFam(CBuffer &packet, unsigned short SubType, unsigned long RequestId);
+
+  friend void *OscarServiceSendQueue_tep(void *p);
+};
+
+#endif
diff -urN licq/include/licq_packets.h licq-ssbi/include/licq_packets.h
--- licq/include/licq_packets.h	2008-01-03 16:09:22.000000000 +0200
+++ licq-ssbi/include/licq_packets.h	2008-01-22 01:11:26.000000000 +0200
@@ -134,7 +134,7 @@
   void InitBuffer();
 
   static bool s_bRegistered;
-  static unsigned short s_nSequence;
+  static unsigned short s_nSequence[32];
   static unsigned short s_nSubSequence;
   static pthread_mutex_t s_xMutex;
 
@@ -145,6 +145,7 @@
   unsigned short m_nSubType;
   unsigned short m_nSubCommand;
   unsigned short m_nExtraInfo;
+  unsigned short m_nService;
 
   char *m_szSequenceOffset;
 };
@@ -219,7 +220,7 @@
 class CPU_SendCookie : public CSrvPacketTcp
 {
 public:
-  CPU_SendCookie(const char *, int len);
+  CPU_SendCookie(const char *, int len, unsigned short nService = 0);
   virtual ~CPU_SendCookie();
 };
 
@@ -237,7 +238,8 @@
 class CPU_GenericFamily : public CPU_CommonFamily
 {
 public:
-  CPU_GenericFamily(unsigned short Family, unsigned short SubType);
+  CPU_GenericFamily(unsigned short Family, unsigned short SubType,
+                    unsigned short nService = 0);
   virtual ~CPU_GenericFamily();
 };
 
@@ -312,7 +314,9 @@
 class CPU_ImICQ : public CPU_CommonFamily
 {
 public:
-    CPU_ImICQ();
+  CPU_ImICQ();
+  CPU_ImICQ(unsigned short VerArray[][2], unsigned short NumVer,
+            unsigned short nService);
 };
 
 //-----ICQMode------------------------------------------------------------------
@@ -333,7 +337,7 @@
 class CPU_RateAck : public CPU_CommonFamily
 {
 public:
-  CPU_RateAck();
+  CPU_RateAck(unsigned short nService = 0);
 };
 
 //-----GenericUinList------------------------------------------------------------
@@ -487,6 +491,8 @@
 {
 public:
    CPU_ClientReady();
+   CPU_ClientReady(unsigned short VerArray[][4], unsigned short NumVer,
+                   unsigned short nService);
 };
 
 //-----ClientAckNameInfo--------------------------------------------------------
@@ -1126,6 +1132,21 @@
   CPU_RequestInfo(const char *szId);
 };
 
+//-----RequestBuddyIcon------------------------------------------------------
+class CPU_RequestBuddyIcon : public CPU_CommonFamily
+{
+public:
+  CPU_RequestBuddyIcon(const char *szId, unsigned short _nBuddyIconType,
+                       char _nBuddyIconHashType, const char *_szBuddyIconHash,
+                       unsigned short nService);
+};
+
+class CPU_RequestService : public CPU_CommonFamily
+{
+public:
+  CPU_RequestService(unsigned short nFam);
+};
+
 //-----AIMFetchAwayMessage--------------------------------------------------
 class CPU_AIMFetchAwayMessage : public CPU_CommonFamily
 {
diff -urN licq/include/licq_proxy.h licq-ssbi/include/licq_proxy.h
--- licq/include/licq_proxy.h	2006-11-11 21:24:51.000000000 +0200
+++ licq-ssbi/include/licq_proxy.h	2008-01-22 01:11:26.000000000 +0200
@@ -1,3 +1,6 @@
+#ifndef PROXY_H
+#define PROXY_H
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -68,3 +71,5 @@
   bool HTTPInitProxy();
   bool HTTPOpenProxyConnection(const char *_szRemoteName, unsigned short _nRemotePort);
 };
+
+#endif
diff -urN licq/include/licq_socket.h licq-ssbi/include/licq_socket.h
--- licq/include/licq_socket.h	2006-11-11 21:24:51.000000000 +0200
+++ licq-ssbi/include/licq_socket.h	2008-01-22 01:11:26.000000000 +0200
@@ -166,6 +166,7 @@
   // Functions specific to Server TCP communication
   bool SendPacket(CBuffer *b);
   bool RecvPacket();
+  bool ConnectTo(const char* server, unsigned short port, ProxyServer *xProxy);
 };
 
 
diff -urN licq/include/licq_user.h licq-ssbi/include/licq_user.h
--- licq/include/licq_user.h	2008-01-21 22:47:35.000000000 +0200
+++ licq-ssbi/include/licq_user.h	2008-01-22 01:11:26.000000000 +0200
@@ -515,6 +515,10 @@
 
   // Picture Info
   bool GetPicturePresent()              { return m_bPicturePresent; }
+  unsigned short BuddyIconType()	{ return m_nBuddyIconType; }
+  char BuddyIconHashType()		{ return m_nBuddyIconHashType; }
+  char *BuddyIconHash()			{ return m_szBuddyIconHash; }
+  char *OurBuddyIconHash()		{ return m_szOurBuddyIconHash; }
 
   // Dynamic info fields for protocol plugins
   string GetPPField(const string &);
@@ -634,6 +638,10 @@
 
   // Picture info
   void SetPicturePresent(bool b)      { m_bPicturePresent = b; SavePictureInfo(); }
+  void SetBuddyIconType(unsigned short s) { m_nBuddyIconType = s; }
+  void SetBuddyIconHashType(char s)   { m_nBuddyIconHashType = s; }
+  void SetBuddyIconHash(char *s)      { SetString(&m_szBuddyIconHash, s); }
+  void SetOurBuddyIconHash(char *s)   { SetString(&m_szOurBuddyIconHash, s); }
 
   // Licq Info
   void SetAwaitingAuth(bool b)        { m_bAwaitingAuth = b; }
@@ -940,6 +948,9 @@
 
   // Picture Info
   bool m_bPicturePresent;
+  unsigned short m_nBuddyIconType;
+  char m_nBuddyIconHashType;
+  char *m_szBuddyIconHash, *m_szOurBuddyIconHash;
 
   // Dynamic info fields for protocol plugins
   map<string, string> m_mPPFields;
diff -urN licq/plugins/qt-gui/src/sigman.cpp licq-ssbi/plugins/qt-gui/src/sigman.cpp
--- licq/plugins/qt-gui/src/sigman.cpp	2007-10-22 16:21:27.000000000 +0300
+++ licq-ssbi/plugins/qt-gui/src/sigman.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -175,6 +175,7 @@
   case MAKESNAC(ICQ_SNACxFAM_MESSAGE, ICQ_SNACxMSG_SENDxSERVER):
   case MAKESNAC(ICQ_SNACxFAM_LOCATION, ICQ_SNACxREQUESTxUSERxINFO):
   case MAKESNAC(ICQ_SNACxFAM_LOCATION, ICQ_SNACxLOC_INFOxREQ):
+  case MAKESNAC(ICQ_SNACxFAM_SSBI, ICQ_SNACxSSBI_ICONxREQUEST):
     emit signal_doneUserFcn(e);
     break;
 
diff -urN licq/plugins/qt-gui/src/userinfodlg.cpp licq-ssbi/plugins/qt-gui/src/userinfodlg.cpp
--- licq/plugins/qt-gui/src/userinfodlg.cpp	2007-11-25 17:37:31.000000000 +0200
+++ licq-ssbi/plugins/qt-gui/src/userinfodlg.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -2358,11 +2358,7 @@
     }
     case PictureInfo:
     {
-      ICQUser *u = gUserManager.FetchUser(m_szId, m_nPPID, LOCK_R);
-      if (u == NULL) return;
-      bool bSendServer = (u->SocketDesc(ICQ_CHNxINFO) < 0);
-      gUserManager.DropUser(u);
-      icqEventTag = server->icqRequestPicture(m_szId, bSendServer);
+      icqEventTag = server->ProtoRequestPicture(m_szId, m_nPPID);
       break;
     }
   }
diff -urN licq/src/buffer.cpp licq-ssbi/src/buffer.cpp
--- licq/src/buffer.cpp	2007-08-21 14:51:52.000000000 +0300
+++ licq-ssbi/src/buffer.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -333,6 +333,12 @@
   return sz;
 }
 
+char *CBuffer::UnpackBinBlock(char *sz, unsigned short _nSize)
+{
+  for (unsigned short i = 0; i < _nSize; i++) *this >> sz[i];
+  return sz;
+}
+
 char *CBuffer::UnpackStringBE(char* sz, unsigned short _usiSize)
 {
   unsigned short nLen;
diff -urN licq/src/icqd.cpp licq-ssbi/src/icqd.cpp
--- licq/src/icqd.cpp	2007-11-26 13:26:35.000000000 +0200
+++ licq-ssbi/src/icqd.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -32,6 +32,7 @@
 
 #include "licq_icq.h"
 #include "licq_user.h"
+#include "licq_oscarservice.h"
 #include "licq_constants.h"
 #include "licq_file.h"
 #include "licq_log.h"
@@ -270,8 +271,12 @@
   m_szProxyPasswd = (char *)malloc(strlen(t_str) + 1);
   strcpy(m_szProxyPasswd, t_str);
 
+  // Services
+  m_xSSBIService = NULL;
+
   // Misc
   licqConf.ReadBool("UseSS", m_bUseSS, true); // server side list
+  licqConf.ReadBool("UseSSBI", m_bUseSSBI, true); // server side buddy icons
   licqConf.ReadBool("SendTypingNotification", m_bSendTN, true);
   licqConf.ReadBool("ReconnectAfterUinClash", m_bReconnectAfterUinClash, false);
 
@@ -447,10 +452,23 @@
   nResult = pthread_create(&thread_updateusers, NULL, &UpdateUsers_tep, this);
   if (nResult != 0)
   {
-    gLog.Error("%sUnable to start ping thread:\n%s%s.\n", L_ERRORxSTR, L_BLANKxSTR, strerror(nResult));
+    gLog.Error("%sUnable to start users update thread:\n%s%s.\n", L_ERRORxSTR, L_BLANKxSTR, strerror(nResult));
     return false;
   }
 
+  if (UseServerSideBuddyIcons())
+  {
+    m_xSSBIService = new COscarService(this, ICQ_SNACxFAM_SSBI);
+    nResult = pthread_create(&thread_ssbiservice, NULL,
+                             &OscarServiceSendQueue_tep, m_xSSBIService);
+    if (nResult != 0)
+    {
+      gLog.Error(tr("%sUnable to start SSBI service thread:\n%s%s.\n"),
+                 L_ERRORxSTR, L_BLANKxSTR, strerror(nResult));
+      return false;
+    }
+  }
+
   return true;
 }
 
@@ -804,6 +822,7 @@
 
   // Misc
   licqConf.WriteBool("UseSS", m_bUseSS); // server side list
+  licqConf.WriteBool("UseSSBI", m_bUseSSBI); // server side buddy icons
   licqConf.WriteBool("SendTypingNotification", m_bSendTN); 
   licqConf.WriteBool("ReconnectAfterUinClash", m_bReconnectAfterUinClash);
 
@@ -975,38 +994,46 @@
     delete m_xProxy;
     m_xProxy = NULL;
   }
+  m_xProxy = CreateProxy();
+}
+
+ProxyServer *CICQDaemon::CreateProxy()
+{
+  ProxyServer *Proxy = NULL;
 
   switch (m_nProxyType)
   {
     case PROXY_TYPE_HTTP :
-      m_xProxy = new HTTPProxyServer();
+      Proxy = new HTTPProxyServer();
       break;
     default:
       break;
   }
 
-  if (m_xProxy != NULL)
+  if (Proxy != NULL)
   {
     gLog.Info(tr("%sResolving proxy: %s:%d...\n"), L_INITxSTR, m_szProxyHost, m_nProxyPort);
-    if (!m_xProxy->SetProxyAddr(m_szProxyHost, m_nProxyPort)) {
+    if (!Proxy->SetProxyAddr(m_szProxyHost, m_nProxyPort))
+    {
       char buf[128];
 
       gLog.Warn(tr("%sUnable to resolve proxy server %s:\n%s%s.\n"), L_ERRORxSTR,
-                 m_szProxyHost, L_BLANKxSTR, m_xProxy->ErrorStr(buf, 128));
-      delete m_xProxy;
-      m_xProxy = NULL;
+                m_szProxyHost, L_BLANKxSTR, Proxy->ErrorStr(buf, 128));
+      delete Proxy;
+      Proxy = NULL;
     }
 
-    if (m_xProxy)
+    if (Proxy)
     {
       if (m_bProxyAuthEnabled)
-        m_xProxy->SetProxyAuth(m_szProxyLogin, m_szProxyPasswd);
+        Proxy->SetProxyAuth(m_szProxyLogin, m_szProxyPasswd);
 
-      m_xProxy->InitProxy();
+      Proxy->InitProxy();
     }
   }
-}
 
+  return Proxy;
+}
 
 unsigned short VersionToUse(unsigned short v_in)
 {
@@ -1111,6 +1138,25 @@
     m_nIgnoreTypes &= ~n;
 }
 
+void CICQDaemon::SetUseServerSideBuddyIcons(bool b)
+{
+  if (b && m_xSSBIService == NULL)
+  {
+    m_xSSBIService = new COscarService(this, ICQ_SNACxFAM_SSBI);
+    int nResult = pthread_create(&thread_ssbiservice, NULL,
+                                 &OscarServiceSendQueue_tep, m_xSSBIService);
+    if (nResult != 0)
+    {
+      gLog.Error(tr("%sUnable to start SSBI service thread:\n%s%s.\n"),
+                 L_ERRORxSTR, L_BLANKxSTR, strerror(nResult));
+    }
+    else
+      m_bUseSSBI = true;
+  }
+  else
+    m_bUseSSBI = b;
+}
+                                    
 bool CICQDaemon::AddUserToList(const char *szId, unsigned long nPPID,
                                bool bNotify, bool bTempUser)
 {
@@ -2029,6 +2075,13 @@
   return(e);
 }
 
+void CICQDaemon::PushEvent(ICQEvent *e)
+{
+  assert(e != NULL);
+  pthread_mutex_lock(&mutex_runningevents);
+  m_lxRunningEvents.push_back(e);
+  pthread_mutex_unlock(&mutex_runningevents);
+}
 
 /*------------------------------------------------------------------------------
  * PushExtendedEvent
diff -urN licq/src/icqd-srv.cpp licq-ssbi/src/icqd-srv.cpp
--- licq/src/icqd-srv.cpp	2008-01-03 16:09:23.000000000 +0200
+++ licq-ssbi/src/icqd-srv.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -27,6 +27,7 @@
 
 #include "licq_icqd.h"
 #include "licq_translate.h"
+#include "licq_oscarservice.h"
 #include "licq_packets.h"
 #include "licq_socket.h"
 #include "licq_user.h"
@@ -716,6 +717,43 @@
   return icqRequestMetaInfo(szUin);
 }
 
+//-----ProtoRequestPicture------------------------------------------------------
+unsigned long CICQDaemon::ProtoRequestPicture(const char *_szId, unsigned long _nPPID)
+{
+  unsigned long nRet = 0;
+
+  if (_nPPID == LICQ_PPID)
+  {
+    ICQUser *u = gUserManager.FetchUser(_szId, LICQ_PPID, LOCK_R);
+    if (u == NULL) return 0;
+
+    if (UseServerSideBuddyIcons() && strlen(u->BuddyIconHash()) > 0)
+    {
+      gUserManager.DropUser(u);
+      nRet = m_xSSBIService->SendEvent(_szId, ICQ_SNACxSSBI_ICONxREQUEST, true);
+    }
+    else
+    {
+      bool bSendServer = (u->SocketDesc(ICQ_CHNxINFO) < 0);
+      gUserManager.DropUser(u);
+      nRet = icqRequestPicture(_szId, bSendServer);
+    }
+  }
+  else
+    PushProtoSignal(new CRequestPicture(_szId), _nPPID);
+  
+  return nRet;
+}
+
+//-----icqRequestService--------------------------------------------------------
+void CICQDaemon::icqRequestService(unsigned short nFam)
+{
+  CPU_CommonFamily *p = new CPU_RequestService(nFam);
+  gLog.Info(tr("%sRequesting service socket for FAM 0x%02X (#%hu/#%d)...\n"),
+            L_SRVxSTR, nFam, p->Sequence(), p->SubSequence());
+  SendEvent_Server(p);
+}
+
 //-----icqSetStatus-------------------------------------------------------------
 unsigned long CICQDaemon::ProtoSetStatus(unsigned long _nPPID,
                                          unsigned short _nNewStatus)
@@ -1736,6 +1774,7 @@
       case MAKESNAC(ICQ_SNACxFAM_NEWUIN, ICQ_SNACxREGISTER_USER):
       case MAKESNAC(ICQ_SNACxFAM_LOCATION, ICQ_SNACxREQUESTxUSERxINFO):
       case MAKESNAC(ICQ_SNACxFAM_LOCATION, ICQ_SNACxLOC_INFOxREQ):
+      case MAKESNAC(ICQ_SNACxFAM_SSBI, ICQ_SNACxSSBI_ICONxREQUEST):
         PushPluginEvent(e);
         break;
 
@@ -1876,6 +1915,16 @@
 
 void CICQDaemon::postLogoff(int nSD, ICQEvent *cancelledEvent)
 {
+  if (m_xSSBIService)
+  {
+    if (m_xSSBIService->GetSocketDesc() != -1)
+    {
+      gSocketManager.CloseSocket(m_xSSBIService->GetSocketDesc());
+      m_xSSBIService->ResetSocket();
+      m_xSSBIService->ChangeStatus(STATUS_UNINITIALIZED);
+      m_xSSBIService->ClearQueue();
+    }
+  }
   pthread_mutex_lock(&mutex_runningevents);
   pthread_mutex_lock(&mutex_sendqueue_server);
   pthread_mutex_lock(&mutex_extendedevents);
@@ -2025,7 +2074,6 @@
       delete s;
       return (-1);
     }
-    s->SetProxy(m_xProxy);
   }
   else if (m_xProxy != NULL)
   {
@@ -2033,37 +2081,8 @@
     m_xProxy = NULL;
   }
   
-  char ipbuf[32];
-
-  if (m_xProxy == NULL)
-  {
-    gLog.Info(tr("%sResolving %s port %d...\n"), L_SRVxSTR, server, port);
-    if (!s->SetRemoteAddr(server, port)) {
-      char buf[128];
-      gLog.Warn(tr("%sUnable to resolve %s:\n%s%s.\n"), L_ERRORxSTR,
-                server, L_BLANKxSTR, s->ErrorStr(buf, 128));
-      delete s;
-      return (-1); // no route to host (not connected)
-    }
-    gLog.Info(tr("%sICQ server found at %s:%d.\n"), L_SRVxSTR,
-	      s->RemoteIpStr(ipbuf), s->RemotePort());
-  }
-  else
-  {
-    // It doesn't matter if it resolves or not, the proxy should do it then
-    s->SetRemoteAddr(server, port);
-  }
-
-  if (m_xProxy == NULL)
-    gLog.Info(tr("%sOpening socket to server.\n"), L_SRVxSTR);
-  else
-    gLog.Info("%sOpening socket to server via proxy.\n", L_SRVxSTR);
-  if (!s->OpenConnection())
+  if (!s->ConnectTo(server, port, m_xProxy))
   {
-    char buf[128];
-    gLog.Warn(tr("%sUnable to connect to %s:%d:\n%s%s.\n"), L_ERRORxSTR,
-              s->RemoteIpStr(ipbuf), s->RemotePort(), L_BLANKxSTR,
-              s->ErrorStr(buf, 128));
     delete s;
     return -1;
   }
@@ -2215,9 +2234,15 @@
 
 void CICQDaemon::ProcessServiceFam(CBuffer &packet, unsigned short nSubtype)
 {
-  packet.UnpackUnsignedShortBE(); // flags
+  unsigned short flags = packet.UnpackUnsignedShortBE(); // flags
   packet.UnpackUnsignedLongBE(); // sequence
 
+  if (flags & 0x8000) // version of the family that this SNAC, just ignore it
+  {
+    unsigned short len = packet.UnpackUnsignedShortBE();
+    packet.incDataPosRead(len);
+  }
+
   switch (nSubtype)
   {
   case ICQ_SNACxSUB_READYxSERVER:
@@ -2241,6 +2266,67 @@
       break;
     }
 
+  case ICQ_SNACxSUB_REDIRECT:
+    {
+      unsigned short nFam = 0;
+
+      if (!packet.readTLV())
+      {
+        gLog.Warn(tr("%sError during parsing service redirect packet!\n"), L_WARNxSTR);
+        break;
+      }
+      if (packet.getTLVLen(0x000D) == 2)
+        nFam = packet.UnpackUnsignedShortTLV(0x000D);
+
+      gLog.Info(tr("%sRedirect for service 0x%02X received.\n"), L_SRVxSTR, nFam);
+
+      char *szServer = packet.UnpackStringTLV(0x0005);
+      char *szCookie = packet.UnpackStringTLV(0x0006);
+      unsigned short nCookieLen = packet.getTLVLen(0x0006);
+      if (!szServer || !szCookie)
+      {
+        gLog.Warn(tr("%sInvalid servername (%s) or cookie (%s) in service redirect packet!\n"),
+                  L_WARNxSTR, szServer ? szServer : "(null)", szCookie ? szCookie : "(null)");
+        if (szServer) delete [] szServer;
+        if (szCookie) delete [] szCookie;
+        break;
+      }
+
+      char *szPort = strchr(szServer, ':');
+      unsigned short nPort;
+      if (szPort)
+      {
+        *szPort++ = '\0';
+        nPort = atoi(szPort);
+      } else
+      {
+        nPort = m_nICQServerPort;
+      }
+
+      switch (nFam)
+      {
+        case ICQ_SNACxFAM_SSBI:
+          if (m_xSSBIService)
+          {
+            m_xSSBIService->SetConnectCredential(szServer, nPort, szCookie, nCookieLen);
+            m_xSSBIService->ChangeStatus(STATUS_SERVICE_REQ_ACKED);
+            break;
+          } else
+          {
+            gLog.Warn(tr("%sService redirect packet for unallocated SSBI service.\n"),
+                      L_WARNxSTR);
+            break;
+          }
+        default:
+          gLog.Warn(tr("%sService redirect packet for unhandled service 0x%02X.\n"),
+                    L_WARNxSTR, nFam);
+      }
+
+      delete [] szServer;
+      delete [] szCookie;
+      break;
+    }
+
   case ICQ_SNACxSRV_ACKxIMxICQ:
     {
       // ICQOwner *o = gUserManager.FetchOwner(LICQ_PPID, LOCK_R);
@@ -2842,6 +2928,41 @@
       }
     }
 
+    if (packet.hasTLV(0x001d))	// Server-stored buddy icon information
+    {
+      CBuffer SSBI_info = packet.UnpackTLV(0x001d);
+      unsigned short IconType = SSBI_info.UnpackUnsignedShortBE();
+      char HashType = SSBI_info.UnpackChar();
+      char HashLenght = SSBI_info.UnpackChar();
+      
+      switch (IconType)
+      {
+        case 0x01:	// Simple Buddy Icon 
+        case 0x0C:	// Photo Buddy Icon
+        {
+          if (HashType == 1 && HashLenght > 0 && HashLenght <= 16)
+          {
+            char *Hash = new char[HashLenght];
+            char *HashHex = new char[HashLenght*2 + 1];
+            
+            SSBI_info.UnpackBinBlock(Hash, HashLenght);
+            u->SetBuddyIconHash(PrintHex(HashHex, Hash, HashLenght));
+            u->SetBuddyIconType(IconType);
+            u->SetBuddyIconHashType(HashType);
+            u->SavePictureInfo();
+            delete [] Hash;
+            delete [] HashHex;
+          }
+          break;
+        }
+
+        default:	// Unsupported types of SSBI
+          gLog.Warn(tr("%sUnsupported type 0x%02X of buddy icon for %s.\n"),
+                    L_WARNxSTR, IconType, u->GetAlias());
+          break;
+      }
+    }
+    
     // maybe use this for auto update info later
     u->SetClientTimestamp(nInfoTimestamp);
     u->SetClientInfoTimestamp(nInfoPluginTimestamp);
diff -urN licq/src/icqd-tcp.cpp licq-ssbi/src/icqd-tcp.cpp
--- licq/src/icqd-tcp.cpp	2007-10-10 13:36:54.000000000 +0300
+++ licq-ssbi/src/icqd-tcp.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -3430,6 +3430,9 @@
           {
             gLog.Info("%sPicture reply from %s.\n", szInfo, u->GetAlias());
             packet.incDataPosRead(nEntries); // filename, don't care
+            unsigned long nLen = packet.UnpackUnsignedLong();
+            if (nLen == 0)	// do not create empty .pic files
+              break;
 
             char szFilename[MAX_FILENAME_LEN];
             szFilename[MAX_FILENAME_LEN - 1] = '\0';
@@ -3444,7 +3447,6 @@
               break;
             }
 
-            unsigned long nLen = packet.UnpackUnsignedLong();
             char data[nLen];
             for (unsigned long i = 0; i < nLen; i++)
             {
@@ -3452,6 +3454,7 @@
             }
 
             write(nFD, data, nLen);
+            close(nFD);
 
             u->SetEnableSave(false);
             u->SetPicturePresent(true);
diff -urN licq/src/icqd-threads.cpp licq-ssbi/src/icqd-threads.cpp
--- licq/src/icqd-threads.cpp	2007-10-10 13:36:54.000000000 +0300
+++ licq-ssbi/src/icqd-threads.cpp	2008-01-22 01:11:43.000000000 +0200
@@ -17,6 +17,7 @@
 #include "licq_icqd.h"
 #include "licq_log.h"
 #include "licq_packets.h"
+#include "licq_oscarservice.h"
 #include "licq_plugind.h"
 #include "licq.h"
 
@@ -665,7 +666,7 @@
   CICQDaemon *d = (CICQDaemon *)p;
 
   fd_set f;
-  int nSocketsAvailable, nCurrentSocket, l;
+  int nSocketsAvailable, nCurrentSocket, nServiceSocket, l;
   char buf[1024];
 
   while (true)
@@ -691,6 +692,14 @@
 
     /*pthread_testcancel();
     pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);*/
+    if (d->m_xSSBIService)
+    {
+      COscarService *svc = d->m_xSSBIService;
+      nServiceSocket = svc->GetSocketDesc();
+    }
+    else
+      nServiceSocket = -1;
+                                                  
     nCurrentSocket = 0;
     while (nSocketsAvailable > 0 && nCurrentSocket < l)
     {
@@ -764,6 +773,37 @@
             }
           }
 
+          // Message from the service sockets -----------------------------------
+          else if (nCurrentSocket == nServiceSocket)
+          {
+            DEBUG_THREADS("[MonitorSockets_tep] Data on SSBI service socket.\n");
+            COscarService *svc = d->m_xSSBIService;
+            SrvSocket *sock_svc = static_cast<SrvSocket*>(s);
+            if (sock_svc->Recv())
+            {
+              CBuffer packet(sock_svc->RecvBuffer());
+              sock_svc->ClearRecvBuffer();
+              gSocketManager.DropSocket(sock_svc);
+              if (!svc->ProcessPacket(packet))
+              {
+                gLog.Warn(tr("%sCan't process packet for service 0x%02X.\n"),
+                          L_WARNxSTR, svc->GetFam());
+                svc->ResetSocket();
+                svc->ChangeStatus(STATUS_UNINITIALIZED);
+                gSocketManager.CloseSocket(nCurrentSocket);
+              }
+            }
+            else
+            {
+              gLog.Warn(tr("%sCan't receive packet for service 0x%02X.\n"),
+                        L_WARNxSTR, svc->GetFam());
+              svc->ResetSocket();
+              svc->ChangeStatus(STATUS_UNINITIALIZED);
+              gSocketManager.DropSocket(sock_svc);
+              gSocketManager.CloseSocket(nCurrentSocket);
+            }
+          }
+
           // Connection on the server port -------------------------------------
           else if (nCurrentSocket == d->m_nTCPSocketDesc)
           {
@@ -901,7 +941,11 @@
 
   // Cancel the update users thread
   pthread_cancel(d->thread_updateusers);
-  
+
+  // Cancel the SSBI service thread
+  if (d->m_xSSBIService)
+    pthread_cancel(d->thread_ssbiservice);
+
   // Join our threads
   pthread_join(d->thread_monitorsockets, NULL);
 
@@ -937,6 +981,7 @@
       FOR_EACH_PROTO_USER_START(LICQ_PPID, LOCK_R)
       {
         bool bSent = false;
+        bool bSSBI = false;
 
         if (d->AutoUpdateInfo() && !pUser->UserUpdated() &&
             pUser->ClientTimestamp() != pUser->OurClientTimestamp()
@@ -946,6 +991,15 @@
           bSent = true;
         }
 
+        if (d->UseServerSideBuddyIcons() && d->AutoUpdateInfo() &&
+            strlen(pUser->BuddyIconHash()) > 0 &&
+            strcmp(pUser->BuddyIconHash(), pUser->OurBuddyIconHash()) != 0)
+        {
+          d->m_xSSBIService->SendEvent(pUser->IdString(), ICQ_SNACxSSBI_ICONxREQUEST, true);
+          bSent = true;
+          bSSBI = true;
+        }
+        
         if (!pUser->StatusOffline() && !pUser->UserUpdated() &&
             //Don't bother clients that we know don't support plugins
             pUser->Version() >= 7 &&
@@ -974,7 +1028,8 @@
             gLog.Info("Updating %s's info plugins.\n", pUser->GetAlias());
             d->icqRequestInfoPlugin(pUser, true, PLUGIN_QUERYxINFO);
             d->icqRequestInfoPlugin(pUser, true, PLUGIN_PHONExBOOK);
-            d->icqRequestInfoPlugin(pUser, true, PLUGIN_PICTURE);
+            if (!bSSBI) // Send only if we din't request SSBI already
+              d->icqRequestInfoPlugin(pUser, true, PLUGIN_PICTURE);
             bSent = true;
           }
 
diff -urN licq/src/icqevent.cpp licq-ssbi/src/icqevent.cpp
--- licq/src/icqevent.cpp	2006-11-11 21:24:53.000000000 +0200
+++ licq-ssbi/src/icqevent.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -109,18 +109,32 @@
 //   : m_xBuffer(p.getBuffer())
 {
   // set up internal variables
-  m_pPacket = p;
   m_bCancelled = false;
   m_Deleted = false;
   m_NoAck = false;
-  m_nChannel = p->Channel();
-  m_nCommand = p->Command();
-  m_nSNAC = p->SNAC();
-  m_nSubCommand = p->SubCommand();
-  m_nSequence = p->Sequence();
-  m_nSubSequence = p->SubSequence();
-  m_nSubType = (p->SNAC() & 0xFFFF);
-  m_nExtraInfo = p->ExtraInfo();
+  if (p)
+  {
+    m_pPacket = p;
+    m_nChannel = p->Channel();
+    m_nCommand = p->Command();
+    m_nSNAC = p->SNAC();
+    m_nSubCommand = p->SubCommand();
+    m_nSequence = p->Sequence();
+    m_nSubSequence = p->SubSequence();
+    m_nSubType = (p->SNAC() & 0xFFFF);
+    m_nExtraInfo = p->ExtraInfo();
+  } else
+  {
+    m_pPacket = NULL;
+    m_nChannel = 0;
+    m_nCommand = 0;
+    m_nSNAC = 0;
+    m_nSubCommand = 0;
+    m_nSequence = 0;
+    m_nSubSequence = 0;
+    m_nSubType = 0;
+    m_nExtraInfo = 0;
+  }
   m_nDestinationUin = 0;
   m_szId = _szId ? strdup(_szId) : 0;
   m_nPPID = _nPPID;
@@ -200,6 +214,20 @@
 }
 
 
+//-----ICQEvent::AttachPacket---------------------------------------------------
+void ICQEvent::AttachPacket(CPacket *p)
+{
+  m_pPacket = p;
+  m_nChannel = p->Channel();
+  m_nCommand = p->Command();
+  m_nSNAC = p->SNAC();
+  m_nSubCommand = p->SubCommand();
+  m_nSequence = p->Sequence();
+  m_nSubSequence = p->SubSequence();
+  m_nSubType = (p->SNAC() & 0xFFFF);
+  m_nExtraInfo = p->ExtraInfo();
+}
+  
 //-----ICQEvent::CompareEvent---------------------------------------------------
 bool ICQEvent::CompareEvent(int sockfd, unsigned short _nSequence) const
 {
@@ -470,6 +498,15 @@
   free(m_szZipCode);
 }
 
+CRequestPicture::CRequestPicture(const char *szId)
+  : CSignal(PROTOxREQUESTxPICTURE, szId)
+{
+}
+
+CRequestPicture::~CRequestPicture()
+{
+}
+
 CBlockUserSignal::CBlockUserSignal(const char *szId)
   : CSignal(PROTOxBLOCKxUSER, szId)
 {
diff -urN licq/src/icqpacket.cpp licq-ssbi/src/icqpacket.cpp
--- licq/src/icqpacket.cpp	2008-01-03 16:09:23.000000000 +0200
+++ licq-ssbi/src/icqpacket.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -268,7 +268,8 @@
 
 //======Server TCP============================================================
 bool CSrvPacketTcp::s_bRegistered = false;
-unsigned short CSrvPacketTcp::s_nSequence = 0;
+unsigned short CSrvPacketTcp::s_nSequence[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 unsigned short CSrvPacketTcp::s_nSubSequence = 0;
 pthread_mutex_t CSrvPacketTcp::s_xMutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -276,13 +277,14 @@
 {
   m_nChannel = nChannel;
   pthread_mutex_lock(&s_xMutex);
-  m_nSequence = s_nSequence++;
+  // will set m_nSequence later, in InitBuffer;
   m_nSubSequence = s_nSubSequence++;
   pthread_mutex_unlock(&s_xMutex);
   m_nFamily = m_nSubType = m_nSubCommand = m_nExtraInfo = 0;
 
   buffer = NULL;
   m_nSize = 0;
+  m_nService = 0;
   m_szSequenceOffset = NULL;
 }
 
@@ -300,6 +302,13 @@
 
 void CSrvPacketTcp::InitBuffer()
 {
+  pthread_mutex_lock(&s_xMutex);
+  if (s_nSequence[m_nService] == 0xffff)
+    s_nSequence[m_nService] = rand() & 0x7fff;
+  m_nSequence = s_nSequence[m_nService]++;
+  s_nSequence[m_nService] &= 0x7fff;
+  pthread_mutex_unlock(&s_xMutex);
+
   buffer = new CBuffer(m_nSize+6);
   buffer->PackChar(0x2a);
   buffer->PackChar(m_nChannel);
@@ -686,9 +695,8 @@
   m_nSize = 4;
 
   pthread_mutex_lock(&s_xMutex);
-  s_nSequence = rand() & 0x7fff;
+  s_nSequence[m_nService] = 0xffff;
   s_bRegistered = true;
-  m_nSequence = s_nSequence++;
   pthread_mutex_unlock(&s_xMutex);
 
   InitBuffer();
@@ -792,9 +800,8 @@
 
   pthread_mutex_lock(&s_xMutex);
   if (!s_bRegistered) {
-    s_nSequence = rand() & 0x7fff;
+    s_nSequence[m_nService] = 0xffff;
     s_bRegistered = true;
-    m_nSequence = s_nSequence++;
   }
   pthread_mutex_unlock(&s_xMutex);
 
@@ -847,13 +854,14 @@
 }
 
 //-----SendCookie------------------------------------------------------------
-CPU_SendCookie::CPU_SendCookie(const char *szCookie, int nLen)
+CPU_SendCookie::CPU_SendCookie(const char *szCookie, int nLen,
+                               unsigned short nService)
   : CSrvPacketTcp(ICQ_CHNxNEW)
 {
+  m_nService = nService;
   m_nSize = nLen + 8;
   pthread_mutex_lock(&s_xMutex);
-  s_nSequence = (rand() & 0x7fff);
-  m_nSequence = s_nSequence++;
+  s_nSequence[m_nService] = 0xffff;
   pthread_mutex_unlock(&s_xMutex);
   InitBuffer();
 
@@ -887,7 +895,22 @@
   buffer->PackUnsignedLongBE(0x000b0001);
 }
 
-//-----ImICQ-----------------------------------------------------------------
+CPU_ImICQ::CPU_ImICQ(unsigned short VerArray[][2], unsigned short NumVer,
+                     unsigned short nService)
+  : CPU_CommonFamily(ICQ_SNACxFAM_SERVICE, ICQ_SNACxSRV_IMxICQ)
+{
+  m_nService = nService;
+  m_nSize += NumVer * 4;
+  InitBuffer();
+  
+  for (int i = 0; i < NumVer; i++)
+  {
+    buffer->PackUnsignedShortBE(VerArray[i][0]);
+    buffer->PackUnsignedShortBE(VerArray[i][1]);
+  }
+}
+
+//-----ImICQMode-------------------------------------------------------------
 CPU_ICQMode::CPU_ICQMode(unsigned short channel, unsigned long flags)
   : CPU_CommonFamily(ICQ_SNACxFAM_MESSAGE, ICQ_SNACxMSG_SETxICQxMODE)
 {
@@ -907,9 +930,10 @@
 }
 
 //-----RateAck-----------------------------------------------------------------
-CPU_RateAck::CPU_RateAck()
+CPU_RateAck::CPU_RateAck(unsigned short nService)
   : CPU_CommonFamily(ICQ_SNACxFAM_SERVICE, ICQ_SNACxSND_RATE_ACK)
 {
+  m_nService = nService;
   m_nSize += 10;
 
   InitBuffer();
@@ -923,7 +947,7 @@
 CPU_CapabilitySettings::CPU_CapabilitySettings()
   : CPU_CommonFamily(ICQ_SNACxFAM_LOCATION, ICQ_SNACxLOC_SETxUSERxINFO)
 {
-  char data[7][CAP_LENGTH];
+  char data[8][CAP_LENGTH];
   m_nSize += 4 + sizeof(data);
   InitBuffer();
 
@@ -934,6 +958,7 @@
   memcpy(data[4], ICQ_CAPABILITY_AIMxINTER, CAP_LENGTH);
   memcpy(data[5], ICQ_CAPABILITY_RTFxMSGS, CAP_LENGTH);
   memcpy(data[6], ICQ_CAPABILITY_ICHAT, CAP_LENGTH);
+  memcpy(data[7], ICQ_CAPABILITY_SSBI, CAP_LENGTH);
 
   // Send our licq version
   data[3][12] = INT_VERSION / 1000;
@@ -946,6 +971,40 @@
   buffer->PackTLV(0x05, sizeof(data), (char *)data);  
 }
 
+//-----RequestBuddyIcon---------------------------------------------------------
+CPU_RequestBuddyIcon::CPU_RequestBuddyIcon(const char *_szId,
+                      unsigned short _nBuddyIconType, char _nBuddyIconHashType,
+                      const char *_szBuddyIconHash, unsigned short nService)
+  : CPU_CommonFamily(ICQ_SNACxFAM_SSBI, ICQ_SNACxSSBI_ICONxREQUEST)
+{
+  int nSize = strlen(_szId);
+  int nHashLenght = strlen(_szBuddyIconHash)/2;
+  char *Hash = new char[nHashLenght];
+  m_nService = nService;
+  m_nSize += 6 + nSize + nHashLenght;
+
+  InitBuffer();
+  
+  buffer->PackChar(nSize);
+  buffer->Pack(_szId, nSize);
+  buffer->PackChar(0x01);	// unknown, probably type of request
+  buffer->PackUnsignedShortBE(_nBuddyIconType);
+  buffer->PackChar(_nBuddyIconHashType);
+  buffer->PackChar(nHashLenght);
+  buffer->Pack(ReadHex(Hash, _szBuddyIconHash, nHashLenght), nHashLenght);
+}
+
+//-----RequestService-----------------------------------------------------------
+CPU_RequestService::CPU_RequestService(unsigned short nFam)
+  : CPU_CommonFamily(ICQ_SNACxFAM_SERVICE, ICQ_SNACxSUB_NEW_SERVICE)
+{
+  m_nSize += 2;
+
+  InitBuffer();
+
+  buffer->PackUnsignedShortBE(nFam);
+}
+
 //-----SetPrivacy---------------------------------------------------------------
 CPU_SetPrivacy::CPU_SetPrivacy(unsigned char _cPrivacy)
   : CPU_CommonFamily(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_ROSTxUPD_GROUP)
@@ -1190,9 +1249,11 @@
   buffer->Pack(uin, n);
 }
 
-CPU_GenericFamily::CPU_GenericFamily(unsigned short Family, unsigned short SubType)
+CPU_GenericFamily::CPU_GenericFamily(unsigned short Family, unsigned short SubType,
+                                     unsigned short nService)
   : CPU_CommonFamily(Family, SubType)
 {
+  m_nService = nService;
   m_nSize += 0;
   InitBuffer();
 }
@@ -1284,6 +1345,23 @@
 #endif
 }
 
+CPU_ClientReady::CPU_ClientReady(unsigned short VerArray[][4], unsigned short NumVer,
+                                 unsigned short nService)
+  : CPU_CommonFamily(ICQ_SNACxFAM_SERVICE, ICQ_SNACxSUB_READYxCLIENT)
+{
+  m_nService = nService;
+  m_nSize += NumVer * 8;
+  InitBuffer();
+  
+  for (int i = 0; i < NumVer; i++)
+  {
+    buffer->PackUnsignedShortBE(VerArray[i][0]);
+    buffer->PackUnsignedShortBE(VerArray[i][1]);
+    buffer->PackUnsignedShortBE(VerArray[i][2]);
+    buffer->PackUnsignedShortBE(VerArray[i][3]);
+  }
+}
+
 CPU_AckNameInfo::CPU_AckNameInfo()
   : CPU_CommonFamily(ICQ_SNACxFAM_SERVICE, ICQ_SNACxSND_NAMExINFOxACK)
 {
diff -urN licq/src/Makefile.am licq-ssbi/src/Makefile.am
--- licq/src/Makefile.am	2006-11-11 21:24:53.000000000 +0200
+++ licq-ssbi/src/Makefile.am	2008-01-22 01:11:26.000000000 +0200
@@ -11,7 +11,7 @@
 bin_PROGRAMS = licq
 licq_SOURCES = licq.cpp main.cpp \
 	 icqpacket.cpp proxy.cpp socket.cpp icqd.cpp \
-	 icqd-tcp.cpp icqd-srv.cpp icqd-threads.cpp \
+	 icqd-tcp.cpp icqd-srv.cpp icqd-threads.cpp oscarservice.cpp \
 	 icqevent.cpp buffer.cpp user.cpp history.cpp \
 	 utility.cpp countrycodes.c log.cpp translate.cpp \
 	 file.cpp message.cpp support.c pthread_rdwr.c \
diff -urN licq/src/oscarservice.cpp licq-ssbi/src/oscarservice.cpp
--- licq/src/oscarservice.cpp	1970-01-01 03:00:00.000000000 +0300
+++ licq-ssbi/src/oscarservice.cpp	2008-01-22 01:11:46.000000000 +0200
@@ -0,0 +1,660 @@
+// -*- c-basic-offset: 2 -*-
+/* ----------------------------------------------------------------------------
+ * Licq - A ICQ Client for Unix
+ * Copyright (C) 2007 Licq developers
+ *
+ * This program is licensed under the terms found in the LICENSE file.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+                     
+#include <cstdlib>
+#include <cstdio>
+
+// Localization
+#include "gettext.h"
+
+#include "licq_oscarservice.h"
+#include "licq_icqd.h"
+#include "licq_events.h"
+#include "licq_socket.h"
+#include "licq_proxy.h"
+#include "licq_packets.h"
+#include "licq_buffer.h"
+#include "licq_log.h"
+#include "support.h"
+
+COscarService::COscarService(CICQDaemon *Daemon, unsigned short Fam)
+{
+  myDaemon = Daemon;
+  myFam = Fam;
+  mySocketDesc = -1;
+  myProxy = NULL;
+  myStatus = STATUS_UNINITIALIZED;
+  myServer = NULL;
+  myCookie = NULL;
+  pthread_mutex_init(&mutex_sendqueue, NULL);
+  pthread_cond_init(&cond_sendqueue, NULL);
+  pthread_mutex_init(&mutex_status, NULL);
+  pthread_cond_init(&cond_status, NULL);
+}
+
+COscarService::~COscarService()
+{
+  if (myProxy) delete myProxy;
+  if (myServer) free(myServer);
+  if (myCookie) delete [] myCookie;
+}
+
+void COscarService::ChangeStatus(EOscarServiceStatus s)
+{
+  pthread_mutex_lock(&mutex_status);
+  myStatus = s;
+  pthread_cond_signal(&cond_status);
+  pthread_mutex_unlock(&mutex_status);
+}
+
+bool COscarService::WaitForStatus(EOscarServiceStatus s)
+{
+  pthread_mutex_lock(&mutex_status);
+
+  struct timespec ts;
+  ts.tv_nsec = 0;
+  //wait for 120 seconds
+  ts.tv_sec = time(NULL) + 120;
+  
+  if (pthread_cond_timedwait(&cond_status, &mutex_status, &ts) == ETIMEDOUT)
+  {
+    pthread_mutex_unlock(&mutex_status);
+    return false;
+  }
+  if (myStatus == s)
+  {
+    pthread_mutex_unlock(&mutex_status);
+    return true;
+  }
+
+  pthread_mutex_unlock(&mutex_status);
+  return false;
+}
+
+void COscarService::SetConnectCredential(char *Server, unsigned short Port,
+                                         char *Cookie, unsigned short CookieLen)
+{
+  if (myServer) free(myServer);
+  myServer = strdup(Server);
+  myPort = Port;
+  if (myCookie) delete [] myCookie;
+  myCookie = new char[CookieLen];
+  memcpy(myCookie, Cookie, CookieLen);
+  myCookieLen = CookieLen;
+}
+
+bool COscarService::SendPacket(CPacket *p)
+{
+  INetSocket *s = gSocketManager.FetchSocket(mySocketDesc);
+  if (s == NULL) return false;
+  CBuffer *b = p->Finalize(s);
+  if (!s->Send(b))
+  {
+    char ErrorBuf[128];
+    s->ErrorStr(ErrorBuf, 128);
+    gLog.Warn(tr("%sError sending event (FAM #%02X, Subtype #%02X, Sequence #%hu):\n%s%s.\n"),
+              L_WARNxSTR, (unsigned short)((p->SNAC() >> 16) & 0xffff), (unsigned short)(p->SNAC() & 0xffff),
+              p->Sequence(), L_BLANKxSTR, ErrorBuf);
+    gSocketManager.DropSocket(s);
+    delete b;
+    return false;
+  }
+  gSocketManager.DropSocket(s);
+  delete b;
+  return true;
+}
+
+void COscarService::ClearQueue()
+{
+  pthread_mutex_lock(&mutex_sendqueue);
+  std::list<ICQEvent *>::iterator iter;
+  unsigned long i = mySendQueue.size();
+  for (iter = mySendQueue.begin(); i > 0; i--)
+  {
+    ICQEvent *e = *iter;
+    mySendQueue.erase(iter);
+    if (e != NULL)
+    {
+      gLog.Info("Event #%hu is still on the service 0x%02X queue!\n", e->Sequence(), myFam);
+      delete e;
+    }
+  }
+  pthread_mutex_unlock(&mutex_sendqueue);
+}
+
+unsigned long COscarService::SendEvent(const char *Id,
+                                       unsigned short SubType, bool Request)
+{
+  ICQEvent *e = new ICQEvent(myDaemon, mySocketDesc, NULL, CONNECT_SERVER,
+                             Id, LICQ_PPID, NULL);
+  e->SetSubType(SubType);
+  if (Request)
+    myDaemon->PushEvent(e);
+  else
+    e->SetNoAck(true);
+  pthread_mutex_lock(&mutex_sendqueue);
+  mySendQueue.push_back(e);
+  pthread_cond_signal(&cond_sendqueue);
+  pthread_mutex_unlock(&mutex_sendqueue);
+
+  return e->EventId();
+}
+
+bool COscarService::SendSSBIFam(ICQEvent *e)
+{
+  switch (e->SubType())
+  {
+    case ICQ_SNACxSSBI_ICONxREQUEST:
+    {
+      ICQUser *u = gUserManager.FetchUser(e->Id(), LICQ_PPID, LOCK_R);
+      CPU_RequestBuddyIcon *p = new CPU_RequestBuddyIcon(e->Id(), u->BuddyIconType(),
+                                    u->BuddyIconHashType(), u->BuddyIconHash(), myFam);
+      gLog.Info(tr("%sRequesting buddy icon for %s (#%hu/#%d)...\n"),
+                L_SRVxSTR, u->GetAlias(), p->Sequence(), p->SubSequence());
+      gUserManager.DropUser(u);
+      e->AttachPacket(p);
+      return (SendPacket(p));
+    }
+    
+    default:
+      gLog.Warn(tr("%sEvent with unsupported subtype (%02X) for FAM %02X failed.\n"),
+                L_WARNxSTR, e->SubType(), myFam);
+      return false;
+  }
+
+  return false;
+}
+
+bool COscarService::ProcessPacket(CBuffer &packet)
+{
+  unsigned short Len;
+  unsigned short Sequence;
+  char startCode, Channel;
+
+  // read in the serveice header info
+  packet >> startCode;
+
+  if (startCode != 0x2a)
+  {
+    gLog.Warn(tr("%sbad start code %d for packet in socket of service 0x%02X.\n"),
+               L_WARNxSTR, startCode, myFam);
+    return false;
+  }
+
+  packet >> Channel
+         >> Sequence
+         >> Len;
+
+  rev_e_short(Sequence);
+  rev_e_short(Len);
+
+  switch (Channel)
+  {
+    case ICQ_CHNxNEW:
+      ProcessNewChannel(packet);
+      break;
+
+    case ICQ_CHNxDATA:
+      ProcessDataChannel(packet);
+      break;
+
+    case ICQ_CHNxCLOSE:
+      gLog.Info(tr("%sServer send us request for close service 0x%02X.\n"),
+                L_SRVxSTR, myFam);
+      return false;
+      break;
+
+    default:
+      gLog.Warn(tr("%sPacket from unhandled channel %02x for service 0x%02X.\n"),
+                L_WARNxSTR, Channel, myFam);
+      break;
+  }
+
+  return true;
+}
+
+void COscarService::ProcessNewChannel(CBuffer &packet)
+{
+  unsigned long Version = packet.UnpackUnsignedLongBE();
+  
+  if (Version != 0x00000001)
+  {
+    gLog.Warn(tr("%sPacket with wrong version (0x%08lx) from new channel for service 0x%02X.\n"),
+              L_WARNxSTR, Version, myFam);
+  }
+}
+              
+void COscarService::ProcessDataChannel(CBuffer &packet)
+{
+  unsigned short Family, SubType, Flags;
+  unsigned long RequestId;
+
+  packet >> Family >> SubType >> Flags >> RequestId;
+  rev_e_short(Family);
+  rev_e_short(SubType);
+  rev_e_short(Flags);
+  rev_e_long(RequestId);
+
+  if (Flags & 0x8000) // version of the family that this SNAC, just ignore it
+  {
+    unsigned short len = packet.UnpackUnsignedShortBE();
+    packet.incDataPosRead(len);
+  }
+
+  switch (Family)
+  {
+    case ICQ_SNACxFAM_SERVICE:
+      ProcessServiceFam(packet, SubType, RequestId);
+      break;
+
+    case ICQ_SNACxFAM_SSBI:
+      if (myFam == ICQ_SNACxFAM_SSBI)
+        ProcessSSBIFam(packet, SubType, RequestId);
+      else
+        gLog.Warn(tr("%sUsupported family %04hx\n on data channel of service %02X.\n"),
+                  L_WARNxSTR, Family, myFam);
+      break;
+
+    default:
+      gLog.Warn(tr("%sUnknown or usupported family %04hx\n on data channel of service %02X.\n"),
+                L_WARNxSTR, Family, myFam);
+      break;
+  }
+}
+
+void COscarService::ProcessServiceFam(CBuffer &packet, unsigned short SubType,
+                                      unsigned long RequestId)
+{
+  switch (SubType)
+  {
+    case ICQ_SNACxSUB_ERROR:
+    {
+      unsigned short err = packet.UnpackUnsignedShortBE();
+      unsigned short suberr = 0;
+      
+      packet.readTLV();
+      if (packet.getTLVLen(0x0008) == 2)
+        suberr = packet.UnpackUnsignedShortTLV(0x0008);
+      gLog.Warn(tr("%sError #%02x.%02x in control FAM request (%ld) for service 0x%02X.\n"),
+                L_WARNxSTR, err, suberr, RequestId, myFam);
+      break;
+    }
+
+    case ICQ_SNACxSUB_READYxSERVER:
+      gLog.Info(tr("%sServer says he's ready for service 0x%02X.\n"),
+                L_SRVxSTR, myFam);
+      ChangeStatus(STATUS_SRV_READY_RECV);
+      break;
+
+    case ICQ_SNACxSRV_ACKxIMxICQ:
+      gLog.Info(tr("%sServer sent us channel capability list for service 0x%02X.\n"),
+                L_SRVxSTR, myFam);
+      ChangeStatus(STATUS_SRV_VER_RECV);
+      break;
+
+    case ICQ_SNACxSUB_RATE_INFO:
+      gLog.Info(tr("%sServer sent us rate-limits information for service 0x%02X.\n"),
+                L_SRVxSTR, myFam);
+      ChangeStatus(STATUS_SRV_RATE_RECV);
+      break;
+
+    default:
+      gLog.Warn(tr("%sUnknown or unsupported service FAM subtype 0x%02X for service 0x%02X.\n"),
+                L_WARNxSTR, SubType, myFam);
+      break;
+  }
+}
+
+void COscarService::ProcessSSBIFam(CBuffer &packet, unsigned short SubType,
+                                   unsigned long RequestId)
+{
+  switch (SubType)
+  {
+    case ICQ_SNACxSSBI_ERROR:
+    {
+      unsigned short err = packet.UnpackUnsignedShortBE();
+      unsigned short suberr = 0;
+      
+      packet.readTLV();
+      if (packet.getTLVLen(0x0008) == 2)
+        suberr = packet.UnpackUnsignedShortTLV(0x0008);
+      gLog.Warn(tr("%sError #%02x.%02x in SSBI request (%ld) for service 0x%02X.\n"),
+                    L_WARNxSTR, err, suberr, RequestId, myFam);
+
+      ICQEvent *e = myDaemon->DoneServerEvent(RequestId, EVENT_ERROR);
+      if (e)
+        myDaemon->ProcessDoneEvent(e);
+      break;
+    }
+
+    case ICQ_SNACxSSBI_ICONxREPLY:
+    {
+      char *Id = packet.UnpackUserString();
+      ICQUser *u = gUserManager.FetchUser(Id, LICQ_PPID, LOCK_W);
+      if (u == NULL)
+      {
+        gLog.Warn(tr("%sBuddy icon for unknown user (%s).\n"),
+                  L_WARNxSTR, Id);
+        delete [] Id;
+        break;
+      }
+      delete [] Id;
+
+      unsigned short IconType = packet.UnpackUnsignedShortBE();
+      char HashType = packet.UnpackChar();                     
+      char HashLenght = packet.UnpackChar();
+      switch (IconType)
+      {
+        case 0x01:	// Simple Buddy Icon
+        case 0x0C:	// Photo Buddy Icon
+          if (HashType == 1 && HashLenght > 0 && HashLenght <= 16)
+          {
+            char *Hash = new char[HashLenght];
+            char *HashHex = new char[HashLenght*2 + 1];
+            packet.UnpackBinBlock(Hash, HashLenght);
+            packet.UnpackChar(); // unknown (command ?)
+            packet.UnpackUnsignedShortBE(); // IconType once more
+            packet.UnpackChar(); // HashType once more
+            char HashLenght2 = packet.UnpackChar(); // HashLenght once more
+            packet.incDataPosRead(HashLenght2); // Hash once more
+            u->SetOurBuddyIconHash(PrintHex(HashHex, Hash, HashLenght));
+            delete [] Hash;
+            delete [] HashHex;
+
+            gLog.Info(tr("%sBuddy icon reply for %s.\n"), L_SRVxSTR, u->GetAlias());
+            unsigned short IconLen = packet.UnpackUnsignedShortBE();
+            if (IconLen > 0) // do not create empty .pic files
+            {
+              char Filename[MAX_FILENAME_LEN];
+              Filename[MAX_FILENAME_LEN - 1] = '\0';
+              snprintf(Filename, MAX_FILENAME_LEN - 1, "%s/%s/%lu.pic",
+                       BASE_DIR, USER_DIR, u->Uin());
+              int FD = open(Filename, O_WRONLY | O_CREAT | O_TRUNC, 00664);
+              if (FD == -1)
+              {
+                gLog.Error(tr("%sUnable to open picture file (%s):\n%s%s.\n"),
+                           L_ERRORxSTR, Filename, L_BLANKxSTR, strerror(errno));
+                break;
+              }
+
+              char *Icon = new char[IconLen];
+              packet.UnpackBinBlock(Icon, IconLen);
+              write(FD, Icon, IconLen);
+              close(FD);
+              delete [] Icon;
+
+              u->SetEnableSave(false);
+              u->SetPicturePresent(true);
+              u->SetEnableSave(true);
+            }
+            u->SavePictureInfo();
+            myDaemon->PushPluginSignal(new CICQSignal(SIGNAL_UPDATExUSER, USER_PICTURE,
+                                                      u->IdString(), u->PPID()));
+
+            ICQEvent *e = myDaemon->DoneServerEvent(RequestId, EVENT_SUCCESS);
+            if (e)
+              myDaemon->ProcessDoneEvent(e);
+          }
+          else
+          {
+            gLog.Warn(tr("%sBuddy icon reply for %s with wrong or unsupported hashtype (%d) or hashlenght (%d).\n"),
+                      L_WARNxSTR, u->GetAlias(), HashType, HashLenght);
+            ICQEvent *e = myDaemon->DoneServerEvent(RequestId, EVENT_FAILED);
+            if (e)
+              myDaemon->ProcessDoneEvent(e);
+          }
+          break;
+
+        default:
+          gLog.Warn(tr("%sBuddy icon reply for %s with wrong or unsupported icontype (0x%02x).\n"),
+                    L_WARNxSTR, u->GetAlias(), IconType);
+          ICQEvent *e = myDaemon->DoneServerEvent(RequestId, EVENT_FAILED);
+          if (e)
+            myDaemon->ProcessDoneEvent(e);
+          break;
+      }
+      gUserManager.DropUser(u);
+      break;
+    }
+
+    default:
+      break;
+  }
+}
+
+bool COscarService::Initialize()
+{
+  ChangeStatus(STATUS_SERVICE_REQ_SENT);
+  myDaemon->icqRequestService(myFam);
+  
+  if (!WaitForStatus(STATUS_SERVICE_REQ_ACKED))
+  {
+    gLog.Warn(tr("%sGive up waiting for redirect reply while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  ChangeStatus(STATUS_CONNECTED);
+  SrvSocket *s = new SrvSocket(gUserManager.OwnerUin());
+  gLog.Info(tr("%sConnecting to separate server for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (myDaemon->GetProxy() == NULL)
+  {
+    if (myProxy != NULL)
+    {
+      delete myProxy;
+      myProxy = NULL;
+    }
+  }
+  else
+  {
+    if (myProxy == NULL)
+      myProxy = myDaemon->CreateProxy();
+  }
+  if (!s->ConnectTo(myServer, myPort, myProxy))
+  {
+    gLog.Warn(tr("%sCan't establish service 0x%02X socket.\n"),
+               L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+  mySocketDesc = s->Descriptor();
+  gSocketManager.AddSocket(s);
+  gSocketManager.DropSocket(s);
+  // Alert the select thread that there is a new socket
+  write(myDaemon->pipe_newsocket[PIPE_WRITE], "S", 1);
+      
+  CPU_SendCookie *p1 = new CPU_SendCookie(myCookie, myCookieLen, myFam);
+  gLog.Info(tr("%sSending cookie for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (!SendPacket(p1))
+  {
+    gLog.Warn(tr("%sCan't send cookie while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  if (!WaitForStatus(STATUS_SRV_READY_RECV))
+  {
+    gLog.Warn(tr("%sGive up waiting for server ready packet while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+  
+  unsigned short VerArray[2][2] = {{ 0x0001, 0x0004 },	// Service FAM
+                                   { 0x0010, 0x0001 }};	// SSBI	FAM
+  CPU_ImICQ *p2 = new CPU_ImICQ(VerArray, 2, myFam);
+  gLog.Info(tr("%sSending our families versions for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (!SendPacket(p2))
+  {
+    gLog.Warn(tr("%sCan't send channel capability request while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+  
+  if (!WaitForStatus(STATUS_SRV_VER_RECV))
+  {
+    gLog.Warn(tr("%sGive up waiting for channel capability list while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  CPU_GenericFamily *p3 = new CPU_GenericFamily(ICQ_SNACxFAM_SERVICE,
+                                                ICQ_SNACxSUB_REQ_RATE_INFO, myFam);
+  gLog.Info(tr("%sSending request of rate-limits for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (!SendPacket(p3))
+  {
+    gLog.Warn(tr("%sCan't send request for rate-limits while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  if (!WaitForStatus(STATUS_SRV_RATE_RECV))
+  {
+    gLog.Warn(tr("%sGive up waiting for rate-limits while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  CPU_RateAck *p4 = new CPU_RateAck(myFam);
+  gLog.Info(tr("%sSending ack for rate-limits for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (!SendPacket(p4))
+  {
+    gLog.Warn(tr("%sCan't send rate-limits ack while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+  unsigned short VerArray2[2][4] = {{ 0x0001, 0x0004, 0x0110, 0x08e4 },  // Service FAM
+                                    { 0x0010, 0x0001, 0x0110, 0x08e4 }}; // SSBI FAM
+  CPU_ClientReady *p5 = new CPU_ClientReady(VerArray2, 2, myFam);
+  gLog.Info(tr("%sSending client ready for service 0x%02X.\n"),
+            L_SRVxSTR, myFam);
+  if (!SendPacket(p5))
+  {
+    gLog.Warn(tr("%sCan't send client ready while initializing service 0x%02X.\n"),
+              L_WARNxSTR, myFam);
+    ChangeStatus(STATUS_UNINITIALIZED);
+    return false;
+  }
+
+  ChangeStatus(STATUS_READY);
+  return true;
+}
+
+void *OscarServiceSendQueue_tep(void *p)
+{
+  pthread_detach(pthread_self());
+  
+  COscarService *os = (COscarService *)p;
+  CICQDaemon *d = os->myDaemon;
+  
+  while (true)
+  {
+    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+    pthread_mutex_lock(&os->mutex_sendqueue);
+    if (!os->mySendQueue.empty())
+    {
+      std::list<ICQEvent *>::iterator iter = os->mySendQueue.begin();
+      ICQEvent *e = *iter;
+      os->mySendQueue.erase(iter);
+      pthread_mutex_unlock(&os->mutex_sendqueue);
+
+      if (e->IsCancelled())
+      {
+        delete e;
+        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+        pthread_testcancel();
+        continue;
+      }
+
+      if (d->Status() != STATUS_ONLINE)
+      {
+        gLog.Warn(tr("%sCan't send event for service 0x%02X because we are not online.\n"),
+                  L_WARNxSTR, os->myFam);
+        if (d->DoneEvent(e, EVENT_ERROR) != NULL)
+          d->ProcessDoneEvent(e);
+        else
+          delete e;
+        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+        pthread_testcancel();
+        continue;
+      }
+      
+      if (os->mySocketDesc == -1)
+      {
+        gLog.Info(tr("%sInitializing socket for service 0x%02X.\n"), L_SRVxSTR, os->myFam);
+        if (!os->Initialize())
+        {
+          gLog.Warn(tr("%sInitialization of socket for service 0x%02X failed, failing event\n"),
+                    L_WARNxSTR, os->myFam);
+          if (d->DoneEvent(e, EVENT_ERROR) != NULL)
+            d->ProcessDoneEvent(e);
+          else
+            delete e;
+          pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+          pthread_testcancel();
+          continue;
+        }
+      }
+
+      bool Sent;
+      switch (os->myFam)
+      {
+        case ICQ_SNACxFAM_SSBI:
+          Sent = os->SendSSBIFam(e);
+          break;
+
+        default:
+          gLog.Warn(tr("%sEvent for unknown or unsupported service 0x%02X failed.\n"),
+                    L_WARNxSTR, os->myFam);
+          Sent = false;
+          break;
+      }
+ 
+      if (!Sent)
+      {
+        if (d->DoneEvent(e, EVENT_ERROR) != NULL)
+          d->ProcessDoneEvent(e);
+        else
+          delete e;
+      }
+
+      if (e->NoAck())
+          delete e;
+      
+      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+      pthread_testcancel();
+      continue;
+    }
+    else
+    {
+      pthread_cond_wait(&os->cond_sendqueue, &os->mutex_sendqueue);
+      pthread_mutex_unlock(&os->mutex_sendqueue);
+      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+      pthread_testcancel();
+    }
+  }
+
+  pthread_exit(NULL);
+}
diff -urN licq/src/socket.cpp licq-ssbi/src/socket.cpp
--- licq/src/socket.cpp	2007-10-22 16:20:56.000000000 +0300
+++ licq-ssbi/src/socket.cpp	2008-01-22 01:11:26.000000000 +0200
@@ -781,6 +781,50 @@
   return (true);
 }
 
+/*-----SrvSocket::ConnectTo--------------------------------------------------
+* Establish connection to server (through proxy or not)
+*---------------------------------------------------------------------------*/
+bool SrvSocket::ConnectTo(const char* server, unsigned short port,
+                          ProxyServer *xProxy)
+{
+  char ipbuf[32];
+
+  if (xProxy == NULL)
+  {
+    gLog.Info(tr("%sResolving %s port %d...\n"), L_SRVxSTR, server, port);
+    if (!SetRemoteAddr(server, port)) {
+      char buf[128];
+      gLog.Warn(tr("%sUnable to resolve %s:\n%s%s.\n"), L_ERRORxSTR,
+                server, L_BLANKxSTR, ErrorStr(buf, 128));
+      return false;
+    }
+    gLog.Info(tr("%sICQ server found at %s:%d.\n"), L_SRVxSTR,
+              RemoteIpStr(ipbuf), RemotePort());
+  }
+  else
+  {
+    // It doesn't matter if it resolves or not, the proxy should do it then
+    SetProxy(xProxy);
+    SetRemoteAddr(server, port);
+  }
+
+  if (xProxy == NULL)
+    gLog.Info(tr("%sOpening socket to server.\n"), L_SRVxSTR);
+  else
+    gLog.Info(tr("%sOpening socket to server %s:%d via proxy.\n"),
+              L_SRVxSTR, server, port);
+  if (!OpenConnection())
+  {
+    char buf[128];
+    gLog.Warn(tr("%sUnable to connect to %s:%d:\n%s%s.\n"), L_ERRORxSTR,
+              RemoteIpStr(ipbuf), RemotePort(), L_BLANKxSTR,
+              ErrorStr(buf, 128));
+    return false;
+  }
+
+  return true;
+}
+    
 //=====TCPSocket===============================================================
 TCPSocket::TCPSocket(unsigned long _nOwner) : INetSocket(_nOwner)
 {
diff -urN licq/src/support.c licq-ssbi/src/support.c
--- licq/src/support.c	2006-08-10 21:17:45.000000000 +0300
+++ licq-ssbi/src/support.c	2008-01-22 01:11:26.000000000 +0200
@@ -112,6 +112,62 @@
   return (nRet == -1) ? _nSize - 1 : nRet; 
 }
 
+char *PrintHex(char *szPrint, const char *szHex, size_t nSize)
+{
+  int i, j = 0;
+  
+  for(i = 0; i < nSize ; i++)
+  {
+    unsigned char byte = (unsigned char)szHex[i];
+
+    unsigned char high = (byte >> 4) & 0x0f;
+    unsigned char low = byte & 0x0f;
+
+    if (high > 9)
+      szPrint[j++] = 'A' + (high - 10);
+    else
+      szPrint[j++] = '0' + high;
+
+    if (low > 9)
+      szPrint[j++] = 'A' + (low - 10);
+    else
+      szPrint[j++] = '0' + low;
+  }
+  szPrint[j] = '\0';
+
+  return szPrint;
+}
+
+char *ReadHex(char *szHex, const char *szRead, size_t nSize)
+{
+  int i, j = 0;
+  for(i = 0; i < nSize ; i++)
+  {
+    unsigned char digit, high, low;
+
+    if (szRead[j] == '\0') break;
+    digit = (unsigned char)szRead[j++];
+    high = 0;
+    if (digit >= 'A')
+      high = digit - 'A' + 10;
+    else if (digit >= '0')
+      high = digit - '0';
+    high <<= 4;
+
+    if (szRead[j] == '\0') break;
+    digit = (unsigned char)szRead[j++];
+    low = 0;
+    if (digit >= 'A')
+      low = digit - 'A' + 10;
+    else if (digit >= '0')
+      low = digit - '0';
+
+    szHex[i] = high + low;
+  }
+  
+  return szHex;
+}
+
 int Redirect(const char *_szFile)
 {
   int fd = open(_szFile, O_WRONLY | O_CREAT | O_APPEND, 00660);
diff -urN licq/src/support.h licq-ssbi/src/support.h
--- licq/src/support.h	2006-08-10 21:17:45.000000000 +0300
+++ licq-ssbi/src/support.h	2008-01-22 01:11:26.000000000 +0200
@@ -34,6 +34,9 @@
 
 int UinString(char *_szBuf, size_t _nSize, unsigned long _nUin);
 
+char *PrintHex(char *szPrint, const char *szHex, size_t nSize);
+char *ReadHex(char *szHex, const char *szRead, size_t nSize);
+
 int Redirect(const char *);
 
 int strlen_safe(const char *);
diff -urN licq/src/user.cpp licq-ssbi/src/user.cpp
--- licq/src/user.cpp	2007-11-25 17:37:21.000000000 +0200
+++ licq-ssbi/src/user.cpp	2008-01-22 01:11:30.000000000 +0200
@@ -2011,8 +2011,15 @@
 //-----ICQUser::LoadPictureInfo----------------------------------------------
 void ICQUser::LoadPictureInfo()
 {
+  char szTemp[MAX_LINE_LEN];
   m_fConf.SetSection("user");
   m_fConf.ReadBool("PicturePresent", m_bPicturePresent, false);
+  m_fConf.ReadNum("BuddyIconType", m_nBuddyIconType, 0);
+  m_fConf.ReadNum("BuddyIconHashType", m_nBuddyIconHashType, 0);
+  m_fConf.ReadStr("BuddyIconHash", szTemp, "");
+  SetString(&m_szBuddyIconHash, szTemp );
+  m_fConf.ReadStr("OurBuddyIconHash", szTemp, "");
+  SetString(&m_szOurBuddyIconHash, szTemp );
 }
 
 //-----ICQUser::LoadLicqInfo-------------------------------------------------
@@ -2333,6 +2340,10 @@
 
   // Picture
   m_bPicturePresent = false;
+  m_nBuddyIconType = 0;
+  m_nBuddyIconHashType = 0;
+  m_szBuddyIconHash = strdup("");
+  m_szOurBuddyIconHash = strdup("");
 
   // GPG key
   m_szGPGKey = strdup("");
@@ -3513,6 +3524,10 @@
   }
   m_fConf.SetSection("user");
   m_fConf.WriteBool("PicturePresent", m_bPicturePresent);
+  m_fConf.WriteNum("BuddyIconType", m_nBuddyIconType);
+  m_fConf.WriteNum("BuddyIconHashType", m_nBuddyIconHashType);
+  m_fConf.WriteStr("BuddyIconHash", m_szBuddyIconHash);
+  m_fConf.WriteStr("OurBuddyIconHash", m_szOurBuddyIconHash);
   if (!m_fConf.FlushFile())
   {
     gLog.Error("%sError opening '%s' for writing.\n%sSee log for details.\n",
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.