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