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