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 Thu Apr 14 00:20:27 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003