summaryrefslogtreecommitdiffstats
path: root/svl/source/passwordcontainer
diff options
context:
space:
mode:
Diffstat (limited to 'svl/source/passwordcontainer')
-rw-r--r--svl/source/passwordcontainer/passwordcontainer.component27
-rw-r--r--svl/source/passwordcontainer/passwordcontainer.cxx1424
-rw-r--r--svl/source/passwordcontainer/passwordcontainer.hxx406
-rw-r--r--svl/source/passwordcontainer/syscreds.cxx263
-rw-r--r--svl/source/passwordcontainer/syscreds.hxx80
5 files changed, 2200 insertions, 0 deletions
diff --git a/svl/source/passwordcontainer/passwordcontainer.component b/svl/source/passwordcontainer/passwordcontainer.component
new file mode 100644
index 000000000..109f45c50
--- /dev/null
+++ b/svl/source/passwordcontainer/passwordcontainer.component
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="stardiv.svl.PasswordContainer"
+ single-instance="true"
+ constructor="svl_PasswordContainer_get_implementation">
+ <service name="com.sun.star.task.PasswordContainer"/>
+ </implementation>
+</component>
diff --git a/svl/source/passwordcontainer/passwordcontainer.cxx b/svl/source/passwordcontainer/passwordcontainer.cxx
new file mode 100644
index 000000000..0abddc2d6
--- /dev/null
+++ b/svl/source/passwordcontainer/passwordcontainer.cxx
@@ -0,0 +1,1424 @@
+/* -*- 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
+ ::osl::MutexGuard 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()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ m_xStorageFile.reset();
+
+ if( mComponent.is() )
+ {
+ mComponent->removeEventListener(this);
+ mComponent.clear();
+ }
+}
+
+void SAL_CALL PasswordContainer::disposing( const EventObject& )
+{
+ ::osl::MutexGuard 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, 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>(o3tl::toUInt32(aMasterPasswd.subView( 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, 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>(o3tl::toUInt32(aMasterPasswd.subView( 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 )
+{
+ ::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 );
+}
+
+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));
+ aBuffer.append(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 )
+{
+ ::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/...
+ 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(static_cast< sal_Unicode >('a' + (i >> 4)));
+ aBuffer.append(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 )
+{
+ ::osl::MutexGuard 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 )
+{
+ ::osl::MutexGuard 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()
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ 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 )
+{
+ 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.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 )
+{
+ bool bResult = false;
+ OUString aEncodedMP, aEncodedMPIV;
+ uno::Reference< task::XInteractionHandler > xTmpHandler = xHandler;
+ ::osl::MutexGuard aGuard( mMutex );
+
+ // 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;
+ ::osl::MutexGuard 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( 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_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 )
+ 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_xStorageFile )
+ {
+ m_aMasterPassword.clear();
+ m_xStorageFile->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
+ }
+}
+
+sal_Bool SAL_CALL PasswordContainer::hasMasterPassword( )
+{
+ ::osl::MutexGuard 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 )
+{
+ ::osl::MutexGuard aGuard( mMutex );
+
+ if ( !m_xStorageFile )
+ throw uno::RuntimeException();
+
+ if ( !bAllow )
+ removeMasterPassword();
+
+ if (m_xStorageFile->useStorage() == static_cast<bool>(bAllow))
+ return bAllow;
+
+ m_xStorageFile->setUseStorage( bAllow );
+ return !bAllow;
+}
+
+sal_Bool SAL_CALL PasswordContainer::isPersistentStoringAllowed()
+{
+ ::osl::MutexGuard 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;
+ ::osl::MutexGuard 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( 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_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 )
+ 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_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()
+{
+ ::osl::MutexGuard 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: */
diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx
new file mode 100644
index 000000000..ca4b549f3
--- /dev/null
+++ b/svl/source/passwordcontainer/passwordcontainer.hxx
@@ -0,0 +1,406 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_PASSWORDCONTAINER_HXX
+#define INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_PASSWORDCONTAINER_HXX
+
+#include <utility>
+#include <vector>
+#include <map>
+#include <optional>
+#include <com/sun/star/task/XPasswordContainer2.hpp>
+#include <com/sun/star/task/PasswordRequestMode.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <unotools/configitem.hxx>
+#include <ucbhelper/interactionrequest.hxx>
+
+#include <rtl/random.h>
+#include <rtl/ref.hxx>
+#include <osl/mutex.hxx>
+
+#include "syscreds.hxx"
+
+#define MEMORY_RECORD 0
+#define PERSISTENT_RECORD 1
+
+
+class NamePasswordRecord
+{
+ OUString m_aName;
+
+ // there are two lists of passwords, memory passwords and persistent passwords
+ bool m_bHasMemoryPasswords;
+ ::std::vector< OUString > m_aMemoryPasswords;
+
+ // persistent passwords are encrypted in one string
+ bool m_bHasPersistentPassword;
+ OUString m_aPersistentPassword;
+ OUString m_aPersistentIV;
+
+ void InitArrays( bool bHasMemoryList, ::std::vector< OUString >&& aMemoryList,
+ bool bHasPersistentList, const OUString& aPersistentList, const OUString& aPersistentIV )
+ {
+ m_bHasMemoryPasswords = bHasMemoryList;
+ if ( bHasMemoryList )
+ m_aMemoryPasswords = aMemoryList;
+
+ m_bHasPersistentPassword = bHasPersistentList;
+ if ( bHasPersistentList )
+ {
+ m_aPersistentPassword = aPersistentList;
+ m_aPersistentIV = aPersistentIV;
+ }
+ }
+
+public:
+
+ NamePasswordRecord( OUString aName )
+ : m_aName(std::move( aName ))
+ , m_bHasMemoryPasswords( false )
+ , m_bHasPersistentPassword( false )
+ {
+ }
+
+ NamePasswordRecord( OUString aName, OUString aPersistentList, OUString aPersistentIV )
+ : m_aName(std::move( aName ))
+ , m_bHasMemoryPasswords( false )
+ , m_bHasPersistentPassword( true )
+ , m_aPersistentPassword(std::move( aPersistentList ))
+ , m_aPersistentIV(std::move( aPersistentIV ))
+ {
+ }
+
+ NamePasswordRecord( const NamePasswordRecord& aRecord )
+ : m_aName( aRecord.m_aName )
+ , m_bHasMemoryPasswords( false )
+ , m_bHasPersistentPassword( false )
+ {
+ InitArrays( aRecord.m_bHasMemoryPasswords, std::vector(aRecord.m_aMemoryPasswords),
+ aRecord.m_bHasPersistentPassword, aRecord.m_aPersistentPassword, aRecord.m_aPersistentIV );
+ }
+
+ NamePasswordRecord& operator=( const NamePasswordRecord& aRecord )
+ {
+ if (this != &aRecord)
+ {
+ m_aName = aRecord.m_aName;
+
+ m_aMemoryPasswords.clear();
+ m_aPersistentPassword.clear();
+ m_aPersistentIV.clear();
+ InitArrays( aRecord.m_bHasMemoryPasswords, std::vector(aRecord.m_aMemoryPasswords),
+ aRecord.m_bHasPersistentPassword, aRecord.m_aPersistentPassword, aRecord.m_aPersistentIV );
+ }
+ return *this;
+ }
+
+ const OUString& GetUserName() const
+ {
+ return m_aName;
+ }
+
+ bool HasPasswords( sal_Int8 nStatus ) const
+ {
+ if ( nStatus == MEMORY_RECORD )
+ return m_bHasMemoryPasswords;
+ if ( nStatus == PERSISTENT_RECORD )
+ return m_bHasPersistentPassword;
+
+ return false;
+ }
+
+ ::std::vector< OUString > GetMemoryPasswords() const
+ {
+ if ( m_bHasMemoryPasswords )
+ return m_aMemoryPasswords;
+
+ return ::std::vector< OUString >();
+ }
+
+ OUString GetPersistentPasswords() const
+ {
+ if ( m_bHasPersistentPassword )
+ return m_aPersistentPassword;
+
+ return OUString();
+ }
+
+ OUString GetPersistentIV() const
+ {
+ if ( m_bHasPersistentPassword )
+ return m_aPersistentIV;
+
+ return OUString();
+ }
+
+ void SetMemoryPasswords( ::std::vector< OUString >&& aMemList )
+ {
+ m_aMemoryPasswords = std::move(aMemList);
+ m_bHasMemoryPasswords = true;
+ }
+
+ void SetPersistentPasswords( const OUString& aPersList, const OUString& aPersIV )
+ {
+ m_aPersistentPassword = aPersList;
+ m_aPersistentIV = aPersIV;
+ m_bHasPersistentPassword = true;
+ }
+
+ void RemovePasswords( sal_Int8 nStatus )
+ {
+ if ( nStatus == MEMORY_RECORD )
+ {
+ m_bHasMemoryPasswords = false;
+ m_aMemoryPasswords.clear();
+ }
+ else if ( nStatus == PERSISTENT_RECORD )
+ {
+ m_bHasPersistentPassword = false;
+ m_aPersistentPassword.clear();
+ m_aPersistentIV.clear();
+ }
+ }
+
+};
+
+
+typedef ::std::pair< const OUString, ::std::vector< NamePasswordRecord > > PairUrlRecord;
+typedef ::std::map< OUString, ::std::vector< NamePasswordRecord > > PasswordMap;
+
+// org.openoffice.Office.Common/Passwords/StorageVersion bump if details of
+// how password details are saved changes. Enables migration from previous
+// schemes.
+constexpr sal_Int32 nCurrentStorageVersion = 1;
+
+class PasswordContainer;
+
+class StorageItem
+ : public ::utl::ConfigItem
+{
+private:
+ PasswordContainer* mainCont;
+ bool hasEncoded;
+ OUString mEncoded;
+ OUString mEncodedIV;
+
+ virtual void ImplCommit() override;
+
+public:
+ StorageItem( PasswordContainer* point, const OUString& path ) :
+ ConfigItem( path, ConfigItemMode::NONE ),
+ mainCont( point ),
+ hasEncoded( false )
+ {
+ css::uno::Sequence< OUString > aNode { path + "/Store" };
+ EnableNotification( aNode );
+ }
+
+ PasswordMap getInfo();
+ void update( const OUString& url, const NamePasswordRecord& rec );
+ void remove( const OUString& url, const OUString& rec );
+ void clear();
+
+ sal_Int32 getStorageVersion();
+
+ bool getEncodedMasterPassword( OUString& aResult, OUString& aResultIV );
+ void setEncodedMasterPassword( const OUString& aResult, const OUString& aResultIV, bool bAcceptEmpty = false );
+ void setUseStorage( bool bUse );
+ bool useStorage();
+
+ virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+};
+
+
+class PasswordContainer : public ::cppu::WeakImplHelper<
+ css::task::XPasswordContainer2,
+ css::lang::XServiceInfo,
+ css::lang::XEventListener >
+{
+private:
+ PasswordMap m_aContainer;
+ std::optional<StorageItem> m_xStorageFile;
+ ::osl::Mutex mMutex;
+ OUString m_aMasterPassword; // master password is set when the string is not empty
+ css::uno::Reference< css::lang::XComponent > mComponent;
+ SysCredentialsConfig mUrlContainer;
+
+ class RandomPool
+ {
+ private:
+ rtlRandomPool m_aRandomPool;
+ public:
+ RandomPool() : m_aRandomPool(rtl_random_createPool())
+ {
+ }
+ rtlRandomPool get()
+ {
+ return m_aRandomPool;
+ }
+ ~RandomPool()
+ {
+ // Clean up random pool memory
+ rtl_random_destroyPool(m_aRandomPool);
+ }
+ };
+
+ RandomPool mRandomPool;
+
+ OUString createIV();
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::task::UserRecord > CopyToUserRecordSequence(
+ const ::std::vector< NamePasswordRecord >& original,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler );
+
+ css::task::UserRecord CopyToUserRecord(
+ const NamePasswordRecord& aRecord,
+ bool& io_bTryToDecode,
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::task::UserRecord > FindUsr(
+ const ::std::vector< NamePasswordRecord >& userlist,
+ std::u16string_view name,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler );
+ /// @throws css::uno::RuntimeException
+ bool createUrlRecord(
+ const PasswordMap::iterator & rIter,
+ bool bName,
+ std::u16string_view aName,
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler,
+ css::task::UrlRecord & rRec );
+
+ /// @throws css::uno::RuntimeException
+ css::task::UrlRecord find(
+ const OUString& aURL,
+ std::u16string_view aName,
+ bool bName, // only needed to support empty user names
+ const css::uno::Reference< css::task::XInteractionHandler >& aHandler );
+
+ static OUString GetDefaultMasterPassword();
+
+ static OUString RequestPasswordFromUser(
+ css::task::PasswordRequestMode aRMode,
+ const css::uno::Reference< css::task::XInteractionHandler >& xHandler );
+
+ /// @throws css::uno::RuntimeException
+ OUString const & GetMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& Handler );
+
+ /// @throws css::uno::RuntimeException
+ void UpdateVector( const OUString& url, ::std::vector< NamePasswordRecord >& toUpdate, NamePasswordRecord const & rec, bool writeFile );
+
+ /// @throws css::uno::RuntimeException
+ void PrivateAdd( const OUString& aUrl,
+ const OUString& aUserName,
+ const css::uno::Sequence< OUString >& aPasswords,
+ char aMode,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler );
+
+ /// @throws css::uno::RuntimeException
+ static ::std::vector< OUString > DecodePasswords( std::u16string_view aLine, std::u16string_view aIV, const OUString& aMasterPassword, css::task::PasswordRequestMode mode );
+
+ /// @throws css::uno::RuntimeException
+ static OUString EncodePasswords(const std::vector< OUString >& lines, std::u16string_view aIV, const OUString& aMasterPassword );
+
+public:
+ PasswordContainer( const css::uno::Reference< css::uno::XComponentContext >& );
+ virtual ~PasswordContainer() override;
+
+ virtual void SAL_CALL add( const OUString& aUrl,
+ const OUString& aUserName,
+ const css::uno::Sequence< OUString >& aPasswords,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override;
+
+ virtual void SAL_CALL addPersistent( const OUString& aUrl,
+ const OUString& aUserName,
+ const css::uno::Sequence< OUString >& aPasswords,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override;
+
+ virtual css::task::UrlRecord SAL_CALL
+ find( const OUString& aUrl,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override;
+
+ virtual css::task::UrlRecord SAL_CALL
+ findForName( const OUString& aUrl,
+ const OUString& aUserName,
+ const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override;
+
+ virtual void SAL_CALL remove( const OUString& aUrl,
+ const OUString& aUserName ) override;
+
+ virtual void SAL_CALL removePersistent( const OUString& aUrl,
+ const OUString& aUserName ) override;
+
+ virtual void SAL_CALL removeAllPersistent() override;
+
+ virtual css::uno::Sequence< css::task::UrlRecord > SAL_CALL
+ getAllPersistent( const css::uno::Reference< css::task::XInteractionHandler >& Handler ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames( ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XMasterPasswordHandling
+ virtual sal_Bool SAL_CALL authorizateWithMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override;
+ virtual sal_Bool SAL_CALL changeMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override;
+ virtual void SAL_CALL removeMasterPassword() override;
+ virtual sal_Bool SAL_CALL hasMasterPassword( ) override;
+ virtual sal_Bool SAL_CALL allowPersistentStoring( sal_Bool bAllow ) override;
+ virtual sal_Bool SAL_CALL isPersistentStoringAllowed( ) override;
+
+ // XMasterPasswordHandling2
+ virtual sal_Bool SAL_CALL useDefaultMasterPassword( const css::uno::Reference< css::task::XInteractionHandler >& xHandler ) override;
+ virtual sal_Bool SAL_CALL isDefaultMasterPasswordUsed( ) override;
+
+ // XUrlContainer
+ virtual void SAL_CALL addUrl( const OUString& Url, sal_Bool MakePersistent ) override;
+ virtual OUString SAL_CALL findUrl( const OUString& Url ) override;
+ virtual void SAL_CALL removeUrl( const OUString& Url ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getUrls( sal_Bool OnlyPersistent ) override;
+
+ void Notify();
+};
+
+
+class MasterPasswordRequest_Impl : public ucbhelper::InteractionRequest
+{
+ ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > m_xAuthSupplier;
+
+public:
+ MasterPasswordRequest_Impl( css::task::PasswordRequestMode Mode );
+
+ const ::rtl::Reference< ucbhelper::InteractionSupplyAuthentication > &
+ getAuthenticationSupplier() const { return m_xAuthSupplier; }
+
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svl/source/passwordcontainer/syscreds.cxx b/svl/source/passwordcontainer/syscreds.cxx
new file mode 100644
index 000000000..a3354b853
--- /dev/null
+++ b/svl/source/passwordcontainer/syscreds.cxx
@@ -0,0 +1,263 @@
+/* -*- 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 "syscreds.hxx"
+#include <osl/diagnose.h>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star;
+
+SysCredentialsConfigItem::SysCredentialsConfigItem(
+ SysCredentialsConfig * pOwner )
+: utl::ConfigItem( "Office.Common/Passwords", ConfigItemMode::NONE ),
+ m_bInited( false ),
+ m_pOwner( pOwner )
+{
+ uno::Sequence<OUString> aNode { "Office.Common/Passwords/AuthenticateUsingSystemCredentials" };
+ EnableNotification( aNode );
+}
+
+//virtual
+void SysCredentialsConfigItem::Notify(
+ const uno::Sequence< OUString > & /*seqPropertyNames*/ )
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_bInited = false;
+ // rebuild m_seqURLs
+ getSystemCredentialsURLs();
+ }
+ m_pOwner->persistentConfigChanged();
+}
+
+void SysCredentialsConfigItem::ImplCommit()
+{
+ // does nothing
+}
+
+uno::Sequence< OUString >
+SysCredentialsConfigItem::getSystemCredentialsURLs()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bInited )
+ {
+ // read config item
+ uno::Sequence<OUString> aPropNames { "AuthenticateUsingSystemCredentials" };
+ uno::Sequence< uno::Any > aAnyValues(
+ utl::ConfigItem::GetProperties( aPropNames ) );
+
+ OSL_ENSURE(
+ aAnyValues.getLength() == 1,
+ "SysCredentialsConfigItem::getSystemCredentialsURLs: "
+ "Error reading config item!" );
+
+ uno::Sequence< OUString > aValues;
+ if ( ( aAnyValues[ 0 ] >>= aValues ) ||
+ ( !aAnyValues[ 0 ].hasValue() ) )
+ {
+ m_seqURLs = aValues;
+ m_bInited = true;
+ }
+ }
+ return m_seqURLs;
+}
+
+void SysCredentialsConfigItem::setSystemCredentialsURLs(
+ const uno::Sequence< OUString > & seqURLList )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // write config item.
+ uno::Sequence< OUString > aPropNames{ "AuthenticateUsingSystemCredentials" };
+ uno::Sequence< uno::Any > aPropValues{ uno::Any(seqURLList) };
+
+ utl::ConfigItem::SetModified();
+ utl::ConfigItem::PutProperties( aPropNames, aPropValues );
+
+ m_seqURLs = seqURLList;
+ m_bInited = true;
+}
+
+
+namespace
+{
+ // TODO: This code is actually copied from svl/source/passwordcontainer.cxx
+ bool removeLastSegment( OUString & aURL )
+ {
+ sal_Int32 aInd = aURL.lastIndexOf( '/' );
+
+ if( aInd > 0 )
+ {
+ sal_Int32 aPrevInd = aURL.lastIndexOf( '/', aInd );
+ if ( aURL.indexOf( "://" ) != aPrevInd - 2 ||
+ aInd != aURL.getLength() - 1 )
+ {
+ aURL = aURL.copy( 0, aInd );
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool findURL( std::set<OUString> const & rContainer, OUString const & aURL, OUString & aResult )
+ {
+ // TODO: This code is actually copied from svl/source/passwordcontainer.cxx
+ if( !rContainer.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/...
+ auto aIter = rContainer.find( aUrl );
+ if( aIter != rContainer.end() )
+ {
+ aResult = *aIter;
+ return true;
+ }
+ else
+ {
+ OUString tmpUrl( aUrl );
+ if ( !tmpUrl.endsWith("/") )
+ tmpUrl += "/";
+
+ aIter = rContainer.lower_bound( tmpUrl );
+ if( aIter != rContainer.end() && aIter->match( tmpUrl ) )
+ {
+ aResult = *aIter;
+ return true;
+ }
+ }
+ }
+ while( removeLastSegment( aUrl ) && !aUrl.isEmpty() );
+ }
+ aResult.clear();
+ return false;
+ }
+
+} // namespace
+
+SysCredentialsConfig::SysCredentialsConfig()
+: m_aConfigItem( this ),
+ m_bCfgInited( false )
+{
+}
+
+void SysCredentialsConfig::initCfg()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bCfgInited )
+ {
+ const uno::Sequence< OUString > aURLs(
+ m_aConfigItem.getSystemCredentialsURLs() );
+ m_aCfgContainer.insert( aURLs.begin(), aURLs.end() );
+ m_bCfgInited = true;
+ }
+}
+
+void SysCredentialsConfig::writeCfg()
+{
+ osl::MutexGuard aGuard( m_aMutex );
+
+ OSL_ENSURE( m_bCfgInited, "SysCredentialsConfig::writeCfg : not initialized!" );
+
+ m_aConfigItem.setSystemCredentialsURLs( comphelper::containerToSequence(m_aCfgContainer) );
+}
+
+OUString SysCredentialsConfig::find( OUString const & aURL )
+{
+ osl::MutexGuard aGuard( m_aMutex );
+ OUString aResult;
+ if ( findURL( m_aMemContainer, aURL, aResult ) )
+ return aResult;
+
+ initCfg();
+ if ( findURL( m_aCfgContainer, aURL, aResult ) )
+ return aResult;
+
+ return OUString();
+}
+
+void SysCredentialsConfig::add( OUString const & rURL, bool bPersistent )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( bPersistent )
+ {
+ m_aMemContainer.erase( rURL );
+
+ initCfg();
+ m_aCfgContainer.insert( rURL );
+ writeCfg();
+ }
+ else
+ {
+ initCfg();
+ if ( m_aCfgContainer.erase( rURL ) > 0 )
+ writeCfg();
+
+ m_aMemContainer.insert( rURL );
+ }
+}
+
+void SysCredentialsConfig::remove( OUString const & rURL )
+{
+ m_aMemContainer.erase( rURL );
+
+ initCfg();
+ if ( m_aCfgContainer.erase( rURL ) > 0 )
+ writeCfg();
+}
+
+uno::Sequence< OUString > SysCredentialsConfig::list( bool bOnlyPersistent )
+{
+ initCfg();
+ sal_Int32 nCount = m_aCfgContainer.size()
+ + ( bOnlyPersistent ? 0 : m_aMemContainer.size() );
+ uno::Sequence< OUString > aResult( nCount );
+ auto aResultRange = asNonConstRange(aResult);
+ sal_Int32 n = 0;
+
+ for ( const auto& rItem : m_aCfgContainer )
+ {
+ aResultRange[ n ] = rItem;
+ ++n;
+ }
+
+ if ( !bOnlyPersistent )
+ {
+ for ( const auto& rItem : m_aMemContainer )
+ {
+ aResultRange[ n ] = rItem;
+ ++n;
+ }
+ }
+ return aResult;
+}
+
+void SysCredentialsConfig::persistentConfigChanged()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_bCfgInited = false; // re-init on demand.
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svl/source/passwordcontainer/syscreds.hxx b/svl/source/passwordcontainer/syscreds.hxx
new file mode 100644
index 000000000..4f2d67347
--- /dev/null
+++ b/svl/source/passwordcontainer/syscreds.hxx
@@ -0,0 +1,80 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX
+#define INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX
+
+#include <set>
+#include <memory>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <unotools/configitem.hxx>
+
+class SysCredentialsConfig;
+
+class SysCredentialsConfigItem : public utl::ConfigItem
+{
+ public:
+ explicit SysCredentialsConfigItem( SysCredentialsConfig * pOwner );
+ //virtual ~SysCredentialsConfigItem();
+
+ virtual void Notify( const css::uno::Sequence< OUString > & seqPropertyNames ) override;
+
+ css::uno::Sequence< OUString > getSystemCredentialsURLs();
+
+ void setSystemCredentialsURLs( const css::uno::Sequence< OUString > & seqURLList );
+
+ //bool isSystemCredentialsURL( const OUString & rURL ) const;
+
+ private:
+ virtual void ImplCommit() override;
+
+ ::osl::Mutex m_aMutex;
+ bool m_bInited;
+ css::uno::Sequence< OUString > m_seqURLs;
+ SysCredentialsConfig * m_pOwner;
+};
+
+class SysCredentialsConfig
+{
+ public:
+ SysCredentialsConfig();
+
+ OUString find( OUString const & rURL );
+ void add( OUString const & rURL, bool bPersistent );
+ void remove( OUString const & rURL );
+ css::uno::Sequence< OUString > list( bool bOnlyPersistent );
+
+ void persistentConfigChanged();
+
+ private:
+ void initCfg();
+ void writeCfg();
+
+ ::osl::Mutex m_aMutex;
+ std::set< OUString > m_aMemContainer;
+ std::set< OUString > m_aCfgContainer;
+ SysCredentialsConfigItem m_aConfigItem;
+ bool m_bCfgInited;
+};
+
+#endif // INCLUDED_SVL_SOURCE_PASSWORDCONTAINER_SYSCREDS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */