Blokkal
an Extendable KDE Blogging Client
SourceForge.net Logo

blokkalaccount.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2006 - 2008 by Martin Mueller                           *
00003  *   orvio@orvio.de                                                        *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 #include "blokkalaccount.h"
00021 #include "blokkalaccount.moc"
00022 
00023 #include "blokkalaccountconfig.h"
00024 #include "blokkalprotocol.h"
00025 #include "blokkalblog.h"
00026 #include "blokkalblogconfig.h"
00027 #include "blokkalwalletmanager.h"
00028 
00029 #include "blokkalio/categorymanager.h"
00030 
00031 #include "blokkalui/globalsettings.h"
00032 
00033 #include <kstandarddirs.h>
00034 #include <kaction.h>
00035 #include <kactioncollection.h>
00036 #include <klocale.h>
00037 #include <kdebug.h>
00038 #include <kwallet.h>
00039 #include <kpassworddialog.h>
00040 #include <knotification.h>
00041 
00042 #include <QDir>
00043 #include <QTimer>
00044 #include <QDomElement>
00045 
00046 class Blokkal::Account::Private {
00047 public:
00048         Private( void ):
00049         removeAccount( FALSE ),
00050         blogMap( QMap< QString, Blokkal::Blog * >() ),
00051         unregisteredBlogs( QMap< QString, QDomElement >() ),
00052         categoryManager( 0 ),
00053         autoReconnectCounter( 0 ){}
00054 
00055         Protocol * protocol;
00056 
00057         AccountConfig * accountConfig;
00058 
00059         Blokkal::Account::ConnectionStatus connectionStatus;
00060 
00061         bool removeAccount;
00062         QMap< QString, Blokkal::Blog * > blogMap;
00063         QMap< QString, QDomElement > unregisteredBlogs;
00064         
00065         Io::CategoryManager * categoryManager;
00066         unsigned int autoReconnectCounter;
00067 };
00068 
00069 Blokkal::Account::Account( Blokkal::Protocol * protocol, const QString & id ) :
00070 QObject( protocol ),
00071 d( new Private() )
00072 {
00073         d->protocol = protocol;
00074         d->accountConfig = new AccountConfig( id, protocol );
00075         d->connectionStatus = Disconnected;
00076 
00077         d->categoryManager = new Io::CategoryManager( this );
00078 }
00079 
00080 Blokkal::Account::~Account( void )
00081 {
00082         emit accountDestroyed( this );
00083         //never delete the protocol!
00084         delete d->accountConfig;
00085         d->accountConfig = 0;
00086         delete d;
00087 }
00088 
00089 Blokkal::AccountConfig * Blokkal::Account::config( void ) const
00090 {
00091         return d->accountConfig;
00092 }
00093 
00094 Blokkal::Protocol * Blokkal::Account::protocol( void ) const
00095 {
00096         return d->protocol;
00097 }
00098 
00099 QString Blokkal::Account::serverName( void ) const
00100 {
00101         return config()->readEntry( "servername" );
00102 }
00103 
00104 KUrl Blokkal::Account::connectAddress( void ) const
00105 {
00106         return config()->readEntry( "connectaddress" );
00107 }
00108 
00109 QString Blokkal::Account::id( void ) const
00110 {
00111         return config()->id();
00112 }
00113 
00114 QString Blokkal::Account::userName( void ) const
00115 {
00116         return config()->readEntry( "username" );
00117 }
00118 
00119 KIcon Blokkal::Account::icon( void ) const
00120 {
00121         return protocol()->icon();
00122 }
00123 
00124 void Blokkal::Account::removeAccount( void )
00125 {
00126         d->removeAccount = TRUE;
00127         disconnectAccount( UserDisconnect );
00128 }
00129 
00130 bool Blokkal::Account::usesDefaultConnectAddress( void ) const
00131 {
00132         return config()->readBoolEntry( "defaultconnectaddress" );
00133 }
00134 
00135 Blokkal::Account::ConnectionStatus Blokkal::Account::connectionStatus( void ) const
00136 {
00137         return d->connectionStatus;
00138 }
00139 
00140 void Blokkal::Account::setConnectionStatus( Blokkal::Account::ConnectionStatus status )
00141 {
00142         d->connectionStatus = status;
00143         if( status == Connected ) {
00144                 d->autoReconnectCounter = 0;
00145         }
00146         emit connectionStatusChanged( this );
00147 }
00148 
00149 void Blokkal::Account::disconnectAccount( Blokkal::Account::DisconnectReason reason, const QString & reasonString )
00150 {
00151         if( reason != UserDisconnect ) {
00152                 QString message;
00153                 if( !reasonString.isEmpty() || reason == AuthenticationDisconnect ) {
00154                         message = i18n( "<b>%1</b> disconnected : %2" ,
00155                                         id(),
00156                                         reasonString.isEmpty() ? i18n( "authentication failed" ) : reasonString );
00157                 }
00158                 else {
00159                         message = i18n( "<b>%1</b> disconnected" , id() );
00160                 }
00161                 KNotification * notification = new KNotification( "account_disconnected", Ui::GlobalSettings::self()->mainWidget() );
00162                 notification->setText( message );
00163                 notification->sendEvent();
00164         }
00165 
00166         QList<Blog*> bloglist = blogs();
00167         for( QList<Blog*>::ConstIterator it = bloglist.begin();
00168              it != bloglist.end();
00169              ++it )
00170         {
00171                 (*it)->saveProperties();
00172         }
00173         
00174         setConnectionStatus( Disconnected );
00175 
00176         if( d->removeAccount ) {
00177                 config()->remove();
00178                 deleteLater();
00179         }
00180         else if( reason == AuthenticationDisconnect ) {
00181                 QTimer::singleShot( 0, this, SLOT( connectAccount() ) );
00182         }
00183         else if( reason != UserDisconnect && d->autoReconnectCounter < 3 ){
00184                 d->autoReconnectCounter++;
00185                 kDebug() << "auto reconnect: " << d->autoReconnectCounter << endl;
00186                 QTimer::singleShot( 30 * 1000, this, SLOT( connectAccount() ) );
00187         }
00188 }
00189 
00190 QList< Blokkal::Blog * > Blokkal::Account::blogs( void ) const
00191 {
00192         return d->blogMap.values();
00193 }
00194 
00195 Blokkal::Blog *  Blokkal::Account::registerBlog( Blokkal::Blog * blog )
00196 {
00197         if( d->blogMap.contains( blog->id() ) ) {
00198                 if( d->blogMap[blog->id()] == blog ) {
00199                         return blog;
00200                 }
00201                 else {
00202                         blog->deleteLater();
00203                         return 0;
00204                 }
00205         }
00206 
00207         //this blog is actually new
00208         d->blogMap.insert( blog->id(), blog );
00209         if( d->unregisteredBlogs.contains( blog->id() ) ) {
00210                 config()->node().appendChild( d->unregisteredBlogs[blog->id()] );
00211                 d->unregisteredBlogs.remove( blog->id() );
00212         }
00213         connect( blog, SIGNAL( blogDestroyed( Blokkal::Blog * ) ),
00214                  this, SLOT( unregisterBlog( Blokkal::Blog * ) ) );
00215         emit blogRegistered( blog );
00216         return blog;
00217 }
00218 
00219 void Blokkal::Account::unregisterBlog( Blokkal::Blog * blog )
00220 {
00221         if( !d->blogMap.contains( blog->id() ) ) {
00222                 d->blogMap.remove( blog->id() );
00223                 emit blogUnregistered( blog );
00224         }
00225 }
00226 
00227 Blokkal::Blog * Blokkal::Account::blog( const QString & id ) const
00228 {
00229         if( d->blogMap.contains( id ) ) {
00230                 return d->blogMap[ id ];
00231         }
00232 
00233         return 0;
00234 }
00235 
00236 QString Blokkal::Account::dataDirectory( void ) const
00237 {
00238         QString dataDirName = KStandardDirs::locateLocal( "appdata",
00239                 QString::fromLatin1( "accounts/%1/" ).arg( id() ),
00240                 TRUE );
00241         
00242         /*QDir oldAccountDir( KStandardDirs::locateLocal( "appdata", QString::fromLatin1( "%1/" ).arg( id() ), FALSE ) );
00243         if( oldAccountDir.exists() ) {
00244                 kDebug() << "found old style account directory: " << oldAccountDir.absolutePath() << endl;
00245                 kDebug() << "moving it to new location: " << dataDirName << endl;
00246                 if( !oldAccountDir.rename( oldAccountDir.absolutePath(), dataDirName ) ) {
00247                         kError() << "failed to move directory " << oldAccountDir.absolutePath() << " to " << dataDirName << endl;
00248                 }
00249                 else {
00250                         kDebug() << "successfully moved directory " << oldAccountDir.absolutePath() << " to " << dataDirName << endl;
00251                 }
00252         }*/
00253 
00254         return dataDirName;
00255 }
00256 
00257 void Blokkal::Account::setServerName( const QString & serverName )
00258 {
00259         config()->writeEntry( "servername", serverName );
00260 }
00261 
00262 void Blokkal::Account::setConnectAddress( const QString & address )
00263 {
00264         config()->writeEntry( "connectaddress", address );
00265 }
00266 
00267 void Blokkal::Account::setUsesDefaultConnectAddress( bool useDefault )
00268 {
00269         config()->writeEntry( "defaultconnectaddress", useDefault );
00270 }
00271 
00272 void Blokkal::Account::setUserName( const QString & user )
00273 {
00274         config()->writeEntry( "username", user );
00275 }
00276 
00277 bool Blokkal::Account::isConnected( void ) const
00278 {
00279         return connectionStatus() == Connected;
00280 }
00281 
00282 bool Blokkal::Account::isDisconnected( void ) const
00283 {
00284         return connectionStatus() == Disconnected;
00285 }
00286 
00287 QDomElement Blokkal::Account::blogNode( const QString & id )
00288 {
00289         //search the document first
00290         QDomElement currentElement;
00291         for( QDomNode currentNode = config()->node().namedItem( "blog" );
00292              !currentNode.isNull();
00293              currentNode = currentNode.nextSibling() )
00294         {
00295                 if( currentNode.nodeName() == "blog" && currentNode.isElement() ) {
00296                         currentElement = currentNode.toElement();
00297                         if( currentElement.attribute( "id" ) == id
00298                             || currentElement.attribute( "name" ) == id )
00299                         {
00300                                 return currentElement;
00301                         }
00302                 }
00303         }
00304 
00305         //try free unregistered blogs next
00306         if( d->unregisteredBlogs.contains( id ) ) {
00307                 return d->unregisteredBlogs[id];
00308         }
00309 
00310         //create new unregistered blog node
00311         currentElement = config()->node().ownerDocument().createElement( "blog" );
00312         currentElement.setAttribute( "id", id );
00313         d->unregisteredBlogs.insert( id, currentElement );
00314         return currentElement;
00315 }
00316 
00317 void Blokkal::Account::restoreBlogs( void )
00318 {
00319         QDomElement currentElement;
00320         Blog * currentBlog;
00321         BlogConfig * config;
00322         
00323         for( QDomNode currentNode = this->config()->node().namedItem( "blog" );
00324              !currentNode.isNull();
00325              currentNode = currentNode.nextSibling() )
00326         {
00327                 if( currentNode.nodeName() == "blog" && currentNode.isElement() ) {
00328                         currentElement = currentNode.toElement();
00329                         const QString id = currentElement.attribute( "name" ).isEmpty()
00330                                 ? currentElement.attribute( "id")
00331                                 : currentElement.attribute( "name" );
00332                         if( id.isEmpty() ) {
00333                                 this->config()->node().removeChild( currentElement );
00334                                 continue;
00335                         }
00336                         
00337                         config = new BlogConfig( id, this );
00338                         currentBlog = restoreBlog( config );
00339                         if( currentBlog ) {
00340                                 registerBlog( currentBlog );
00341                         }
00342 
00343                         delete config;
00344                 }
00345         }
00346 }
00347 
00348 bool Blokkal::Account::autoConnect( void ) const
00349 {
00350         return config()->readBoolEntry( "autoconnect", TRUE );
00351 }
00352 
00353 void Blokkal::Account::setAutoConnect( bool enable ) const
00354 {
00355         config()->writeEntry( "autoconnect", enable );
00356 }
00357 
00358 Blokkal::Io::CategoryManager * Blokkal::Account::categoryManager( void ) const
00359 {
00360         return d->categoryManager;
00361 }
00362 
00363 //-----------------
00364 //PasswordedAccount
00365 //-----------------
00366 class Blokkal::PasswordedAccount::Private {
00367 public:
00368         Private( void ) :
00369         passwordWrong( FALSE ) {}
00370 
00371         bool passwordWrong;
00372         QString password;
00373 };
00374 Blokkal::PasswordedAccount::PasswordedAccount( Blokkal::Protocol * protocol, const QString & id ):
00375 Account( protocol, id ),
00376 d( new Private() )
00377 {
00378 }
00379 
00380 Blokkal::PasswordedAccount::~PasswordedAccount( void )
00381 {
00382         delete d;
00383 }
00384 
00385 void Blokkal::PasswordedAccount::removeAccount( void )
00386 {
00387         new Blokkal::PasswordDeleter( id() );
00388         Account::removeAccount( );
00389 }
00390 
00391 void Blokkal::PasswordedAccount::connectAccount( void )
00392 {
00393         setConnectionStatus( Connecting );
00394         if( d->password.isNull() || d->passwordWrong ) {
00395                 WalletManager::self()->openWallet( this, SLOT( slotWalletOpened( KWallet::Wallet * ) ) );
00396         }
00397         else {
00398                 connectInner();
00399         }
00400 }
00401 
00402 void Blokkal::PasswordedAccount::disconnectAccount( Blokkal::Account::DisconnectReason reason, const QString & reasonString )
00403 {
00404         if( reason == AuthenticationDisconnect ) {
00405                 d->password == QString::null;
00406                 d->passwordWrong = TRUE;
00407         }
00408 
00409         Account::disconnectAccount( reason, reasonString );
00410 }
00411 
00412 const QString & Blokkal::PasswordedAccount::password( void ) const
00413 {
00414         return d->password;
00415 }
00416 
00417 void Blokkal::PasswordedAccount::slotWalletOpened( KWallet::Wallet * wallet )
00418 {
00419         bool isValid = TRUE;
00420         if( wallet ) {
00421                 if( d->passwordWrong ) {
00422                         d->password = promptUser( isValid, wallet );
00423                 }
00424                 else if( wallet->hasEntry( id() ) ) {
00425                         wallet->readPassword( id(), d->password );
00426                 }
00427                 else {
00428                         d->password = promptUser( isValid, wallet );
00429                 }               
00430         }
00431         else {
00432                 d->password = promptUser( isValid, wallet );
00433         }
00434 
00435         if( isValid ) {
00436                 d->passwordWrong = FALSE;
00437                 connectInner();
00438         }
00439         else {
00440                 disconnectAccount( UserDisconnect );
00441         }
00442 }
00443 
00444 QString Blokkal::PasswordedAccount::promptUser( bool & success, KWallet::Wallet * wallet )
00445 {
00446         const QString passwordPrompt = i18n( "Password required for account: \"%1\"", id() );
00447         QString password;
00448 
00449         KPasswordDialog * passwordDialog = new KPasswordDialog( Blokkal::Ui::GlobalSettings::self()->mainWidget(),
00450                 wallet ?  KPasswordDialog::ShowKeepPassword : KPasswordDialog::NoFlags ); //enable keep button only when the wallet is present
00451         
00452         passwordDialog->setPrompt( passwordPrompt );
00453 
00454         
00455         if( passwordDialog->exec() ) {
00456                 password = passwordDialog->password();
00457                 if( passwordDialog->keepPassword() && wallet ) {
00458                         wallet->writePassword( id(), password );
00459                 }
00460                 success = TRUE;
00461         }
00462         else {
00463                 success = FALSE;
00464                 password = QString::null;
00465         }
00466 
00467         delete passwordDialog;
00468         passwordDialog = 0;
00469         return password;
00470 }
00471 
00472 QList< QAction * > Blokkal::Account::actionList( void )
00473 {
00474         return QList<QAction*>();
00475 }