diff options
Diffstat (limited to 'svl/source/passwordcontainer')
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.component | 27 | ||||
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.cxx | 1442 | ||||
-rw-r--r-- | svl/source/passwordcontainer/passwordcontainer.hxx | 412 | ||||
-rw-r--r-- | svl/source/passwordcontainer/syscreds.cxx | 269 | ||||
-rw-r--r-- | svl/source/passwordcontainer/syscreds.hxx | 81 |
5 files changed, 2231 insertions, 0 deletions
diff --git a/svl/source/passwordcontainer/passwordcontainer.component b/svl/source/passwordcontainer/passwordcontainer.component new file mode 100644 index 0000000000..109f45c502 --- /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 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: */ diff --git a/svl/source/passwordcontainer/passwordcontainer.hxx b/svl/source/passwordcontainer/passwordcontainer.hxx new file mode 100644 index 0000000000..80fae574f1 --- /dev/null +++ b/svl/source/passwordcontainer/passwordcontainer.hxx @@ -0,0 +1,412 @@ +/* -*- 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 <mutex> +#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; + std::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, std::u16string_view aMasterPassword, css::task::PasswordRequestMode mode ); + + /// @throws css::uno::RuntimeException + static OUString EncodePasswords(const std::vector< OUString >& lines, std::u16string_view aIV, std::u16string_view 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(); +private: + bool authorizateWithMasterPassword( std::unique_lock<std::mutex>& rGuard, const css::uno::Reference< css::task::XInteractionHandler >& xHandler ); + css::uno::Sequence< css::task::UrlRecord > getAllPersistent( std::unique_lock<std::mutex>& rGuard, const css::uno::Reference< css::task::XInteractionHandler >& Handler ); + void removeAllPersistent(std::unique_lock<std::mutex>& rGuard); + void removeMasterPassword(std::unique_lock<std::mutex>& rGuard); +}; + + +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 0000000000..8de4a82f0d --- /dev/null +++ b/svl/source/passwordcontainer/syscreds.cxx @@ -0,0 +1,269 @@ +/* -*- 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*/ ) +{ + { + std::unique_lock aGuard( m_aMutex ); + m_bInited = false; + // rebuild m_seqURLs + getSystemCredentialsURLs(aGuard); + } + m_pOwner->persistentConfigChanged(); +} + +void SysCredentialsConfigItem::ImplCommit() +{ + // does nothing +} + +uno::Sequence< OUString > +SysCredentialsConfigItem::getSystemCredentialsURLs() +{ + std::unique_lock aGuard(m_aMutex); + return getSystemCredentialsURLs(aGuard); +} + +uno::Sequence< OUString > +SysCredentialsConfigItem::getSystemCredentialsURLs(std::unique_lock<std::mutex>& /*rGuard*/) +{ + 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 ) +{ + // write config item. + uno::Sequence< OUString > aPropNames{ "AuthenticateUsingSystemCredentials" }; + uno::Sequence< uno::Any > aPropValues{ uno::Any(seqURLList) }; + + utl::ConfigItem::SetModified(); + utl::ConfigItem::PutProperties( aPropNames, aPropValues ); + + std::unique_lock aGuard( m_aMutex ); + + 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(std::unique_lock<std::mutex>& /*rGuard*/) +{ + if ( !m_bCfgInited ) + { + const uno::Sequence< OUString > aURLs( + m_aConfigItem.getSystemCredentialsURLs() ); + m_aCfgContainer.insert( aURLs.begin(), aURLs.end() ); + m_bCfgInited = true; + } +} + +void SysCredentialsConfig::writeCfg(std::unique_lock<std::mutex>& /*rGuard*/) +{ + OSL_ENSURE( m_bCfgInited, "SysCredentialsConfig::writeCfg : not initialized!" ); + + m_aConfigItem.setSystemCredentialsURLs( comphelper::containerToSequence(m_aCfgContainer) ); +} + +OUString SysCredentialsConfig::find( OUString const & aURL ) +{ + std::unique_lock aGuard( m_aMutex ); + OUString aResult; + if ( findURL( m_aMemContainer, aURL, aResult ) ) + return aResult; + + initCfg(aGuard); + if ( findURL( m_aCfgContainer, aURL, aResult ) ) + return aResult; + + return OUString(); +} + +void SysCredentialsConfig::add( OUString const & rURL, bool bPersistent ) +{ + std::unique_lock aGuard( m_aMutex ); + + if ( bPersistent ) + { + m_aMemContainer.erase( rURL ); + + initCfg(aGuard); + m_aCfgContainer.insert( rURL ); + writeCfg(aGuard); + } + else + { + initCfg(aGuard); + if ( m_aCfgContainer.erase( rURL ) > 0 ) + writeCfg(aGuard); + + m_aMemContainer.insert( rURL ); + } +} + +void SysCredentialsConfig::remove( OUString const & rURL ) +{ + std::unique_lock aGuard(m_aMutex); + + m_aMemContainer.erase( rURL ); + + initCfg(aGuard); + if ( m_aCfgContainer.erase( rURL ) > 0 ) + writeCfg(aGuard); +} + +uno::Sequence< OUString > SysCredentialsConfig::list( bool bOnlyPersistent ) +{ + std::unique_lock aGuard(m_aMutex); + initCfg(aGuard); + 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() +{ + std::unique_lock 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 0000000000..75241c4e79 --- /dev/null +++ b/svl/source/passwordcontainer/syscreds.hxx @@ -0,0 +1,81 @@ +/* -*- 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 <memory> +#include <mutex> +#include <set> +#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: + css::uno::Sequence< OUString > getSystemCredentialsURLs(std::unique_lock<std::mutex>& rGuard); + virtual void ImplCommit() override; + + std::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(std::unique_lock<std::mutex>& rGuard); + void writeCfg(std::unique_lock<std::mutex>& rGuard); + + std::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: */ |