/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: passwordcontainer.cxx,v $ * * $Revision: 1.15 $ * * last change: $Author: ihi $ $Date: 2007-11-26 16:45:00 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svtools.hxx" #include "passwordcontainer.hxx" #include #ifndef _CPPUHELPER_FACTORY_HXX_ #include "cppuhelper/factory.hxx" #endif #ifndef _COM_SUN_STAR_REGISTRY_XSIMPLEREGISTRY_HPP_ #include #endif #ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_ #include #endif #ifndef _COM_SUN_STAR_TASK_MASTERPASSWORDREQUEST_HPP_ #include #endif #ifndef _COM_SUN_STAR_TASK_NOMASTEREXCEPTION_HPP_ #include #endif #include #include #include #ifndef _TOOLS_INETSTRM_HXX // @@@ #include #endif using namespace std; using namespace osl; using namespace utl; using namespace com::sun::star; using namespace com::sun::star::uno; using namespace com::sun::star::registry; using namespace com::sun::star::lang; using namespace com::sun::star::task; using namespace com::sun::star::ucb; //------------------------------------------------------------------------- //------------------------------------------------------------------------- static ::rtl::OUString createIndex( vector< ::rtl::OUString > lines ) { ::rtl::OString aResult; const sal_Char* pLine; for( unsigned int i = 0; i < lines.size(); i++ ) { if( i ) aResult += ::rtl::OString( "__" ); ::rtl::OString line = ::rtl::OUStringToOString( lines[i], RTL_TEXTENCODING_UTF8 ); pLine = line.getStr(); while( *pLine ) { if( ( *pLine >= 'A' && *pLine <= 'Z' ) || ( *pLine >= 'a' && *pLine <= 'z' ) || ( *pLine >= '0' && *pLine <= '9' ) ) { aResult += ::rtl::OString::valueOf( *pLine ); } else { aResult += ::rtl::OString("_"); aResult += ::rtl::OString::valueOf( (sal_Int32) *pLine, 16 ); } pLine++; } } return ::rtl::OUString::createFromAscii( aResult.getStr() ); } //------------------------------------------------------------------------- static vector< ::rtl::OUString > getInfoFromInd( ::rtl::OUString aInd ) { vector< ::rtl::OUString > aResult; sal_Bool aStart = sal_True; ::rtl::OString line = ::rtl::OUStringToOString( aInd, RTL_TEXTENCODING_ASCII_US ); const sal_Char* pLine = line.getStr(); do { ::rtl::OUString newItem; if( !aStart ) pLine += 2; else aStart = sal_False; while( *pLine && !( pLine[0] == '_' && pLine[1] == '_' )) if( *pLine != '_' ) { newItem += ::rtl::OUString::valueOf( (sal_Unicode) *pLine ); pLine++; } else { ::rtl::OUString aNum; for( int i = 1; i < 3; i++ ) { if( !pLine[i] || ( ( pLine[i] < '0' || pLine[i] > '9' ) && ( pLine[i] < 'a' || pLine[i] > 'f' ) && ( pLine[i] < 'A' || pLine[i] > 'F' ) ) ) { OSL_ENSURE( sal_False, "Wrong index syntax!\n" ); return aResult; } aNum += ::rtl::OUString::valueOf( (sal_Unicode) pLine[i] ); } newItem += ::rtl::OUString::valueOf( (sal_Unicode) aNum.toInt32( 16 ) ); pLine += 3; } aResult.push_back( newItem ); } while( pLine[0] == '_' && pLine[1] == '_' ); if( *pLine ) OSL_ENSURE( sal_False, "Wrong index syntax!\n" ); return aResult; } //------------------------------------------------------------------------- static sal_Bool shorterUrl( ::rtl::OUString& url ) { sal_Int32 aInd = url.lastIndexOf( sal_Unicode( '/' ) ); if( aInd > 0 && url.indexOf( ::rtl::OUString::createFromAscii( "://" ) ) != aInd-2 ) { url = url.copy( 0, aInd ); return sal_True; } return sal_False; } //------------------------------------------------------------------------- static ::rtl::OUString getAsciiLine( const ::rtl::ByteSequence& buf ) { ::rtl::OUString aResult; ::rtl::ByteSequence outbuf( buf.getLength()*2+1 ); for( int ind = 0; ind < buf.getLength(); ind++ ) { outbuf[ind*2] = ( ((sal_uInt8)buf[ind]) >> 4 ) + 'a'; outbuf[ind*2+1] = ( ((sal_uInt8)buf[ind]) & 0x0f ) + 'a'; } outbuf[buf.getLength()*2] = '\0'; aResult = ::rtl::OUString::createFromAscii( (sal_Char*)outbuf.getArray() ); return aResult; } //------------------------------------------------------------------------- static ::rtl::ByteSequence getBufFromAsciiLine( ::rtl::OUString line ) { OSL_ENSURE( line.getLength() % 2 == 0, "Wrong syntax!\n" ); ::rtl::OString tmpLine = ::rtl::OUStringToOString( line, RTL_TEXTENCODING_ASCII_US ); ::rtl::ByteSequence aResult(line.getLength()/2); for( int ind = 0; ind < tmpLine.getLength()/2; ind++ ) { aResult[ind] = ( (sal_uInt8)( tmpLine.getStr()[ind*2] - 'a' ) << 4 ) | (sal_uInt8)( tmpLine.getStr()[ind*2+1] - 'a' ); } return aResult; } //------------------------------------------------------------------------- static Sequence< ::rtl::OUString > copyVectorToSequence( const vector< ::rtl::OUString >& original ) { Sequence< ::rtl::OUString > newOne ( original.size() ); for( unsigned int i = 0; i < original.size() ; i++ ) newOne[i] = original[i]; return newOne; } static vector< ::rtl::OUString > copySequenceToVector( const Sequence< ::rtl::OUString >& original ) { vector< ::rtl::OUString > newOne ( original.getLength() ); for( int i = 0; i < original.getLength() ; i++ ) newOne[i] = original[i]; return newOne; } //------------------------------------------------------------------------- //------------------------------------------------------------------------- PassMap StorageItem::getInfo() { PassMap aResult; Sequence< ::rtl::OUString > aNodeNames = ConfigItem::GetNodeNames( ::rtl::OUString::createFromAscii("Store") ); sal_Int32 aNodeCount = aNodeNames.getLength(); Sequence< ::rtl::OUString > aPropNames( aNodeCount ); sal_Int32 aNodeInd; for( aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd ) { aPropNames[aNodeInd] = ::rtl::OUString::createFromAscii( "Store/Passwordstorage['" ); aPropNames[aNodeInd] += aNodeNames[aNodeInd]; aPropNames[aNodeInd] += ::rtl::OUString::createFromAscii( "']/Password" ); } Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aPropNames ); if( aPropertyValues.getLength() != aNodeNames.getLength() ) { OSL_ENSURE( aPropertyValues.getLength() == aNodeNames.getLength(), "Problems during reading\n" ); return aResult; } for( aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd ) { vector< ::rtl::OUString > aUrlUsr = getInfoFromInd( aNodeNames[aNodeInd] ); if( aUrlUsr.size() == 2 ) { ::rtl::OUString aUrl = aUrlUsr[0]; ::rtl::OUString aName = aUrlUsr[1]; vector< ::rtl::OUString > aPass; ::rtl::OUString aEPasswd; aPropertyValues[aNodeInd] >>= aEPasswd; aPass.push_back( aEPasswd ); PassMap::iterator aIter = aResult.find( aUrl ); if( aIter != aResult.end() ) aIter->second.push_back( NamePassRecord( aName, aPass, PERSISTENT_RECORD ) ); else { NamePassRecord aNewRecord( aName, aPass, PERSISTENT_RECORD ); list< NamePassRecord > listToAdd( 1, aNewRecord ); aResult.insert( PairUrlRecord( aUrl, listToAdd ) ); } } else OSL_ENSURE( sal_False, "Wrong index sintax!\n" ); } return aResult; } //------------------------------------------------------------------------- void StorageItem::setUseStorage( sal_Bool bUse ) { Sequence< ::rtl::OUString > sendNames(1); Sequence< uno::Any > sendVals(1); sendNames[0] = ::rtl::OUString::createFromAscii( "UseStorage" ); sendVals[0] <<= bUse; ConfigItem::SetModified(); ConfigItem::PutProperties( sendNames, sendVals ); } //------------------------------------------------------------------------- sal_Bool StorageItem::useStorage() { Sequence< ::rtl::OUString > aNodeNames( 1 ); aNodeNames[0] = ::rtl::OUString::createFromAscii( "UseStorage" ); Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames ); if( aPropertyValues.getLength() != aNodeNames.getLength() ) { OSL_ENSURE( aPropertyValues.getLength() == aNodeNames.getLength(), "Problems during reading\n" ); return sal_False; } sal_Bool aResult = false; aPropertyValues[0] >>= aResult; return aResult; } //------------------------------------------------------------------------- sal_Bool StorageItem::getEncodedMP( ::rtl::OUString& aResult ) { if( hasEncoded ) { aResult = mEncoded; return sal_True; } Sequence< ::rtl::OUString > aNodeNames( 2 ); aNodeNames[0] = ::rtl::OUString::createFromAscii( "HasMaster" ); aNodeNames[1] = ::rtl::OUString::createFromAscii( "Master" ); Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames ); if( aPropertyValues.getLength() != aNodeNames.getLength() ) { OSL_ENSURE( aPropertyValues.getLength() == aNodeNames.getLength(), "Problems during reading\n" ); return sal_False; } aPropertyValues[0] >>= hasEncoded; aPropertyValues[1] >>= mEncoded; aResult = mEncoded; return hasEncoded; } //------------------------------------------------------------------------- void StorageItem::setEncodedMP( const ::rtl::OUString& aEncoded ) { Sequence< ::rtl::OUString > sendNames(2); Sequence< uno::Any > sendVals(2); sendNames[0] = ::rtl::OUString::createFromAscii( "HasMaster" ); sendNames[1] = ::rtl::OUString::createFromAscii( "Master" ); sal_Bool bHasMaster = ( aEncoded.getLength() > 0 ); sendVals[0] <<= bHasMaster; sendVals[1] <<= aEncoded; ConfigItem::SetModified(); ConfigItem::PutProperties( sendNames, sendVals ); hasEncoded = bHasMaster; mEncoded = aEncoded; } //------------------------------------------------------------------------- void StorageItem::remove( const ::rtl::OUString& url, const ::rtl::OUString& name ) { vector < ::rtl::OUString > forIndex; forIndex.push_back( url ); forIndex.push_back( name ); Sequence< ::rtl::OUString > sendSeq(1); sendSeq[0] = createIndex( forIndex ); // sendSeq[0] = ::rtl::OUString::createFromAscii( "Store/Passwordstorage['" ); // sendSeq[0] += createIndex( forIndex ); // sendSeq[0] += ::rtl::OUString::createFromAscii( "']" ); ConfigItem::ClearNodeElements( ::rtl::OUString::createFromAscii( "Store" ), sendSeq ); } //------------------------------------------------------------------------- void StorageItem::clear() { Sequence< ::rtl::OUString > sendSeq(1); ConfigItem::ClearNodeSet( ::rtl::OUString::createFromAscii( "Store" ) ); } //------------------------------------------------------------------------- void StorageItem::update( const ::rtl::OUString& url, const NamePassRecord& rec ) { vector < ::rtl::OUString > forIndex; forIndex.push_back( url ); forIndex.push_back( rec.mName ); Sequence< beans::PropertyValue > sendSeq(1); sendSeq[0].Name = ::rtl::OUString::createFromAscii( "Store/Passwordstorage['" ); sendSeq[0].Name += createIndex( forIndex ); sendSeq[0].Name += ::rtl::OUString::createFromAscii( "']/Password" ); if( rec.mPass.size() ) { sendSeq[0].Value <<= rec.mPass[0]; ConfigItem::SetModified(); ConfigItem::SetSetProperties( ::rtl::OUString::createFromAscii( "Store" ), sendSeq ); } else OSL_ENSURE( sal_False, "No password!\n" ); } //------------------------------------------------------------------------- void StorageItem::Notify( const Sequence< ::rtl::OUString >& ) { // this feature still should not be used if( mainCont ) mainCont->Notify(); } //------------------------------------------------------------------------- void StorageItem::Commit() { // Do nothing, we stored everything we want already } //------------------------------------------------------------------------- //------------------------------------------------------------------------- PasswordContainer::PasswordContainer( const Reference& xServiceFactory ): m_pStorageFile( NULL ) { // m_pStorageFile->Notify() can be called ::osl::MutexGuard aGuard( mMutex ); mComponent = Reference< XComponent >( xServiceFactory, UNO_QUERY ); mComponent->addEventListener( this ); m_pStorageFile = new StorageItem( this, ::rtl::OUString::createFromAscii( "Office.Common/Passwords" ) ); if( m_pStorageFile ) if( m_pStorageFile->useStorage() ) container = m_pStorageFile->getInfo(); } //------------------------------------------------------------------------- PasswordContainer::~PasswordContainer() { ::osl::MutexGuard aGuard( mMutex ); if( m_pStorageFile ) { delete m_pStorageFile; m_pStorageFile = NULL; } if( mComponent.is() ) { mComponent->removeEventListener(this); mComponent = Reference< XComponent >(); } } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::disposing( const EventObject& ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if( m_pStorageFile ) { delete m_pStorageFile; m_pStorageFile = NULL; } if( mComponent.is() ) { //mComponent->removeEventListener(this); mComponent = Reference< XComponent >(); } } //------------------------------------------------------------------------- vector< ::rtl::OUString > PasswordContainer::DecodePasswords( const ::rtl::OUString& aLine, const ::rtl::OUString& aMasterPasswd ) throw(RuntimeException) { if( aMasterPasswd.getLength() ) { rtlCipher aDecoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream ); OSL_ENSURE( aDecoder, "Can't create decoder\n" ); if( aDecoder ) { OSL_ENSURE( aMasterPasswd.getLength() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!\n" ); unsigned char code[RTL_DIGEST_LENGTH_MD5]; for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) code[ ind ] = (char)(aMasterPasswd.copy( ind*2, 2 ).toInt32(16)); rtlCipherError result = rtl_cipher_init ( aDecoder, rtl_Cipher_DirectionDecode, code, RTL_DIGEST_LENGTH_MD5, NULL, 0 ); if( result == rtl_Cipher_E_None ) { ::rtl::ByteSequence aSeq = getBufFromAsciiLine( aLine ); ::rtl::ByteSequence resSeq( aSeq.getLength() ); result = rtl_cipher_decode ( aDecoder, (sal_uInt8*)aSeq.getArray(), aSeq.getLength(), (sal_uInt8*)resSeq.getArray(), resSeq.getLength() ); ::rtl::OUString aPasswd( ( sal_Char* )resSeq.getArray(), resSeq.getLength(), RTL_TEXTENCODING_UTF8 ); rtl_cipher_destroy (aDecoder); return getInfoFromInd( aPasswd ); } rtl_cipher_destroy (aDecoder); } } else { OSL_ENSURE( sal_False, "No master password provided!\n" ); // throw special exception } // problems with decoding OSL_ENSURE( sal_False, "Problem with decoding\n" ); throw RuntimeException( ::rtl::OUString::createFromAscii( "Can't decode!" ), Reference< XInterface >() ); } //------------------------------------------------------------------------- ::rtl::OUString PasswordContainer::EncodePasswords( vector< ::rtl::OUString > lines, const ::rtl::OUString& aMasterPasswd ) throw(RuntimeException) { if( aMasterPasswd.getLength() ) { ::rtl::OString aSeq = ::rtl::OUStringToOString( createIndex( lines ), RTL_TEXTENCODING_UTF8 ); rtlCipher aEncoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream ); OSL_ENSURE( aEncoder, "Can't create encoder\n" ); if( aEncoder ) { OSL_ENSURE( aMasterPasswd.getLength() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!\n" ); unsigned char code[RTL_DIGEST_LENGTH_MD5]; for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ ) code[ ind ] = (char)(aMasterPasswd.copy( ind*2, 2 ).toInt32(16)); rtlCipherError result = rtl_cipher_init ( aEncoder, rtl_Cipher_DirectionEncode, code, RTL_DIGEST_LENGTH_MD5, NULL, 0 ); if( result == rtl_Cipher_E_None ) { ::rtl::ByteSequence resSeq(aSeq.getLength()+1); result = rtl_cipher_encode ( aEncoder, (sal_uInt8*)aSeq.getStr(), aSeq.getLength()+1, (sal_uInt8*)resSeq.getArray(), resSeq.getLength() ); /* //test rtlCipherError result = rtl_cipher_init ( aEncoder, rtl_Cipher_DirectionDecode, code, RTL_DIGEST_LENGTH_MD5, NULL, 0 ); if( result == rtl_Cipher_E_None ) { ::rtl::OUString testOU = getAsciiLine( resSeq ); ::rtl::ByteSequence aSeq1 = getBufFromAsciiLine( testOU ); ::rtl::ByteSequence resSeq1( aSeq1.getLength() ); if( resSeq.getLength() == aSeq1.getLength() ) { for( int ind = 0; ind < aSeq1.getLength(); ind++ ) if( resSeq[ind] != aSeq1[ind] ) testOU = ::rtl::OUString(); } result = rtl_cipher_decode ( aEncoder, (sal_uInt8*)aSeq1.getArray(), aSeq1.getLength(), (sal_uInt8*)resSeq1.getArray(), resSeq1.getLength() ); ::rtl::OUString aPasswd( ( sal_Char* )resSeq1.getArray(), resSeq1.getLength(), RTL_TEXTENCODING_UTF8 ); } */ rtl_cipher_destroy (aEncoder); if( result == rtl_Cipher_E_None ) return getAsciiLine( resSeq ); } rtl_cipher_destroy (aEncoder); } } else { OSL_ENSURE( sal_False, "No master password provided!\n" ); // throw special exception } // problems with encoding OSL_ENSURE( sal_False, "Problem with encoding\n" ); throw RuntimeException( ::rtl::OUString::createFromAscii( "Can't encode!" ), Reference< XInterface >() ); } //------------------------------------------------------------------------- void PasswordContainer::UpdateVector( const ::rtl::OUString& url, list< NamePassRecord >& toUpdate, NamePassRecord& rec, sal_Bool writeFile, const Reference< XInteractionHandler >& /*xHandler*/ ) throw(RuntimeException) { for( list< NamePassRecord >::iterator aNPIter = toUpdate.begin(); aNPIter != toUpdate.end(); aNPIter++ ) if( aNPIter->mName.equals( rec.mName ) ) { if( aNPIter->mStatus == PERSISTENT_RECORD ) rec.mStatus = PERSISTENT_RECORD; if( rec.mStatus == PERSISTENT_RECORD && writeFile ) { // the password must be already encoded m_pStorageFile->update( url, rec ); // change existing ( url, name ) record in the configfile } (*aNPIter) = rec; return; } if( rec.mStatus == PERSISTENT_RECORD && writeFile ) { // the password must be already encoded m_pStorageFile->update( url, rec ); // add new name to the existing url } toUpdate.insert( toUpdate.begin(), rec ); } //------------------------------------------------------------------------- Sequence< UserRecord > PasswordContainer::CopyToUserRecordSequence( const list< NamePassRecord >& original, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { Sequence< UserRecord > aResult( original.size() ); sal_uInt32 nInd = 0; for( list< NamePassRecord >::const_iterator aNPIter = original.begin(); aNPIter != original.end(); aNPIter++, nInd++ ) { if( aNPIter->mStatus == PERSISTENT_RECORD ) { OSL_ENSURE( aNPIter->mPass.size(), "No encripted password!\n" ); try { aResult[nInd] = UserRecord( aNPIter->mName, copyVectorToSequence( DecodePasswords( aNPIter->mPass[0], GetMasterPassword( Handler ) ) ) ); } catch( NoMasterException& ) { // if master password could not be detected the entry will be just ignored } } else aResult[nInd] = UserRecord( aNPIter->mName, copyVectorToSequence( aNPIter->mPass ) ); } return aResult; } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::add( const ::rtl::OUString& Url, const ::rtl::OUString& UserName, const Sequence< ::rtl::OUString >& Passwords, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); PrivateAdd( Url, UserName, Passwords, SINGLE_RECORD, Handler ); } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::addPersistent( const ::rtl::OUString& Url, const ::rtl::OUString& UserName, const Sequence< ::rtl::OUString >& Passwords, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, Handler ); } //------------------------------------------------------------------------- void PasswordContainer::PrivateAdd( const ::rtl::OUString& Url, const ::rtl::OUString& UserName, const Sequence< ::rtl::OUString >& Passwords, char Mode, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { vector< ::rtl::OUString > storePass = copySequenceToVector( Passwords ); if( Mode == PERSISTENT_RECORD ) { ::rtl::OUString aEncPass = EncodePasswords( storePass, GetMasterPassword( Handler ) ); storePass = vector< ::rtl::OUString >( 1, aEncPass ); } else storePass = copySequenceToVector( Passwords ); if( !container.empty() ) { PassMap::iterator aIter = container.find( Url ); if( aIter != container.end() ) { NamePassRecord aNamePassRecord( UserName, storePass, Mode ); UpdateVector( aIter->first, aIter->second, aNamePassRecord, sal_True, Handler ); return; } } NamePassRecord aNewRecord( UserName, storePass, Mode ); list< NamePassRecord > listToAdd( 1, aNewRecord ); container.insert( PairUrlRecord( Url, listToAdd ) ); if( Mode == PERSISTENT_RECORD && m_pStorageFile && m_pStorageFile->useStorage() ) m_pStorageFile->update( Url, aNewRecord ); } //------------------------------------------------------------------------- UrlRecord SAL_CALL PasswordContainer::find( const ::rtl::OUString& url, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if( !container.empty() ) { ::rtl::OUString aUrl( url ); PassMap::iterator aIter = container.find( aUrl ); if( aIter != container.end() ) return UrlRecord( aIter->first, CopyToUserRecordSequence( aIter->second, Handler ) ); // each iteration remove last '/...' section from the aUrl // while it's possible, up to the most left '://' while( shorterUrl( aUrl ) ) { // first look for /somename and then look for /somename/... aIter = container.find( aUrl ); if( aIter != container.end() ) return UrlRecord( aIter->first, CopyToUserRecordSequence( aIter->second, Handler ) ); else { ::rtl::OUString tmpUrl( aUrl ); tmpUrl += ::rtl::OUString::createFromAscii( "/" ); aIter = container.lower_bound( aUrl ); if( aIter != container.end() ) return UrlRecord( aIter->first, CopyToUserRecordSequence( aIter->second, Handler ) ); } } } return UrlRecord(); } //------------------------------------------------------------------------- Sequence< UserRecord > PasswordContainer::FindUsr( const list< NamePassRecord >& userlist, const ::rtl::OUString& name, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { sal_uInt32 nInd = 0; for( list< NamePassRecord >::const_iterator aNPIter = userlist.begin(); aNPIter != userlist.end(); aNPIter++, nInd++ ) { if( aNPIter->mName.equals( name ) ) { Sequence< UserRecord > aResult(1); if( aNPIter->mStatus == PERSISTENT_RECORD ) { OSL_ENSURE( aNPIter->mPass.size(), "No encripted password!\n" ); *aResult.getArray() = UserRecord( name, copyVectorToSequence( DecodePasswords( aNPIter->mPass[0], GetMasterPassword( Handler ) ) ) ); } else *aResult.getArray() = UserRecord( name, copyVectorToSequence( aNPIter->mPass ) ); return aResult; } } return Sequence< UserRecord >(); } //------------------------------------------------------------------------- UrlRecord SAL_CALL PasswordContainer::findForName( const ::rtl::OUString& url, const ::rtl::OUString& name, const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if( !container.empty() ) { ::rtl::OUString aUrl( url ); PassMap::iterator aIter = container.find( aUrl ); if( aIter != container.end() ) { Sequence< UserRecord > aUsrRec = FindUsr( aIter->second, name, Handler ); if( aUsrRec.getLength() ) return UrlRecord( aIter->first, aUsrRec ); } // each iteration remove last '/...' section from the aUrl // while it's possible, up to the most left '://' while( shorterUrl( aUrl ) ) { // first look for /somename and then look for /somename/... aIter = container.find( aUrl ); if( aIter != container.end() ) { Sequence< UserRecord > aUsrRec = FindUsr( aIter->second, name, Handler ); if( aUsrRec.getLength() ) return UrlRecord( aIter->first, aUsrRec ); } else { ::rtl::OUString tmpUrl( aUrl ); tmpUrl += ::rtl::OUString::createFromAscii( "/" ); aIter = container.lower_bound( aUrl ); if( aIter != container.end() ) { Sequence< UserRecord > aUsrRec = FindUsr( aIter->second, name, Handler ); if( aUsrRec.getLength() ) return UrlRecord( aIter->first, aUsrRec ); } } } } return UrlRecord(); } //------------------------------------------------------------------------- ::rtl::OUString PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode, const uno::Reference< task::XInteractionHandler >& xHandler ) { // empty string means that the call was cancelled or just failed ::rtl::OUString aResult; if ( xHandler.is() ) { ::rtl::Reference< MasterPasswordRequest_Impl > xRequest = new MasterPasswordRequest_Impl( aRMode ); xHandler->handle( xRequest.get() ); ::rtl::Reference< ucbhelper::InteractionContinuation > xSelection = xRequest->getSelection(); if ( xSelection.is() ) { Reference< XInteractionAbort > xAbort( xSelection.get(), UNO_QUERY ); if ( !xAbort.is() ) { const ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > & xSupp = xRequest->getAuthenticationSupplier(); aResult = xSupp->getPassword(); } } } return aResult; } //------------------------------------------------------------------------- ::rtl::OUString PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& Handler ) throw(RuntimeException) { PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER; if( !m_pStorageFile || !m_pStorageFile->useStorage() ) throw NoMasterException( ::rtl::OUString::createFromAscii( "Password storing is not active!" ), Reference< XInterface >(), aRMode ); if( !m_aMasterPasswd.getLength() && Handler.is() ) { ::rtl::OUString aEncodedMP; sal_Bool bAskAgain; if( !m_pStorageFile->getEncodedMP( aEncodedMP ) ) aRMode = PasswordRequestMode_PASSWORD_CREATE; do { bAskAgain = sal_False; ::rtl::OUString aPass = RequestPasswordFromUser( aRMode, Handler ); if ( aPass.getLength() ) { if( aRMode == PasswordRequestMode_PASSWORD_CREATE ) { m_aMasterPasswd = aPass; vector< ::rtl::OUString > aMaster( 1, m_aMasterPasswd ); m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); } else { vector< ::rtl::OUString > aRM( DecodePasswords( aEncodedMP, aPass ) ); if( !aRM.size() || !aPass.equals( aRM[0] ) ) { bAskAgain = sal_True; aRMode = PasswordRequestMode_PASSWORD_REENTER; } else m_aMasterPasswd = aPass; } } } while( bAskAgain ); } if ( !m_aMasterPasswd.getLength() ) throw NoMasterException( ::rtl::OUString::createFromAscii( "No master password!" ), Reference< XInterface >(), aRMode ); return m_aMasterPasswd; } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::remove( const ::rtl::OUString& url, const ::rtl::OUString& name ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); ::rtl::OUString aUrl( url ); if( !container.empty() ) { PassMap::iterator aIter = container.find( aUrl ); if( aIter == container.end() ) { sal_Int32 aInd = aUrl.lastIndexOf( sal_Unicode( '/' ) ); if( aInd > 0 && aUrl.getLength()-1 == aInd ) aUrl = aUrl.copy( 0, aUrl.getLength() - 1 ); else aUrl += ::rtl::OUString::createFromAscii( "/" ); aIter = container.find( aUrl ); } if( aIter != container.end() ) { for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); aNPIter++ ) if( aNPIter->mName.equals( name ) ) { if( aNPIter->mStatus == PERSISTENT_RECORD && m_pStorageFile ) m_pStorageFile->remove( url, name ); // remove record ( url, name ) // the iterator will not be used any more so it can be removed directly aIter->second.erase( aNPIter ); if( aIter->second.begin() == aIter->second.end() ) container.erase( aIter ); return; } } } } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::removePersistent( const ::rtl::OUString& url, const ::rtl::OUString& name ) throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); ::rtl::OUString aUrl( url ); if( !container.empty() ) { PassMap::iterator aIter = container.find( aUrl ); if( aIter == container.end() ) { sal_Int32 aInd = aUrl.lastIndexOf( sal_Unicode( '/' ) ); if( aInd > 0 && aUrl.getLength()-1 == aInd ) aUrl = aUrl.copy( 0, aUrl.getLength() - 1 ); else aUrl += ::rtl::OUString::createFromAscii( "/" ); aIter = container.find( aUrl ); } if( aIter != container.end() ) { for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); aNPIter++ ) if( aNPIter->mName.equals( name ) ) { if( aNPIter->mStatus == PERSISTENT_RECORD ) { if ( m_aMasterPasswd.getLength() ) { aNPIter->mPass = DecodePasswords( aNPIter->mPass[0], m_aMasterPasswd ); aNPIter->mStatus = SINGLE_RECORD; } else { // the iterator will not be used any more so it can be removed directly aIter->second.erase( aNPIter ); if( aIter->second.begin() == aIter->second.end() ) container.erase( aIter ); } if ( m_pStorageFile ) m_pStorageFile->remove( url, name ); // remove record ( url, name ) return; } } } } } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::removeAllPersistent() throw(RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if( m_pStorageFile ) m_pStorageFile->clear(); for( PassMap::iterator aIter = container.begin(); aIter != container.end(); ) { for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); ) { if( aNPIter->mStatus == PERSISTENT_RECORD ) { if ( m_aMasterPasswd.getLength() ) { aNPIter->mPass = DecodePasswords( aNPIter->mPass[0], m_aMasterPasswd ); aNPIter->mStatus = SINGLE_RECORD; aNPIter++; } else { list< NamePassRecord >::iterator aIterToDelete( aNPIter ); aNPIter++; aIter->second.erase( aIterToDelete ); } } else aNPIter++; } if( aIter->second.begin() == aIter->second.end() ) { PassMap::iterator aIterToDelete( aIter ); aIter++; container.erase( aIterToDelete ); } else aIter++; } } //------------------------------------------------------------------------- Sequence< UrlRecord > SAL_CALL PasswordContainer::getAllPersistent( const Reference< XInteractionHandler >& xHandler ) throw(RuntimeException) { Sequence< UrlRecord > aResult; ::osl::MutexGuard aGuard( mMutex ); for( PassMap::iterator aIter = container.begin(); aIter != container.end(); aIter++ ) { Sequence< UserRecord > aUsers; for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); aNPIter++ ) if( aNPIter->mStatus == PERSISTENT_RECORD ) { sal_Int32 oldLen = aUsers.getLength(); aUsers.realloc( oldLen + 1 ); aUsers[ oldLen ] = UserRecord( aNPIter->mName, copyVectorToSequence( DecodePasswords( aNPIter->mPass[0], GetMasterPassword( xHandler ) ) ) ); } if( aUsers.getLength() ) { sal_Int32 oldLen = aResult.getLength(); aResult.realloc( oldLen + 1 ); aResult[ oldLen ] = UrlRecord( aIter->first, aUsers ); } } return aResult; } //------------------------------------------------------------------------- sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler ) throw (uno::RuntimeException) { sal_Bool bResult = sal_False; ::rtl::OUString aEncodedMP; uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler; ::osl::MutexGuard aGuard( mMutex ); // the method should fail if there is no master password if( m_pStorageFile && m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) ) { if ( !xTmpHandler.is() ) { uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW ); xTmpHandler.set( xFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.task.InteractionHandler" ) ) ), uno::UNO_QUERY_THROW ); } if ( m_aMasterPasswd.getLength() ) { // there is a password, it should be just rechecked PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER; ::rtl::OUString aPass; do { aPass = RequestPasswordFromUser( aRMode, xTmpHandler ); bResult = ( aPass.getLength() && aPass.equals( m_aMasterPasswd ) ); aRMode = PasswordRequestMode_PASSWORD_REENTER; // further questions with error notification } while( !bResult && aPass.getLength() ); } else { try { // ask for the password, if user provide no correct password an exception will be thrown bResult = ( GetMasterPassword( xTmpHandler ).getLength() > 0 ); } catch( uno::Exception& ) {} } } return bResult; } //------------------------------------------------------------------------- sal_Bool SAL_CALL PasswordContainer::changeMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler ) throw (uno::RuntimeException) { sal_Bool bResult = sal_False; uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler; ::osl::MutexGuard aGuard( mMutex ); if ( m_pStorageFile && m_pStorageFile->useStorage() ) { if ( !xTmpHandler.is() ) { uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW ); xTmpHandler.set( xFactory->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.task.InteractionHandler" ) ) ), uno::UNO_QUERY_THROW ); } sal_Bool bCanChangePassword = sal_True; // if there is already a stored master password it should be entered by the user before the change happen ::rtl::OUString aEncodedMP; if( m_aMasterPasswd.getLength() || m_pStorageFile->getEncodedMP( aEncodedMP ) ) bCanChangePassword = authorizateWithMasterPassword( xTmpHandler ); if ( bCanChangePassword ) { // ask for the new password, but do not set it PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_CREATE; ::rtl::OUString aPass = RequestPasswordFromUser( aRMode, xTmpHandler ); if ( aPass.getLength() ) { // get all the persistent entries if it is possible Sequence< UrlRecord > aPersistent = getAllPersistent( uno::Reference< task::XInteractionHandler >() ); // remove the master password and the entries persistence removeMasterPassword(); // store the new master password m_aMasterPasswd = aPass; vector< ::rtl::OUString > aMaster( 1, m_aMasterPasswd ); m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) ); // store all the entries with the new password for ( int nURLInd = 0; nURLInd < aPersistent.getLength(); nURLInd++ ) for ( int nNameInd = 0; nNameInd< aPersistent[nURLInd].UserList.getLength(); nNameInd++ ) addPersistent( aPersistent[nURLInd].Url, aPersistent[nURLInd].UserList[nNameInd].UserName, aPersistent[nURLInd].UserList[nNameInd].Passwords, uno::Reference< task::XInteractionHandler >() ); bResult = sal_True; } } } return bResult; } //------------------------------------------------------------------------- void SAL_CALL PasswordContainer::removeMasterPassword() throw (uno::RuntimeException) { // remove all the stored passwords and the master password removeAllPersistent(); ::osl::MutexGuard aGuard( mMutex ); if ( m_pStorageFile ) { m_aMasterPasswd = ::rtl::OUString(); m_pStorageFile->setEncodedMP( ::rtl::OUString() ); // let the master password be removed from configuration } } //------------------------------------------------------------------------- ::sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( ) throw (::com::sun::star::uno::RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if ( !m_pStorageFile ) throw uno::RuntimeException(); ::rtl::OUString aEncodedMP; return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) ); } //------------------------------------------------------------------------- ::sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( ::sal_Bool bAllow ) throw (::com::sun::star::uno::RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if ( !m_pStorageFile ) throw uno::RuntimeException(); if ( !bAllow ) removeMasterPassword(); if ( m_pStorageFile->useStorage() == bAllow ) return bAllow; m_pStorageFile->setUseStorage( bAllow ); return !bAllow; } //------------------------------------------------------------------------- ::sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed() throw (::com::sun::star::uno::RuntimeException) { ::osl::MutexGuard aGuard( mMutex ); if ( !m_pStorageFile ) throw uno::RuntimeException(); return m_pStorageFile->useStorage(); } //------------------------------------------------------------------------- void PasswordContainer::Notify() { ::osl::MutexGuard aGuard( mMutex ); PassMap::iterator aIter; for( aIter = container.begin(); aIter != container.end(); aIter++ ) { for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); aNPIter++ ) if( aNPIter->mStatus == PERSISTENT_RECORD ) aNPIter->mStatus = SINGLE_RECORD; } PassMap addon; if( m_pStorageFile ) addon = m_pStorageFile->getInfo(); for( aIter = addon.begin(); aIter != addon.end(); aIter++ ) { PassMap::iterator aSearchIter = container.find( aIter->first ); if( aSearchIter != container.end() ) for( list< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); aNPIter++ ) UpdateVector( aSearchIter->first, aSearchIter->second, *aNPIter, sal_False, Reference< XInteractionHandler >() ); else container.insert( PairUrlRecord( aIter->first, aIter->second ) ); } } //------------------------------------------------------------------------- ::rtl::OUString SAL_CALL PasswordContainer::getImplementationName( ) throw(uno::RuntimeException) { return impl_getStaticImplementationName(); } //------------------------------------------------------------------------- sal_Bool SAL_CALL PasswordContainer::supportsService( const ::rtl::OUString& ServiceName ) throw(uno::RuntimeException) { if ( ServiceName.compareToAscii("com.sun.star.task.PasswordContainer") == 0 ) return sal_True; else return sal_False; } //------------------------------------------------------------------------- Sequence< ::rtl::OUString > SAL_CALL PasswordContainer::getSupportedServiceNames( ) throw(uno::RuntimeException) { return impl_getStaticSupportedServiceNames(); } //------------------------------------------------------------------------- Sequence< ::rtl::OUString > SAL_CALL PasswordContainer::impl_getStaticSupportedServiceNames( ) throw(uno::RuntimeException) { Sequence< ::rtl::OUString > aRet(1); *aRet.getArray() = ::rtl::OUString::createFromAscii("com.sun.star.task.PasswordContainer"); return aRet; } //------------------------------------------------------------------------- ::rtl::OUString SAL_CALL PasswordContainer::impl_getStaticImplementationName() throw(uno::RuntimeException) { return ::rtl::OUString::createFromAscii("stardiv.svtools.PasswordContainer"); } //------------------------------------------------------------------------- Reference< XInterface > SAL_CALL PasswordContainer::impl_createInstance( const Reference< XMultiServiceFactory >& xServiceManager ) throw( RuntimeException ) { return Reference< XInterface >( *new PasswordContainer( xServiceManager ) ); } //------------------------------------------------------------------------- Reference< XSingleServiceFactory > SAL_CALL PasswordContainer::impl_createFactory( const Reference< XMultiServiceFactory >& ServiceManager ) throw(RuntimeException) { Reference< XSingleServiceFactory > xReturn( ::cppu::createOneInstanceFactory( ServiceManager, PasswordContainer::impl_getStaticImplementationName(), PasswordContainer::impl_createInstance, PasswordContainer::impl_getStaticSupportedServiceNames())); return xReturn ; } //------------------------------------------------------------------------- //------------------------------------------------------------------------- MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode ) { MasterPasswordRequest aRequest; aRequest.Classification = InteractionClassification_ERROR; aRequest.Mode = Mode; setRequest( makeAny( aRequest ) ); // Fill continuations... Sequence< RememberAuthentication > aRememberModes( 1 ); aRememberModes[ 0 ] = RememberAuthentication_NO; m_xAuthSupplier = new ::ucbhelper::InteractionSupplyAuthentication( this, sal_False, // bCanSetRealm sal_False, // bCanSetUserName sal_True, // bCanSetPassword sal_False, // bCanSetAccount aRememberModes, // rRememberPasswordModes RememberAuthentication_NO, // eDefaultRememberPasswordMode aRememberModes, // rRememberAccountModes RememberAuthentication_NO // eDefaultRememberAccountMode ); Sequence< Reference< XInteractionContinuation > > aContinuations( 3 ); aContinuations[ 0 ] = new ::ucbhelper::InteractionAbort( this ); aContinuations[ 1 ] = new ::ucbhelper::InteractionRetry( this ); aContinuations[ 2 ] = m_xAuthSupplier.get(); setContinuations( aContinuations ); } //------------------------------------------------------------------------- //------------------------------------------------------------------------- extern "C" { SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment ( const sal_Char ** ppEnvTypeName, uno_Environment ** /* ppEnv */) { *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; } SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL component_writeInfo ( void * /* pServiceManager */, void * pRegistryKey) { if (pRegistryKey) { Reference< XRegistryKey > xRegistryKey ( reinterpret_cast< XRegistryKey* >( pRegistryKey )); Reference< XRegistryKey > xNewKey; xNewKey = xRegistryKey->createKey( ::rtl::OUString::createFromAscii( "/stardiv.svtools.PasswordContainer/UNO/SERVICES" )); xNewKey->createKey( ::rtl::OUString::createFromAscii("com.sun.star.task.PasswordContainer")); return sal_True; } return sal_False; } SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory ( const sal_Char * pImplementationName, void * pServiceManager, void * /* pRegistryKey */) { void * pResult = 0; if (pServiceManager) { Reference< XSingleServiceFactory > xFactory; if (PasswordContainer::impl_getStaticImplementationName().compareToAscii (pImplementationName) == 0) { xFactory = PasswordContainer::impl_createFactory ( reinterpret_cast< XMultiServiceFactory* >(pServiceManager)); } if (xFactory.is()) { xFactory->acquire(); pResult = xFactory.get(); } } return pResult; } } // extern "C"