summaryrefslogtreecommitdiffstats
path: root/svl/source/passwordcontainer/passwordcontainer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'svl/source/passwordcontainer/passwordcontainer.cxx')
-rw-r--r--svl/source/passwordcontainer/passwordcontainer.cxx1442
1 files changed, 1442 insertions, 0 deletions
diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx
new file mode 100644
index 0000000000..333e2921b4
--- /dev/null
+++ b/svl/source/passwordcontainer/passwordcontainer.cxx
@@ -0,0 +1,1442 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <string_view>
+
+#include "passwordcontainer.hxx"
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <o3tl/string_view.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 <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("_" + OUString::number(*pLine, 16) );
+ }
+
+ pLine++;
+ }
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+
+static std::vector< OUString > getInfoFromInd( std::u16string_view 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( std::u16string_view line )
+{
+ OSL_ENSURE( line.size() % 2 == 0, "Wrong syntax!" );
+ OString tmpLine = OUStringToOString( line, RTL_TEXTENCODING_ASCII_US );
+ ::rtl::ByteSequence aResult(line.size()/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;
+}
+
+
+PasswordMap StorageItem::getInfo()
+{
+ PasswordMap aResult;
+
+ const Sequence< OUString > aNodeNames = ConfigItem::GetNodeNames( "Store" );
+ sal_Int32 aNodeCount = aNodeNames.getLength();
+ Sequence< OUString > aPropNames( aNodeCount * 2);
+
+ std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.getArray(),
+ [](const OUString& rName) -> OUString {
+ return "Store/Passwordstorage['" + rName + "']/Password"; });
+ std::transform(aNodeNames.begin(), aNodeNames.end(), aPropNames.getArray() + aNodeCount,
+ [](const OUString& rName) -> OUString {
+ return "Store/Passwordstorage['" + rName + "']/InitializationVector"; });
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aPropNames );
+
+ if( aPropertyValues.getLength() != aNodeCount * 2)
+ {
+ 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;
+ OUString aIV;
+ aPropertyValues[aNodeInd] >>= aEPasswd;
+ aPropertyValues[aNodeInd + aNodeCount] >>= aIV;
+
+ PasswordMap::iterator aIter = aResult.find( aUrl );
+ if( aIter != aResult.end() )
+ aIter->second.emplace_back( aName, aEPasswd, aIV );
+ else
+ {
+ NamePasswordRecord aNewRecord( aName, aEPasswd, aIV );
+ std::vector< NamePasswordRecord > listToAdd( 1, aNewRecord );
+
+ aResult.insert( PairUrlRecord( aUrl, listToAdd ) );
+ }
+ }
+ else
+ OSL_FAIL( "Wrong index syntax!" );
+ }
+
+ return aResult;
+}
+
+
+void StorageItem::setUseStorage( bool bUse )
+{
+ ConfigItem::SetModified();
+ ConfigItem::PutProperties( { "UseStorage" }, { uno::Any(bUse) } );
+}
+
+
+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;
+}
+
+
+sal_Int32 StorageItem::getStorageVersion()
+{
+ Sequence<OUString> aNodeNames { "StorageVersion" };
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
+
+ if( aPropertyValues.getLength() != aNodeNames.getLength() )
+ {
+ OSL_FAIL( "Problems during reading" );
+ return 0;
+ }
+
+ sal_Int32 nResult = 0;
+ aPropertyValues[0] >>= nResult;
+
+ return nResult;
+}
+
+bool StorageItem::getEncodedMasterPassword( OUString& aResult, OUString& aResultIV )
+{
+ if( hasEncoded )
+ {
+ aResult = mEncoded;
+ aResultIV = mEncodedIV;
+ return true;
+ }
+
+ Sequence< OUString > aNodeNames{ "HasMaster", "Master", "MasterInitializationVector" };
+
+ Sequence< Any > aPropertyValues = ConfigItem::GetProperties( aNodeNames );
+
+ if( aPropertyValues.getLength() != aNodeNames.getLength() )
+ {
+ OSL_FAIL( "Problems during reading" );
+ return false;
+ }
+
+ aPropertyValues[0] >>= hasEncoded;
+ aPropertyValues[1] >>= mEncoded;
+ aPropertyValues[2] >>= mEncodedIV;
+
+ aResult = mEncoded;
+ aResultIV = mEncodedIV;
+
+ return hasEncoded;
+}
+
+
+void StorageItem::setEncodedMasterPassword( const OUString& aEncoded, const OUString& aEncodedIV, bool bAcceptEmpty )
+{
+ bool bHasMaster = ( !aEncoded.isEmpty() || bAcceptEmpty );
+
+ ConfigItem::SetModified();
+ ConfigItem::PutProperties( { "HasMaster", "Master", "MasterInitializationVector", "StorageVersion" },
+ { uno::Any(bHasMaster), uno::Any(aEncoded),
+ uno::Any(aEncodedIV), uno::Any(nCurrentStorageVersion) } );
+
+ hasEncoded = bHasMaster;
+ mEncoded = aEncoded;
+ mEncodedIV = aEncodedIV;
+}
+
+
+void StorageItem::remove( const OUString& aURL, const OUString& aName )
+{
+ Sequence< OUString > sendSeq { createIndex( { aURL, aName } ) };
+
+ ConfigItem::ClearNodeElements( "Store", sendSeq );
+}
+
+
+void StorageItem::clear()
+{
+ ConfigItem::ClearNodeSet( "Store" );
+}
+
+
+void StorageItem::update( const OUString& aURL, const NamePasswordRecord& aRecord )
+{
+ if ( !aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ OSL_FAIL( "Unexpected storing of a record!" );
+ return;
+ }
+
+ Sequence< beans::PropertyValue > sendSeq{ comphelper::makePropertyValue(
+ "Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/InitializationVector",
+ aRecord.GetPersistentIV()), comphelper::makePropertyValue(
+ "Store/Passwordstorage['" + createIndex( { aURL, aRecord.GetUserName() } ) + "']/Password",
+ aRecord.GetPersistentPasswords()) };
+
+ 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<XComponentContext>& rxContext )
+{
+ // m_pStorageFile->Notify() can be called
+ std::unique_lock aGuard( mMutex );
+
+ mComponent.set( rxContext->getServiceManager(), UNO_QUERY );
+ mComponent->addEventListener( this );
+
+ m_xStorageFile.emplace( this, "Office.Common/Passwords" );
+ if( m_xStorageFile->useStorage() )
+ m_aContainer = m_xStorageFile->getInfo();
+}
+
+
+PasswordContainer::~PasswordContainer()
+{
+ std::unique_lock aGuard( mMutex );
+
+ m_xStorageFile.reset();
+
+ if( mComponent.is() )
+ {
+ mComponent->removeEventListener(this);
+ mComponent.clear();
+ }
+}
+
+void SAL_CALL PasswordContainer::disposing( const EventObject& )
+{
+ std::unique_lock aGuard( mMutex );
+
+ m_xStorageFile.reset();
+
+ if( mComponent.is() )
+ {
+ //mComponent->removeEventListener(this);
+ mComponent.clear();
+ }
+}
+
+std::vector< OUString > PasswordContainer::DecodePasswords( std::u16string_view aLine, std::u16string_view aIV, std::u16string_view aMasterPasswd, css::task::PasswordRequestMode mode )
+{
+ if( !aMasterPasswd.empty() )
+ {
+ rtlCipher aDecoder = rtl_cipher_create (rtl_Cipher_AlgorithmBF, rtl_Cipher_ModeStream );
+ OSL_ENSURE( aDecoder, "Can't create decoder" );
+
+ if( aDecoder )
+ {
+ OSL_ENSURE( aMasterPasswd.size() == 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>(o3tl::toUInt32(aMasterPasswd.substr( ind*2, 2 ), 16));
+
+ unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0};
+ if (!aIV.empty())
+ {
+ for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
+ {
+ auto tmp = aIV.substr( ind*2, 2 );
+ iv[ ind ] = static_cast<char>(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
+ }
+ }
+
+ rtlCipherError result = rtl_cipher_init (
+ aDecoder, rtl_Cipher_DirectionDecode,
+ code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 );
+
+ 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, std::u16string_view aIV, std::u16string_view aMasterPasswd)
+{
+ if( !aMasterPasswd.empty() )
+ {
+ 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.size() == 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>(o3tl::toUInt32(aMasterPasswd.substr( ind*2, 2 ), 16));
+
+ unsigned char iv[RTL_DIGEST_LENGTH_MD5] = {0};
+ if (!aIV.empty())
+ {
+ for( int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ind++ )
+ {
+ auto tmp = aIV.substr( ind*2, 2 );
+ iv[ ind ] = static_cast<char>(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
+ }
+ }
+
+ rtlCipherError result = rtl_cipher_init (
+ aEncoder, rtl_Cipher_DirectionEncode,
+ code, RTL_DIGEST_LENGTH_MD5, iv, RTL_DIGEST_LENGTH_MD5 );
+
+ 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< NamePasswordRecord >& toUpdate, NamePasswordRecord const & aRecord, bool writeFile )
+{
+ for (auto & aNPIter : toUpdate)
+ if( aNPIter.GetUserName() == aRecord.GetUserName() )
+ {
+ if( aRecord.HasPasswords( MEMORY_RECORD ) )
+ aNPIter.SetMemoryPasswords( aRecord.GetMemoryPasswords() );
+
+ if( aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ aNPIter.SetPersistentPasswords( aRecord.GetPersistentPasswords(), aRecord.GetPersistentIV() );
+
+ if( writeFile )
+ {
+ // the password must be already encoded
+ m_xStorageFile->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_xStorageFile->update( aURL, aRecord ); // add new aName to the existing url
+ }
+
+ toUpdate.insert( toUpdate.begin(), aRecord );
+}
+
+
+UserRecord PasswordContainer::CopyToUserRecord( const NamePasswordRecord& aRecord, bool& io_bTryToDecode, const Reference< XInteractionHandler >& aHandler )
+{
+ ::std::vector< OUString > aPasswords;
+ if( aRecord.HasPasswords( MEMORY_RECORD ) )
+ aPasswords = aRecord.GetMemoryPasswords();
+
+ if( io_bTryToDecode && aRecord.HasPasswords( PERSISTENT_RECORD ) )
+ {
+ try
+ {
+ ::std::vector< OUString > aDecodedPasswords = DecodePasswords( aRecord.GetPersistentPasswords(), aRecord.GetPersistentIV(),
+ 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< NamePasswordRecord >& original, const Reference< XInteractionHandler >& aHandler )
+{
+ Sequence< UserRecord > aResult( original.size() );
+ auto aResultRange = asNonConstRange(aResult);
+ sal_uInt32 nInd = 0;
+ bool bTryToDecode = true;
+
+ for (auto const& aNPIter : original)
+ {
+ aResultRange[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 )
+{
+ std::unique_lock 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 )
+{
+ std::unique_lock aGuard( mMutex );
+
+ PrivateAdd( Url, UserName, Passwords, PERSISTENT_RECORD, aHandler );
+}
+
+OUString PasswordContainer::createIV()
+{
+ rtlRandomPool randomPool = mRandomPool.get();
+ unsigned char iv[RTL_DIGEST_LENGTH_MD5];
+ rtl_random_getBytes(randomPool, iv, RTL_DIGEST_LENGTH_MD5);
+ OUStringBuffer aBuffer;
+ for (sal_uInt8 i : iv)
+ {
+ aBuffer.append(OUString::number(i >> 4, 16) + OUString::number(i & 15, 16));
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+void PasswordContainer::PrivateAdd( const OUString& Url, const OUString& UserName, const Sequence< OUString >& Passwords, char Mode, const Reference< XInteractionHandler >& aHandler )
+{
+ NamePasswordRecord aRecord( UserName );
+ ::std::vector< OUString > aStorePass = comphelper::sequenceToContainer< std::vector<OUString> >( Passwords );
+
+ if( Mode == PERSISTENT_RECORD )
+ {
+ OUString sIV = createIV();
+ OUString sEncodedPasswords = EncodePasswords(aStorePass, sIV, GetMasterPassword(aHandler));
+ aRecord.SetPersistentPasswords(sEncodedPasswords, sIV);
+ }
+ else if( Mode == MEMORY_RECORD )
+ aRecord.SetMemoryPasswords( std::move(aStorePass) );
+ else
+ {
+ OSL_FAIL( "Unexpected persistence status!" );
+ return;
+ }
+
+ if( !m_aContainer.empty() )
+ {
+ PasswordMap::iterator aIter = m_aContainer.find( Url );
+
+ if( aIter != m_aContainer.end() )
+ {
+ UpdateVector( aIter->first, aIter->second, aRecord, true );
+ return;
+ }
+ }
+
+ std::vector< NamePasswordRecord > listToAdd( 1, aRecord );
+ m_aContainer.insert( PairUrlRecord( Url, listToAdd ) );
+
+ if( Mode == PERSISTENT_RECORD && m_xStorageFile && m_xStorageFile->useStorage() )
+ m_xStorageFile->update( Url, aRecord );
+
+}
+
+
+UrlRecord SAL_CALL PasswordContainer::find( const OUString& aURL, const Reference< XInteractionHandler >& aHandler )
+{
+ return find( aURL, u"", 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< NamePasswordRecord >& userlist, std::u16string_view aName, const Reference< XInteractionHandler >& aHandler )
+{
+ for (auto const& aNPIter : userlist)
+ {
+ if( aNPIter.GetUserName() == aName )
+ {
+ bool bTryToDecode = true;
+ Sequence< UserRecord > aResult { CopyToUserRecord( aNPIter, bTryToDecode, aHandler ) };
+
+ return aResult;
+ }
+ }
+
+ return Sequence< UserRecord >();
+}
+
+
+bool PasswordContainer::createUrlRecord(
+ const PasswordMap::iterator & rIter,
+ bool bName,
+ std::u16string_view 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,
+ std::u16string_view aName,
+ bool bName, // only needed to support empty user names
+ const Reference< XInteractionHandler >& aHandler )
+{
+ std::unique_lock 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/...
+ PasswordMap::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 );
+
+ ::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;
+}
+
+// Mangle the key to match an old bug
+static OUString ReencodeAsOldHash(std::u16string_view rPass)
+{
+ OUStringBuffer aBuffer;
+ for (int ind = 0; ind < RTL_DIGEST_LENGTH_MD5; ++ind)
+ {
+ auto tmp = rPass.substr(ind * 2, 2);
+ unsigned char i = static_cast<char>(rtl_ustr_toInt64_WithLength(tmp.data(), 16, tmp.size()));
+ aBuffer.append(OUStringChar(static_cast< sal_Unicode >('a' + (i >> 4)))
+ + OUStringChar(static_cast< sal_Unicode >('a' + (i & 15))));
+ }
+ return aBuffer.makeStringAndClear();
+}
+
+OUString const & PasswordContainer::GetMasterPassword( const Reference< XInteractionHandler >& aHandler )
+{
+ PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
+ if( !m_xStorageFile || !m_xStorageFile->useStorage() )
+ throw NoMasterException("Password storing is not active!", Reference< XInterface >(), aRMode );
+
+ if( m_aMasterPassword.isEmpty() && aHandler.is() )
+ {
+ OUString aEncodedMP, aEncodedMPIV;
+ bool bDefaultPassword = false;
+
+ if( !m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
+ aRMode = PasswordRequestMode_PASSWORD_CREATE;
+ else if ( aEncodedMP.isEmpty() )
+ {
+ m_aMasterPassword = 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_aMasterPassword = aPass;
+ std::vector< OUString > aMaster( 1, m_aMasterPassword );
+
+ OUString sIV = createIV();
+ m_xStorageFile->setEncodedMasterPassword(EncodePasswords(aMaster, sIV, m_aMasterPassword), sIV);
+ }
+ else
+ {
+ if (m_xStorageFile->getStorageVersion() == 0)
+ aPass = ReencodeAsOldHash(aPass);
+
+ std::vector< OUString > aRM( DecodePasswords( aEncodedMP, aEncodedMPIV, aPass, aRMode ) );
+ if( aRM.empty() || aPass != aRM[0] )
+ {
+ bAskAgain = true;
+ aRMode = PasswordRequestMode_PASSWORD_REENTER;
+ }
+ else
+ m_aMasterPassword = aPass;
+ }
+ }
+
+ } while( bAskAgain );
+ }
+ }
+
+ if ( m_aMasterPassword.isEmpty() )
+ throw NoMasterException("No master password!", Reference< XInterface >(), aRMode );
+
+ return m_aMasterPassword;
+}
+
+
+void SAL_CALL PasswordContainer::remove( const OUString& aURL, const OUString& aName )
+{
+ std::unique_lock aGuard( mMutex );
+
+ OUString aUrl( aURL );
+ if( m_aContainer.empty() )
+ return;
+
+ PasswordMap::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 NamePasswordRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
+
+ if (aNPIter != aIter->second.end())
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) && m_xStorageFile )
+ m_xStorageFile->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 )
+{
+ std::unique_lock aGuard( mMutex );
+
+ OUString aUrl( aURL );
+ if( m_aContainer.empty() )
+ return;
+
+ PasswordMap::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 NamePasswordRecord& rNPRecord) { return rNPRecord.GetUserName() == aName; });
+
+ if (aNPIter == aIter->second.end())
+ return;
+
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
+ {
+ // TODO/LATER: should the password be converted to MemoryPassword?
+ aNPIter->RemovePasswords( PERSISTENT_RECORD );
+
+ if ( m_xStorageFile )
+ m_xStorageFile->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()
+{
+ std::unique_lock aGuard(mMutex);
+ removeAllPersistent(aGuard);
+}
+
+void PasswordContainer::removeAllPersistent(std::unique_lock<std::mutex>& /*rGuard*/)
+{
+ if( m_xStorageFile )
+ m_xStorageFile->clear();
+
+ for( PasswordMap::iterator aIter = m_aContainer.begin(); aIter != m_aContainer.end(); )
+ {
+ for( std::vector< NamePasswordRecord >::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_xStorageFile )
+ m_xStorageFile->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 )
+{
+ std::unique_lock aGuard( mMutex );
+ return getAllPersistent(aGuard, xHandler);
+}
+
+Sequence< UrlRecord > PasswordContainer::getAllPersistent( std::unique_lock<std::mutex>& /*rGuard*/, const Reference< XInteractionHandler >& xHandler )
+{
+ Sequence< UrlRecord > aResult;
+
+ 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.getArray()[ oldLen ] = UserRecord( aNP.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP.GetPersistentPasswords(), aNP.GetPersistentIV(),
+ GetMasterPassword( xHandler ), css::task::PasswordRequestMode_PASSWORD_ENTER ) ) );
+ }
+
+ if( aUsers.hasElements() )
+ {
+ sal_Int32 oldLen = aResult.getLength();
+ aResult.realloc( oldLen + 1 );
+ aResult.getArray()[ oldLen ] = UrlRecord( rEntry.first, aUsers );
+ }
+ }
+
+ return aResult;
+}
+
+sal_Bool SAL_CALL PasswordContainer::authorizateWithMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ std::unique_lock aGuard( mMutex );
+ return authorizateWithMasterPassword(aGuard, xHandler);
+}
+
+bool PasswordContainer::authorizateWithMasterPassword( std::unique_lock<std::mutex>& /*rGuard*/, const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ bool bResult = false;
+ OUString aEncodedMP, aEncodedMPIV;
+ uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
+
+ // the method should fail if there is no master password
+ if( m_xStorageFile && m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
+ {
+ 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_aMasterPassword.isEmpty() )
+ {
+ // there is a password, it should be just rechecked
+ PasswordRequestMode aRMode = PasswordRequestMode_PASSWORD_ENTER;
+ OUString aPass;
+
+ do {
+ aPass = RequestPasswordFromUser( aRMode, xTmpHandler );
+
+ if (!aPass.isEmpty() && m_xStorageFile->getStorageVersion() == 0)
+ {
+ aPass = ReencodeAsOldHash(aPass);
+ }
+
+ bResult = ( !aPass.isEmpty() && aPass == m_aMasterPassword );
+ 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;
+ std::unique_lock aGuard( mMutex );
+
+ if ( m_xStorageFile && m_xStorageFile->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, aEncodedMPIV;
+ if( !m_aMasterPassword.isEmpty() || m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) )
+ bCanChangePassword = authorizateWithMasterPassword( aGuard, 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( aGuard, uno::Reference< task::XInteractionHandler >() );
+
+ // remove the master password and the entries persistence
+ removeMasterPassword(aGuard);
+
+ // store the new master password
+ m_aMasterPassword = aPass;
+ std::vector< OUString > aMaster( 1, m_aMasterPassword );
+ OUString aIV = createIV();
+ m_xStorageFile->setEncodedMasterPassword(EncodePasswords(aMaster, aIV, m_aMasterPassword), aIV);
+
+ // store all the entries with the new password
+ for ( const auto& rURL : aPersistent )
+ for ( const auto& rUser : rURL.UserList )
+ PrivateAdd( rURL.Url, rUser.UserName, rUser.Passwords, PERSISTENT_RECORD,
+ uno::Reference< task::XInteractionHandler >() );
+
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SAL_CALL PasswordContainer::removeMasterPassword()
+{
+ std::unique_lock aGuard(mMutex);
+ removeMasterPassword(aGuard);
+}
+
+void PasswordContainer::removeMasterPassword(std::unique_lock<std::mutex>& rGuard)
+{
+ // remove all the stored passwords and the master password
+ removeAllPersistent(rGuard);
+
+ if ( m_xStorageFile )
+ {
+ m_aMasterPassword.clear();
+ m_xStorageFile->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
+ }
+}
+
+sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( )
+{
+ std::unique_lock aGuard( mMutex );
+
+ if ( !m_xStorageFile )
+ throw uno::RuntimeException();
+
+ OUString aEncodedMP, aEncodedMPIV;
+ return ( m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) );
+}
+
+sal_Bool SAL_CALL PasswordContainer::allowPersistentStoring( sal_Bool bAllow )
+{
+ std::unique_lock aGuard( mMutex );
+
+ if ( !m_xStorageFile )
+ throw uno::RuntimeException();
+
+ if ( !bAllow )
+ removeMasterPassword(aGuard);
+
+ if (m_xStorageFile->useStorage() == static_cast<bool>(bAllow))
+ return bAllow;
+
+ m_xStorageFile->setUseStorage( bAllow );
+ return !bAllow;
+}
+
+sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed()
+{
+ std::unique_lock aGuard( mMutex );
+
+ if ( !m_xStorageFile )
+ throw uno::RuntimeException();
+
+ return m_xStorageFile->useStorage();
+}
+
+sal_Bool SAL_CALL PasswordContainer::useDefaultMasterPassword( const uno::Reference< task::XInteractionHandler >& xHandler )
+{
+ bool bResult = false;
+ uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
+ std::unique_lock aGuard( mMutex );
+
+ if ( m_xStorageFile && m_xStorageFile->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, aEncodedMPIV;
+ if( m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) && !aEncodedMP.isEmpty() )
+ bCanChangePassword = authorizateWithMasterPassword( aGuard, 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( aGuard, uno::Reference< task::XInteractionHandler >() );
+
+ // remove the master password and the entries persistence
+ removeMasterPassword(aGuard);
+
+ // store the empty string to flag the default master password
+ m_aMasterPassword = aPass;
+ m_xStorageFile->setEncodedMasterPassword( OUString(), OUString(), true );
+
+ // store all the entries with the new password
+ for ( const auto& rURL : aPersistent )
+ for ( const auto& rUser : rURL.UserList )
+ PrivateAdd( rURL.Url, rUser.UserName, rUser.Passwords, PERSISTENT_RECORD,
+ uno::Reference< task::XInteractionHandler >() );
+
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+
+}
+
+sal_Bool SAL_CALL PasswordContainer::isDefaultMasterPasswordUsed()
+{
+ std::unique_lock aGuard( mMutex );
+
+ if ( !m_xStorageFile )
+ throw uno::RuntimeException();
+
+ OUString aEncodedMP, aEncodedMPIV;
+ return ( m_xStorageFile->useStorage() && m_xStorageFile->getEncodedMasterPassword( aEncodedMP, aEncodedMPIV ) && 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()
+{
+ std::unique_lock aGuard( mMutex );
+
+ // remove the cached persistent values in the memory
+ for( auto& rEntry : m_aContainer )
+ {
+ for( std::vector< NamePasswordRecord >::iterator aNPIter = rEntry.second.begin(); aNPIter != rEntry.second.end(); )
+ {
+ if( aNPIter->HasPasswords( PERSISTENT_RECORD ) )
+ {
+ aNPIter->RemovePasswords( PERSISTENT_RECORD );
+
+ if ( m_xStorageFile )
+ m_xStorageFile->remove( rEntry.first, aNPIter->GetUserName() ); // remove record ( aURL, aName )
+ }
+
+ if( !aNPIter->HasPasswords( MEMORY_RECORD ) )
+ {
+ aNPIter = rEntry.second.erase(aNPIter);
+ }
+ else
+ ++aNPIter;
+ }
+ }
+
+ PasswordMap addon;
+ if( m_xStorageFile )
+ addon = m_xStorageFile->getInfo();
+
+ for( const auto& rEntry : addon )
+ {
+ PasswordMap::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 "stardiv.svl.PasswordContainer";
+}
+
+sal_Bool SAL_CALL PasswordContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL PasswordContainer::getSupportedServiceNames( )
+{
+ return { "com.sun.star.task.PasswordContainer" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+svl_PasswordContainer_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new PasswordContainer(context));
+}
+
+
+MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode )
+{
+ MasterPasswordRequest aRequest;
+
+ aRequest.Classification = InteractionClassification_ERROR;
+ aRequest.Mode = Mode;
+
+ setRequest( Any( aRequest ) );
+
+ // Fill continuations...
+ Sequence< RememberAuthentication > aRememberModes{ 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{
+ new ::ucbhelper::InteractionAbort( this ),
+ new ::ucbhelper::InteractionRetry( this ),
+ m_xAuthSupplier
+ };
+
+ setContinuations( aContinuations );
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */