/* -*- 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 utl;
using namespace com::sun::star;
using namespace com::sun::star::uno;
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
            {
                OUStringBuffer 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.append(OUStringChar( pLine[i] ));
                }
 
                newItem.append( sal_Unicode( o3tl::toUInt32(aNum, 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( u"Store"_ustr );
    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( { u"UseStorage"_ustr }, { uno::Any(bUse) } );
}
 
 
bool StorageItem::useStorage()
{
    Sequence<OUString> aNodeNames { u"UseStorage"_ustr };
 
    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 { u"StorageVersion"_ustr };
 
    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{ u"HasMaster"_ustr, u"Master"_ustr, u"MasterInitializationVector"_ustr };
 
    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( { u"HasMaster"_ustr, u"Master"_ustr, u"MasterInitializationVector"_ustr, u"StorageVersion"_ustr },
                               { 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( u"Store"_ustr, sendSeq );
}
 
 
void StorageItem::clear()
{
    ConfigItem::ClearNodeSet( u"Store"_ustr );
}
 
 
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( u"Store"_ustr, 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(
        u"Can't decode!"_ustr, 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(u"Can't encode!"_ustr );
}
 
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()
{
    unsigned char iv[RTL_DIGEST_LENGTH_MD5];
    if (rtl_random_getBytes(nullptr, iv, RTL_DIGEST_LENGTH_MD5) != rtl_Random_E_None)
    {
        throw uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
    }
    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->getXWeak(), 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(u"Password storing is not active!"_ustr, 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(u"No master password!"_ustr, 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(u"storage file not set"_ustr);
 
    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(u"storage file not set"_ustr);
 
    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(u"storage file not set"_ustr);
 
    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(u"storage file not set"_ustr);
 
    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 u"stardiv.svl.PasswordContainer"_ustr;
}
 
sal_Bool SAL_CALL PasswordContainer::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService( this,  ServiceName );
}
 
Sequence< OUString > SAL_CALL PasswordContainer::getSupportedServiceNames(  )
{
    return { u"com.sun.star.task.PasswordContainer"_ustr };
}
 
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: */

↑ V530 The return value of function 'append' is required to be utilized.

↑ V530 The return value of function 'append' is required to be utilized.

↑ V530 The return value of function 'append' is required to be utilized.