kio Library API Documentation

kpasswdserver.cpp

00001 /*
00002     This file is part of the KDE Cookie Jar
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.19.6.1 2004/11/26 13:00:44 faure Exp $
00024 
00025 #include <time.h>
00026 
00027 #include <qtimer.h>
00028 
00029 #include <kapplication.h>
00030 #include <klocale.h>
00031 #include <kmessagebox.h>
00032 #include <kdebug.h>
00033 #include <kio/passdlg.h>
00034 #include <kwallet.h>
00035 
00036 #include "config.h"
00037 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00038 #include <X11/X.h>
00039 #include <X11/Xlib.h>
00040 #endif
00041 
00042 #include "kpasswdserver.h"
00043 
00044 extern "C" {
00045     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        info.setModified(false);
00120        return info;
00121     }
00122 
00123     updateAuthExpire(key, result, windowId, false);
00124 
00125     return copyAuthInfo(result);
00126 }
00127 
00128 KIO::AuthInfo
00129 KPasswdServer::queryAuthInfo(KIO::AuthInfo info, QString errorMsg, long windowId, long seqNr)
00130 {
00131     kdDebug(130) << "KPasswdServer::queryAuthInfo: User= " << info.username
00132               << ", Message= " << info.prompt << ", WindowId = " << windowId << endl;
00133     QString key = createCacheKey(info);
00134     Request *request = new Request;
00135     request->client = callingDcopClient();
00136     request->transaction = request->client->beginTransaction();
00137     request->key = key;
00138     request->info = info;
00139     request->windowId = windowId;
00140     request->seqNr = seqNr;
00141     if (errorMsg == "<NoAuthPrompt>")
00142     {
00143        request->errorMsg = QString::null;
00144        request->prompt = false;
00145     }
00146     else
00147     {
00148        request->errorMsg = errorMsg;
00149        request->prompt = true;
00150     }
00151     m_authPending.append(request);
00152 
00153     if (m_authPending.count() == 1)
00154        QTimer::singleShot(0, this, SLOT(processRequest()));
00155 
00156     return info;
00157 }
00158 
00159 void
00160 KPasswdServer::addAuthInfo(KIO::AuthInfo info, long windowId)
00161 {
00162     kdDebug(130) << "KPasswdServer::addAuthInfo: User= " << info.username
00163               << ", RealmValue= " << info.realmValue << ", WindowId = " << windowId << endl;
00164     QString key = createCacheKey(info);
00165 
00166     m_seqNr++;
00167 
00168     addAuthInfoItem(key, info, windowId, m_seqNr, false);
00169 }
00170 
00171 void
00172 KPasswdServer::processRequest()
00173 {
00174     Request *request = m_authPending.first();
00175     if (!request)
00176        return;
00177 
00178     KIO::AuthInfo &info = request->info;
00179 
00180     kdDebug(130) << "KPasswdServer::processRequest: User= " << info.username
00181               << ", Message= " << info.prompt << endl;
00182 
00183     const AuthInfo *result = findAuthInfoItem(request->key, request->info);
00184 
00185     if (result && (request->seqNr < result->seqNr))
00186     {
00187         kdDebug(130) << "KPasswdServer::processRequest: auto retry!" << endl;
00188         if (result->isCanceled)
00189         {
00190            info.setModified(false);
00191         }
00192         else
00193         {
00194            updateAuthExpire(request->key, result, request->windowId, false);
00195            info = copyAuthInfo(result);
00196         }
00197     }
00198     else
00199     {
00200         m_seqNr++;
00201         bool askPw = request->prompt;
00202         if (result && !info.username.isEmpty() &&
00203             !request->errorMsg.isEmpty())
00204         {
00205            QString prompt = request->errorMsg;
00206            prompt += i18n("  Do you want to retry?");
00207            int dlgResult = KMessageBox::warningContinueCancel(0, prompt,
00208                            i18n("Authentication"), i18n("Retry"));
00209            if (dlgResult != KMessageBox::Continue)
00210               askPw = false;
00211         }
00212 
00213         int dlgResult = QDialog::Rejected;
00214         if (askPw)
00215         {
00216             QString username = info.username;
00217             QString password = info.password;
00218             bool hasWalletData = false;
00219 
00220             KWallet::Wallet* wallet = 0;
00221             if ( ( username.isEmpty() || password.isEmpty() )
00222                 && !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), request->key) )
00223             {
00224                 // no login+pass provided, check if kwallet has one
00225                 wallet = KWallet::Wallet::openWallet(
00226                     KWallet::Wallet::NetworkWallet(), request->windowId );
00227                 if ( wallet && wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) )
00228                 {
00229                     wallet->setFolder( KWallet::Wallet::PasswordFolder() );
00230                     QMap<QString,QString> map;
00231                     if ( wallet->readMap( request->key, map ) == 0 )
00232                     {
00233                         QMap<QString, QString>::ConstIterator it = map.find( "password" );
00234                         if ( it != map.end() )
00235                             password = it.data();
00236 
00237                         if ( !info.readOnly ) {
00238                             it = map.find( "login" );
00239                             if ( it != map.end() )
00240                                 username = it.data();
00241                         }
00242                         hasWalletData = true;
00243                     }
00244                 }
00245             }
00246 
00247             KIO::PasswordDialog dlg( info.prompt, username, info.keepPassword );
00248             if (info.caption.isEmpty())
00249                dlg.setPlainCaption( i18n("Authorization Dialog") );
00250             else
00251                dlg.setPlainCaption( info.caption );
00252 
00253             if ( !info.comment.isEmpty() )
00254                dlg.addCommentLine( info.commentLabel, info.comment );
00255 
00256             if ( !password.isEmpty() )
00257                dlg.setPassword( password );
00258 
00259             if (info.readOnly)
00260                dlg.setUserReadOnly( true );
00261 
00262             if (hasWalletData)
00263                 dlg.setKeepPassword( true );
00264 
00265 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00266             XSetTransientForHint( qt_xdisplay(), dlg.winId(), request->windowId);
00267 #endif
00268 
00269             dlgResult = dlg.exec();
00270 
00271             if (dlgResult == QDialog::Accepted)
00272             {
00273                info.username = dlg.username();
00274                info.password = dlg.password();
00275                info.keepPassword = dlg.keepPassword();
00276 
00277                // When the user checks "keep password", that means:
00278                // * if the wallet is enabled, store it there for long-term, and in kpasswdserver
00279                // only for the duration of the window (#92928)
00280                // * otherwise store in kpasswdserver for the duration of the KDE session.
00281                if ( info.keepPassword ) {
00282                    if ( !wallet )
00283                        wallet = KWallet::Wallet::openWallet(
00284                            KWallet::Wallet::NetworkWallet(), request->windowId );
00285                    QString password;
00286                    if ( wallet ) {
00287                        bool ok = true;
00288                        if ( !wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) )
00289                            ok = wallet->createFolder( KWallet::Wallet::PasswordFolder() );
00290                        if ( ok )
00291                        {
00292                            wallet->setFolder( KWallet::Wallet::PasswordFolder() );
00293                            QMap<QString,QString> map;
00294                            map.insert( "login", info.username );
00295                            map.insert( "password", info.password );
00296                            wallet->writeMap( request->key, map );
00297                            // password is in wallet, don't keep it in memory after window is closed
00298                            info.keepPassword = false;
00299                        }
00300                    }
00301                }
00302             }
00303             delete wallet;
00304         }
00305         if ( dlgResult != QDialog::Accepted )
00306         {
00307             addAuthInfoItem(request->key, info, 0, m_seqNr, true);
00308             info.setModified( false );
00309         }
00310         else
00311         {
00312             addAuthInfoItem(request->key, info, request->windowId, m_seqNr, false);
00313             info.setModified( true );
00314         }
00315     }
00316 
00317     QCString replyType;
00318     QByteArray replyData;
00319 
00320     QDataStream stream2(replyData, IO_WriteOnly);
00321     stream2 << info << m_seqNr;
00322     replyType = "KIO::AuthInfo";
00323     request->client->endTransaction( request->transaction,
00324                                      replyType, replyData);
00325 
00326     m_authPending.remove((unsigned int) 0);
00327 
00328     // Check all requests in the wait queue.
00329     for(Request *waitRequest = m_authWait.first();
00330         waitRequest; )
00331     {
00332        bool keepQueued = false;
00333        QString key = waitRequest->key;
00334 
00335        request = m_authPending.first();
00336        QString path2 = waitRequest->info.url.directory(false, false);
00337        for(; request; request = m_authPending.next())
00338        {
00339            if (request->key != key)
00340                continue;
00341 
00342            if (info.verifyPath)
00343            {
00344                QString path1 = request->info.url.directory(false, false);
00345                if (!path2.startsWith(path1))
00346                    continue;
00347            }
00348 
00349            keepQueued = true;
00350            break;
00351        }
00352        if (keepQueued)
00353        {
00354            waitRequest = m_authWait.next();
00355        }
00356        else
00357        {
00358            const AuthInfo *result = findAuthInfoItem(waitRequest->key, waitRequest->info);
00359 
00360            QCString replyType;
00361            QByteArray replyData;
00362 
00363            QDataStream stream2(replyData, IO_WriteOnly);
00364 
00365            if (!result || result->isCanceled)
00366            {
00367                waitRequest->info.setModified(false);
00368                stream2 << waitRequest->info;
00369            }
00370            else
00371            {
00372                updateAuthExpire(waitRequest->key, result, waitRequest->windowId, false);
00373                KIO::AuthInfo info = copyAuthInfo(result);
00374                stream2 << info;
00375            }
00376 
00377            replyType = "KIO::AuthInfo";
00378            waitRequest->client->endTransaction( waitRequest->transaction,
00379                                                 replyType, replyData);
00380 
00381            m_authWait.remove();
00382            waitRequest = m_authWait.current();
00383        }
00384     }
00385 
00386     if (m_authPending.count())
00387        QTimer::singleShot(0, this, SLOT(processRequest()));
00388 
00389 }
00390 
00391 QString KPasswdServer::createCacheKey( const KIO::AuthInfo &info )
00392 {
00393     if( !info.url.isValid() ) {
00394         // Note that a null key will break findAuthInfoItem later on...
00395         kdWarning(130) << "createCacheKey: invalid URL " << info.url << endl;
00396         return QString::null;
00397     }
00398 
00399     // Generate the basic key sequence.
00400     QString key = info.url.protocol();
00401     key += '-';
00402     if (!info.url.user().isEmpty())
00403     {
00404        key += info.url.user();
00405        key += "@";
00406     }
00407     key += info.url.host();
00408     int port = info.url.port();
00409     if( port )
00410     {
00411       key += ':';
00412       key += QString::number(port);
00413     }
00414 
00415     return key;
00416 }
00417 
00418 KIO::AuthInfo
00419 KPasswdServer::copyAuthInfo(const AuthInfo *i)
00420 {
00421     KIO::AuthInfo result;
00422     result.url = i->url;
00423     result.username = i->username;
00424     result.password = i->password;
00425     result.realmValue = i->realmValue;
00426     result.digestInfo = i->digestInfo;
00427     result.setModified(true);
00428 
00429     return result;
00430 }
00431 
00432 const KPasswdServer::AuthInfo *
00433 KPasswdServer::findAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00434 {
00435    AuthInfoList *authList = m_authDict.find(key);
00436    if (!authList)
00437       return 0;
00438 
00439    QString path2 = info.url.directory(false, false);
00440    for(AuthInfo *current = authList->first();
00441        current; )
00442    {
00443        if ((current->expire == AuthInfo::expTime) &&
00444           (difftime(time(0), current->expireTime) > 0))
00445        {
00446           authList->remove();
00447           current = authList->current();
00448           continue;
00449        }
00450 
00451        if (info.verifyPath)
00452        {
00453           QString path1 = current->directory;
00454           if (path2.startsWith(path1) &&
00455               (info.username.isEmpty() || info.username == current->username))
00456              return current;
00457        }
00458        else
00459        {
00460           if (current->realmValue == info.realmValue &&
00461               (info.username.isEmpty() || info.username == current->username))
00462              return current; // TODO: Update directory info,
00463        }
00464 
00465        current = authList->next();
00466    }
00467    return 0;
00468 }
00469 
00470 void
00471 KPasswdServer::removeAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00472 {
00473    AuthInfoList *authList = m_authDict.find(key);
00474    if (!authList)
00475       return;
00476 
00477    for(AuthInfo *current = authList->first();
00478        current; )
00479    {
00480        if (current->realmValue == info.realmValue)
00481        {
00482           authList->remove();
00483           current = authList->current();
00484        }
00485        else
00486        {
00487           current = authList->next();
00488        }
00489    }
00490    if (authList->isEmpty())
00491    {
00492        m_authDict.remove(key);
00493    }
00494 }
00495 
00496 
00497 void
00498 KPasswdServer::addAuthInfoItem(const QString &key, const KIO::AuthInfo &info, long windowId, long seqNr, bool canceled)
00499 {
00500    AuthInfoList *authList = m_authDict.find(key);
00501    if (!authList)
00502    {
00503       authList = new AuthInfoList;
00504       m_authDict.insert(key, authList);
00505    }
00506    AuthInfo *current = authList->first();
00507    for(; current; current = authList->next())
00508    {
00509        if (current->realmValue == info.realmValue)
00510        {
00511           authList->take();
00512           break;
00513        }
00514    }
00515 
00516    if (!current)
00517    {
00518       current = new AuthInfo;
00519       current->expire = AuthInfo::expTime;
00520       kdDebug(130) << "Creating AuthInfo" << endl;
00521    }
00522    else
00523    {
00524       kdDebug(130) << "Updating AuthInfo" << endl;
00525    }
00526 
00527    current->url = info.url;
00528    current->directory = info.url.directory(false, false);
00529    current->username = info.username;
00530    current->password = info.password;
00531    current->realmValue = info.realmValue;
00532    current->digestInfo = info.digestInfo;
00533    current->seqNr = seqNr;
00534    current->isCanceled = canceled;
00535 
00536    updateAuthExpire(key, current, windowId, info.keepPassword && !canceled);
00537 
00538    // Insert into list, keep the list sorted "longest path" first.
00539    authList->inSort(current);
00540 }
00541 
00542 void
00543 KPasswdServer::updateAuthExpire(const QString &key, const AuthInfo *auth, long windowId, bool keep)
00544 {
00545    AuthInfo *current = const_cast<AuthInfo *>(auth);
00546    if (keep)
00547    {
00548       current->expire = AuthInfo::expNever;
00549    }
00550    else if (windowId && (current->expire != AuthInfo::expNever))
00551    {
00552       current->expire = AuthInfo::expWindowClose;
00553       if (!current->windowList.contains(windowId))
00554          current->windowList.append(windowId);
00555    }
00556    else if (current->expire == AuthInfo::expTime)
00557    {
00558       current->expireTime = time(0)+10;
00559    }
00560 
00561    // Update mWindowIdList
00562    if (windowId)
00563    {
00564       QStringList *keysChanged = mWindowIdList.find(windowId);
00565       if (!keysChanged)
00566       {
00567          keysChanged = new QStringList;
00568          mWindowIdList.insert(windowId, keysChanged);
00569       }
00570       if (!keysChanged->contains(key))
00571          keysChanged->append(key);
00572    }
00573 }
00574 
00575 void
00576 KPasswdServer::removeAuthForWindowId(long windowId)
00577 {
00578    QStringList *keysChanged = mWindowIdList.find(windowId);
00579    if (!keysChanged) return;
00580 
00581    for(QStringList::ConstIterator it = keysChanged->begin();
00582        it != keysChanged->end(); ++it)
00583    {
00584       QString key = *it;
00585       AuthInfoList *authList = m_authDict.find(key);
00586       if (!authList)
00587          continue;
00588 
00589       AuthInfo *current = authList->first();
00590       for(; current; )
00591       {
00592         if (current->expire == AuthInfo::expWindowClose)
00593         {
00594            if (current->windowList.remove(windowId) && current->windowList.isEmpty())
00595            {
00596               authList->remove();
00597               current = authList->current();
00598               continue;
00599            }
00600         }
00601         current = authList->next();
00602       }
00603    }
00604 }
00605 
00606 #include "kpasswdserver.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Jan 15 13:33:27 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003