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