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