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 kdDebug(130) << "KPasswdServer::checkAuthInfo: User= " << info.username
00182 << ", WindowId = " << windowId << endl;
00183
00184 QString key = createCacheKey(info);
00185
00186 Request *request = m_authPending.first();
00187 QString path2 = info.url.directory(false, false);
00188 for(; request; request = m_authPending.next())
00189 {
00190 if (request->key != key)
00191 continue;
00192
00193 if (info.verifyPath)
00194 {
00195 QString path1 = request->info.url.directory(false, false);
00196 if (!path2.startsWith(path1))
00197 continue;
00198 }
00199
00200 request = new Request;
00201 request->client = callingDcopClient();
00202 request->transaction = request->client->beginTransaction();
00203 request->key = key;
00204 request->info = info;
00205 m_authWait.append(request);
00206 return info;
00207 }
00208
00209 const AuthInfo *result = findAuthInfoItem(key, info);
00210 if (!result || result->isCanceled)
00211 {
00212 if (!result &&
00213 (info.username.isEmpty() || info.password.isEmpty()) &&
00214 !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
00215 KWallet::Wallet::PasswordFolder(), makeWalletKey(key, info.realmValue)))
00216 {
00217 QMap<QString, QString> knownLogins;
00218 if (openWallet(windowId)) {
00219 if (readFromWallet(m_wallet, key, info.realmValue, info.username, info.password,
00220 info.readOnly, knownLogins))
00221 {
00222 info.setModified(true);
00223 return info;
00224 }
00225 }
00226 }
00227
00228 info.setModified(false);
00229 return info;
00230 }
00231
00232 updateAuthExpire(key, result, windowId, false);
00233
00234 return copyAuthInfo(result);
00235 }
00236
00237 KIO::AuthInfo
00238 KPasswdServer::queryAuthInfo(KIO::AuthInfo info, QString errorMsg, long windowId, long seqNr)
00239 {
00240 kdDebug(130) << "KPasswdServer::queryAuthInfo: User= " << info.username
00241 << ", Message= " << info.prompt << ", WindowId = " << windowId << endl;
00242 if ( !info.password.isEmpty() )
00243 kdDebug(130) << "password was set by caller" << endl;
00244
00245 QString key = createCacheKey(info);
00246 Request *request = new Request;
00247 request->client = callingDcopClient();
00248 request->transaction = request->client->beginTransaction();
00249 request->key = key;
00250 request->info = info;
00251 request->windowId = windowId;
00252 request->seqNr = seqNr;
00253 if (errorMsg == "<NoAuthPrompt>")
00254 {
00255 request->errorMsg = QString::null;
00256 request->prompt = false;
00257 }
00258 else
00259 {
00260 request->errorMsg = errorMsg;
00261 request->prompt = true;
00262 }
00263 m_authPending.append(request);
00264
00265 if (m_authPending.count() == 1)
00266 QTimer::singleShot(0, this, SLOT(processRequest()));
00267
00268 return info;
00269 }
00270
00271 void
00272 KPasswdServer::addAuthInfo(KIO::AuthInfo info, long windowId)
00273 {
00274 kdDebug(130) << "KPasswdServer::addAuthInfo: User= " << info.username
00275 << ", RealmValue= " << info.realmValue << ", WindowId = " << windowId << endl;
00276 QString key = createCacheKey(info);
00277
00278 m_seqNr++;
00279
00280 addAuthInfoItem(key, info, windowId, m_seqNr, false);
00281 }
00282
00283 bool
00284 KPasswdServer::openWallet( WId windowId )
00285 {
00286 if ( m_wallet && !m_wallet->isOpen() ) {
00287 delete m_wallet;
00288 m_wallet = 0;
00289 }
00290 if ( !m_wallet )
00291 m_wallet = KWallet::Wallet::openWallet(
00292 KWallet::Wallet::NetworkWallet(), windowId );
00293 return m_wallet != 0;
00294 }
00295
00296 void
00297 KPasswdServer::processRequest()
00298 {
00299 Request *request = m_authPending.first();
00300 if (!request)
00301 return;
00302
00303 KIO::AuthInfo &info = request->info;
00304
00305 kdDebug(130) << "KPasswdServer::processRequest: User= " << info.username
00306 << ", Message= " << info.prompt << endl;
00307 const AuthInfo *result = findAuthInfoItem(request->key, request->info);
00308
00309 if (result && (request->seqNr < result->seqNr))
00310 {
00311 kdDebug(130) << "KPasswdServer::processRequest: auto retry!" << endl;
00312 if (result->isCanceled)
00313 {
00314 info.setModified(false);
00315 }
00316 else
00317 {
00318 updateAuthExpire(request->key, result, request->windowId, false);
00319 info = copyAuthInfo(result);
00320 }
00321 }
00322 else
00323 {
00324 m_seqNr++;
00325 bool askPw = request->prompt;
00326 if (result && !info.username.isEmpty() &&
00327 !request->errorMsg.isEmpty())
00328 {
00329 QString prompt = request->errorMsg;
00330 prompt += i18n(" Do you want to retry?");
00331 int dlgResult = KMessageBox::warningContinueCancel(0, prompt,
00332 i18n("Authentication"), i18n("Retry"));
00333 if (dlgResult != KMessageBox::Continue)
00334 askPw = false;
00335 }
00336
00337 int dlgResult = QDialog::Rejected;
00338 if (askPw)
00339 {
00340 QString username = info.username;
00341 QString password = info.password;
00342 bool hasWalletData = false;
00343 QMap<QString, QString> knownLogins;
00344
00345 if ( ( username.isEmpty() || password.isEmpty() )
00346 && !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), makeWalletKey( request->key, info.realmValue )) )
00347 {
00348
00349 if ( openWallet( request->windowId ) )
00350 hasWalletData = readFromWallet( m_wallet, request->key, info.realmValue, username, password, info.readOnly, knownLogins );
00351 }
00352
00353 KIO::PasswordDialog dlg( info.prompt, username, info.keepPassword );
00354 if (info.caption.isEmpty())
00355 dlg.setPlainCaption( i18n("Authorization Dialog") );
00356 else
00357 dlg.setPlainCaption( info.caption );
00358
00359 if ( !info.comment.isEmpty() )
00360 dlg.addCommentLine( info.commentLabel, info.comment );
00361
00362 if ( !password.isEmpty() )
00363 dlg.setPassword( password );
00364
00365 if (info.readOnly)
00366 dlg.setUserReadOnly( true );
00367 else
00368 dlg.setKnownLogins( knownLogins );
00369
00370 if (hasWalletData)
00371 dlg.setKeepPassword( true );
00372
00373 #ifdef Q_WS_X11
00374 XSetTransientForHint( qt_xdisplay(), dlg.winId(), request->windowId);
00375 #endif
00376
00377 dlgResult = dlg.exec();
00378
00379 if (dlgResult == QDialog::Accepted)
00380 {
00381 info.username = dlg.username();
00382 info.password = dlg.password();
00383 info.keepPassword = dlg.keepPassword();
00384
00385
00386
00387
00388
00389 if ( info.keepPassword ) {
00390 if ( openWallet( request->windowId ) ) {
00391 if ( storeInWallet( m_wallet, request->key, info ) )
00392
00393 info.keepPassword = false;
00394 }
00395 }
00396 }
00397 }
00398 if ( dlgResult != QDialog::Accepted )
00399 {
00400 addAuthInfoItem(request->key, info, 0, m_seqNr, true);
00401 info.setModified( false );
00402 }
00403 else
00404 {
00405 addAuthInfoItem(request->key, info, request->windowId, m_seqNr, false);
00406 info.setModified( true );
00407 }
00408 }
00409
00410 QCString replyType;
00411 QByteArray replyData;
00412
00413 QDataStream stream2(replyData, IO_WriteOnly);
00414 stream2 << info << m_seqNr;
00415 replyType = "KIO::AuthInfo";
00416 request->client->endTransaction( request->transaction,
00417 replyType, replyData);
00418
00419 m_authPending.remove((unsigned int) 0);
00420
00421
00422 for(Request *waitRequest = m_authWait.first();
00423 waitRequest; )
00424 {
00425 bool keepQueued = false;
00426 QString key = waitRequest->key;
00427
00428 request = m_authPending.first();
00429 QString path2 = waitRequest->info.url.directory(false, false);
00430 for(; request; request = m_authPending.next())
00431 {
00432 if (request->key != key)
00433 continue;
00434
00435 if (info.verifyPath)
00436 {
00437 QString path1 = request->info.url.directory(false, false);
00438 if (!path2.startsWith(path1))
00439 continue;
00440 }
00441
00442 keepQueued = true;
00443 break;
00444 }
00445 if (keepQueued)
00446 {
00447 waitRequest = m_authWait.next();
00448 }
00449 else
00450 {
00451 const AuthInfo *result = findAuthInfoItem(waitRequest->key, waitRequest->info);
00452
00453 QCString replyType;
00454 QByteArray replyData;
00455
00456 QDataStream stream2(replyData, IO_WriteOnly);
00457
00458 if (!result || result->isCanceled)
00459 {
00460 waitRequest->info.setModified(false);
00461 stream2 << waitRequest->info;
00462 }
00463 else
00464 {
00465 updateAuthExpire(waitRequest->key, result, waitRequest->windowId, false);
00466 KIO::AuthInfo info = copyAuthInfo(result);
00467 stream2 << info;
00468 }
00469
00470 replyType = "KIO::AuthInfo";
00471 waitRequest->client->endTransaction( waitRequest->transaction,
00472 replyType, replyData);
00473
00474 m_authWait.remove();
00475 waitRequest = m_authWait.current();
00476 }
00477 }
00478
00479 if (m_authPending.count())
00480 QTimer::singleShot(0, this, SLOT(processRequest()));
00481
00482 }
00483
00484 QString KPasswdServer::createCacheKey( const KIO::AuthInfo &info )
00485 {
00486 if( !info.url.isValid() ) {
00487
00488 kdWarning(130) << "createCacheKey: invalid URL " << info.url << endl;
00489 return QString::null;
00490 }
00491
00492
00493 QString key = info.url.protocol();
00494 key += '-';
00495 if (!info.url.user().isEmpty())
00496 {
00497 key += info.url.user();
00498 key += "@";
00499 }
00500 key += info.url.host();
00501 int port = info.url.port();
00502 if( port )
00503 {
00504 key += ':';
00505 key += QString::number(port);
00506 }
00507
00508 return key;
00509 }
00510
00511 KIO::AuthInfo
00512 KPasswdServer::copyAuthInfo(const AuthInfo *i)
00513 {
00514 KIO::AuthInfo result;
00515 result.url = i->url;
00516 result.username = i->username;
00517 result.password = i->password;
00518 result.realmValue = i->realmValue;
00519 result.digestInfo = i->digestInfo;
00520 result.setModified(true);
00521
00522 return result;
00523 }
00524
00525 const KPasswdServer::AuthInfo *
00526 KPasswdServer::findAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00527 {
00528 AuthInfoList *authList = m_authDict.find(key);
00529 if (!authList)
00530 return 0;
00531
00532 QString path2 = info.url.directory(false, false);
00533 for(AuthInfo *current = authList->first();
00534 current; )
00535 {
00536 if ((current->expire == AuthInfo::expTime) &&
00537 (difftime(time(0), current->expireTime) > 0))
00538 {
00539 authList->remove();
00540 current = authList->current();
00541 continue;
00542 }
00543
00544 if (info.verifyPath)
00545 {
00546 QString path1 = current->directory;
00547 if (path2.startsWith(path1) &&
00548 (info.username.isEmpty() || info.username == current->username))
00549 return current;
00550 }
00551 else
00552 {
00553 if (current->realmValue == info.realmValue &&
00554 (info.username.isEmpty() || info.username == current->username))
00555 return current;
00556 }
00557
00558 current = authList->next();
00559 }
00560 return 0;
00561 }
00562
00563 void
00564 KPasswdServer::removeAuthInfoItem(const QString &key, const KIO::AuthInfo &info)
00565 {
00566 AuthInfoList *authList = m_authDict.find(key);
00567 if (!authList)
00568 return;
00569
00570 for(AuthInfo *current = authList->first();
00571 current; )
00572 {
00573 if (current->realmValue == info.realmValue)
00574 {
00575 authList->remove();
00576 current = authList->current();
00577 }
00578 else
00579 {
00580 current = authList->next();
00581 }
00582 }
00583 if (authList->isEmpty())
00584 {
00585 m_authDict.remove(key);
00586 }
00587 }
00588
00589
00590 void
00591 KPasswdServer::addAuthInfoItem(const QString &key, const KIO::AuthInfo &info, long windowId, long seqNr, bool canceled)
00592 {
00593 AuthInfoList *authList = m_authDict.find(key);
00594 if (!authList)
00595 {
00596 authList = new AuthInfoList;
00597 m_authDict.insert(key, authList);
00598 }
00599 AuthInfo *current = authList->first();
00600 for(; current; current = authList->next())
00601 {
00602 if (current->realmValue == info.realmValue)
00603 {
00604 authList->take();
00605 break;
00606 }
00607 }
00608
00609 if (!current)
00610 {
00611 current = new AuthInfo;
00612 current->expire = AuthInfo::expTime;
00613 kdDebug(130) << "Creating AuthInfo" << endl;
00614 }
00615 else
00616 {
00617 kdDebug(130) << "Updating AuthInfo" << endl;
00618 }
00619
00620 current->url = info.url;
00621 current->directory = info.url.directory(false, false);
00622 current->username = info.username;
00623 current->password = info.password;
00624 current->realmValue = info.realmValue;
00625 current->digestInfo = info.digestInfo;
00626 current->seqNr = seqNr;
00627 current->isCanceled = canceled;
00628
00629 updateAuthExpire(key, current, windowId, info.keepPassword && !canceled);
00630
00631
00632 authList->inSort(current);
00633 }
00634
00635 void
00636 KPasswdServer::updateAuthExpire(const QString &key, const AuthInfo *auth, long windowId, bool keep)
00637 {
00638 AuthInfo *current = const_cast<AuthInfo *>(auth);
00639 if (keep)
00640 {
00641 current->expire = AuthInfo::expNever;
00642 }
00643 else if (windowId && (current->expire != AuthInfo::expNever))
00644 {
00645 current->expire = AuthInfo::expWindowClose;
00646 if (!current->windowList.contains(windowId))
00647 current->windowList.append(windowId);
00648 }
00649 else if (current->expire == AuthInfo::expTime)
00650 {
00651 current->expireTime = time(0)+10;
00652 }
00653
00654
00655 if (windowId)
00656 {
00657 QStringList *keysChanged = mWindowIdList.find(windowId);
00658 if (!keysChanged)
00659 {
00660 keysChanged = new QStringList;
00661 mWindowIdList.insert(windowId, keysChanged);
00662 }
00663 if (!keysChanged->contains(key))
00664 keysChanged->append(key);
00665 }
00666 }
00667
00668 void
00669 KPasswdServer::removeAuthForWindowId(long windowId)
00670 {
00671 QStringList *keysChanged = mWindowIdList.find(windowId);
00672 if (!keysChanged) return;
00673
00674 for(QStringList::ConstIterator it = keysChanged->begin();
00675 it != keysChanged->end(); ++it)
00676 {
00677 QString key = *it;
00678 AuthInfoList *authList = m_authDict.find(key);
00679 if (!authList)
00680 continue;
00681
00682 AuthInfo *current = authList->first();
00683 for(; current; )
00684 {
00685 if (current->expire == AuthInfo::expWindowClose)
00686 {
00687 if (current->windowList.remove(windowId) && current->windowList.isEmpty())
00688 {
00689 authList->remove();
00690 current = authList->current();
00691 continue;
00692 }
00693 }
00694 current = authList->next();
00695 }
00696 }
00697 }
00698
00699 #include "kpasswdserver.moc"