File ssbi-licq2.patch of Package Licq

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",
openSUSE Build Service is sponsored by