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 2003/10/21 11:27:55 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 both in the cache (kpasswdserver process)
00278                // and in the wallet, if enabled.
00279                if ( info.keepPassword ) {
00280                    if ( !wallet )
00281                        wallet = KWallet::Wallet::openWallet(
00282                            KWallet::Wallet::NetworkWallet(), request->windowId );
00283                    QString password;
00284                    if ( wallet ) {
00285                        bool ok = true;
00286                        if ( !wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) )
00287                            ok = wallet->createFolder( KWallet::Wallet::PasswordFolder() );
00288                        if ( ok )
00289                        {
00290                            wallet->setFolder( KWallet::Wallet::PasswordFolder() );
00291                            QMap<QString,QString> map;
00292                            map.insert( "login", info.username );
00293                            map.insert( "password", info.password );
00294                            wallet->writeMap( request->key, map );
00295                        }
00296                    }
00297                }
00298             }
00299             delete wallet;
00300         }
00301         if ( dlgResult != QDialog::Accepted )
00302         {
00303             addAuthInfoItem(request->key, info, 0, m_seqNr, true);
00304             info.setModified( false );
00305         }
00306         else
00307         {
00308             addAuthInfoItem(request->key, info, request->windowId, m_seqNr, false);
00309             info.setModified( true );
00310         }
00311     }
00312 
00313     QCString replyType;
00314     QByteArray replyData;
00315 
00316     QDataStream stream2(replyData, IO_WriteOnly);
00317     stream2 << info << m_seqNr;
00318     replyType = "KIO::AuthInfo";
00319     request->client->endTransaction( request->transaction,
00320                                      replyType, replyData);
00321 
00322     m_authPending.remove((unsigned int) 0);
00323 
00324     // Check all requests in the wait queue.
00325     for(Request *waitRequest = m_authWait.first();
00326         waitRequest; )
00327     {
00328        bool keepQueued = false;
00329        QString key = waitRequest->key;
00330 
00331        request = m_authPending.first();
00332        QString path2 = waitRequest->info.url.directory(false, false);
00333        for(; request; request = m_authPending.next())
00334        {
00335            if (request->key != key)
00336                continue;
00337 
00338            if (info.verifyPath)
00339            {
00340                QString path1 = request->info.url.directory(false, false);
00341                if (!path2.startsWith(path1))
00342                    continue;
00343            }
00344 
00345            keepQueued = true;
00346            break;
00347        }
00348        if (keepQueued)
00349        {
00350            waitRequest = m_authWait.next();
00351        }
00352        else
00353        {
00354            const AuthInfo *result = findAuthInfoItem(waitRequest->key, waitRequest->info);
00355 
00356            QCString replyType;
00357            QByteArray replyData;
00358 
00359            QDataStream stream2(replyData, IO_WriteOnly);
00360 
00361            if (!result || result->isCanceled)
00362            {
00363                waitRequest->info.setModified(false);
00364                stream2 << waitRequest->info;
00365            }
00366            else
00367            {
00368                updateAuthExpire(waitRequest->key, result, waitRequest->windowId, false);
00369                KIO::AuthInfo info = copyAuthInfo(result);
00370                stream2 << info;
00371            }
00372 
00373            replyType = "KIO::AuthInfo";
00374            waitRequest->client->endTransaction( waitRequest->transaction,
00375                                                 replyType, replyData);
00376 
00377            m_authWait.remove();
00378            waitRequest = m_authWait.current();
00379        }
00380     }
00381 
00382     if (m_authPending.count())
00383        QTimer::singleShot(0, this, SLOT(processRequest()));
00384 
00385 }
00386 
00387 QString KPasswdServer::createCacheKey( const KIO::AuthInfo &info )
00388 {
00389     if( !info.url.isValid() )
00390         return QString::null;
00391 
00392     // Generate the basic key sequence.
00393     QString key = info.url.protocol();
00394     key += '-';
00395     if (!info.url.user().isEmpty())
00396     {
00397        key += info.url.user();
00398        key += "@";
    }
    key += info.url.host();
    int port = info.url.port();
    if( port )
    {
      key += ':';
      key += QString::number(port);
    }

    return key;
}

KIO::AuthInfo
KPasswdServer::copyAuthInfo(const AuthInfo *i)
{
    KIO::AuthInfo result;
    result.url = i->url;
    result.username = i->username;
    result.password = i->password;
    result.realmValue = i->realmValue;
    result.digestInfo = i->digestInfo;
    result.setModified(true);

    return result;
}

const KPasswdServer::AuthInfo *
KPasswdServer::findAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
{
   AuthInfoList *authList = m_authDict.find(key);
   if (!authList)
      return 0;

   QString path2 = info.url.directory(false, false);
   for(AuthInfo *current = authList->first();
       current; )
   {
       if ((current->expire == AuthInfo::expTime) &&
          (difftime(time(0), current->expireTime) > 0))
       {
          authList->remove();
          current = authList->current();
          continue;
       }

       if (info.verifyPath)
       {
          QString path1 = current->directory;
          if (path2.startsWith(path1) &&
              (info.username.isEmpty() || info.username == current->username))
             return current;
       }
       else
       {
          if (current->realmValue == info.realmValue &&
              (info.username.isEmpty() || info.username == current->username))
             return current; // TODO: Update directory info,
       }

       current = authList->next();
   }
   return 0;
}

void
KPasswdServer::removeAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
{
   AuthInfoList *authList = m_authDict.find(key);
   if (!authList)
      return;

   for(AuthInfo *current = authList->first();
       current; )
   {
       if (current->realmValue == info.realmValue)
       {
          authList->remove();
          current = authList->current();
       }
       else
       {
          current = authList->next();
       }
   }
   if (authList->isEmpty())
   {
       m_authDict.remove(key);
   }
}


void
KPasswdServer::addAuthInfoItem(const QString &key, const KIO::AuthInfo &info, long windowId, long seqNr, bool canceled)
{
   AuthInfoList *authList = m_authDict.find(key);
   if (!authList)
   {
      authList = new AuthInfoList;
      m_authDict.insert(key, authList);
   }
   AuthInfo *current = authList->first();
   for(; current; current = authList->next())
   {
       if (current->realmValue == info.realmValue)
       {
          authList->take();
          break;
       }
   }

   if (!current)
   {
      current = new AuthInfo;
      current->expire = AuthInfo::expTime;
      kdDebug(130) << "Creating AuthInfo" << endl;
00399    }
00400    else
00401    {
00402       kdDebug(130) << "Updating AuthInfo" << endl;
00403    }
00404 
00405    current->url = info.url;
00406    current->directory = info.url.directory(false, false);
00407    current->username = info.username;
00408    current->password = info.password;
00409    current->realmValue = info.realmValue;
00410    current->digestInfo = info.digestInfo;
00411    current->seqNr = seqNr;
00412    current->isCanceled = canceled;
00413 
00414    updateAuthExpire(key, current, windowId, info.keepPassword && !canceled);
00415 
00416    // Insert into list, keep the list sorted "longest path" first.
00417    authList->inSort(current);
00418 }
00419 
00420 void
00421 KPasswdServer::updateAuthExpire(const QString &key, const AuthInfo *auth, long windowId, bool keep)
00422 {
00423    AuthInfo *current = const_cast<AuthInfo *>(auth);
00424    if (keep)
00425    {
00426       current->expire = AuthInfo::expNever;
00427    }
00428    else if (windowId && (current->expire != AuthInfo::expNever))
00429    {
00430       current->expire = AuthInfo::expWindowClose;
00431       if (!current->windowList.contains(windowId))
00432          current->windowList.append(windowId);
00433    }
00434    else if (current->expire == AuthInfo::expTime)
00435    {
00436       current->expireTime = time(0)+10;
00437    }
00438 
00439    // Update mWindowIdList
00440    if (windowId)
00441    {
00442       QStringList *keysChanged = mWindowIdList.find(windowId);
00443       if (!keysChanged)
00444       {
00445          keysChanged = new QStringList;
00446          mWindowIdList.insert(windowId, keysChanged);
00447       }
00448       if (!keysChanged->contains(key))
00449          keysChanged->append(key);
00450    }
00451 }
00452 
00453 void
00454 KPasswdServer::removeAuthForWindowId(long windowId)
00455 {
00456    QStringList *keysChanged = mWindowIdList.find(windowId);
00457    if (!keysChanged) return;
00458 
00459    for(QStringList::ConstIterator it = keysChanged->begin();
00460        it != keysChanged->end(); ++it)
00461    {
00462       QString key = *it;
00463       AuthInfoList *authList = m_authDict.find(key);
00464       if (!authList)
00465          continue;
00466 
00467       AuthInfo *current = authList->first();
00468       for(; current; )
00469       {
00470         if (current->expire == AuthInfo::expWindowClose)
00471         {
00472            if (current->windowList.remove(windowId) && current->windowList.isEmpty())
00473            {
00474               authList->remove();
00475               current = authList->current();
00476               continue;
00477            }
00478         }
00479         current = authList->next();
00480       }
00481    }
00482 }
00483 
00484 #include "kpasswdserver.moc"
00485 
KDE Logo
This file is part of the documentation for kio Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 14 09:17:11 2006 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003