kio Library API Documentation

kpasswdserver.cpp

00001 /*
00002     This file is part of the KDE Password Server
00003 
00004     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
00005     Copyright (C) 2005 David Faure (faure@kde.org)
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU General Public License
00009     version 2 as published by the Free Software Foundation.
00010 
00011     This software is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this library; see the file COPYING. If not, write to
00018     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019     Boston, MA 02111-1307, USA.
00020 */
00021 //----------------------------------------------------------------------------
00022 //
00023 // KDE Password Server
00024 // $Id: kpasswdserver.cpp 426310 2005-06-16 23:37:01Z dfaure $
00025 
00026 #include "kpasswdserver.h"
00027 
00028 #include <time.h>
00029 
00030 #include <qtimer.h>
00031 
00032 #include <kapplication.h>
00033 #include <klocale.h>
00034 #include <kmessagebox.h>
00035 #include <kdebug.h>
00036 #include <kio/passdlg.h>
00037 #include <kwallet.h>
00038 
00039 #include "config.h"
00040 #ifdef Q_WS_X11
00041 #include <X11/X.h>
00042 #include <X11/Xlib.h>
00043 #endif
00044 
00045 extern "C" {
00046     KDE_EXPORT KDEDModule *create_kpasswdserver(const QCString &name)
00047     {
00048        return new KPasswdServer(name);
00049     }
00050 }
00051 
00052 int
00053 KPasswdServer::AuthInfoList::compareItems(QPtrCollection::Item n1, QPtrCollection::Item n2)
00054 {
00055    if (!n1 || !n2)
00056       return 0;
00057 
00058    AuthInfo *i1 = (AuthInfo *) n1;
00059    AuthInfo *i2 = (AuthInfo *) n2;
00060 
00061    int l1 = i1->directory.length();
00062    int l2 = i2->directory.length();
00063 
00064    if (l1 > l2)
00065       return -1;
00066    if (l1 < l2)
00067       return 1;
00068    return 0;
00069 }
00070 
00071 
00072 KPasswdServer::KPasswdServer(const QCString &name)
00073  : KDEDModule(name)
00074 {
00075     m_authDict.setAutoDelete(true);
00076     m_authPending.setAutoDelete(true);
00077     m_seqNr = 0;
00078     m_wallet = 0;
00079     connect(this, SIGNAL(windowUnregistered(long)),
00080             this, SLOT(removeAuthForWindowId(long)));
00081 }
00082 
00083 KPasswdServer::~KPasswdServer()
00084 {
00085     delete m_wallet;
00086 }
00087 
00088 KIO::AuthInfo
00089 KPasswdServer::checkAuthInfo(KIO::AuthInfo info, long windowId)
00090 {
00091     kdDebug(130) << "KPasswdServer::checkAuthInfo: User= " << info.username
00092               << ", WindowId = " << windowId << endl;
00093 
00094     QString key = createCacheKey(info);
00095 
00096     Request *request = m_authPending.first();
00097     QString path2 = info.url.directory(false, false);
00098     for(; request; request = m_authPending.next())
00099     {
00100        if (request->key != key)
00101            continue;
00102 
00103        if (info.verifyPath)
00104        {
00105           QString path1 = request->info.url.directory(false, false);
00106           if (!path2.startsWith(path1))
00107              continue;
00108        }
00109 
00110        request = new Request;
00111        request->client = callingDcopClient();
00112        request->transaction = request->client->beginTransaction();
00113        request->key = key;
00114        request->info = info;
00115        m_authWait.append(request);
00116        return info;
00117     }
00118 
00119     const AuthInfo *result = findAuthInfoItem(key, info);
00120     if (!result || result->isCanceled)
00121     {
00122        if (!result &&
00123            (info.username.isEmpty() || info.password.isEmpty()) &&
00124            !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
00125                                              KWallet::Wallet::PasswordFolder(), key))
00126        {
00127           QMap<QString, QString> knownLogins;
00128           if (openWallet(windowId)) {
00129               if (readFromWallet(m_wallet, key, info.username, info.password,
00130                              info.readOnly, knownLogins))
00131           {
00132               info.setModified(true);
00133               return info;
00134           }
00135       }
00136        }
00137 
00138        info.setModified(false);
00139        return info;
00140     }
00141 
00142     updateAuthExpire(key, result, windowId, false);
00143 
00144     return copyAuthInfo(result);
00145 }
00146 
00147 KIO::AuthInfo
00148 KPasswdServer::queryAuthInfo(KIO::AuthInfo info, QString errorMsg, long windowId, long seqNr)
00149 {
00150     kdDebug(130) << "KPasswdServer::queryAuthInfo: User= " << info.username
00151               << ", Message= " << info.prompt << ", WindowId = " << windowId << endl;
00152     QString key = createCacheKey(info);
00153     Request *request = new Request;
00154     request->client = callingDcopClient();
00155     request->transaction = request->client->beginTransaction();
00156     request->key = key;
00157     request->info = info;
00158     request->windowId = windowId;
00159     request->seqNr = seqNr;
00160     if (errorMsg == "<NoAuthPrompt>")
00161     {
00162        request->errorMsg = QString::null;
00163        request->prompt = false;
00164     }
00165     else
00166     {
00167        request->errorMsg = errorMsg;
00168        request->prompt = true;
00169     }
00170     m_authPending.append(request);
00171 
00172     if (m_authPending.count() == 1)
00173        QTimer::singleShot(0, this, SLOT(processRequest()));
00174 
00175     return info;
00176 }
00177 
00178 void
00179 KPasswdServer::addAuthInfo(KIO::AuthInfo info, long windowId)
00180 {
00181     kdDebug(130) << "KPasswdServer::addAuthInfo: User= " << info.username
00182               << ", RealmValue= " << info.realmValue << ", WindowId = " << windowId << endl;
00183     QString key = createCacheKey(info);
00184 
00185     m_seqNr++;
00186 
00187     addAuthInfoItem(key, info, windowId, m_seqNr, false);
00188 }
00189 
00190 bool
00191 KPasswdServer::openWallet( WId windowId )
00192 {
00193     if ( m_wallet && !m_wallet->isOpen() ) { // forced closed
00194         delete m_wallet;
00195         m_wallet = 0;
00196     }
00197     if ( !m_wallet )
00198         m_wallet = KWallet::Wallet::openWallet(
00199             KWallet::Wallet::NetworkWallet(), windowId );
00200     return m_wallet != 0;
00201 }
00202 
00203 void
00204 KPasswdServer::processRequest()
00205 {
00206     Request *request = m_authPending.first();
00207     if (!request)
00208        return;
00209 
00210     KIO::AuthInfo &info = request->info;
00211 
00212     kdDebug(130) << "KPasswdServer::processRequest: User= " << info.username
00213               << ", Message= " << info.prompt << endl;
00214 
00215     const AuthInfo *result = findAuthInfoItem(request->key, request->info);
00216 
00217     if (result && (request->seqNr < result->seqNr))
00218     {
00219         kdDebug(130) << "KPasswdServer::processRequest: auto retry!" << endl;
00220         if (result->isCanceled)
00221         {
00222            info.setModified(false);
00223         }
00224         else
00225         {
00226            updateAuthExpire(request->key, result, request->windowId, false);
00227            info = copyAuthInfo(result);
00228         }
00229     }
00230     else
00231     {
00232         m_seqNr++;
00233         bool askPw = request->prompt;
00234         if (result && !info.username.isEmpty() &&
00235             !request->errorMsg.isEmpty())
00236         {
00237            QString prompt = request->errorMsg;
00238            prompt += i18n("  Do you want to retry?");
00239            int dlgResult = KMessageBox::warningContinueCancel(0, prompt,
00240                            i18n("Authentication"), i18n("Retry"));
00241            if (dlgResult != KMessageBox::Continue)
00242               askPw = false;
00243         }
00244 
00245         int dlgResult = QDialog::Rejected;
00246         if (askPw)
00247         {
00248             QString username = info.username;
00249             QString password = info.password;
00250             bool hasWalletData = false;
00251             QMap<QString, QString> knownLogins;
00252 
00253             if ( ( username.isEmpty() || password.isEmpty() )
00254                 && !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), request->key) )
00255             {
00256                 // no login+pass provided, check if kwallet has one
00257                 if ( openWallet( request->windowId ) )
00258                     hasWalletData = readFromWallet( m_wallet, request->key, username, password, info.readOnly, knownLogins );
00259             }
00260 
00261             KIO::PasswordDialog dlg( info.prompt, username, info.keepPassword );
00262             if (info.caption.isEmpty())
00263                dlg.setPlainCaption( i18n("Authorization Dialog") );
00264             else
00265                dlg.setPlainCaption( info.caption );
00266 
00267             if ( !info.comment.isEmpty() )
00268                dlg.addCommentLine( info.commentLabel, info.comment );
00269 
00270             if ( !password.isEmpty() )
00271                dlg.setPassword( password );
00272 
00273             if (info.readOnly)
00274                dlg.setUserReadOnly( true );
00275             else
00276                dlg.setKnownLogins( knownLogins );
00277 
00278             if (hasWalletData)
00279                 dlg.setKeepPassword( true );
00280 
00281 #ifdef Q_WS_X11
00282             XSetTransientForHint( qt_xdisplay(), dlg.winId(), request->windowId);
00283 #endif
00284 
00285             dlgResult = dlg.exec();
00286 
00287             if (dlgResult == QDialog::Accepted)
00288             {
00289                info.username = dlg.username();
00290                info.password = dlg.password();
00291                info.keepPassword = dlg.keepPassword();
00292 
00293                // When the user checks "keep password", that means:
00294                // * if the wallet is enabled, store it there for long-term, and in kpasswdserver
00295                // only for the duration of the window (#92928)
00296                // * otherwise store in kpasswdserver for the duration of the KDE session.
00297                if ( info.keepPassword ) {
00298                    if ( openWallet( request->windowId ) ) {
00299                        if ( storeInWallet( m_wallet, request->key, info ) )
00300                            // password is in wallet, don't keep it in memory after window is closed
00301                            info.keepPassword = false;
00302                    }
00303                }
00304             }
00305         }
00306         if ( dlgResult != QDialog::Accepted )
00307         {
00308             addAuthInfoItem(request->key, info, 0, m_seqNr, true);
00309             info.setModified( false );
00310         }
00311         else
00312         {
00313             addAuthInfoItem(request->key, info, request->windowId, m_seqNr, false);
00314             info.setModified( true );
00315         }
00316     }
00317 
00318     QCString replyType;
00319     QByteArray replyData;
00320 
00321     QDataStream stream2(replyData, IO_WriteOnly);
00322     stream2 << info << m_seqNr;
00323     replyType = "KIO::AuthInfo";
00324     request->client->endTransaction( request->transaction,
00325                                      replyType, replyData);
00326 
00327     m_authPending.remove((unsigned int) 0);
00328 
00329     // Check all requests in the wait queue.
00330     for(Request *waitRequest = m_authWait.first();
00331         waitRequest; )
00332     {
00333        bool keepQueued = false;
00334        QString key = waitRequest->key;
00335 
00336        request = m_authPending.first();
00337        QString path2 = waitRequest->info.url.directory(false, false);
00338        for(; request; request = m_authPending.next())
00339        {
00340            if (request->key != key)
00341                continue;
00342 
00343            if (info.verifyPath)
00344            {
00345                QString path1 = request->info.url.directory(false, false);
00346                if (!path2.startsWith(path1))
00347                    continue;
00348            }
00349 
00350            keepQueued = true;
00351            break;
00352        }
00353        if (keepQueued)
00354        {
00355            waitRequest = m_authWait.next();
00356        }
00357        else
00358        {
00359            const AuthInfo *result = findAuthInfoItem(waitRequest->key, waitRequest->info);
00360 
00361            QCString replyType;
00362            QByteArray replyData;
00363 
00364            QDataStream stream2(replyData, IO_WriteOnly);
00365 
00366            if (!result || result->isCanceled)
00367            {
00368                waitRequest->info.setModified(false);
00369                stream2 << waitRequest->info;
00370            }
00371            else
00372            {
00373                updateAuthExpire(waitRequest->key, result, waitRequest->windowId, false);
00374                KIO::AuthInfo info = copyAuthInfo(result);
00375                stream2 << info;
00376            }
00377 
00378            replyType = "KIO::AuthInfo";
00379            waitRequest->client->endTransaction( waitRequest->transaction,
00380                                                 replyType, replyData);
00381 
00382            m_authWait.remove();
00383            waitRequest = m_authWait.current();
00384        }
00385     }
00386 
00387     if (m_authPending.count())
00388        QTimer::singleShot(0, this, SLOT(processRequest()));
00389 
00390 }
00391 
00392 bool KPasswdServer::storeInWallet( KWallet::Wallet* wallet, const QString& key, const KIO::AuthInfo &info )
00393 {
00394     if ( !wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) )
00395         if ( !wallet->createFolder( KWallet::Wallet::PasswordFolder() ) )
00396             return false;
00397     wallet->setFolder( KWallet::Wallet::PasswordFolder() );
00398     // Before saving, check if there's already an entry with this login.
00399     // If so, replace it (with the new password). Otherwise, add a new entry.
00400     typedef QMap<QString,QString> Map;
00401     int entryNumber = 1;
00402     Map map;
00403     kdDebug() << k_funcinfo << "key=" << key << "  reading existing map" << endl;
00404     if ( wallet->readMap( key, map ) == 0 ) {
00405         Map::ConstIterator end = map.end();
00406         Map::ConstIterator it = map.find( "login" );
00407         while ( it != end ) {
00408             if ( it.data() == info.username ) {
00409                 break; // overwrite this entry
00410             }
00411 
00412             it = map.find( QString( "login-" ) + QString::number( ++entryNumber ) );
00413         }
00414         // If no entry was found, create a new entry - entryNumber is set already.
00415     }
00416     QString loginKey = "login";
00417     QString passwordKey = "password";
00418     if ( entryNumber > 1 ) {
00419         const QString suffix = "-" + QString::number( entryNumber );
00420         loginKey += suffix;
00421         passwordKey += suffix;
00422     }
00423     kdDebug() << k_funcinfo << "writing to " << loginKey << "," << passwordKey << endl;
00424     // note the overwrite=true by default
00425     map.insert( loginKey, info.username );
00426     map.insert( passwordKey, info.password );
00427     wallet->writeMap( key, map );
00428     return true;
00429 }
00430 
00431 bool KPasswdServer::readFromWallet( KWallet::Wallet* wallet, const QString& key, QString& username, QString& password, bool userReadOnly, QMap<QString,QString>& knownLogins )
00432 {
00433     if ( wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) )
00434     {
00435         wallet->setFolder( KWallet::Wallet::PasswordFolder() );
00436         QMap<QString,QString> map;
00437         kdDebug() << k_funcinfo << "reading with key=" << key << endl;
00438         if ( wallet->readMap( key, map ) == 0 )
00439         {
00440             typedef QMap<QString,QString> Map;
00441             int entryNumber = 1;
00442             Map::ConstIterator end = map.end();
00443             Map::ConstIterator it = map.find( "login" );
00444             while ( it != end ) {
00445                 QString passwordKey = "password";
00446                 if ( entryNumber > 1 )
00447                     passwordKey += "-" + QString::number( entryNumber );
00448                 Map::ConstIterator pwdit = map.find( passwordKey );
00449                 if ( pwdit != end ) {
00450                     if ( it.data() == username )
00451                         password = pwdit.data();
00452                     knownLogins.insert( it.data(), pwdit.data() );
00453                 }
00454 
00455                 it = map.find( QString( "login-" ) + QString::number( ++entryNumber ) );
00456             }
00457 
00458             if ( !userReadOnly && username.isEmpty() ) {
00459                 // Pick one, any one...
00460                 username = knownLogins.begin().key();
00461                 password = knownLogins.begin().data();
00462             }
00463 
00464             return true;
00465         }
00466     }
00467     return false;
00468 }
00469 
00470 QString KPasswdServer::createCacheKey( const KIO::AuthInfo &info )
00471 {
00472     if( !info.url.isValid() ) {
00473         // Note that a null key will break findAuthInfoItem later on...
00474         kdWarning(130) << "createCacheKey: invalid URL " << info.url << endl;
00475         return QString::null;
00476     }
00477 
00478     // Generate the basic key sequence.
00479     QString key = info.url.protocol();
00480     key += '-';
00481     if (!info.url.user().isEmpty())
00482     {
00483        key += info.url.user();
00484        key += "@";
00485     }
00486     key += info.url.host();
00487     int port = info.url.port();
00488     if( port )
00489     {
00490       key += ':';
00491       key += QString::number(port);
00492     }
00493 
00494     return key;
00495 }
00496 
00497 KIO::AuthInfo
00498 KPasswdServer::copyAuthInfo(const AuthInfo *i)
00499 {
00500     KIO::AuthInfo result;
00501     result.url = i->url;
00502     result.username = i->username;
00503     result.password = i->password;
00504     result.realmValue = i->realmValue;
00505     result.digestInfo = i->digestInfo;
00506     result.setModified(true);
00507 
00508     return result;
00509 }
00510 
00511 const KPasswdServer::AuthInfo *
00512 KPasswdServer::findAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00513 {
00514    AuthInfoList *authList = m_authDict.find(key);
00515    if (!authList)
00516       return 0;
00517 
00518    QString path2 = info.url.directory(false, false);
00519    for(AuthInfo *current = authList->first();
00520        current; )
00521    {
00522        if ((current->expire == AuthInfo::expTime) &&
00523           (difftime(time(0), current->expireTime) > 0))
00524        {
00525           authList->remove();
00526           current = authList->current();
00527           continue;
00528        }
00529 
00530        if (info.verifyPath)
00531        {
00532           QString path1 = current->directory;
00533           if (path2.startsWith(path1) &&
00534               (info.username.isEmpty() || info.username == current->username))
00535              return current;
00536        }
00537        else
00538        {
00539           if (current->realmValue == info.realmValue &&
00540               (info.username.isEmpty() || info.username == current->username))
00541              return current; // TODO: Update directory info,
00542        }
00543 
00544        current = authList->next();
00545    }
00546    return 0;
00547 }
00548 
00549 void
00550 KPasswdServer::removeAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00551 {
00552    AuthInfoList *authList = m_authDict.find(key);
00553    if (!authList)
00554       return;
00555 
00556    for(AuthInfo *current = authList->first();
00557        current; )
00558    {
00559        if (current->realmValue == info.realmValue)
00560        {
00561           authList->remove();
00562           current = authList->current();
00563        }
00564        else
00565        {
00566           current = authList->next();
00567        }
00568    }
00569    if (authList->isEmpty())
00570    {
00571        m_authDict.remove(key);
00572    }
00573 }
00574 
00575 
00576 void
00577 KPasswdServer::addAuthInfoItem(const QString &key, const KIO::AuthInfo &info, long windowId, long seqNr, bool canceled)
00578 {
00579    AuthInfoList *authList = m_authDict.find(key);
00580    if (!authList)
00581    {
00582       authList = new AuthInfoList;
00583       m_authDict.insert(key, authList);
00584    }
00585    AuthInfo *current = authList->first();
00586    for(; current; current = authList->next())
00587    {
00588        if (current->realmValue == info.realmValue)
00589        {
00590           authList->take();
00591           break;
00592        }
00593    }
00594 
00595    if (!current)
00596    {
00597       current = new AuthInfo;
00598       current->expire = AuthInfo::expTime;
00599       kdDebug(130) << "Creating AuthInfo" << endl;
00600    }
00601    else
00602    {
00603       kdDebug(130) << "Updating AuthInfo" << endl;
00604    }
00605 
00606    current->url = info.url;
00607    current->directory = info.url.directory(false, false);
00608    current->username = info.username;
00609    current->password = info.password;
00610    current->realmValue = info.realmValue;
00611    current->digestInfo = info.digestInfo;
00612    current->seqNr = seqNr;
00613    current->isCanceled = canceled;
00614 
00615    updateAuthExpire(key, current, windowId, info.keepPassword && !canceled);
00616 
00617    // Insert into list, keep the list sorted "longest path" first.
00618    authList->inSort(current);
00619 }
00620 
00621 void
00622 KPasswdServer::updateAuthExpire(const QString &key, const AuthInfo *auth, long windowId, bool keep)
00623 {
00624    AuthInfo *current = const_cast<AuthInfo *>(auth);
00625    if (keep)
00626    {
00627       current->expire = AuthInfo::expNever;
00628    }
00629    else if (windowId && (current->expire != AuthInfo::expNever))
00630    {
00631       current->expire = AuthInfo::expWindowClose;
00632       if (!current->windowList.contains(windowId))
00633          current->windowList.append(windowId);
00634    }
00635    else if (current->expire == AuthInfo::expTime)
00636    {
00637       current->expireTime = time(0)+10;
00638    }
00639 
00640    // Update mWindowIdList
00641    if (windowId)
00642    {
00643       QStringList *keysChanged = mWindowIdList.find(windowId);
00644       if (!keysChanged)
00645       {
00646          keysChanged = new QStringList;
00647          mWindowIdList.insert(windowId, keysChanged);
00648       }
00649       if (!keysChanged->contains(key))
00650          keysChanged->append(key);
00651    }
00652 }
00653 
00654 void
00655 KPasswdServer::removeAuthForWindowId(long windowId)
00656 {
00657    QStringList *keysChanged = mWindowIdList.find(windowId);
00658    if (!keysChanged) return;
00659 
00660    for(QStringList::ConstIterator it = keysChanged->begin();
00661        it != keysChanged->end(); ++it)
00662    {
00663       QString key = *it;
00664       AuthInfoList *authList = m_authDict.find(key);
00665       if (!authList)
00666          continue;
00667 
00668       AuthInfo *current = authList->first();
00669       for(; current; )
00670       {
00671         if (current->expire == AuthInfo::expWindowClose)
00672         {
00673            if (current->windowList.remove(windowId) && current->windowList.isEmpty())
00674            {
00675               authList->remove();
00676               current = authList->current();
00677               continue;
00678            }
00679         }
00680         current = authList->next();
00681       }
00682    }
00683 }
00684 
00685 #include "kpasswdserver.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Sep 16 06:56:30 2005 by doxygen 1.4.4 written by Dimitri van Heesch, © 1997-2003