kio Library: kpasswdserver.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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 392296 2005-02-22 17:32:36Z wheeler $
    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"

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:02 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003