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 #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
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
00282
00283
00284
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
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
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
00391
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;
00402 }
00403
00404 it = map.find( QString( "login-" ) + QString::number( ++entryNumber ) );
00405 }
00406
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
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
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
00466 kdWarning(130) << "createCacheKey: invalid URL " << info.url << endl;
00467 return QString::null;
00468 }
00469
00470
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;
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
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
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"