summaryrefslogtreecommitdiffstats
path: root/svl/source/passwordcontainer/passwordcontainer.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--svl/source/passwordcontainer/passwordcontainer.cxx1400
1 files changed, 1400 insertions, 0 deletions
diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx
new file mode 100644
index 000000000..8bdc1adb7
--- /dev/null
+++ b/svl/source/passwordcontainer/passwordcontainer.cxx
@@ -0,0 +1,1400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "passwordcontainer.hxx"
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/task/MasterPasswordRequest.hpp>
+#include <com/sun/star/task/NoMasterException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <rtl/cipher.h>
+#include <rtl/digest.h>
+#include <rtl/byteseq.hxx>
+#include <rtl/ustrbuf.hxx>
+
+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 OUString createIndex(const std::vector< OUString >& lines)
+{
+ OUStringBuffer aResult;
+
+ for( size_t i = 0; i < lines.size(); i++ )
+ {
+ if( i )
+ aResult.append("__");
+ OString line = OUStringToOString( lines[i], RTL_TEXTENCODING_UTF8 );
+ const char* pLine = line.getStr();
+
+ while( *pLine )
+ {
+ if (rtl::isAsciiAlphanumeric(static_cast<unsigned char>(*pLine)))
+ {
+ aResult.append(*pLine);
+ }
+ else
+ {
+ aResult.append("_").append( OUString::number( *pLine, 16 ) );
+ }
+
+ pLine++;
+ }
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+
+static std::vector< OUString > getInfoFromInd( const OUString& aInd )
+{
+ std::vector< OUString > aResult;
+ bool aStart = true;
+
+ OString line = OUStringToOString( aInd, RTL_TEXTENCODING_ASCII_US );
+ const char* pLine = line.getStr();
+ do
+ {
+ OUStringBuffer newItem;
+ if( !aStart )
+ pLine += 2;
+ else
+ aStart = false;
+
+ while( *pLine && !( pLine[0] == '_' && pLine[1] == '_' ))
+ if( *pLine != '_' )
+ {
+ newItem.append( *pLine );
+ pLine++;
+ }
+ else
+ {
+ 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_FAIL( "Wrong index syntax!" );
+ return aResult;
+ }
+
+ aNum += OUStringChar( pLine[i] );
+ }
+
+ newItem.append( sal_Unicode( aNum.toUInt32( 16 ) ) );
+ pLine += 3;
+ }
+
+ aResult.push_back( newItem.makeStringAndClear() );
+ } while( pLine[0] == '_' && pLine[1] == '_' );
+
+ if( *pLine )
+ OSL_FAIL( "Wrong index syntax!" );
+
+ return aResult;
+}
+
+
+static bool shorterUrl( OUString& aURL )
+{
+ sal_Int32 aInd = aURL.lastIndexOf( '/' );
+ if( aInd > 0 && aURL.indexOf( "://" ) != aInd-2 )
+ {
+ aURL = aURL.copy( 0, aInd );
+ return true;
+ }
+
+ return false;
+}
+
+
+static OUString getAsciiLine( const ::rtl::ByteSequence& buf )
+{
+ OUString aResult;
+
+ ::rtl::ByteSequence outbuf( buf.getLength()*2+1 );
+
+ for( int ind = 0; ind < buf.getLength(); ind++ )
+ {
+ outbuf[ind*2] = ( static_cast<sal_uInt8>(buf[ind]) >> 4 ) + 'a';
+ outbuf[ind*2+1] = ( static_cast<sal_uInt8>(buf[ind]) & 0x0f ) + 'a';
+ }
+ outbuf[buf.getLength()*2] = '\0';
+
+ aResult = OUString::createFromAscii( reinterpret_cast<char*>(outbuf.getArray()) );
+
+ return aResult;
+}
+
+
+static ::rtl::ByteSequence getBufFromAsciiLine( const OUString& line )
+{
+ OSL_ENSURE( line.getLength() % 2 == 0, "Wrong syntax!" );
+ OString tmpLine = OUStringToOString( line, RTL_TEXTENCODING_ASCII_US );
+ ::rtl::ByteSequence aResult(line.getLength()/2);
+
+ for( int ind = 0; ind < tmpLine.getLength()/2; ind++ )
+ {
+ aResult[ind] = ( static_cast<sal_uInt8>( tmpLine[ind*2] - 'a' ) << 4 ) | static_cast<sal_uInt8>( tmpLine[ind*2+1] - 'a' );
+ }
+
+ return aResult;
+}
+
+
+PassMap StorageItem::getInfo()
+{
+ PassMap aResult;
+
+ Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( "Store" );
+ sal_Int32 aNodeCount = aNodeNames.getLength();
+ Sequence< OUString > aPropNames( aNodeCount );
+
+ std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.begin(),
+ [](const OUString& rName) -> OUString {
+ return "Store/Passwordstorage['" + rName + "']/Password"; });
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aPropNames );
+
+ if( aPropertyValues.getLength() != aNodeCount )
+ {
+ OSL_FAIL( "Problems during reading" );
+ return aResult;
+ }
+
+ for( sal_Int32 aNodeInd = 0; aNodeInd < aNodeCount; ++aNodeInd )
+ {
+ std::vector< OUString > aUrlUsr = getInfoFromInd( aNodeNames[aNodeInd] );
+
+ if( aUrlUsr.size() == 2 )
+ {
+ OUString aUrl = aUrlUsr[0];
+ OUString aName = aUrlUsr[1];
+
+ OUString aEPasswd;
+ aPropertyValues[aNodeInd] >>= aEPasswd;
+
+ PassMap::iterator aIter = aResult.find( aUrl );
+ if( aIter != aResult.end() )
+ aIter->second.emplace_back( aName, aEPasswd );
+ else
+ {
+ NamePassRecord aNewRecord( aName, aEPasswd );
+ std::vector< NamePassRecord > listToAdd( 1, aNewRecord );
+
+ aResult.insert( PairUrlRecord( aUrl, listToAdd ) );
+ }
+ }
+ else
+ OSL_FAIL( "Wrong index syntax!" );
+ }
+
+ return aResult;
+}
+
+
+void StorageItem::setUseStorage( bool bUse )
+{
+ Sequence< OUString > sendNames(1);
+ Sequence< uno::Any > sendVals(1);
+
+ sendNames[0] = "UseStorage";
+
+ sendVals[0] <<= bUse;
+
+ ConfigItem::SetModified();
+ ConfigItem::PutProperties( sendNames, sendVals );
+}
+
+
+bool StorageItem::useStorage()
+{
+ Sequence<OUString> aNodeNames { "UseStorage" };
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
+
+ if( aPropertyValues.getLength() != aNodeNames.getLength() )
+ {
+ OSL_FAIL( "Problems during reading" );
+ return false;
+ }
+
+ bool aResult = false;
+ aPropertyValues[0] >>= aResult;
+
+ return aResult;
+}
+
+
+bool StorageItem::getEncodedMP( OUString& aResult )
+{
+ if( hasEncoded )
+ {
+ aResult = mEncoded;
+ return true;
+ }
+
+ Sequence< OUString > aNodeNames( 2 );
+ aNodeNames[0] = "HasMaster";
+ aNodeNames[1] = "Master";
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
+
+ if( aPropertyValues.getLength() != aNodeNames.getLength() )
+ {
+ OSL_FAIL( "Problems during reading" );
+ return false;
+ }
+
+ aPropertyValues[0] >>= hasEncoded;
+ aPropertyValues[1] >>= mEncoded;
+
+ aResult = mEncoded;
+
+ return hasEncoded;
+}
+
+
+void StorageItem::setEncodedMP( const OUString& aEncoded, bool bAcceptEmpty )
+{
+ Sequence< OUString > sendNames(2);
+ Sequence< uno::Any > sendVals(2);
+
+ sendNames[0] = "HasMaster";
+ sendNames[1] = "Master";
+
+ bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );
+ sendVals[0] <<= bHasMaster;
+ sendVals[1] <<= aEncoded;
+
+ ConfigItem::SetModified();
+ ConfigItem::PutProperties( sendNames, sendVals );
+
+ hasEncoded = bHasMaster;
+ mEncoded = aEncoded;
+}
+
+
+void StorageItem::remove( const OUString& aURL, const OUString& aName )
+{
+ std::vector < OUString > forIndex;
+ forIndex.push_back( aURL );
+ forIndex.push_back( aName );
+
+ Sequence< OUString > sendSeq(1);
+
+ sendSeq[0] = createIndex( forIndex );
+
+ ConfigItem::ClearNodeElements( "Store", sendSeq );
+}
+
+
+void StorageItem::clear()
+{
+ ConfigItem::ClearNodeSet( "Store" );
+}
+
+
+void StorageItem::update( const OUString& aURL, const NamePassRecord& aRecord )
+{
+ if ( !aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ OSL_FAIL( "Unexpected storing of a record!" );
+ return;
+ }
+
+ std::vector < OUString > forIndex;
+ forIndex.push_back( aURL );
+ forIndex.push_back( aRecord.GetUserName() );
+
+ Sequence< beans::PropertyValue > sendSeq(1);
+
+ sendSeq[0].Name = "Store/Passwordstorage['" + createIndex( forIndex ) + "']/Password";
+
+ sendSeq[0].Value <<= aRecord.GetPersPasswords();
+
+ ConfigItem::SetModified();
+ ConfigItem::SetSetProperties( "Store", sendSeq );
+}
+
+
+void StorageItem::Notify( const Sequence< OUString >& )
+{
+ // this feature still should not be used
+ if( mainCont )
+ mainCont->Notify();
+}
+
+
+void StorageItem::ImplCommit()
+{
+ // Do nothing, we stored everything we want already
+}
+
+
+PasswordContainer::PasswordContainer( const Reference<XMultiServiceFactory>& xServiceFactory )
+{
+ // m_pStorageFile->Notify() can be called
+ ::osl::MutexGuard aGuard( mMutex );
+
+ mComponent.set( xServiceFactory, UNO_QUERY );
+ mComponent->addEventListener( this );
+
+ m_pStorageFile.reset( new StorageItem( this, "Office.Common/Passwords" ) );
+ if( m_pStorageFile->useStorage() )
+ m_aContainer = m_pStorageFile->getInfo();
+}
+
+
+PasswordContainer::~PasswordContainer()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ m_pStorageFile.reset();
+
+ if( mComponent.is() )
+ {
+ mComponent->removeEventListener(this);
+ mComponent.clear();
+ }
+}
+
+void SAL_CALL PasswordContainer::disposing( const EventObject& )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ m_pStorageFile.reset();
+
+ if( mComponent.is() )
+ {
+ //mComponent->removeEventListener(this);
+ mComponent.clear();
+ }
+}
+
+std::vector< OUString > PasswordContainer::DecodePasswords( const OUString& aLine, const OUString& aMasterPasswd, css::task::PasswordRequestMode mode )
+{
+ if( !aMasterPasswd.isEmpty() )
+ {
+ rtlCipher aDecoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
+ OSL_ENSURE( aDecoder, "Can't create decoder" );
+
+ if( aDecoder )
+ {
+ OSL_ENSURE( aMasterPasswd.getLength() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!" );
+
+ unsigned char code[RTL_DIGEST_LENGTH_MD5];
+ for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
+ code[ ind ] = static_cast<char>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16));
+
+ rtlCipherError result = rtl_cipher_init (
+ aDecoder, rtl_Cipher_DirectionDecode,
+ code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 );
+
+ if( result == rtl_Cipher_E_None )
+ {
+ ::rtl::ByteSequence aSeq = getBufFromAsciiLine( aLine );
+
+ ::rtl::ByteSequence resSeq( aSeq.getLength() );
+
+ rtl_cipher_decode ( aDecoder, aSeq.getArray(), aSeq.getLength(),
+ reinterpret_cast<sal_uInt8*>(resSeq.getArray()), resSeq.getLength() );
+
+ OUString aPasswd( reinterpret_cast<char*>(resSeq.getArray()), resSeq.getLength(), RTL_TEXTENCODING_UTF8 );
+
+ rtl_cipher_destroy (aDecoder);
+
+ return getInfoFromInd( aPasswd );
+ }
+
+ rtl_cipher_destroy (aDecoder);
+ }
+ }
+ else
+ {
+ OSL_FAIL( "No master password provided!" );
+ // throw special exception
+ }
+
+ // problems with decoding
+ OSL_FAIL( "Problem with decoding" );
+ throw css::task::NoMasterException(
+ "Can't decode!", css::uno::Reference<css::uno::XInterface>(), mode);
+}
+
+OUString PasswordContainer::EncodePasswords(const std::vector< OUString >& lines, const OUString& aMasterPasswd )
+{
+ if( !aMasterPasswd.isEmpty() )
+ {
+ OString aSeq = OUStringToOString( createIndex( lines ), RTL_TEXTENCODING_UTF8 );
+
+ rtlCipher aEncoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
+ OSL_ENSURE( aEncoder, "Can't create encoder" );
+
+ if( aEncoder )
+ {
+ OSL_ENSURE( aMasterPasswd.getLength() == RTL_DIGEST_LENGTH_MD5 * 2, "Wrong master password format!" );
+
+ unsigned char code[RTL_DIGEST_LENGTH_MD5];
+ for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
+ code[ ind ] = static_cast<char>(aMasterPasswd.copy( ind*2, 2 ).toUInt32(16));
+
+ rtlCipherError result = rtl_cipher_init (
+ aEncoder, rtl_Cipher_DirectionEncode,
+ code, RTL_DIGEST_LENGTH_MD5, nullptr, 0 );
+
+ if( result == rtl_Cipher_E_None )
+ {
+ ::rtl::ByteSequence resSeq(aSeq.getLength()+1);
+
+ result = rtl_cipher_encode ( aEncoder, aSeq.getStr(), aSeq.getLength()+1,
+ reinterpret_cast<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 )
+ {
+ 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 = "";
+ }
+
+ result = rtl_cipher_decode ( aEncoder, (sal_uInt8*)aSeq1.getArray(), aSeq1.getLength(),
+ (sal_uInt8*)resSeq1.getArray(), resSeq1.getLength() );
+
+ OUString aPasswd( ( 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_FAIL( "No master password provided!" );
+ // throw special exception
+ }
+
+ // problems with encoding
+ OSL_FAIL( "Problem with encoding" );
+ throw RuntimeException("Can't encode!" );
+}
+
+void PasswordContainer::UpdateVector( const OUString& aURL, std::vector< NamePassRecord >& toUpdate, NamePassRecord const & aRecord, bool writeFile )
+{
+ for (auto & aNPIter : toUpdate)
+ if( aNPIter.GetUserName() == aRecord.GetUserName() )
+ {
+ if( aRecord.HasPasswords( MEMORY_RECORD ) )
+ aNPIter.SetMemPasswords( aRecord.GetMemPasswords() );
+
+ if( aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ aNPIter.SetPersPasswords( aRecord.GetPersPasswords() );
+
+ if( writeFile )
+ {
+ // the password must be already encoded
+ m_pStorageFile->update( aURL, aRecord ); // change existing ( aURL, aName ) record in the configfile
+ }
+ }
+
+ return;
+ }
+
+
+ if( aRecord.HasPasswords( PERSISTENT_RECORD ) && writeFile )
+ {
+ // the password must be already encoded
+ m_pStorageFile->update( aURL, aRecord ); // add new aName to the existing url
+ }
+
+ toUpdate.insert( toUpdate.begin(), aRecord );
+}
+
+
+UserRecord PasswordContainer::CopyToUserRecord( const NamePassRecord& aRecord, bool& io_bTryToDecode, const Reference< XInteractionHandler >& aHandler )
+{
+ ::std::vector< OUString > aPasswords;
+ if( aRecord.HasPasswords( MEMORY_RECORD ) )
+ aPasswords = aRecord.GetMemPasswords();
+
+ if( io_bTryToDecode && aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ try
+ {
+ ::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersPasswords(), GetMasterPassword( aHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER );
+ aPasswords.insert( aPasswords.end(), aDecodedPasswords.begin(), aDecodedPasswords.end() );
+ }
+ catch( NoMasterException& )
+ {
+ // if master password could not be detected the entry will be just ignored
+ io_bTryToDecode = false;
+ }
+ }
+
+ return UserRecord( aRecord.GetUserName(), comphelper::containerToSequence( aPasswords ) );
+}
+
+
+Sequence< UserRecord > PasswordContainer::CopyToUserRecordSequence( const std::vector< NamePassRecord >& original, const Reference< XInteractionHandler >& aHandler )
+{
+ Sequence< UserRecord > aResult( original.size() );
+ sal_uInt32 nInd = 0;
+ bool bTryToDecode = true;
+
+ for (auto const& aNPIter : original)
+ {
+ aResult[nInd] = CopyToUserRecord( aNPIter, bTryToDecode, aHandler );
+ ++nInd;
+ }
+
+ return aResult;
+}
+
+
+void SAL_CALL PasswordContainer::add( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, const Reference< XInteractionHandler >& aHandler )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ PrivateAdd( Url, UserName, Passwords, MEMORY_RECORD, aHandler );
+}
+
+
+void SAL_CALL PasswordContainer::addPersistent( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, const Reference< XInteractionHandler >& aHandler )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, aHandler );
+}
+
+
+void PasswordContainer::PrivateAdd( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, char Mode, const Reference< XInteractionHandler >& aHandler )
+{
+ NamePassRecord aRecord( UserName );
+ ::std::vector< OUString > aStorePass = comphelper::sequenceToContainer< std::vector<OUString> >( Passwords );
+
+ if( Mode == PERSISTENT_RECORD )
+ aRecord.SetPersPasswords( EncodePasswords( aStorePass, GetMasterPassword( aHandler ) ) );
+ else if( Mode == MEMORY_RECORD )
+ aRecord.SetMemPasswords( aStorePass );
+ else
+ {
+ OSL_FAIL( "Unexpected persistence status!" );
+ return;
+ }
+
+ if( !m_aContainer.empty() )
+ {
+ PassMap::iterator aIter = m_aContainer.find( Url );
+
+ if( aIter != m_aContainer.end() )
+ {
+ UpdateVector( aIter->first, aIter->second, aRecord, true );
+ return;
+ }
+ }
+
+ std::vector< NamePassRecord > listToAdd( 1, aRecord );
+ m_aContainer.insert( PairUrlRecord( Url, listToAdd ) );
+
+ if( Mode == PERSISTENT_RECORD && m_pStorageFile && m_pStorageFile->useStorage() )
+ m_pStorageFile->update( Url, aRecord );
+
+}
+
+
+UrlRecord SAL_CALL PasswordContainer::find( const OUString& aURL, const Reference< XInteractionHandler >& aHandler )
+{
+ return find( aURL, OUString(), false, aHandler );
+}
+
+
+UrlRecord SAL_CALL PasswordContainer::findForName( const OUString& aURL, const OUString& aName, const Reference< XInteractionHandler >& aHandler )
+{
+ return find( aURL, aName, true, aHandler );
+}
+
+
+Sequence< UserRecord > PasswordContainer::FindUsr( const std::vector< NamePassRecord >& userlist, const OUString& aName, const Reference< XInteractionHandler >& aHandler )
+{
+ sal_uInt32 nInd = 0;
+ for (auto const& aNPIter : userlist)
+ {
+ if( aNPIter.GetUserName() == aName )
+ {
+ Sequence< UserRecord > aResult(1);
+ bool bTryToDecode = true;
+ aResult[0] = CopyToUserRecord( aNPIter, bTryToDecode, aHandler );
+
+ return aResult;
+ }
+ ++nInd;
+ }
+
+ return Sequence< UserRecord >();
+}
+
+
+bool PasswordContainer::createUrlRecord(
+ const PassMap::iterator & rIter,
+ bool bName,
+ const OUString & aName,
+ const Reference< XInteractionHandler >& aHandler,
+ UrlRecord & rRec )
+{
+ if ( bName )
+ {
+ Sequence< UserRecord > aUsrRec
+ = FindUsr( rIter->second, aName, aHandler );
+ if( aUsrRec.hasElements() )
+ {
+ rRec = UrlRecord( rIter->first, aUsrRec );
+ return true;
+ }
+ }
+ else
+ {
+ rRec = UrlRecord(
+ rIter->first,
+ CopyToUserRecordSequence( rIter->second, aHandler ) );
+ return true;
+ }
+ return false;
+}
+
+
+UrlRecord PasswordContainer::find(
+ const OUString& aURL,
+ const OUString& aName,
+ bool bName, // only needed to support empty user names
+ const Reference< XInteractionHandler >& aHandler )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if( !m_aContainer.empty() && !aURL.isEmpty() )
+ {
+ OUString aUrl( aURL );
+
+ // each iteration remove last '/...' section from the aUrl
+ // while it's possible, up to the most left '://'
+ do
+ {
+ // first look for <url>/somename and then look for <url>/somename/...
+ PassMap::iterator aIter = m_aContainer.find( aUrl );
+ if( aIter != m_aContainer.end() )
+ {
+ UrlRecord aRec;
+ if ( createUrlRecord( aIter, bName, aName, aHandler, aRec ) )
+ return aRec;
+ }
+ else
+ {
+ OUString tmpUrl( aUrl );
+ if ( !tmpUrl.endsWith("/") )
+ tmpUrl += "/";
+
+ aIter = m_aContainer.lower_bound( tmpUrl );
+ if( aIter != m_aContainer.end() && aIter->first.match( tmpUrl ) )
+ {
+ UrlRecord aRec;
+ if ( createUrlRecord( aIter, bName, aName, aHandler, aRec ) )
+ return aRec;
+ }
+ }
+ }
+ while( shorterUrl( aUrl ) && !aUrl.isEmpty() );
+ }
+
+ return UrlRecord();
+}
+
+OUString PasswordContainer::GetDefaultMasterPassword()
+{
+ OUStringBuffer aResult;
+ for ( sal_Int32 nInd = 0; nInd < RTL_DIGEST_LENGTH_MD5; nInd++ )
+ aResult.append("aa");
+
+ return aResult.makeStringAndClear();
+}
+
+OUString PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode, const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ // empty string means that the call was cancelled or just failed
+ 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;
+}
+
+
+OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler )
+{
+ PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
+ if( !m_pStorageFile || !m_pStorageFile->useStorage() )
+ throw NoMasterException("Password storing is not active!", Reference< XInterface >(), aRMode );
+
+ if( m_aMasterPasswd.isEmpty() && aHandler.is() )
+ {
+ OUString aEncodedMP;
+ bool bDefaultPassword = false;
+
+ if( !m_pStorageFile->getEncodedMP( aEncodedMP ) )
+ aRMode = PasswordRequestMode_PASSWORD_CREATE;
+ else if ( aEncodedMP.isEmpty() )
+ {
+ m_aMasterPasswd = GetDefaultMasterPassword();
+ bDefaultPassword = true;
+ }
+
+ if ( !bDefaultPassword )
+ {
+ bool bAskAgain = false;
+ do {
+ bAskAgain = false;
+
+ OUString aPass = RequestPasswordFromUser( aRMode, aHandler );
+ if ( !aPass.isEmpty() )
+ {
+ if( aRMode == PasswordRequestMode_PASSWORD_CREATE )
+ {
+ m_aMasterPasswd = aPass;
+ std::vector< OUString > aMaster( 1, m_aMasterPasswd );
+
+ m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) );
+ }
+ else
+ {
+ std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aPass, aRMode ) );
+ if( aRM.empty() || aPass != aRM[0] )
+ {
+ bAskAgain = true;
+ aRMode = PasswordRequestMode_PASSWORD_REENTER;
+ }
+ else
+ m_aMasterPasswd = aPass;
+ }
+ }
+
+ } while( bAskAgain );
+ }
+ }
+
+ if ( m_aMasterPasswd.isEmpty() )
+ throw NoMasterException("No master password!", Reference< XInterface >(), aRMode );
+
+ return m_aMasterPasswd;
+}
+
+
+void SAL_CALL PasswordContainer::remove( const OUString& aURL, const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ OUString aUrl( aURL );
+ if( m_aContainer.empty() )
+ return;
+
+ PassMap::iterator aIter = m_aContainer.find( aUrl );
+
+ if( aIter == m_aContainer.end() )
+ {
+ if( aUrl.endsWith("/") )
+ aUrl = aUrl.copy( 0, aUrl.getLength() - 1 );
+ else
+ aUrl += "/";
+
+ aIter = m_aContainer.find( aUrl );
+ }
+
+ if( aIter == m_aContainer.end() )
+ return;
+
+ auto aNPIter = std::find_if(aIter->second.begin(), aIter->second.end(),
+ [&aName](const NamePassRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
+
+ if (aNPIter != aIter->second.end())
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) && m_pStorageFile )
+ m_pStorageFile->remove( aURL, aName ); // remove record ( aURL, aName )
+
+ // the iterator will not be used any more so it can be removed directly
+ aIter->second.erase( aNPIter );
+
+ if( aIter->second.empty() )
+ m_aContainer.erase( aIter );
+ }
+}
+
+
+void SAL_CALL PasswordContainer::removePersistent( const OUString& aURL, const OUString& aName )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ OUString aUrl( aURL );
+ if( m_aContainer.empty() )
+ return;
+
+ PassMap::iterator aIter = m_aContainer.find( aUrl );
+
+ if( aIter == m_aContainer.end() )
+ {
+ if( aUrl.endsWith("/") )
+ aUrl = aUrl.copy( 0, aUrl.getLength() - 1 );
+ else
+ aUrl += "/";
+
+ aIter = m_aContainer.find( aUrl );
+ }
+
+ if( aIter == m_aContainer.end() )
+ return;
+
+ auto aNPIter = std::find_if(aIter->second.begin(), aIter->second.end(),
+ [&aName](const NamePassRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
+
+ if (aNPIter != aIter->second.end())
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
+ {
+ // TODO/LATER: should the password be converted to MemoryPassword?
+ aNPIter->RemovePasswords( PERSISTENT_RECORD );
+
+ if ( m_pStorageFile )
+ m_pStorageFile->remove( aURL, aName ); // remove record ( aURL, aName )
+ }
+
+ if( !aNPIter->HasPasswords( MEMORY_RECORD ) )
+ aIter->second.erase( aNPIter );
+
+ if( aIter->second.empty() )
+ m_aContainer.erase( aIter );
+ }
+}
+
+void SAL_CALL PasswordContainer::removeAllPersistent()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if( m_pStorageFile )
+ m_pStorageFile->clear();
+
+ for( PassMap::iterator aIter = m_aContainer.begin(); aIter != m_aContainer.end(); )
+ {
+ for( std::vector< NamePassRecord >::iterator aNPIter = aIter->second.begin(); aNPIter != aIter->second.end(); )
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
+ {
+ // TODO/LATER: should the password be converted to MemoryPassword?
+ aNPIter->RemovePasswords( PERSISTENT_RECORD );
+
+ if ( m_pStorageFile )
+ m_pStorageFile->remove( aIter->first, aNPIter->GetUserName() ); // remove record ( aURL, aName )
+ }
+
+ if( !aNPIter->HasPasswords( MEMORY_RECORD ) )
+ {
+ aNPIter = aIter->second.erase(aNPIter);
+ }
+ else
+ ++aNPIter;
+ }
+
+ if( aIter->second.empty() )
+ {
+ aIter = m_aContainer.erase(aIter);
+ }
+ else
+ ++aIter;
+ }
+}
+
+Sequence< UrlRecord > SAL_CALL PasswordContainer::getAllPersistent( const Reference< XInteractionHandler >& xHandler )
+{
+ Sequence< UrlRecord > aResult;
+
+ ::osl::MutexGuard aGuard( mMutex );
+ for( const auto& rEntry : m_aContainer )
+ {
+ Sequence< UserRecord > aUsers;
+ for (auto const& aNP : rEntry.second)
+ if( aNP.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ sal_Int32 oldLen = aUsers.getLength();
+ aUsers.realloc( oldLen + 1 );
+ aUsers[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersPasswords(), GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) );
+ }
+
+ if( aUsers.hasElements() )
+ {
+ sal_Int32 oldLen = aResult.getLength();
+ aResult.realloc( oldLen + 1 );
+ aResult[ oldLen ] = UrlRecord( rEntry.first, aUsers );
+ }
+ }
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ bool bResult = false;
+ 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 ( aEncodedMP.isEmpty() )
+ {
+ // this is a default master password
+ // no UI is necessary
+ bResult = true;
+ }
+ else
+ {
+ if ( !xTmpHandler.is() )
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory( mComponent, uno::UNO_QUERY_THROW );
+ uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
+ xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+ }
+
+ if ( !m_aMasterPasswd.isEmpty() )
+ {
+ // there is a password, it should be just rechecked
+ PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
+ OUString aPass;
+
+ do {
+ aPass = RequestPasswordFromUser( aRMode, xTmpHandler );
+ bResult = ( !aPass.isEmpty() && aPass == m_aMasterPasswd );
+ aRMode = PasswordRequestMode_PASSWORD_REENTER; // further questions with error notification
+ } while( !bResult && !aPass.isEmpty() );
+ }
+ else
+ {
+ try
+ {
+ // ask for the password, if user provide no correct password an exception will be thrown
+ bResult = !GetMasterPassword( xTmpHandler ).isEmpty();
+ }
+ catch( uno::Exception& )
+ {}
+ }
+ }
+ }
+
+ return bResult;
+}
+
+sal_Bool SAL_CALL PasswordContainer::changeMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ bool bResult = 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 );
+ uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
+ xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+ }
+
+ bool bCanChangePassword = true;
+ // if there is already a stored master password it should be entered by the user before the change happen
+ OUString aEncodedMP;
+ if( !m_aMasterPasswd.isEmpty() || m_pStorageFile->getEncodedMP( aEncodedMP ) )
+ bCanChangePassword = authorizateWithMasterPassword( xTmpHandler );
+
+ if ( bCanChangePassword )
+ {
+ // ask for the new password, but do not set it
+ OUString aPass = RequestPasswordFromUser( PasswordRequestMode_PASSWORD_CREATE, xTmpHandler );
+
+ if ( !aPass.isEmpty() )
+ {
+ // get all the persistent entries if it is possible
+ const 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;
+ std::vector< OUString > aMaster( 1, m_aMasterPasswd );
+ m_pStorageFile->setEncodedMP( EncodePasswords( aMaster, m_aMasterPasswd ) );
+
+ // store all the entries with the new password
+ for ( const auto& rURL : aPersistent )
+ for ( const auto& rUser : rURL.UserList )
+ addPersistent( rURL.Url, rUser.UserName, rUser.Passwords,
+ uno::Reference< task::XInteractionHandler >() );
+
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SAL_CALL PasswordContainer::removeMasterPassword()
+{
+ // remove all the stored passwords and the master password
+ removeAllPersistent();
+
+ ::osl::MutexGuard aGuard( mMutex );
+ if ( m_pStorageFile )
+ {
+ m_aMasterPasswd.clear();
+ m_pStorageFile->setEncodedMP( OUString() ); // let the master password be removed from configuration
+ }
+}
+
+sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if ( !m_pStorageFile )
+ throw uno::RuntimeException();
+
+ OUString aEncodedMP;
+ return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) );
+}
+
+sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( sal_Bool bAllow )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if ( !m_pStorageFile )
+ throw uno::RuntimeException();
+
+ if ( !bAllow )
+ removeMasterPassword();
+
+ if (m_pStorageFile->useStorage() == static_cast<bool>(bAllow))
+ return bAllow;
+
+ m_pStorageFile->setUseStorage( bAllow );
+ return !bAllow;
+}
+
+sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if ( !m_pStorageFile )
+ throw uno::RuntimeException();
+
+ return m_pStorageFile->useStorage();
+}
+
+sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ bool bResult = 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 );
+ uno::Reference< uno::XComponentContext > xContext( comphelper::getComponentContext(xFactory) );
+ xTmpHandler.set( InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
+ }
+
+ bool bCanChangePassword = true;
+ // if there is already a stored nondefault master password it should be entered by the user before the change happen
+ OUString aEncodedMP;
+ if( m_pStorageFile->getEncodedMP( aEncodedMP ) && !aEncodedMP.isEmpty() )
+ bCanChangePassword = authorizateWithMasterPassword( xTmpHandler );
+
+ if ( bCanChangePassword )
+ {
+ // generate the default password
+ OUString aPass = GetDefaultMasterPassword();
+ if ( !aPass.isEmpty() )
+ {
+ // get all the persistent entries if it is possible
+ const Sequence< UrlRecord > aPersistent = getAllPersistent( uno::Reference< task::XInteractionHandler >() );
+
+ // remove the master password and the entries persistence
+ removeMasterPassword();
+
+ // store the empty string to flag the default master password
+ m_aMasterPasswd = aPass;
+ m_pStorageFile->setEncodedMP( OUString(), true );
+
+ // store all the entries with the new password
+ for ( const auto& rURL : aPersistent )
+ for ( const auto& rUser : rURL.UserList )
+ addPersistent( rURL.Url, rUser.UserName, rUser.Passwords,
+ uno::Reference< task::XInteractionHandler >() );
+
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+
+}
+
+sal_Bool SAL_CALL PasswordContainer::isDefaultMasterPasswordUsed()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if ( !m_pStorageFile )
+ throw uno::RuntimeException();
+
+ OUString aEncodedMP;
+ return ( m_pStorageFile->useStorage() && m_pStorageFile->getEncodedMP( aEncodedMP ) && aEncodedMP.isEmpty() );
+}
+
+
+void SAL_CALL PasswordContainer::addUrl( const OUString& Url, sal_Bool MakePersistent )
+{
+ mUrlContainer.add( Url, MakePersistent );
+}
+
+OUString SAL_CALL PasswordContainer::findUrl( const OUString& Url )
+{
+ return mUrlContainer.find( Url );
+}
+
+void SAL_CALL PasswordContainer::removeUrl( const OUString& Url )
+{
+ mUrlContainer.remove( Url );
+}
+
+uno::Sequence< OUString > SAL_CALL PasswordContainer::getUrls( sal_Bool OnlyPersistent )
+{
+ return mUrlContainer.list( OnlyPersistent );
+}
+
+
+void PasswordContainer::Notify()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ // remove the cached persistent values in the memory
+ for( auto& rEntry : m_aContainer )
+ {
+ for( std::vector< NamePassRecord >::iterator aNPIter = rEntry.second.begin(); aNPIter != rEntry.second.end(); )
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
+ {
+ aNPIter->RemovePasswords( PERSISTENT_RECORD );
+
+ if ( m_pStorageFile )
+ m_pStorageFile->remove( rEntry.first, aNPIter->GetUserName() ); // remove record ( aURL, aName )
+ }
+
+ if( !aNPIter->HasPasswords( MEMORY_RECORD ) )
+ {
+ aNPIter = rEntry.second.erase(aNPIter);
+ }
+ else
+ ++aNPIter;
+ }
+ }
+
+ PassMap addon;
+ if( m_pStorageFile )
+ addon = m_pStorageFile->getInfo();
+
+ for( const auto& rEntry : addon )
+ {
+ PassMap::iterator aSearchIter = m_aContainer.find( rEntry.first );
+ if( aSearchIter != m_aContainer.end() )
+ for (auto const& aNP : rEntry.second)
+ UpdateVector( aSearchIter->first, aSearchIter->second, aNP, false );
+ else
+ m_aContainer.insert( PairUrlRecord( rEntry.first, rEntry.second ) );
+ }
+}
+
+OUString SAL_CALL PasswordContainer::getImplementationName( )
+{
+ return impl_getStaticImplementationName();
+}
+
+sal_Bool SAL_CALL PasswordContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL PasswordContainer::getSupportedServiceNames( )
+{
+ return impl_getStaticSupportedServiceNames();
+}
+
+Sequence< OUString > PasswordContainer::impl_getStaticSupportedServiceNames( )
+{
+ return { "com.sun.star.task.PasswordContainer" };
+}
+
+OUString PasswordContainer::impl_getStaticImplementationName()
+{
+ return "stardiv.svl.PasswordContainer";
+}
+
+Reference< XInterface > SAL_CALL PasswordContainer::impl_createInstance( const Reference< XMultiServiceFactory >& xServiceManager )
+{
+ return Reference< XInterface >( *new PasswordContainer( xServiceManager ) );
+}
+
+Reference< XSingleServiceFactory > PasswordContainer::impl_createFactory( const Reference< XMultiServiceFactory >& ServiceManager )
+{
+ 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,
+ false, // bCanSetRealm
+ false, // bCanSetUserName
+ true, // bCanSetPassword
+ false, // bCanSetAccount
+ aRememberModes, // rRememberPasswordModes
+ RememberAuthentication_NO, // eDefaultRememberPasswordMode
+ aRememberModes, // rRememberAccountModes
+ RememberAuthentication_NO, // eDefaultRememberAccountMode
+ false // bCanUseSystemCredentials
+ );
+
+ 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 * passwordcontainer_component_getFactory (
+ const char * pImplementationName,
+ SAL_UNUSED_PARAMETER void * pServiceManager,
+ SAL_UNUSED_PARAMETER void * /* pRegistryKey */)
+{
+ void * pResult = nullptr;
+ if (pServiceManager)
+ {
+ Reference< XSingleServiceFactory > xFactory;
+ if (PasswordContainer::impl_getStaticImplementationName().equalsAscii(pImplementationName))
+ {
+ xFactory = PasswordContainer::impl_createFactory (
+ static_cast< XMultiServiceFactory* >(pServiceManager));
+ }
+ if (xFactory.is())
+ {
+ xFactory->acquire();
+ pResult = xFactory.get();
+ }
+ }
+ return pResult;
+}
+
+} // extern "C"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */