soc.2009.transport: c341f7c1: Added patches needed for compiling.

hanzz at soc.pidgin.im hanzz at soc.pidgin.im
Sun Jul 19 07:56:04 EDT 2009


-----------------------------------------------------------------
Revision: c341f7c18ccd3f2c1ed8db98f076f61ef86ffb4e
Ancestor: 8aea4cab92d285662ec2653fe6e5d4c12b4f31d5
Author: hanzz at soc.pidgin.im
Date: 2009-07-19T11:49:08
Branch: im.pidgin.soc.2009.transport
URL: http://d.pidgin.im/viewmtn/revision/info/c341f7c18ccd3f2c1ed8db98f076f61ef86ffb4e

Deleted entries:
        patches/1-gloox-send-file-patch.diff
        patches/2-gloox-receive-file-patch.diff
        patches/gloox-1.0.diff
Added files:
        patches/01-gloox-filetransfer-component.diff
        patches/2-purple-abstract-blist-save.diff
Modified files:
        filetransfermanager.cpp

ChangeLog: 

Added patches needed for compiling.

-------------- next part --------------
============================================================
--- patches/01-gloox-filetransfer-component.diff	a8c0bac0b30a90f4e821dd67a84330985a5348dd
+++ patches/01-gloox-filetransfer-component.diff	a8c0bac0b30a90f4e821dd67a84330985a5348dd
@@ -0,0 +1,472 @@
+Index: clientbase.h
+===================================================================
+--- clientbase.h	(revision 4022)
++++ clientbase.h	(working copy)
+@@ -399,6 +399,7 @@
+        * @param ext The extension to register.
+        */
+       void registerStanzaExtension( StanzaExtension* ext );
++	StanzaExtensionFactory * stanzaExtensionFactory( ) { return m_seFactory; }
+ 
+       /**
+        * Removes the given StanzaExtension type from the StanzaExtensionFactory.
+Index: sihandler.h
+===================================================================
+--- sihandler.h	(revision 4022)
++++ sihandler.h	(working copy)
+@@ -52,7 +52,7 @@
+        * @param sid The stream ID.
+        * @param si The request's complete SI.
+        */
+-      virtual void handleSIRequestResult( const JID& from, const std::string& sid,
++      virtual void handleSIRequestResult( const JID& from, const JID& to, const std::string& sid,
+                                           const SIManager::SI& si ) = 0;
+ 
+       /**
+Index: siprofileft.h
+===================================================================
+--- siprofileft.h	(revision 4022)
++++ siprofileft.h	(working copy)
+@@ -196,7 +196,7 @@
+        * @return The requested stream's ID (SID). Empty if conditions above (file name, size)
+        * are not met.
+        */
+-      const std::string requestFT( const JID& to, const std::string& name, long size,
++      const std::string requestFT( const JID& to, const JID& from, const std::string& name, long size,
+                                    const std::string& hash = EmptyString,
+                                    const std::string& desc = EmptyString,
+                                    const std::string& date = EmptyString,
+@@ -211,7 +211,7 @@
+        * @param type The desired stream type to use for this file transfer. Defaults to
+        * SOCKS5 Bytestream. You should not use @c FTTypeAll here.
+        */
+-      void acceptFT( const JID& to, const std::string& sid,
++      void acceptFT( const JID& to, const JID& from, const std::string& sid,
+                      StreamType type = FTTypeS5B );
+ 
+       /**
+@@ -282,11 +282,11 @@
+         { if( m_socks5Manager ) m_socks5Manager->removeSOCKS5BytestreamServer(); }
+ 
+       // reimplemented from SIProfileHandler
+-      virtual void handleSIRequest( const JID& from, const std::string& id,
++      virtual void handleSIRequest( const JID& from, const JID& to, const std::string& id,
+                                     const SIManager::SI& si );
+ 
+       // reimplemented from SIHandler
+-      virtual void handleSIRequestResult( const JID& from, const std::string& sid,
++      virtual void handleSIRequestResult( const JID& from, const JID& to, const std::string& sid,
+                                           const SIManager::SI& si );
+ 
+       // reimplemented from SIHandler
+Index: capabilities.cpp
+===================================================================
+--- capabilities.cpp	(revision 4022)
++++ capabilities.cpp	(working copy)
+@@ -126,7 +126,7 @@
+     return ret;
+   }
+ 
+-  Disco::ItemList Capabilities::handleDiscoNodeItems( const JID&, const std::string& )
++  Disco::ItemList Capabilities::handleDiscoNodeItems( const JID&, const JID&, const std::string& )
+   {
+     return Disco::ItemList();
+   }
+Index: stanzaextension.h
+===================================================================
+--- stanzaextension.h	(revision 4022)
++++ stanzaextension.h	(working copy)
+@@ -86,9 +86,11 @@
+                                      * (XEP-0060) */
+     ExtSHIM,                        /**< An extension dealing with Stanza Headers and Internet Metadata (XEP-0131). */
+     ExtAttention,                   /**< An extension dealing with Attention (XEP-0224). */
+-    ExtUser                         /**< User-supplied extensions must use IDs above this. Do
++    ExtUser,                        /**< User-supplied extensions must use IDs above this. Do
+                                      * not hard-code ExtUser's value anywhere, it is subject
+                                      * to change. */
++    ExtGateway,
++    ExtStats
+   };
+ 
+   /**
+Index: presence.h
+===================================================================
+--- presence.h	(revision 4022)
++++ presence.h	(working copy)
+@@ -138,7 +138,7 @@
+ 
+       // reimplemented from Stanza
+       virtual Tag* tag() const;
+-
++      Presence( Tag* tag );
+     private:
+ #ifdef PRESENCE_TEST
+     public:
+@@ -147,8 +147,8 @@
+        * Creates a Presence request from the given Tag. The original Tag will be ripped off.
+        * @param tag The Tag to parse.
+        */
+-      Presence( Tag* tag );
+ 
++
+       PresenceType m_subtype;
+       StringMap* m_stati;
+       std::string m_status;
+Index: simanager.h
+===================================================================
+--- simanager.h	(revision 4022)
++++ simanager.h	(working copy)
+@@ -158,7 +158,7 @@
+        * @note The SIManager claims ownership of the Tags supplied to this function, and will
+        * delete them after use.
+        */
+-      const std::string requestSI( SIHandler* sih, const JID& to, const std::string& profile, Tag* child1,
++      const std::string requestSI( SIHandler* sih, const JID& to, const JID& from, const std::string& profile, Tag* child1,
+                                    Tag* child2 = 0, const std::string& mimetype = "binary/octet-stream" );
+ 
+       /**
+@@ -172,7 +172,7 @@
+        * @note The SIManager claims ownership of the Tags supplied to this function, and will
+        * delete them after use.
+        */
+-      void acceptSI( const JID& to, const std::string& id, Tag* child1, Tag* child2 = 0 );
++      void acceptSI( const JID& to, const JID& from, const std::string& id, Tag* child1, Tag* child2 = 0 );
+ 
+       /**
+        * Call this function to decline an SI request previously announced by means of
+Index: adhoc.cpp
+===================================================================
+--- adhoc.cpp	(revision 4022)
++++ adhoc.cpp	(working copy)
+@@ -246,7 +246,7 @@
+ //    return StringList( 1, XMLNS_ADHOC_COMMANDS );
+   }
+ 
+-  Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const std::string& node )
++  Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& to, const std::string& node )
+   {
+     Disco::ItemList l;
+     if( node.empty() )
+Index: capabilities.h
+===================================================================
+--- capabilities.h	(revision 4022)
++++ capabilities.h	(working copy)
+@@ -98,7 +98,7 @@
+                                                              const std::string& node );
+ 
+       // reimplemented from DiscoNodeHandler
+-      virtual Disco::ItemList handleDiscoNodeItems( const JID& from,
++      virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
+                                                     const std::string& node = EmptyString );
+ 
+     private:
+Index: disco.cpp
+===================================================================
+--- disco.cpp	(revision 4022)
++++ disco.cpp	(working copy)
+@@ -265,6 +265,7 @@
+       case IQ::Get:
+       {
+         IQ re( IQ::Result, iq.from(), iq.id() );
++        re.setFrom(iq.to());
+ 
+         const SoftwareVersion* sv = iq.findExtension<SoftwareVersion>( ExtVersion );
+         if( sv )
+@@ -344,7 +345,7 @@
+               DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
+               for( ; in != (*it).second.end(); ++in )
+               {
+-                ItemList il = (*in)->handleDiscoNodeItems( iq.from(), items->node() );
++                ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() );
+                 itemlist.merge( il );
+               }
+               i->setItems( itemlist );
+Index: disconodehandler.h
+===================================================================
+--- disconodehandler.h	(revision 4022)
++++ disconodehandler.h	(working copy)
+@@ -70,7 +70,7 @@
+        * @param node The node this handler is supposed to handle.
+        * @return A list of items supported by this node.
+        */
+-      virtual Disco::ItemList handleDiscoNodeItems( const JID& from,
++      virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
+                                                     const std::string& node = EmptyString ) = 0;
+ 
+   };
+Index: siprofileft.cpp
+===================================================================
+--- siprofileft.cpp	(revision 4022)
++++ siprofileft.cpp	(working copy)
+@@ -60,7 +60,7 @@
+       delete m_socks5Manager;
+   }
+ 
+-  const std::string SIProfileFT::requestFT( const JID& to, const std::string& name, long size,
++  const std::string SIProfileFT::requestFT( const JID& to, const JID& from, const std::string& name, long size,
+                                             const std::string& hash, const std::string& desc,
+                                             const std::string& date, const std::string& mimetype,
+                                             int streamTypes )
+@@ -93,10 +93,10 @@
+     dff->setOptions( sm );
+     feature->addChild( df.tag() );
+ 
+-    return m_manager->requestSI( this, to, XMLNS_SI_FT, file, feature, mimetype );
++    return m_manager->requestSI( this, to, from, XMLNS_SI_FT, file, feature, mimetype );
+   }
+ 
+-  void SIProfileFT::acceptFT( const JID& to, const std::string& sid, StreamType type )
++  void SIProfileFT::acceptFT( const JID& to, const JID& from, const std::string& sid, StreamType type )
+   {
+     if( !m_manager )
+       return;
+@@ -119,7 +119,7 @@
+         if( m_handler )
+         {
+           InBandBytestream* ibb = new InBandBytestream( m_parent, m_parent->logInstance(), to,
+-                                                        m_parent->jid(), sid );
++                                                        from, sid );
+           m_handler->handleFTBytestream( ibb );
+         }
+         break;
+@@ -166,7 +166,7 @@
+       m_socks5Manager->addStreamHost( jid, host, port );
+   }
+ 
+-  void SIProfileFT::handleSIRequest( const JID& from, const std::string& id,
++  void SIProfileFT::handleSIRequest( const JID& from, const JID& to, const std::string& id,
+                                      const SIManager::SI& si )
+   {
+     if( si.profile() != XMLNS_SI_FT || !si.tag1() )
+@@ -214,7 +214,7 @@
+ 
+       const std::string& sid = si.id();
+       m_id2sid[sid] = id;
+-      m_handler->handleFTRequest( from, sid, si.tag1()->findAttribute( "name" ),
++      m_handler->handleFTRequest( from, to, sid, si.tag1()->findAttribute( "name" ),
+                                   atol( si.tag1()->findAttribute( "size" ).c_str() ),
+                                         si.tag1()->findAttribute( "hash" ),
+                                             si.tag1()->findAttribute( "date" ),
+@@ -223,7 +223,7 @@
+     }
+   }
+ 
+-  void SIProfileFT::handleSIRequestResult( const JID& from, const std::string& sid,
++  void SIProfileFT::handleSIRequestResult( const JID& from, const JID& to, const std::string& sid,
+                                            const SIManager::SI& si )
+   {
+     if( si.tag2() )
+@@ -236,24 +236,25 @@
+         if( m_socks5Manager && dff->value() == XMLNS_BYTESTREAMS )
+         {
+           // check return value:
+-          m_socks5Manager->requestSOCKS5Bytestream( from, SOCKS5BytestreamManager::S5BTCP, sid );
++          m_socks5Manager->requestSOCKS5Bytestream( from, to, SOCKS5BytestreamManager::S5BTCP, sid );
+         }
+         else if( m_handler )
+         {
+           if( dff->value() == XMLNS_IBB )
+           {
+             InBandBytestream* ibb = new InBandBytestream( m_parent, m_parent->logInstance(),
+-                                                          m_parent->jid(), from, sid );
++                                                          to, from, sid );
+ 
+             m_handler->handleFTBytestream( ibb );
+           }
+           else if( dff->value() == XMLNS_IQ_OOB )
+           {
+-            const std::string& url = m_handler->handleOOBRequestResult( from, sid );
++            const std::string& url = m_handler->handleOOBRequestResult( from, to, sid );
+             if( !url.empty() )
+             {
+               const std::string& id = m_parent->getID();
+               IQ iq( IQ::Set, from, id );
++			  iq.setFrom(to);
+               iq.addExtension( new OOB( url, EmptyString, true ) );
+               m_parent->send( iq, this, OOBSent );
+             }
+Index: siprofilehandler.h
+===================================================================
+--- siprofilehandler.h	(revision 4022)
++++ siprofilehandler.h	(working copy)
+@@ -52,7 +52,7 @@
+        * SIManager::acceptSI() or SIManager::declineSI().
+        * @param si The request's complete SI.
+        */
+-      virtual void handleSIRequest( const JID& from, const std::string& id, const SIManager::SI& si ) = 0;
++      virtual void handleSIRequest( const JID& from, const JID& to, const std::string& id, const SIManager::SI& si ) = 0;
+ 
+   };
+ 
+Index: siprofilefthandler.h
+===================================================================
+--- siprofilefthandler.h	(revision 4022)
++++ siprofilefthandler.h	(working copy)
+@@ -61,7 +61,7 @@
+        * @param length The number of bytes to send, starting from the given offset. A value of -1
+        * indicates that the entire file is to be transmitted (taking the offset into account).
+        */
+-      virtual void handleFTRequest( const JID& from, const std::string& sid,
++      virtual void handleFTRequest( const JID& from, const JID& to, const std::string& sid,
+                                     const std::string& name, long size, const std::string& hash,
+                                     const std::string& date, const std::string& mimetype,
+                                     const std::string& desc, int stypes, long offset, long length ) = 0;
+@@ -96,7 +96,7 @@
+        * @param sid The stream's ID.
+        * @return The file's URL.
+        */
+-      virtual const std::string handleOOBRequestResult( const JID& from, const std::string& sid ) = 0;
++      virtual const std::string handleOOBRequestResult( const JID& from, const JID& to, const std::string& sid ) = 0;
+ 
+   };
+ 
+Index: socks5bytestreammanager.cpp
+===================================================================
+--- socks5bytestreammanager.cpp	(revision 4022)
++++ socks5bytestreammanager.cpp	(working copy)
+@@ -175,7 +175,7 @@
+     m_hosts.push_back( sh );
+   }
+ 
+-  bool SOCKS5BytestreamManager::requestSOCKS5Bytestream( const JID& to, S5BMode mode,
++  bool SOCKS5BytestreamManager::requestSOCKS5Bytestream( const JID& to, const JID& from, S5BMode mode,
+                                                          const std::string& sid )
+   {
+     if( !m_parent )
+@@ -192,12 +192,13 @@
+     const std::string& id = m_parent->getID();
+     IQ iq( IQ::Set, to, id );
+     iq.addExtension( new Query( msid, mode, m_hosts ) );
++	iq.setFrom(from);
+ 
+     if( m_server )
+     {
+       SHA sha;
+       sha.feed( msid );
+-      sha.feed( m_parent->jid().full() );
++      sha.feed( from.full() );
+       sha.feed( to.full() );
+       m_server->registerHash( sha.hex() );
+     }
+@@ -414,18 +415,18 @@
+                 {
+                   SHA sha;
+                   sha.feed( (*it).second );
+-                  sha.feed( m_parent->jid().full() );
++                  sha.feed( iq.to().full() );
+                   sha.feed( iq.from().full() );
+                   s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ),
+                                               m_parent->logInstance(),
+-                                                  m_parent->jid(), iq.from(),
++                                                  iq.to(), iq.from(),
+                                                       (*it).second );
+                 }
+                 else
+                 {
+                   s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(),
+                                               m_parent->logInstance(),
+-                                                  m_parent->jid(), iq.from(),
++                                                  iq.to(), iq.from(),
+                                                       (*it).second );
+                   s5b->setStreamHosts( StreamHostList( 1, *sh ) );
+                 }
+Index: simanager.cpp
+===================================================================
+--- simanager.cpp	(revision 4022)
++++ simanager.cpp	(working copy)
+@@ -109,7 +109,7 @@
+     }
+   }
+ 
+-  const std::string SIManager::requestSI( SIHandler* sih, const JID& to, const std::string& profile,
++  const std::string SIManager::requestSI( SIHandler* sih, const JID& to, const JID& from, const std::string& profile,
+                                           Tag* child1, Tag* child2, const std::string& mimetype )
+   {
+     if( !m_parent || !sih )
+@@ -120,6 +120,7 @@
+ 
+     IQ iq( IQ::Set, to, id );
+     iq.addExtension( new SI( child1, child2, id2, mimetype, profile ) );
++	iq.setFrom(from);
+ 
+     TrackStruct t;
+     t.sid = id2;
+@@ -131,10 +132,11 @@
+     return id2;
+   }
+ 
+-  void SIManager::acceptSI( const JID& to, const std::string& id, Tag* child1, Tag* child2 )
++  void SIManager::acceptSI( const JID& to, const JID& from, const std::string& id, Tag* child1, Tag* child2 )
+   {
+     IQ iq( IQ::Result, to, id );
+     iq.addExtension( new SI( child1, child2 ) );
++	iq.setFrom(from);
+     m_parent->send( iq );
+   }
+ 
+@@ -198,7 +200,7 @@
+     if( it != m_handlers.end() && (*it).second )
+     {
+       // FIXME: don't pass si->tag()!
+-      (*it).second->handleSIRequest( iq.from(), iq.id(), *si );
++      (*it).second->handleSIRequest( iq.from(), iq.to(), iq.id(), *si );
+       return true;
+     }
+ 
+@@ -230,7 +232,7 @@
+ 
+             // FIXME: remove above commented code and
+             // check corectness of last 3 params!
+-            (*it).second.sih->handleSIRequestResult( iq.from(), (*it).second.sid, *si );
++            (*it).second.sih->handleSIRequestResult( iq.from(), iq.to(), (*it).second.sid, *si );
+             m_track.erase( it );
+           }
+         }
+Index: clientbase.cpp
+===================================================================
+--- clientbase.cpp	(revision 4022)
++++ clientbase.cpp	(working copy)
+@@ -1294,13 +1294,15 @@
+     bool res = false;
+ 
+     // FIXME remove for 1.1
+-//     typedef IqHandlerMapXmlns::const_iterator IQciXmlns;
+-//     std::pair<IQciXmlns, IQciXmlns> g = m_iqNSHandlers.equal_range( iq.xmlns() );
++//     typedef IqHandlerMapXmlns::const_iterator IQciXmlns
++//     Tag *tag = iq.tag()->xmlns();
++//     std::pair<IQciXmlns, IQciXmlns> g = m_iqNSHandlers.equal_range( tag->xmlns() );
+ //     for( IQciXmlns it = g.first; it != g.second; ++it )
+ //     {
+ //       if( (*it).second->handleIq( iq ) )
+ //         res = true;
+ //     }
++//     delete tag;
+ 
+     typedef IqHandlerMap::const_iterator IQci;
+     const StanzaExtensionList& sel = iq.extensions();
+Index: socks5bytestreammanager.h
+===================================================================
+--- socks5bytestreammanager.h	(revision 4022)
++++ socks5bytestreammanager.h	(working copy)
+@@ -106,7 +106,7 @@
+        * @b not indicate that the bytestream has been opened. This is announced by means of the
+        * BytestreamHandler.
+        */
+-      bool requestSOCKS5Bytestream( const JID& to, S5BMode mode, const std::string& sid = EmptyString );
++      bool requestSOCKS5Bytestream( const JID& to, const JID& from, S5BMode mode, const std::string& sid = EmptyString );
+ 
+       /**
+        * To get rid of a bytestream (i.e., close and delete it), call this function. You
+Index: adhoc.h
+===================================================================
+--- adhoc.h	(revision 4022)
++++ adhoc.h	(working copy)
+@@ -432,7 +432,7 @@
+           const std::string& node );
+ 
+       // reimplemented from DiscoNodeHandler
+-      virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const std::string& node );
++      virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, const std::string& node );
+ 
+       // reimplemented from IqHandler
+       virtual bool handleIq( const IQ& iq );
============================================================
--- patches/2-purple-abstract-blist-save.diff	e9c40c7d1f31ad4c3379f4f74f617d937f2caf45
+++ patches/2-purple-abstract-blist-save.diff	e9c40c7d1f31ad4c3379f4f74f617d937f2caf45
@@ -0,0 +1,1823 @@
+#
+# old_revision [08979461f8fd9f122c665f3ee192c20788dd1bca]
+#
+# add_file "libpurple/blistsaving.c"
+#  content [98baed84682e327e333fb87f21e9441eef010764]
+# 
+# add_file "libpurple/blistsaving.h"
+#  content [e571e125d589317871839e622c3ee946df8f266d]
+# 
+# patch "finch/finch.c"
+#  from [7de9b76dc4808a46137c007229f0210ea9ffd659]
+#    to [5d6b5aaadc171f133d4ad6f6eb97ec42c715425e]
+# 
+# patch "finch/gntaccount.c"
+#  from [24d52150587c87297f848a21a98fe4488ad43cbb]
+#    to [4067a68f59ea25675ede9b5bbe79ece2c985a7da]
+# 
+# patch "finch/gntblist.c"
+#  from [d7891e6e97a491a82f1e3baee278be83774087b0]
+#    to [4e4e7efb0b05bf77aabcc21a0a74c31b17a61ede]
+# 
+# patch "libpurple/Makefile.am"
+#  from [22dfd64715e47d7c86458cd3686d2c7857fce25f]
+#    to [be1014b632e6314515ab53d482e2906833260098]
+# 
+# patch "libpurple/account.c"
+#  from [f30d42673ff93f36740312dacb3ee1c876bf2936]
+#    to [0e6b671fb591c61b5cb01909006266db929e7b99]
+# 
+# patch "libpurple/blist.c"
+#  from [880a4f22c97e117941e6d36767fd82cf322c0c91]
+#    to [c84e7f646d0b84b3c692ddeeb017596ac572816f]
+# 
+# patch "libpurple/blist.h"
+#  from [9580da3d4ae9096b2e4b8cbb88ca57a84f03f901]
+#    to [a5774f27060c7e8ad0e67dc536c1a566e46e001a]
+# 
+# patch "libpurple/example/nullclient.c"
+#  from [587f78d7b0f0c1d170ac1eac35d526ca62efa8f8]
+#    to [4d496227427056c83a613c6abdcbd6634ee6e563]
+# 
+# patch "libpurple/privacy.c"
+#  from [5e9715a02d5433f53c7de47f703619bc84be82f5]
+#    to [470a92591d021b1f71178640dfbd13c6dff73b54]
+# 
+# patch "pidgin/gtkaccount.c"
+#  from [f7bb182fa4fd2fb71894e9c2580c2ea1e64e84ae]
+#    to [e99275271fde3c64d09ab6ec083cdfa0803acb42]
+# 
+# patch "pidgin/gtkblist.c"
+#  from [122e9ad802a1125cad575c5cea42bed04d7416f1]
+#    to [80b0cb722f2803e37e5b2e2ecfee9c0f0f095e2e]
+# 
+# patch "pidgin/gtkblist.h"
+#  from [094332f5deb237f91ca4bd5f362bbbba2c4406fd]
+#    to [3a78e84707ed0071a48763371f8c3c386f0daa25]
+# 
+# patch "pidgin/gtkmain.c"
+#  from [e8b5e9c5fa4c46fef1318293e9c3d96ac583d0c5]
+#    to [f462a52c4f262cc8c78443009bdf20168cab867c]
+# 
+# patch "pidgin/gtkprivacy.c"
+#  from [0539636ae120a83ed054e2fc0defb5fc235fd35a]
+#    to [61982de66d62ba27f59afbf2640fa4e8f221486a]
+#
+============================================================
+--- libpurple/blistsaving.c	98baed84682e327e333fb87f21e9441eef010764
++++ libpurple/blistsaving.c	98baed84682e327e333fb87f21e9441eef010764
+@@ -0,0 +1,573 @@
++/* purple
++ *
++ * Purple is the legal property of its developers, whose names are too numerous
++ * to list here.  Please refer to the COPYRIGHT file distributed with this
++ * source distribution.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
++ */
++ 
++#include "internal.h"
++#include "blistsaving.h"
++#include "blist.h"
++#include "util.h"
++#include "value.h"
++#include "xmlnode.h"
++#include "debug.h"
++
++static guint          save_timer = 0;
++static gboolean       blist_loaded = FALSE;
++ 
++/*********************************************************************
++ * Writing to disk                                                   *
++ *********************************************************************/
++
++static void
++value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
++{
++	const char *name;
++	PurpleValue *value;
++	xmlnode *node, *child;
++	char buf[20];
++
++	name    = (const char *)key;
++	value   = (PurpleValue *)hvalue;
++	node    = (xmlnode *)user_data;
++
++	g_return_if_fail(value != NULL);
++
++	child = xmlnode_new_child(node, "setting");
++	xmlnode_set_attrib(child, "name", name);
++
++	if (purple_value_get_type(value) == PURPLE_TYPE_INT) {
++		xmlnode_set_attrib(child, "type", "int");
++		snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
++		xmlnode_insert_data(child, buf, -1);
++	}
++	else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
++		xmlnode_set_attrib(child, "type", "string");
++		xmlnode_insert_data(child, purple_value_get_string(value), -1);
++	}
++	else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
++		xmlnode_set_attrib(child, "type", "bool");
++		snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
++		xmlnode_insert_data(child, buf, -1);
++	}
++}
++
++static void
++chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
++{
++	const char *name;
++	const char *data;
++	xmlnode *node, *child;
++
++	name = (const char *)key;
++	data = (const char *)value;
++	node = (xmlnode *)user_data;
++
++	g_return_if_fail(data != NULL);
++
++	child = xmlnode_new_child(node, "component");
++	xmlnode_set_attrib(child, "name", name);
++	xmlnode_insert_data(child, data, -1);
++}
++
++static xmlnode *
++buddy_to_xmlnode(PurpleBlistNode *bnode)
++{
++	xmlnode *node, *child;
++	PurpleBuddy *buddy;
++
++	buddy = (PurpleBuddy *)bnode;
++
++	node = xmlnode_new("buddy");
++	xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
++	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
++
++	child = xmlnode_new_child(node, "name");
++	xmlnode_insert_data(child, buddy->name, -1);
++
++	if (buddy->alias != NULL)
++	{
++		child = xmlnode_new_child(node, "alias");
++		xmlnode_insert_data(child, buddy->alias, -1);
++	}
++
++	/* Write buddy settings */
++	g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
++
++	return node;
++}
++
++static xmlnode *
++contact_to_xmlnode(PurpleBlistNode *cnode)
++{
++	xmlnode *node, *child;
++	PurpleContact *contact;
++	PurpleBlistNode *bnode;
++
++	contact = (PurpleContact *)cnode;
++
++	node = xmlnode_new("contact");
++
++	if (contact->alias != NULL)
++	{
++		xmlnode_set_attrib(node, "alias", contact->alias);
++	}
++
++	/* Write buddies */
++	for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
++	{
++		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
++			continue;
++		if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
++		{
++			child = buddy_to_xmlnode(bnode);
++			xmlnode_insert_child(node, child);
++		}
++	}
++
++	/* Write contact settings */
++	g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
++
++	return node;
++}
++
++static xmlnode *
++chat_to_xmlnode(PurpleBlistNode *cnode)
++{
++	xmlnode *node, *child;
++	PurpleChat *chat;
++
++	chat = (PurpleChat *)cnode;
++
++	node = xmlnode_new("chat");
++	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
++	xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
++
++	if (chat->alias != NULL)
++	{
++		child = xmlnode_new_child(node, "alias");
++		xmlnode_insert_data(child, chat->alias, -1);
++	}
++
++	/* Write chat components */
++	g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
++
++	/* Write chat settings */
++	g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
++
++	return node;
++}
++
++static xmlnode *
++group_to_xmlnode(PurpleBlistNode *gnode)
++{
++	xmlnode *node, *child;
++	PurpleGroup *group;
++	PurpleBlistNode *cnode;
++
++	group = (PurpleGroup *)gnode;
++
++	node = xmlnode_new("group");
++	xmlnode_set_attrib(node, "name", group->name);
++
++	/* Write settings */
++	g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
++
++	/* Write contacts and chats */
++	for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
++	{
++		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
++			continue;
++		if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
++		{
++			child = contact_to_xmlnode(cnode);
++			xmlnode_insert_child(node, child);
++		}
++		else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
++		{
++			child = chat_to_xmlnode(cnode);
++			xmlnode_insert_child(node, child);
++		}
++	}
++
++	return node;
++}
++
++static xmlnode *
++accountprivacy_to_xmlnode(PurpleAccount *account)
++{
++	xmlnode *node, *child;
++	GSList *cur;
++	char buf[10];
++
++	node = xmlnode_new("account");
++	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
++	xmlnode_set_attrib(node, "name", purple_account_get_username(account));
++	snprintf(buf, sizeof(buf), "%d", account->perm_deny);
++	xmlnode_set_attrib(node, "mode", buf);
++
++	for (cur = account->permit; cur; cur = cur->next)
++	{
++		child = xmlnode_new_child(node, "permit");
++		xmlnode_insert_data(child, cur->data, -1);
++	}
++
++	for (cur = account->deny; cur; cur = cur->next)
++	{
++		child = xmlnode_new_child(node, "block");
++		xmlnode_insert_data(child, cur->data, -1);
++	}
++
++	return node;
++}
++
++static xmlnode *
++blist_to_xmlnode(PurpleBuddyList *blist)
++{
++	xmlnode *node, *child, *grandchild;
++	PurpleBlistNode *gnode;
++	GList *cur;
++
++	node = xmlnode_new("purple");
++	xmlnode_set_attrib(node, "version", "1.0");
++
++	/* Write groups */
++	child = xmlnode_new_child(node, "blist");
++	for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
++	{
++		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(gnode))
++			continue;
++		if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
++		{
++			grandchild = group_to_xmlnode(gnode);
++			xmlnode_insert_child(child, grandchild);
++		}
++	}
++
++	/* Write privacy settings */
++	child = xmlnode_new_child(node, "privacy");
++	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
++	{
++		grandchild = accountprivacy_to_xmlnode(cur->data);
++		xmlnode_insert_child(child, grandchild);
++	}
++
++	return node;
++}
++
++static void
++purple_blist_sync(PurpleBuddyList *blist)
++{
++	xmlnode *node;
++	char *data;
++
++	if (!blist_loaded)
++	{
++		purple_debug_error("blist", "Attempted to save buddy list before it "
++						 "was read!\n");
++		return;
++	}
++
++	node = blist_to_xmlnode(blist);
++	data = xmlnode_to_formatted_str(node, NULL);
++	purple_util_write_data_to_file("blist.xml", data, -1);
++	g_free(data);
++	xmlnode_free(node);
++}
++
++static gboolean
++save_cb(PurpleBuddyList *blist)
++{
++	purple_blist_sync(blist);
++	save_timer = 0;
++	return FALSE;
++}
++
++
++/*********************************************************************
++ * Reading from disk                                                 *
++ *********************************************************************/
++
++static PurpleBlistNode *purple_blist_get_last_sibling(PurpleBlistNode *node)
++{
++	PurpleBlistNode *n = node;
++	if (!n)
++		return NULL;
++	while (n->next)
++		n = n->next;
++	return n;
++}
++
++static PurpleBlistNode *purple_blist_get_last_child(PurpleBlistNode *node)
++{
++	if (!node)
++		return NULL;
++	return purple_blist_get_last_sibling(node->child);
++}
++
++static void
++parse_setting(PurpleBlistNode *node, xmlnode *setting)
++{
++	const char *name = xmlnode_get_attrib(setting, "name");
++	const char *type = xmlnode_get_attrib(setting, "type");
++	char *value = xmlnode_get_data(setting);
++
++	if (!value)
++		return;
++
++	if (!type || purple_strequal(type, "string"))
++		purple_blist_node_set_string(node, name, value);
++	else if (purple_strequal(type, "bool"))
++		purple_blist_node_set_bool(node, name, atoi(value));
++	else if (purple_strequal(type, "int"))
++		purple_blist_node_set_int(node, name, atoi(value));
++
++	g_free(value);
++}
++
++static void
++parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
++{
++	PurpleAccount *account;
++	PurpleBuddy *buddy;
++	char *name = NULL, *alias = NULL;
++	const char *acct_name, *proto, *protocol;
++	xmlnode *x;
++
++	acct_name = xmlnode_get_attrib(bnode, "account");
++	protocol = xmlnode_get_attrib(bnode, "protocol");
++	protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
++	proto = xmlnode_get_attrib(bnode, "proto");
++	proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
++
++	if (!acct_name || (!proto && !protocol))
++		return;
++
++	account = purple_accounts_find(acct_name, proto ? proto : protocol);
++
++	if (!account)
++		return;
++
++	if ((x = xmlnode_get_child(bnode, "name")))
++		name = xmlnode_get_data(x);
++
++	if (!name)
++		return;
++
++	if ((x = xmlnode_get_child(bnode, "alias")))
++		alias = xmlnode_get_data(x);
++
++	buddy = purple_buddy_new(account, name, alias);
++	purple_blist_add_buddy(buddy, contact, group,
++			purple_blist_get_last_child((PurpleBlistNode*)contact));
++
++	for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
++		parse_setting((PurpleBlistNode*)buddy, x);
++	}
++
++	g_free(name);
++	g_free(alias);
++}
++
++static void
++parse_contact(PurpleGroup *group, xmlnode *cnode)
++{
++	PurpleContact *contact = purple_contact_new();
++	xmlnode *x;
++	const char *alias;
++
++	purple_blist_add_contact(contact, group,
++			purple_blist_get_last_child((PurpleBlistNode*)group));
++
++	if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
++		purple_blist_alias_contact(contact, alias);
++	}
++
++	for (x = cnode->child; x; x = x->next) {
++		if (x->type != XMLNODE_TYPE_TAG)
++			continue;
++		if (purple_strequal(x->name, "buddy"))
++			parse_buddy(group, contact, x);
++		else if (purple_strequal(x->name, "setting"))
++			parse_setting((PurpleBlistNode*)contact, x);
++	}
++
++	/* if the contact is empty, don't keep it around.  it causes problems */
++	if (!((PurpleBlistNode*)contact)->child)
++		purple_blist_remove_contact(contact);
++}
++
++static void
++parse_chat(PurpleGroup *group, xmlnode *cnode)
++{
++	PurpleChat *chat;
++	PurpleAccount *account;
++	const char *acct_name, *proto, *protocol;
++	xmlnode *x;
++	char *alias = NULL;
++	GHashTable *components;
++
++	acct_name = xmlnode_get_attrib(cnode, "account");
++	protocol = xmlnode_get_attrib(cnode, "protocol");
++	proto = xmlnode_get_attrib(cnode, "proto");
++
++	if (!acct_name || (!proto && !protocol))
++		return;
++
++	account = purple_accounts_find(acct_name, proto ? proto : protocol);
++
++	if (!account)
++		return;
++
++	if ((x = xmlnode_get_child(cnode, "alias")))
++		alias = xmlnode_get_data(x);
++
++	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
++
++	for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
++		const char *name;
++		char *value;
++
++		name = xmlnode_get_attrib(x, "name");
++		value = xmlnode_get_data(x);
++		g_hash_table_replace(components, g_strdup(name), value);
++	}
++
++	chat = purple_chat_new(account, alias, components);
++	purple_blist_add_chat(chat, group,
++			purple_blist_get_last_child((PurpleBlistNode*)group));
++
++	for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
++		parse_setting((PurpleBlistNode*)chat, x);
++	}
++
++	g_free(alias);
++}
++
++static void
++parse_group(xmlnode *groupnode)
++{
++	const char *name = xmlnode_get_attrib(groupnode, "name");
++	PurpleBuddyList *purplebuddylist  = purple_get_blist();
++	PurpleGroup *group;
++	xmlnode *cnode;
++
++	if (!name)
++		name = _("Buddies");
++
++	group = purple_group_new(name);
++	purple_blist_add_group(group,
++			purple_blist_get_last_sibling(purplebuddylist->root));
++
++	for (cnode = groupnode->child; cnode; cnode = cnode->next) {
++		if (cnode->type != XMLNODE_TYPE_TAG)
++			continue;
++		if (purple_strequal(cnode->name, "setting"))
++			parse_setting((PurpleBlistNode*)group, cnode);
++		else if (purple_strequal(cnode->name, "contact") ||
++				purple_strequal(cnode->name, "person"))
++			parse_contact(group, cnode);
++		else if (purple_strequal(cnode->name, "chat"))
++			parse_chat(group, cnode);
++	}
++}
++
++
++void
++purple_blist_load()
++{
++	xmlnode *purple, *blist, *privacy;
++
++	blist_loaded = TRUE;
++
++	purple = purple_util_read_xml_from_file("blist.xml", _("buddy list"));
++
++	if (purple == NULL)
++		return;
++
++	blist = xmlnode_get_child(purple, "blist");
++	if (blist) {
++		xmlnode *groupnode;
++		for (groupnode = xmlnode_get_child(blist, "group"); groupnode != NULL;
++				groupnode = xmlnode_get_next_twin(groupnode)) {
++			parse_group(groupnode);
++		}
++	}
++
++	privacy = xmlnode_get_child(purple, "privacy");
++	if (privacy) {
++		xmlnode *anode;
++		for (anode = privacy->child; anode; anode = anode->next) {
++			xmlnode *x;
++			PurpleAccount *account;
++			int imode;
++			const char *acct_name, *proto, *mode, *protocol;
++
++			acct_name = xmlnode_get_attrib(anode, "name");
++			protocol = xmlnode_get_attrib(anode, "protocol");
++			proto = xmlnode_get_attrib(anode, "proto");
++			mode = xmlnode_get_attrib(anode, "mode");
++
++			if (!acct_name || (!proto && !protocol) || !mode)
++				continue;
++
++			account = purple_accounts_find(acct_name, proto ? proto : protocol);
++
++			if (!account)
++				continue;
++
++			imode = atoi(mode);
++			account->perm_deny = (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL);
++
++			for (x = anode->child; x; x = x->next) {
++				char *name;
++				if (x->type != XMLNODE_TYPE_TAG)
++					continue;
++
++				if (purple_strequal(x->name, "permit")) {
++					name = xmlnode_get_data(x);
++					purple_privacy_permit_add(account, name, TRUE);
++					g_free(name);
++				} else if (purple_strequal(x->name, "block")) {
++					name = xmlnode_get_data(x);
++					purple_privacy_deny_add(account, name, TRUE);
++					g_free(name);
++				}
++			}
++		}
++	}
++
++	xmlnode_free(purple);
++
++	/* This tells the buddy icon code to do its thing. */
++	_purple_buddy_icons_blist_loaded_cb();
++}
++
++void
++purple_blist_schedule_save()
++{
++	PurpleBuddyList *blist = purple_get_blist();
++	if (save_timer == 0)
++		save_timer = purple_timeout_add_seconds(5, (GSourceFunc) save_cb, blist);
++}
++
++void
++purple_blist_save_callback(PurpleBuddyList *blist, PurpleBlistNode *node) {
++	purple_blist_schedule_save();
++}
++
+============================================================
+--- libpurple/blistsaving.h	e571e125d589317871839e622c3ee946df8f266d
++++ libpurple/blistsaving.h	e571e125d589317871839e622c3ee946df8f266d
+@@ -0,0 +1,47 @@
++/**
++ * @file blistsaving.h Buddy List Saving API
++ * @ingroup core
++ */
++
++/* purple
++ *
++ * Purple is the legal property of its developers, whose names are too numerous
++ * to list here.  Please refer to the COPYRIGHT file distributed with this
++ * source distribution.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
++ */
++#ifndef _PURPLE_BLISTSAVING_H_
++#define _PURPLE_BLISTSAVING_H_
++
++#include <glib.h>
++
++/**
++ * Loads the buddy list from ~/.purple/blist.xml.
++ */
++void purple_blist_load(void);
++
++/**
++ * Schedule a save of the blist.xml file.  This is used by the privacy
++ * API whenever the privacy settings are changed.  If you make a change
++ * to blist.xml using one of the functions in the buddy list API, then
++ * the buddy list is saved automatically, so you should not need to
++ * call this.
++ */
++void purple_blist_schedule_save(void);
++
++void purple_blist_save_callback(PurpleBuddyList *blist, PurpleBlistNode *node);
++
++#endif
+============================================================
+--- finch/finch.c	7de9b76dc4808a46137c007229f0210ea9ffd659
++++ finch/finch.c	5d6b5aaadc171f133d4ad6f6eb97ec42c715425e
+@@ -37,6 +37,7 @@
+ #include "status.h"
+ #include "util.h"
+ #include "whiteboard.h"
++#include "blistsaving.h"
+ 
+ #include "gntdebug.h"
+ #include "gntprefs.h"
+============================================================
+--- finch/gntaccount.c	24d52150587c87297f848a21a98fe4488ad43cbb
++++ finch/gntaccount.c	4067a68f59ea25675ede9b5bbe79ece2c985a7da
+@@ -44,6 +44,7 @@
+ #include <plugin.h>
+ #include <request.h>
+ #include <savedstatuses.h>
++#include <blistsaving.h>
+ 
+ #include "gntaccount.h"
+ #include "gntblist.h"
+@@ -160,6 +161,7 @@ save_account_cb(AccountEditDialog *dialo
+ 		/* Protocol */
+ 		purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
+ 		purple_account_set_username(account, username->str);
++		purple_blist_schedule_save();
+ 	}
+ 	g_string_free(username, TRUE);
+ 
+============================================================
+--- finch/gntblist.c	d7891e6e97a491a82f1e3baee278be83774087b0
++++ finch/gntblist.c	4e4e7efb0b05bf77aabcc21a0a74c31b17a61ede
+@@ -37,6 +37,7 @@
+ #include <status.h>
+ #include <util.h>
+ #include "debug.h"
++#include "blistsaving.h"
+ 
+ #include "gntbox.h"
+ #include "gntcolors.h"
+============================================================
+--- libpurple/Makefile.am	22dfd64715e47d7c86458cd3686d2c7857fce25f
++++ libpurple/Makefile.am	be1014b632e6314515ab53d482e2906833260098
+@@ -90,7 +90,8 @@ purple_coresources = \
+ 	value.c \
+ 	version.c \
+ 	xmlnode.c \
+-	whiteboard.c
++	whiteboard.c \
++	blistsaving.c
+ 
+ purple_coreheaders = \
+ 	account.h \
+@@ -150,7 +151,8 @@ purple_coreheaders = \
+ 	util.h \
+ 	value.h \
+ 	xmlnode.h \
+-	whiteboard.h
++	whiteboard.h \
++	blistsaving.h
+ 
+ purple_builtheaders = purple.h version.h marshallers.h
+ 
+============================================================
+--- libpurple/account.c	f30d42673ff93f36740312dacb3ee1c876bf2936
++++ libpurple/account.c	0e6b671fb591c61b5cb01909006266db929e7b99
+@@ -1506,7 +1506,9 @@ purple_account_set_username(PurpleAccoun
+ 
+ 	/* if the name changes, we should re-write the buddy list
+ 	 * to disk with the new name */
+-	purple_blist_schedule_save();
++// 	 TODO: We must do it somehow with new saving API... but I don't know how to do it...
++// 	Maybe UI should do it before or after calling this function...
++// 	purple_blist_schedule_save();
+ }
+ 
+ void
+============================================================
+--- libpurple/blist.c	880a4f22c97e117941e6d36767fd82cf322c0c91
++++ libpurple/blist.c	c84e7f646d0b84b3c692ddeeb017596ac572816f
+@@ -36,6 +36,7 @@
+ #include "util.h"
+ #include "value.h"
+ #include "xmlnode.h"
++#include "blistsaving.h"
+ 
+ static PurpleBlistUiOps *blist_ui_ops = NULL;
+ 
+@@ -48,8 +49,7 @@ static GHashTable *buddies_cache = NULL;
+  */
+ static GHashTable *buddies_cache = NULL;
+ 
+-static guint          save_timer = 0;
+-static gboolean       blist_loaded = FALSE;
++// static gboolean       blist_loaded = FALSE;
+ 
+ /*********************************************************************
+  * Private utility functions                                         *
+@@ -114,526 +114,7 @@ purple_blist_buddies_cache_remove_accoun
+ 	g_hash_table_remove(buddies_cache, account);
+ }
+ 
+-
+ /*********************************************************************
+- * Writing to disk                                                   *
+- *********************************************************************/
+-
+-static void
+-value_to_xmlnode(gpointer key, gpointer hvalue, gpointer user_data)
+-{
+-	const char *name;
+-	PurpleValue *value;
+-	xmlnode *node, *child;
+-	char buf[21];
+-
+-	name    = (const char *)key;
+-	value   = (PurpleValue *)hvalue;
+-	node    = (xmlnode *)user_data;
+-
+-	g_return_if_fail(value != NULL);
+-
+-	child = xmlnode_new_child(node, "setting");
+-	xmlnode_set_attrib(child, "name", name);
+-
+-	if (purple_value_get_type(value) == PURPLE_TYPE_INT) {
+-		xmlnode_set_attrib(child, "type", "int");
+-		g_snprintf(buf, sizeof(buf), "%d", purple_value_get_int(value));
+-		xmlnode_insert_data(child, buf, -1);
+-	}
+-	else if (purple_value_get_type(value) == PURPLE_TYPE_STRING) {
+-		xmlnode_set_attrib(child, "type", "string");
+-		xmlnode_insert_data(child, purple_value_get_string(value), -1);
+-	}
+-	else if (purple_value_get_type(value) == PURPLE_TYPE_BOOLEAN) {
+-		xmlnode_set_attrib(child, "type", "bool");
+-		g_snprintf(buf, sizeof(buf), "%d", purple_value_get_boolean(value));
+-		xmlnode_insert_data(child, buf, -1);
+-	}
+-}
+-
+-static void
+-chat_component_to_xmlnode(gpointer key, gpointer value, gpointer user_data)
+-{
+-	const char *name;
+-	const char *data;
+-	xmlnode *node, *child;
+-
+-	name = (const char *)key;
+-	data = (const char *)value;
+-	node = (xmlnode *)user_data;
+-
+-	g_return_if_fail(data != NULL);
+-
+-	child = xmlnode_new_child(node, "component");
+-	xmlnode_set_attrib(child, "name", name);
+-	xmlnode_insert_data(child, data, -1);
+-}
+-
+-static xmlnode *
+-buddy_to_xmlnode(PurpleBlistNode *bnode)
+-{
+-	xmlnode *node, *child;
+-	PurpleBuddy *buddy;
+-
+-	buddy = (PurpleBuddy *)bnode;
+-
+-	node = xmlnode_new("buddy");
+-	xmlnode_set_attrib(node, "account", purple_account_get_username(buddy->account));
+-	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(buddy->account));
+-
+-	child = xmlnode_new_child(node, "name");
+-	xmlnode_insert_data(child, buddy->name, -1);
+-
+-	if (buddy->alias != NULL)
+-	{
+-		child = xmlnode_new_child(node, "alias");
+-		xmlnode_insert_data(child, buddy->alias, -1);
+-	}
+-
+-	/* Write buddy settings */
+-	g_hash_table_foreach(buddy->node.settings, value_to_xmlnode, node);
+-
+-	return node;
+-}
+-
+-static xmlnode *
+-contact_to_xmlnode(PurpleBlistNode *cnode)
+-{
+-	xmlnode *node, *child;
+-	PurpleContact *contact;
+-	PurpleBlistNode *bnode;
+-
+-	contact = (PurpleContact *)cnode;
+-
+-	node = xmlnode_new("contact");
+-
+-	if (contact->alias != NULL)
+-	{
+-		xmlnode_set_attrib(node, "alias", contact->alias);
+-	}
+-
+-	/* Write buddies */
+-	for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
+-	{
+-		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(bnode))
+-			continue;
+-		if (PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+-		{
+-			child = buddy_to_xmlnode(bnode);
+-			xmlnode_insert_child(node, child);
+-		}
+-	}
+-
+-	/* Write contact settings */
+-	g_hash_table_foreach(cnode->settings, value_to_xmlnode, node);
+-
+-	return node;
+-}
+-
+-static xmlnode *
+-chat_to_xmlnode(PurpleBlistNode *cnode)
+-{
+-	xmlnode *node, *child;
+-	PurpleChat *chat;
+-
+-	chat = (PurpleChat *)cnode;
+-
+-	node = xmlnode_new("chat");
+-	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(chat->account));
+-	xmlnode_set_attrib(node, "account", purple_account_get_username(chat->account));
+-
+-	if (chat->alias != NULL)
+-	{
+-		child = xmlnode_new_child(node, "alias");
+-		xmlnode_insert_data(child, chat->alias, -1);
+-	}
+-
+-	/* Write chat components */
+-	g_hash_table_foreach(chat->components, chat_component_to_xmlnode, node);
+-
+-	/* Write chat settings */
+-	g_hash_table_foreach(chat->node.settings, value_to_xmlnode, node);
+-
+-	return node;
+-}
+-
+-static xmlnode *
+-group_to_xmlnode(PurpleBlistNode *gnode)
+-{
+-	xmlnode *node, *child;
+-	PurpleGroup *group;
+-	PurpleBlistNode *cnode;
+-
+-	group = (PurpleGroup *)gnode;
+-
+-	node = xmlnode_new("group");
+-	xmlnode_set_attrib(node, "name", group->name);
+-
+-	/* Write settings */
+-	g_hash_table_foreach(group->node.settings, value_to_xmlnode, node);
+-
+-	/* Write contacts and chats */
+-	for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
+-	{
+-		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(cnode))
+-			continue;
+-		if (PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+-		{
+-			child = contact_to_xmlnode(cnode);
+-			xmlnode_insert_child(node, child);
+-		}
+-		else if (PURPLE_BLIST_NODE_IS_CHAT(cnode))
+-		{
+-			child = chat_to_xmlnode(cnode);
+-			xmlnode_insert_child(node, child);
+-		}
+-	}
+-
+-	return node;
+-}
+-
+-static xmlnode *
+-accountprivacy_to_xmlnode(PurpleAccount *account)
+-{
+-	xmlnode *node, *child;
+-	GSList *cur;
+-	char buf[10];
+-
+-	node = xmlnode_new("account");
+-	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
+-	xmlnode_set_attrib(node, "name", purple_account_get_username(account));
+-	g_snprintf(buf, sizeof(buf), "%d", account->perm_deny);
+-	xmlnode_set_attrib(node, "mode", buf);
+-
+-	for (cur = account->permit; cur; cur = cur->next)
+-	{
+-		child = xmlnode_new_child(node, "permit");
+-		xmlnode_insert_data(child, cur->data, -1);
+-	}
+-
+-	for (cur = account->deny; cur; cur = cur->next)
+-	{
+-		child = xmlnode_new_child(node, "block");
+-		xmlnode_insert_data(child, cur->data, -1);
+-	}
+-
+-	return node;
+-}
+-
+-static xmlnode *
+-blist_to_xmlnode(void)
+-{
+-	xmlnode *node, *child, *grandchild;
+-	PurpleBlistNode *gnode;
+-	GList *cur;
+-
+-	node = xmlnode_new("purple");
+-	xmlnode_set_attrib(node, "version", "1.0");
+-
+-	/* Write groups */
+-	child = xmlnode_new_child(node, "blist");
+-	for (gnode = purplebuddylist->root; gnode != NULL; gnode = gnode->next)
+-	{
+-		if (!PURPLE_BLIST_NODE_SHOULD_SAVE(gnode))
+-			continue;
+-		if (PURPLE_BLIST_NODE_IS_GROUP(gnode))
+-		{
+-			grandchild = group_to_xmlnode(gnode);
+-			xmlnode_insert_child(child, grandchild);
+-		}
+-	}
+-
+-	/* Write privacy settings */
+-	child = xmlnode_new_child(node, "privacy");
+-	for (cur = purple_accounts_get_all(); cur != NULL; cur = cur->next)
+-	{
+-		grandchild = accountprivacy_to_xmlnode(cur->data);
+-		xmlnode_insert_child(child, grandchild);
+-	}
+-
+-	return node;
+-}
+-
+-static void
+-purple_blist_sync(void)
+-{
+-	xmlnode *node;
+-	char *data;
+-
+-	if (!blist_loaded)
+-	{
+-		purple_debug_error("blist", "Attempted to save buddy list before it "
+-						 "was read!\n");
+-		return;
+-	}
+-
+-	node = blist_to_xmlnode();
+-	data = xmlnode_to_formatted_str(node, NULL);
+-	purple_util_write_data_to_file("blist.xml", data, -1);
+-	g_free(data);
+-	xmlnode_free(node);
+-}
+-
+-static gboolean
+-save_cb(gpointer data)
+-{
+-	purple_blist_sync();
+-	save_timer = 0;
+-	return FALSE;
+-}
+-
+-void
+-purple_blist_schedule_save()
+-{
+-	if (save_timer == 0)
+-		save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
+-}
+-
+-
+-/*********************************************************************
+- * Reading from disk                                                 *
+- *********************************************************************/
+-
+-static void
+-parse_setting(PurpleBlistNode *node, xmlnode *setting)
+-{
+-	const char *name = xmlnode_get_attrib(setting, "name");
+-	const char *type = xmlnode_get_attrib(setting, "type");
+-	char *value = xmlnode_get_data(setting);
+-
+-	if (!value)
+-		return;
+-
+-	if (!type || purple_strequal(type, "string"))
+-		purple_blist_node_set_string(node, name, value);
+-	else if (purple_strequal(type, "bool"))
+-		purple_blist_node_set_bool(node, name, atoi(value));
+-	else if (purple_strequal(type, "int"))
+-		purple_blist_node_set_int(node, name, atoi(value));
+-
+-	g_free(value);
+-}
+-
+-static void
+-parse_buddy(PurpleGroup *group, PurpleContact *contact, xmlnode *bnode)
+-{
+-	PurpleAccount *account;
+-	PurpleBuddy *buddy;
+-	char *name = NULL, *alias = NULL;
+-	const char *acct_name, *proto, *protocol;
+-	xmlnode *x;
+-
+-	acct_name = xmlnode_get_attrib(bnode, "account");
+-	protocol = xmlnode_get_attrib(bnode, "protocol");
+-	protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
+-	proto = xmlnode_get_attrib(bnode, "proto");
+-	proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
+-
+-	if (!acct_name || (!proto && !protocol))
+-		return;
+-
+-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+-
+-	if (!account)
+-		return;
+-
+-	if ((x = xmlnode_get_child(bnode, "name")))
+-		name = xmlnode_get_data(x);
+-
+-	if (!name)
+-		return;
+-
+-	if ((x = xmlnode_get_child(bnode, "alias")))
+-		alias = xmlnode_get_data(x);
+-
+-	buddy = purple_buddy_new(account, name, alias);
+-	purple_blist_add_buddy(buddy, contact, group,
+-			purple_blist_get_last_child((PurpleBlistNode*)contact));
+-
+-	for (x = xmlnode_get_child(bnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
+-		parse_setting((PurpleBlistNode*)buddy, x);
+-	}
+-
+-	g_free(name);
+-	g_free(alias);
+-}
+-
+-static void
+-parse_contact(PurpleGroup *group, xmlnode *cnode)
+-{
+-	PurpleContact *contact = purple_contact_new();
+-	xmlnode *x;
+-	const char *alias;
+-
+-	purple_blist_add_contact(contact, group,
+-			purple_blist_get_last_child((PurpleBlistNode*)group));
+-
+-	if ((alias = xmlnode_get_attrib(cnode, "alias"))) {
+-		purple_blist_alias_contact(contact, alias);
+-	}
+-
+-	for (x = cnode->child; x; x = x->next) {
+-		if (x->type != XMLNODE_TYPE_TAG)
+-			continue;
+-		if (purple_strequal(x->name, "buddy"))
+-			parse_buddy(group, contact, x);
+-		else if (purple_strequal(x->name, "setting"))
+-			parse_setting((PurpleBlistNode*)contact, x);
+-	}
+-
+-	/* if the contact is empty, don't keep it around.  it causes problems */
+-	if (!((PurpleBlistNode*)contact)->child)
+-		purple_blist_remove_contact(contact);
+-}
+-
+-static void
+-parse_chat(PurpleGroup *group, xmlnode *cnode)
+-{
+-	PurpleChat *chat;
+-	PurpleAccount *account;
+-	const char *acct_name, *proto, *protocol;
+-	xmlnode *x;
+-	char *alias = NULL;
+-	GHashTable *components;
+-
+-	acct_name = xmlnode_get_attrib(cnode, "account");
+-	protocol = xmlnode_get_attrib(cnode, "protocol");
+-	proto = xmlnode_get_attrib(cnode, "proto");
+-
+-	if (!acct_name || (!proto && !protocol))
+-		return;
+-
+-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+-
+-	if (!account)
+-		return;
+-
+-	if ((x = xmlnode_get_child(cnode, "alias")))
+-		alias = xmlnode_get_data(x);
+-
+-	components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+-
+-	for (x = xmlnode_get_child(cnode, "component"); x; x = xmlnode_get_next_twin(x)) {
+-		const char *name;
+-		char *value;
+-
+-		name = xmlnode_get_attrib(x, "name");
+-		value = xmlnode_get_data(x);
+-		g_hash_table_replace(components, g_strdup(name), value);
+-	}
+-
+-	chat = purple_chat_new(account, alias, components);
+-	purple_blist_add_chat(chat, group,
+-			purple_blist_get_last_child((PurpleBlistNode*)group));
+-
+-	for (x = xmlnode_get_child(cnode, "setting"); x; x = xmlnode_get_next_twin(x)) {
+-		parse_setting((PurpleBlistNode*)chat, x);
+-	}
+-
+-	g_free(alias);
+-}
+-
+-static void
+-parse_group(xmlnode *groupnode)
+-{
+-	const char *name = xmlnode_get_attrib(groupnode, "name");
+-	PurpleGroup *group;
+-	xmlnode *cnode;
+-
+-	if (!name)
+-		name = _("Buddies");
+-
+-	group = purple_group_new(name);
+-	purple_blist_add_group(group,
+-			purple_blist_get_last_sibling(purplebuddylist->root));
+-
+-	for (cnode = groupnode->child; cnode; cnode = cnode->next) {
+-		if (cnode->type != XMLNODE_TYPE_TAG)
+-			continue;
+-		if (purple_strequal(cnode->name, "setting"))
+-			parse_setting((PurpleBlistNode*)group, cnode);
+-		else if (purple_strequal(cnode->name, "contact") ||
+-				purple_strequal(cnode->name, "person"))
+-			parse_contact(group, cnode);
+-		else if (purple_strequal(cnode->name, "chat"))
+-			parse_chat(group, cnode);
+-	}
+-}
+-
+-/* TODO: Make static and rename to load_blist */
+-void
+-purple_blist_load()
+-{
+-	xmlnode *purple, *blist, *privacy;
+-
+-	blist_loaded = TRUE;
+-
+-	purple = purple_util_read_xml_from_file("blist.xml", _("buddy list"));
+-
+-	if (purple == NULL)
+-		return;
+-
+-	blist = xmlnode_get_child(purple, "blist");
+-	if (blist) {
+-		xmlnode *groupnode;
+-		for (groupnode = xmlnode_get_child(blist, "group"); groupnode != NULL;
+-				groupnode = xmlnode_get_next_twin(groupnode)) {
+-			parse_group(groupnode);
+-		}
+-	}
+-
+-	privacy = xmlnode_get_child(purple, "privacy");
+-	if (privacy) {
+-		xmlnode *anode;
+-		for (anode = privacy->child; anode; anode = anode->next) {
+-			xmlnode *x;
+-			PurpleAccount *account;
+-			int imode;
+-			const char *acct_name, *proto, *mode, *protocol;
+-
+-			acct_name = xmlnode_get_attrib(anode, "name");
+-			protocol = xmlnode_get_attrib(anode, "protocol");
+-			proto = xmlnode_get_attrib(anode, "proto");
+-			mode = xmlnode_get_attrib(anode, "mode");
+-
+-			if (!acct_name || (!proto && !protocol) || !mode)
+-				continue;
+-
+-			account = purple_accounts_find(acct_name, proto ? proto : protocol);
+-
+-			if (!account)
+-				continue;
+-
+-			imode = atoi(mode);
+-			account->perm_deny = (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL);
+-
+-			for (x = anode->child; x; x = x->next) {
+-				char *name;
+-				if (x->type != XMLNODE_TYPE_TAG)
+-					continue;
+-
+-				if (purple_strequal(x->name, "permit")) {
+-					name = xmlnode_get_data(x);
+-					purple_privacy_permit_add(account, name, TRUE);
+-					g_free(name);
+-				} else if (purple_strequal(x->name, "block")) {
+-					name = xmlnode_get_data(x);
+-					purple_privacy_deny_add(account, name, TRUE);
+-					g_free(name);
+-				}
+-			}
+-		}
+-	}
+-
+-	xmlnode_free(purple);
+-
+-	/* This tells the buddy icon code to do its thing. */
+-	_purple_buddy_icons_blist_loaded_cb();
+-}
+-
+-
+-/*********************************************************************
+  * Stuff                                                             *
+  *********************************************************************/
+ 
+@@ -958,7 +439,8 @@ void purple_blist_rename_buddy(PurpleBud
+ 	g_free(buddy->name);
+ 	buddy->name = g_strdup(name);
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) buddy);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
+@@ -998,7 +480,8 @@ void purple_blist_alias_contact(PurpleCo
+ 		g_free(new_alias); /* could be "\0" */
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) contact);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)contact);
+@@ -1043,7 +526,8 @@ void purple_blist_alias_chat(PurpleChat 
+ 		g_free(new_alias); /* could be "\0" */
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) chat);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)chat);
+@@ -1079,7 +563,8 @@ void purple_blist_alias_buddy(PurpleBudd
+ 		g_free(new_alias); /* could be "\0" */
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) buddy);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
+@@ -1120,7 +605,8 @@ void purple_blist_server_alias_buddy(Pur
+ 		g_free(new_alias); /* could be "\0"; */
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) buddy);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)buddy);
+@@ -1214,7 +700,8 @@ void purple_blist_rename_group(PurpleGro
+ 	}
+ 
+ 	/* Save our changes */
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) source);
+ 
+ 	/* Update the UI */
+ 	if (ops && ops->update)
+@@ -1473,7 +960,8 @@ void purple_blist_add_chat(PurpleChat *c
+ 		if (ops && ops->new_node)
+ 			ops->new_node(cnode);
+ 
+-		purple_blist_schedule_save();
++		if (ops && ops->save_node)
++			ops->save_node(purplebuddylist, cnode);
+ 	}
+ 
+ 	if (node != NULL) {
+@@ -1502,7 +990,8 @@ void purple_blist_add_chat(PurpleChat *c
+ 		}
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) cnode);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode *)cnode);
+@@ -1580,8 +1069,6 @@ void purple_blist_add_buddy(PurpleBuddy 
+ 		if (ops && ops->remove)
+ 			ops->remove(purplebuddylist, bnode);
+ 
+-		purple_blist_schedule_save();
+-
+ 		if (bnode->parent->parent != (PurpleBlistNode*)g) {
+ 			hb = g_new(struct _purple_hbuddy, 1);
+ 			hb->name = g_strdup(purple_normalize(buddy->account, buddy->name));
+@@ -1649,7 +1136,8 @@ void purple_blist_add_buddy(PurpleBuddy 
+ 
+ 	purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) buddy);
+ 
+ 	if (ops && ops->update)
+ 		ops->update(purplebuddylist, (PurpleBlistNode*)buddy);
+@@ -1868,7 +1356,8 @@ void purple_blist_add_contact(PurpleCont
+ 		if (ops && ops->remove)
+ 			ops->remove(purplebuddylist, cnode);
+ 
+-		purple_blist_schedule_save();
++		if (ops && ops->delete_node)
++			ops->delete_node(purplebuddylist, (PurpleBlistNode*) cnode);
+ 	}
+ 
+ 	if (node && (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
+@@ -1894,7 +1383,13 @@ void purple_blist_add_contact(PurpleCont
+ 		g->currentsize++;
+ 	g->totalsize++;
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node)
++	{
++		if (cnode->child)
++			ops->save_node(purplebuddylist, (PurpleBlistNode*) cnode);
++		for (bnode = cnode->child; bnode; bnode = bnode->next)
++			ops->save_node(purplebuddylist, (PurpleBlistNode*) bnode);
++	}
+ 
+ 	if (ops && ops->update)
+ 	{
+@@ -1989,7 +1484,11 @@ void purple_blist_add_group(PurpleGroup 
+ 		purplebuddylist->root = gnode;
+ 	}
+ 
+-	purple_blist_schedule_save();
++	if (ops && ops->save_node) {
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) gnode);
++		for (node = gnode->child; node; node = node->next)
++			ops->save_node(purplebuddylist, node);
++	}
+ 
+ 	if (ops && ops->update) {
+ 		ops->update(purplebuddylist, gnode);
+@@ -2035,12 +1534,13 @@ void purple_blist_remove_contact(PurpleC
+ 		if (node->next)
+ 			node->next->prev = node->prev;
+ 
+-		purple_blist_schedule_save();
+-
+ 		/* Update the UI */
+ 		if (ops && ops->remove)
+ 			ops->remove(purplebuddylist, node);
+ 
++		if (ops && ops->delete_node)
++			ops->delete_node(purplebuddylist, (PurpleBlistNode*) node);
++
+ 		purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ 				PURPLE_BLIST_NODE(contact));
+ 
+@@ -2096,8 +1596,6 @@ void purple_blist_remove_buddy(PurpleBud
+ 		}
+ 	}
+ 
+-	purple_blist_schedule_save();
+-
+ 	/* Remove this buddy from the buddies hash table */
+ 	hb.name = g_strdup(purple_normalize(buddy->account, buddy->name));
+ 	hb.account = buddy->account;
+@@ -2113,6 +1611,9 @@ void purple_blist_remove_buddy(PurpleBud
+ 	if (ops && ops->remove)
+ 		ops->remove(purplebuddylist, node);
+ 
++	if (ops && ops->delete_node)
++		ops->delete_node(purplebuddylist, (PurpleBlistNode*) node);
++
+ 	/* Signal that the buddy has been removed before freeing the memory for it */
+ 	purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy);
+ 
+@@ -2155,13 +1656,15 @@ void purple_blist_remove_chat(PurpleChat
+ 		}
+ 		group->totalsize--;
+ 
+-		purple_blist_schedule_save();
+ 	}
+ 
+ 	/* Update the UI */
+ 	if (ops && ops->remove)
+ 		ops->remove(purplebuddylist, node);
+ 
++	if (ops && ops->delete_node)
++		ops->delete_node(purplebuddylist, (PurpleBlistNode*) node);
++
+ 	purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ 			PURPLE_BLIST_NODE(chat));
+ 
+@@ -2191,12 +1694,13 @@ void purple_blist_remove_group(PurpleGro
+ 	if (node->next)
+ 		node->next->prev = node->prev;
+ 
+-	purple_blist_schedule_save();
+-
+ 	/* Update the UI */
+ 	if (ops && ops->remove)
+ 		ops->remove(purplebuddylist, node);
+ 
++	if (ops && ops->delete_node)
++		ops->delete_node(purplebuddylist, (PurpleBlistNode*) node);
++
+ 	purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
+ 			PURPLE_BLIST_NODE(group));
+ 
+@@ -2808,13 +2312,16 @@ void purple_blist_node_remove_setting(Pu
+ 
+ void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
+ {
++	PurpleBlistUiOps *ops;
+ 	g_return_if_fail(node != NULL);
+ 	g_return_if_fail(node->settings != NULL);
+ 	g_return_if_fail(key != NULL);
+ 
+ 	g_hash_table_remove(node->settings, key);
+ 
+-	purple_blist_schedule_save();
++	ops = purple_blist_get_ui_ops();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, (PurpleBlistNode*) node);
+ }
+ 
+ void
+@@ -2844,6 +2351,7 @@ purple_blist_node_set_bool(PurpleBlistNo
+ purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
+ {
+ 	PurpleValue *value;
++	PurpleBlistUiOps *ops;
+ 
+ 	g_return_if_fail(node != NULL);
+ 	g_return_if_fail(node->settings != NULL);
+@@ -2854,7 +2362,9 @@ purple_blist_node_set_bool(PurpleBlistNo
+ 
+ 	g_hash_table_replace(node->settings, g_strdup(key), value);
+ 
+-	purple_blist_schedule_save();
++	ops = purple_blist_get_ui_ops();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, node);
+ }
+ 
+ gboolean
+@@ -2880,6 +2390,7 @@ purple_blist_node_set_int(PurpleBlistNod
+ purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
+ {
+ 	PurpleValue *value;
++	PurpleBlistUiOps *ops;
+ 
+ 	g_return_if_fail(node != NULL);
+ 	g_return_if_fail(node->settings != NULL);
+@@ -2890,7 +2401,9 @@ purple_blist_node_set_int(PurpleBlistNod
+ 
+ 	g_hash_table_replace(node->settings, g_strdup(key), value);
+ 
+-	purple_blist_schedule_save();
++	ops = purple_blist_get_ui_ops();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, node);
+ }
+ 
+ int
+@@ -2916,6 +2429,7 @@ purple_blist_node_set_string(PurpleBlist
+ purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
+ {
+ 	PurpleValue *value;
++	PurpleBlistUiOps *ops;
+ 
+ 	g_return_if_fail(node != NULL);
+ 	g_return_if_fail(node->settings != NULL);
+@@ -2926,7 +2440,9 @@ purple_blist_node_set_string(PurpleBlist
+ 
+ 	g_hash_table_replace(node->settings, g_strdup(key), value);
+ 
+-	purple_blist_schedule_save();
++	ops = purple_blist_get_ui_ops();
++	if (ops && ops->save_node)
++		ops->save_node(purplebuddylist, node);
+ }
+ 
+ const char *
+@@ -2981,6 +2497,10 @@ purple_blist_set_ui_ops(PurpleBlistUiOps
+ purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
+ {
+ 	blist_ui_ops = ops;
++	if (!ops->save_node)
++		ops->save_node = purple_blist_save_callback;
++	if (!ops->delete_node)
++		ops->delete_node = purple_blist_save_callback;
+ }
+ 
+ PurpleBlistUiOps *
+@@ -3100,13 +2620,13 @@ purple_blist_uninit(void)
+ 	/* This happens if we quit before purple_set_blist is called. */
+ 	if (purplebuddylist == NULL)
+ 		return;
++// UI should save blist before quit
++// 	if (save_timer != 0) {
++// 		purple_timeout_remove(save_timer);
++// 		save_timer = 0;
++// 		purple_blist_sync();
++// 	}
+ 
+-	if (save_timer != 0) {
+-		purple_timeout_remove(save_timer);
+-		save_timer = 0;
+-		purple_blist_sync();
+-	}
+-
+ 	node = purple_blist_get_root();
+ 	while (node) {
+ 		next_node = node->next;
+============================================================
+--- libpurple/blist.h	9580da3d4ae9096b2e4b8cbb88ca57a84f03f901
++++ libpurple/blist.h	a5774f27060c7e8ad0e67dc536c1a566e46e001a
+@@ -216,8 +216,12 @@ struct _PurpleBlistUiOps
+ 							 const char *alias, const char *name);
+ 	void (*request_add_group)(void);
+ 
+-	void (*_purple_reserved1)(void);
+-	void (*_purple_reserved2)(void);
++	void (*save_node)(PurpleBuddyList *blist,
++				PurpleBlistNode *node);
++
++	void (*delete_node)(PurpleBuddyList *blist,
++				PurpleBlistNode *node);
++
+ 	void (*_purple_reserved3)(void);
+ 	void (*_purple_reserved4)(void);
+ };
+@@ -1002,20 +1006,6 @@ int purple_blist_get_group_online_count(
+ /****************************************************************************************/
+ 
+ /**
+- * Loads the buddy list from ~/.purple/blist.xml.
+- */
+-void purple_blist_load(void);
+-
+-/**
+- * Schedule a save of the blist.xml file.  This is used by the privacy
+- * API whenever the privacy settings are changed.  If you make a change
+- * to blist.xml using one of the functions in the buddy list API, then
+- * the buddy list is saved automatically, so you should not need to
+- * call this.
+- */
+-void purple_blist_schedule_save(void);
+-
+-/**
+  * Requests from the user information needed to add a buddy to the
+  * buddy list.
+  *
+============================================================
+--- libpurple/example/nullclient.c	587f78d7b0f0c1d170ac1eac35d526ca62efa8f8
++++ libpurple/example/nullclient.c	4d496227427056c83a613c6abdcbd6634ee6e563
+@@ -212,7 +212,7 @@ init_libpurple(void)
+ 
+ 	/* Create and load the buddylist. */
+ 	purple_set_blist(purple_blist_new());
+-	purple_blist_load();
++// 	purple_blist_load();
+ 
+ 	/* Load the preferences. */
+ 	purple_prefs_load();
+============================================================
+--- libpurple/privacy.c	5e9715a02d5433f53c7de47f703619bc84be82f5
++++ libpurple/privacy.c	470a92591d021b1f71178640dfbd13c6dff73b54
+@@ -62,7 +62,8 @@ purple_privacy_permit_add(PurpleAccount 
+ 	if (privacy_ops != NULL && privacy_ops->permit_added != NULL)
+ 		privacy_ops->permit_added(account, who);
+ 
+-	purple_blist_schedule_save();
++	// UI should save privacy settings in permit_added callback
++// 	purple_blist_schedule_save();
+ 
+ 	/* This lets the UI know a buddy has had its privacy setting changed */
+ 	buddy = purple_find_buddy(account, name);
+@@ -109,7 +110,8 @@ purple_privacy_permit_remove(PurpleAccou
+ 	if (privacy_ops != NULL && privacy_ops->permit_removed != NULL)
+ 		privacy_ops->permit_removed(account, who);
+ 
+-	purple_blist_schedule_save();
++	// UI should do it in permit_removed callback
++// 	purple_blist_schedule_save();
+ 
+ 	buddy = purple_find_buddy(account, name);
+ 	if (buddy != NULL) {
+@@ -154,7 +156,8 @@ purple_privacy_deny_add(PurpleAccount *a
+ 	if (privacy_ops != NULL && privacy_ops->deny_added != NULL)
+ 		privacy_ops->deny_added(account, who);
+ 
+-	purple_blist_schedule_save();
++//	UI should do it in deny_added callback
++// 	purple_blist_schedule_save();
+ 
+ 	buddy = purple_find_buddy(account, name);
+ 	if (buddy != NULL) {
+@@ -205,7 +208,8 @@ purple_privacy_deny_remove(PurpleAccount
+ 	}
+ 
+ 	g_free(name);
+-	purple_blist_schedule_save();
++	// UI should save privacy settings in deny_removed callback
++// 	purple_blist_schedule_save();
+ 
+ 	return TRUE;
+ }
+============================================================
+--- pidgin/gtkaccount.c	f7bb182fa4fd2fb71894e9c2580c2ea1e64e84ae
++++ pidgin/gtkaccount.c	e99275271fde3c64d09ab6ec083cdfa0803acb42
+@@ -39,6 +39,7 @@
+ #include "savedstatuses.h"
+ #include "signals.h"
+ #include "util.h"
++#include "blistsaving.h"
+ 
+ #include "gtkaccount.h"
+ #include "gtkblist.h"
+@@ -1318,6 +1319,7 @@ ok_account_prefs_cb(GtkWidget *w, Accoun
+ 
+ 	purple_account_set_username(account, username);
+ 	g_free(username);
++	purple_blist_schedule_save();
+ 
+ 	/* Add the protocol settings */
+ 	if (dialog->prpl_info) {
+============================================================
+--- pidgin/gtkblist.c	122e9ad802a1125cad575c5cea42bed04d7416f1
++++ pidgin/gtkblist.c	80b0cb722f2803e37e5b2e2ecfee9c0f0f095e2e
+@@ -41,6 +41,7 @@
+ #include "theme-loader.h"
+ #include "theme-manager.h"
+ #include "util.h"
++#include "blistsaving.h"
+ 
+ #include "gtkaccount.h"
+ #include "gtkblist.h"
+============================================================
+--- pidgin/gtkblist.h	094332f5deb237f91ca4bd5f362bbbba2c4406fd
++++ pidgin/gtkblist.h	3a78e84707ed0071a48763371f8c3c386f0daa25
+@@ -153,6 +153,11 @@ void pidgin_blist_init(void);
+ void pidgin_blist_init(void);
+ 
+ /**
++ * Loads the buddy list from ~/.purple/blist.xml.
++ */
++void pidgin_blist_load(void);
++
++/**
+  * Uninitializes the GTK+ blist system.
+  */
+ void pidgin_blist_uninit(void);
+@@ -165,6 +170,15 @@ PurpleBlistUiOps *pidgin_blist_get_ui_op
+ PurpleBlistUiOps *pidgin_blist_get_ui_ops(void);
+ 
+ /**
++ * Schedule a save of the blist.xml file.  This is used by the privacy
++ * API whenever the privacy settings are changed.  If you make a change
++ * to blist.xml using one of the functions in the buddy list API, then
++ * the buddy list is saved automatically, so you should not need to
++ * call this.
++ */
++void pidgin_blist_schedule_save(void);
++
++/**
+  * Returns the default gtk buddy list
+  *
+  * There's normally only one buddy list window, but that isn't a necessity. This function
+============================================================
+--- pidgin/gtkmain.c	e8b5e9c5fa4c46fef1318293e9c3d96ac583d0c5
++++ pidgin/gtkmain.c	f462a52c4f262cc8c78443009bdf20168cab867c
+@@ -41,6 +41,7 @@
+ #include "status.h"
+ #include "util.h"
+ #include "whiteboard.h"
++#include "blistsaving.h"
+ 
+ #include "gtkaccount.h"
+ #include "gtkblist.h"
+============================================================
+--- pidgin/gtkprivacy.c	0539636ae120a83ed054e2fc0defb5fc235fd35a
++++ pidgin/gtkprivacy.c	61982de66d62ba27f59afbf2640fa4e8f221486a
+@@ -31,6 +31,7 @@
+ #include "privacy.h"
+ #include "request.h"
+ #include "util.h"
++#include "blistsaving.h"
+ 
+ #include "gtkblist.h"
+ #include "gtkprivacy.h"
+@@ -573,6 +574,7 @@ pidgin_permit_added_removed(PurpleAccoun
+ {
+ 	if (privacy_dialog != NULL)
+ 		rebuild_allow_list(privacy_dialog);
++	purple_blist_schedule_save();
+ }
+ 
+ static void
+@@ -580,6 +582,7 @@ pidgin_deny_added_removed(PurpleAccount 
+ {
+ 	if (privacy_dialog != NULL)
+ 		rebuild_block_list(privacy_dialog);
++	purple_blist_schedule_save();
+ }
+ 
+ static PurplePrivacyUiOps privacy_ops =
============================================================
--- filetransfermanager.cpp	3a5a120a59944b9f1bcadc34d8f002533649b39e
+++ filetransfermanager.cpp	7463e717a114f3ba22dd2077e73682572eba18dc
@@ -81,7 +81,7 @@ void FileTransferManager::sendFile(std::
             return;
         }
 		std::cout << "requesting filetransfer " << jid <<" " << file <<" as " << name << " " << info.st_size << "\n";
-        std::string sid = m_sip->requestFT(jid, from, /*name,*/ file, info.st_size);
+        std::string sid = m_sip->requestFT(jid, from, name, info.st_size);
         m_info[sid].filename = file;
         m_info[sid].size = info.st_size;
         f.close();


More information about the Commits mailing list