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