kpasswdserver.cpp
00001 /* 00002 This file is part of the KDE Password Server 00003 00004 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) 00005 Copyright (C) 2005 David Faure (faure@kde.org) 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU General Public License 00009 version 2 as published by the Free Software Foundation. 00010 00011 This software is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this library; see the file COPYING. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 //---------------------------------------------------------------------------- 00022 // 00023 // KDE Password Server 00024 // $Id$ 00025 00026 #include "kpasswdserver.h" 00027 00028 #include <time.h> 00029 00030 #include <tqtimer.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 TQCString &name) 00047 { 00048 return new KPasswdServer(name); 00049 } 00050 } 00051 00052 int 00053 KPasswdServer::AuthInfoList::compareItems(TQPtrCollection::Item n1, TQPtrCollection::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 TQCString &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, TQT_SIGNAL(windowUnregistered(long)), 00080 this, TQT_SLOT(removeAuthForWindowId(long))); 00081 } 00082 00083 KPasswdServer::~KPasswdServer() 00084 { 00085 delete m_wallet; 00086 } 00087 00088 // Helper - returns the wallet key to use for read/store/checking for existence. 00089 static TQString makeWalletKey( const TQString& key, const TQString& realm ) 00090 { 00091 return realm.isEmpty() ? key : key + '-' + realm; 00092 } 00093 00094 // Helper for storeInWallet/readFromWallet 00095 static TQString makeMapKey( const char* key, int entryNumber ) 00096 { 00097 TQString str = TQString::fromLatin1( key ); 00098 if ( entryNumber > 1 ) 00099 str += "-" + TQString::number( entryNumber ); 00100 return str; 00101 } 00102 00103 static bool storeInWallet( KWallet::Wallet* wallet, const TQString& 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 // Before saving, check if there's already an entry with this login. 00110 // If so, replace it (with the new password). Otherwise, add a new entry. 00111 typedef TQMap<TQString,TQString> Map; 00112 int entryNumber = 1; 00113 Map map; 00114 TQString 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; // OK, overwrite this entry 00122 } 00123 it = map.find( TQString( "login-" ) + TQString::number( ++entryNumber ) ); 00124 } 00125 // If no entry was found, create a new entry - entryNumber is set already. 00126 } 00127 const TQString loginKey = makeMapKey( "login", entryNumber ); 00128 const TQString passwordKey = makeMapKey( "password", entryNumber ); 00129 kdDebug(130) << "storeInWallet: writing to " << loginKey << "," << passwordKey << endl; 00130 // note the overwrite=true by default 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 TQString& key, const TQString& realm, TQString& username, TQString& password, bool userReadOnly, TQMap<TQString,TQString>& knownLogins ) 00139 { 00140 //kdDebug(130) << "readFromWallet: key=" << key << " username=" << username << " password=" /*<< password*/ << " userReadOnly=" << userReadOnly << " realm=" << realm << endl; 00141 if ( wallet->hasFolder( KWallet::Wallet::PasswordFolder() ) ) 00142 { 00143 wallet->setFolder( KWallet::Wallet::PasswordFolder() ); 00144 00145 TQMap<TQString,TQString> map; 00146 if ( wallet->readMap( makeWalletKey( key, realm ), map ) == 0 ) 00147 { 00148 typedef TQMap<TQString,TQString> Map; 00149 int entryNumber = 1; 00150 Map::ConstIterator end = map.end(); 00151 Map::ConstIterator it = map.find( "login" ); 00152 while ( it != end ) { 00153 //kdDebug(130) << "readFromWallet: found " << it.key() << "=" << it.data() << endl; 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( TQString( "login-" ) + TQString::number( ++entryNumber ) ); 00162 } 00163 //kdDebug(130) << knownLogins.count() << " known logins" << endl; 00164 00165 if ( !userReadOnly && !knownLogins.isEmpty() && username.isEmpty() ) { 00166 // Pick one, any one... 00167 username = knownLogins.begin().key(); 00168 password = knownLogins.begin().data(); 00169 //kdDebug(130) << "readFromWallet: picked the first one : " << username << endl; 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 TQString key = createCacheKey(info); 00193 00194 Request *request = m_authPending.first(); 00195 TQString 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 TQString 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 TQMap<TQString, TQString> 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, TQString 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, TQString 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() ) // should we really allow the caller to pre-fill the password? 00257 kdDebug(130) << "password was set by caller" << endl; 00258 if( usertime != 0 ) 00259 kapp->updateUserTimestamp( usertime ); 00260 00261 TQString 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 = TQString::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 TQTimer::singleShot(0, this, TQT_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 TQString 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() ) { // forced closed 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 TQString 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 = TQDialog::Rejected; 00354 if (askPw) 00355 { 00356 TQString username = info.username; 00357 TQString password = info.password; 00358 bool hasWalletData = false; 00359 TQMap<TQString, TQString> 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 // no login+pass provided, check if kwallet has one 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 == TQDialog::Accepted) 00396 { 00397 info.username = dlg.username(); 00398 info.password = dlg.password(); 00399 info.keepPassword = dlg.keepPassword(); 00400 00401 // When the user checks "keep password", that means: 00402 // * if the wallet is enabled, store it there for long-term, and in kpasswdserver 00403 // only for the duration of the window (#92928) 00404 // * otherwise store in kpasswdserver for the duration of the KDE session. 00405 if ( info.keepPassword ) { 00406 if ( openWallet( request->windowId ) ) { 00407 if ( storeInWallet( m_wallet, request->key, info ) ) 00408 // password is in wallet, don't keep it in memory after window is closed 00409 info.keepPassword = false; 00410 } 00411 } 00412 } 00413 } 00414 if ( dlgResult != TQDialog::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 TQCString replyType; 00427 TQByteArray replyData; 00428 00429 TQDataStream 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 // Check all requests in the wait queue. 00438 for(Request *waitRequest = m_authWait.first(); 00439 waitRequest; ) 00440 { 00441 bool keepQueued = false; 00442 TQString key = waitRequest->key; 00443 00444 request = m_authPending.first(); 00445 TQString 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 TQString 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 TQCString replyType; 00470 TQByteArray replyData; 00471 00472 TQDataStream 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 TQTimer::singleShot(0, this, TQT_SLOT(processRequest())); 00497 00498 } 00499 00500 TQString KPasswdServer::createCacheKey( const KIO::AuthInfo &info ) 00501 { 00502 if( !info.url.isValid() ) { 00503 // Note that a null key will break findAuthInfoItem later on... 00504 kdWarning(130) << "createCacheKey: invalid URL " << info.url << endl; 00505 return TQString::null; 00506 } 00507 00508 // Generate the basic key sequence. 00509 TQString 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 += TQString::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 TQString &key, const KIO::AuthInfo &info) 00543 { 00544 AuthInfoList *authList = m_authDict.find(key); 00545 if (!authList) 00546 return 0; 00547 00548 TQString 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 TQString 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; // TODO: Update directory info, 00572 } 00573 00574 current = authList->next(); 00575 } 00576 return 0; 00577 } 00578 00579 void 00580 KPasswdServer::removeAuthInfoItem(const TQString &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 TQString &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 // Insert into list, keep the list sorted "longest path" first. 00648 authList->inSort(current); 00649 } 00650 00651 void 00652 KPasswdServer::updateAuthExpire(const TQString &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 // Update mWindowIdList 00671 if (windowId) 00672 { 00673 TQStringList *keysChanged = mWindowIdList.find(windowId); 00674 if (!keysChanged) 00675 { 00676 keysChanged = new TQStringList; 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 TQStringList *keysChanged = mWindowIdList.find(windowId); 00688 if (!keysChanged) return; 00689 00690 for(TQStringList::ConstIterator it = keysChanged->begin(); 00691 it != keysChanged->end(); ++it) 00692 { 00693 TQString 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"