1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <string_view>
25 #include "passwordcontainer.hxx"
27 #include <cppuhelper/factory.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/propertyvalue.hxx>
31 #include <comphelper/sequence.hxx>
32 #include <o3tl/string_view.hxx>
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <com/sun/star/task/InteractionHandler.hpp>
35 #include <com/sun/star/task/MasterPasswordRequest.hpp>
36 #include <com/sun/star/task/NoMasterException.hpp>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 #include <osl/diagnose.h>
40 #include <rtl/character.hxx>
41 #include <rtl/cipher.h>
42 #include <rtl/digest.h>
43 #include <rtl/byteseq.hxx>
44 #include <rtl/ustrbuf.hxx>
48 using namespace com::sun::star
;
49 using namespace com::sun::star::uno
;
50 using namespace com::sun::star::registry
;
51 using namespace com::sun::star::lang
;
52 using namespace com::sun::star::task
;
53 using namespace com::sun::star::ucb
;
55 static OUString
createIndex(const std::vector
< OUString
>& lines
)
57 OUStringBuffer aResult
;
59 for( size_t i
= 0; i
< lines
.size(); i
++ )
63 OString line
= OUStringToOString( lines
[i
], RTL_TEXTENCODING_UTF8
);
64 const char* pLine
= line
.getStr();
68 if (rtl::isAsciiAlphanumeric(static_cast<unsigned char>(*pLine
)))
70 aResult
.append(*pLine
);
74 aResult
.append("_" + OUString::number(*pLine
, 16) );
81 return aResult
.makeStringAndClear();
85 static std::vector
< OUString
> getInfoFromInd( std::u16string_view aInd
)
87 std::vector
< OUString
> aResult
;
90 OString line
= OUStringToOString( aInd
, RTL_TEXTENCODING_ASCII_US
);
91 const char* pLine
= line
.getStr();
94 OUStringBuffer newItem
;
100 while( *pLine
&& ( pLine
[0] != '_' || pLine
[1] != '_' ))
103 newItem
.append( *pLine
);
109 for( int i
= 1; i
< 3; i
++ )
112 || ( ( pLine
[i
] < '0' || pLine
[i
] > '9' )
113 && ( pLine
[i
] < 'a' || pLine
[i
] > 'f' )
114 && ( pLine
[i
] < 'A' || pLine
[i
] > 'F' ) ) )
116 OSL_FAIL( "Wrong index syntax!" );
120 aNum
+= OUStringChar( pLine
[i
] );
123 newItem
.append( sal_Unicode( aNum
.toUInt32( 16 ) ) );
127 aResult
.push_back( newItem
.makeStringAndClear() );
128 } while( pLine
[0] == '_' && pLine
[1] == '_' );
131 OSL_FAIL( "Wrong index syntax!" );
137 static bool shorterUrl( OUString
& aURL
)
139 sal_Int32 aInd
= aURL
.lastIndexOf( '/' );
140 if( aInd
> 0 && aURL
.indexOf( "://" ) != aInd
-2 )
142 aURL
= aURL
.copy( 0, aInd
);
150 static OUString
getAsciiLine( const ::rtl::ByteSequence
& buf
)
154 ::rtl::ByteSequence
outbuf( buf
.getLength()*2+1 );
156 for( int ind
= 0; ind
< buf
.getLength(); ind
++ )
158 outbuf
[ind
*2] = ( static_cast<sal_uInt8
>(buf
[ind
]) >> 4 ) + 'a';
159 outbuf
[ind
*2+1] = ( static_cast<sal_uInt8
>(buf
[ind
]) & 0x0f ) + 'a';
161 outbuf
[buf
.getLength()*2] = '\0';
163 aResult
= OUString::createFromAscii( reinterpret_cast<char*>(outbuf
.getArray()) );
169 static ::rtl::ByteSequence
getBufFromAsciiLine( std::u16string_view line
)
171 OSL_ENSURE( line
.size() % 2 == 0, "Wrong syntax!" );
172 OString tmpLine
= OUStringToOString( line
, RTL_TEXTENCODING_ASCII_US
);
173 ::rtl::ByteSequence
aResult(line
.size()/2);
175 for( int ind
= 0; ind
< tmpLine
.getLength()/2; ind
++ )
177 aResult
[ind
] = ( static_cast<sal_uInt8
>( tmpLine
[ind
*2] - 'a' ) << 4 ) | static_cast<sal_uInt8
>( tmpLine
[ind
*2+1] - 'a' );
184 PasswordMap
StorageItem::getInfo()
188 const Sequence
< OUString
> aNodeNames
= ConfigItem::GetNodeNames( "Store" );
189 sal_Int32 aNodeCount
= aNodeNames
.getLength();
190 Sequence
< OUString
> aPropNames( aNodeCount
* 2);
192 std::transform(aNodeNames
.begin(), aNodeNames
.end(), aPropNames
.getArray(),
193 [](const OUString
& rName
) -> OUString
{
194 return "Store/Passwordstorage['" + rName
+ "']/Password"; });
195 std::transform(aNodeNames
.begin(), aNodeNames
.end(), aPropNames
.getArray() + aNodeCount
,
196 [](const OUString
& rName
) -> OUString
{
197 return "Store/Passwordstorage['" + rName
+ "']/InitializationVector"; });
199 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aPropNames
);
201 if( aPropertyValues
.getLength() != aNodeCount
* 2)
203 OSL_FAIL( "Problems during reading" );
207 for( sal_Int32 aNodeInd
= 0; aNodeInd
< aNodeCount
; ++aNodeInd
)
209 std::vector
< OUString
> aUrlUsr
= getInfoFromInd( aNodeNames
[aNodeInd
] );
211 if( aUrlUsr
.size() == 2 )
213 OUString aUrl
= aUrlUsr
[0];
214 OUString aName
= aUrlUsr
[1];
218 aPropertyValues
[aNodeInd
] >>= aEPasswd
;
219 aPropertyValues
[aNodeInd
+ aNodeCount
] >>= aIV
;
221 PasswordMap::iterator aIter
= aResult
.find( aUrl
);
222 if( aIter
!= aResult
.end() )
223 aIter
->second
.emplace_back( aName
, aEPasswd
, aIV
);
226 NamePasswordRecord
aNewRecord( aName
, aEPasswd
, aIV
);
227 std::vector
< NamePasswordRecord
> listToAdd( 1, aNewRecord
);
229 aResult
.insert( PairUrlRecord( aUrl
, listToAdd
) );
233 OSL_FAIL( "Wrong index syntax!" );
240 void StorageItem::setUseStorage( bool bUse
)
242 ConfigItem::SetModified();
243 ConfigItem::PutProperties( { "UseStorage" }, { uno::Any(bUse
) } );
247 bool StorageItem::useStorage()
249 Sequence
<OUString
> aNodeNames
{ "UseStorage" };
251 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
253 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
255 OSL_FAIL( "Problems during reading" );
259 bool aResult
= false;
260 aPropertyValues
[0] >>= aResult
;
266 sal_Int32
StorageItem::getStorageVersion()
268 Sequence
<OUString
> aNodeNames
{ "StorageVersion" };
270 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
272 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
274 OSL_FAIL( "Problems during reading" );
278 sal_Int32 nResult
= 0;
279 aPropertyValues
[0] >>= nResult
;
284 bool StorageItem::getEncodedMasterPassword( OUString
& aResult
, OUString
& aResultIV
)
289 aResultIV
= mEncodedIV
;
293 Sequence
< OUString
> aNodeNames
{ "HasMaster", "Master", "MasterInitializationVector" };
295 Sequence
< Any
> aPropertyValues
= ConfigItem::GetProperties( aNodeNames
);
297 if( aPropertyValues
.getLength() != aNodeNames
.getLength() )
299 OSL_FAIL( "Problems during reading" );
303 aPropertyValues
[0] >>= hasEncoded
;
304 aPropertyValues
[1] >>= mEncoded
;
305 aPropertyValues
[2] >>= mEncodedIV
;
308 aResultIV
= mEncodedIV
;
314 void StorageItem::setEncodedMasterPassword( const OUString
& aEncoded
, const OUString
& aEncodedIV
, bool bAcceptEmpty
)
316 bool bHasMaster
= ( !aEncoded
.isEmpty() || bAcceptEmpty
);
318 ConfigItem::SetModified();
319 ConfigItem::PutProperties( { "HasMaster", "Master", "MasterInitializationVector", "StorageVersion" },
320 { uno::Any(bHasMaster
), uno::Any(aEncoded
),
321 uno::Any(aEncodedIV
), uno::Any(nCurrentStorageVersion
) } );
323 hasEncoded
= bHasMaster
;
325 mEncodedIV
= aEncodedIV
;
329 void StorageItem::remove( const OUString
& aURL
, const OUString
& aName
)
331 Sequence
< OUString
> sendSeq
{ createIndex( { aURL
, aName
} ) };
333 ConfigItem::ClearNodeElements( "Store", sendSeq
);
337 void StorageItem::clear()
339 ConfigItem::ClearNodeSet( "Store" );
343 void StorageItem::update( const OUString
& aURL
, const NamePasswordRecord
& aRecord
)
345 if ( !aRecord
.HasPasswords( PERSISTENT_RECORD
) )
347 OSL_FAIL( "Unexpected storing of a record!" );
351 Sequence
< beans::PropertyValue
> sendSeq
{ comphelper::makePropertyValue(
352 "Store/Passwordstorage['" + createIndex( { aURL
, aRecord
.GetUserName() } ) + "']/InitializationVector",
353 aRecord
.GetPersistentIV()), comphelper::makePropertyValue(
354 "Store/Passwordstorage['" + createIndex( { aURL
, aRecord
.GetUserName() } ) + "']/Password",
355 aRecord
.GetPersistentPasswords()) };
357 ConfigItem::SetModified();
358 ConfigItem::SetSetProperties( "Store", sendSeq
);
362 void StorageItem::Notify( const Sequence
< OUString
>& )
364 // this feature still should not be used
370 void StorageItem::ImplCommit()
372 // Do nothing, we stored everything we want already
376 PasswordContainer::PasswordContainer( const Reference
<XComponentContext
>& rxContext
)
378 // m_pStorageFile->Notify() can be called
379 std::unique_lock
aGuard( mMutex
);
381 mComponent
.set( rxContext
->getServiceManager(), UNO_QUERY
);
382 mComponent
->addEventListener( this );
384 m_xStorageFile
.emplace( this, "Office.Common/Passwords" );
385 if( m_xStorageFile
->useStorage() )
386 m_aContainer
= m_xStorageFile
->getInfo();
390 PasswordContainer::~PasswordContainer()
392 std::unique_lock
aGuard( mMutex
);
394 m_xStorageFile
.reset();
396 if( mComponent
.is() )
398 mComponent
->removeEventListener(this);
403 void SAL_CALL
PasswordContainer::disposing( const EventObject
& )
405 std::unique_lock
aGuard( mMutex
);
407 m_xStorageFile
.reset();
409 if( mComponent
.is() )
411 //mComponent->removeEventListener(this);
416 std::vector
< OUString
> PasswordContainer::DecodePasswords( std::u16string_view aLine
, std::u16string_view aIV
, std::u16string_view aMasterPasswd
, css::task::PasswordRequestMode mode
)
418 if( !aMasterPasswd
.empty() )
420 rtlCipher aDecoder
= rtl_cipher_create (rtl_Cipher_AlgorithmBF
, rtl_Cipher_ModeStream
);
421 OSL_ENSURE( aDecoder
, "Can't create decoder" );
425 OSL_ENSURE( aMasterPasswd
.size() == RTL_DIGEST_LENGTH_MD5
* 2, "Wrong master password format!" );
427 unsigned char code
[RTL_DIGEST_LENGTH_MD5
];
428 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
429 code
[ ind
] = static_cast<char>(o3tl::toUInt32(aMasterPasswd
.substr( ind
*2, 2 ), 16));
431 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
] = {0};
434 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
436 auto tmp
= aIV
.substr( ind
*2, 2 );
437 iv
[ ind
] = static_cast<char>(rtl_ustr_toInt64_WithLength(tmp
.data(), 16, tmp
.size()));
441 rtlCipherError result
= rtl_cipher_init (
442 aDecoder
, rtl_Cipher_DirectionDecode
,
443 code
, RTL_DIGEST_LENGTH_MD5
, iv
, RTL_DIGEST_LENGTH_MD5
);
445 if( result
== rtl_Cipher_E_None
)
447 ::rtl::ByteSequence aSeq
= getBufFromAsciiLine( aLine
);
449 ::rtl::ByteSequence
resSeq( aSeq
.getLength() );
451 rtl_cipher_decode ( aDecoder
, aSeq
.getArray(), aSeq
.getLength(),
452 reinterpret_cast<sal_uInt8
*>(resSeq
.getArray()), resSeq
.getLength() );
454 OUString
aPasswd( reinterpret_cast<char*>(resSeq
.getArray()), resSeq
.getLength(), RTL_TEXTENCODING_UTF8
);
456 rtl_cipher_destroy (aDecoder
);
458 return getInfoFromInd( aPasswd
);
461 rtl_cipher_destroy (aDecoder
);
466 OSL_FAIL( "No master password provided!" );
467 // throw special exception
470 // problems with decoding
471 OSL_FAIL( "Problem with decoding" );
472 throw css::task::NoMasterException(
473 "Can't decode!", css::uno::Reference
<css::uno::XInterface
>(), mode
);
476 OUString
PasswordContainer::EncodePasswords(const std::vector
< OUString
>& lines
, std::u16string_view aIV
, std::u16string_view aMasterPasswd
)
478 if( !aMasterPasswd
.empty() )
480 OString aSeq
= OUStringToOString( createIndex( lines
), RTL_TEXTENCODING_UTF8
);
482 rtlCipher aEncoder
= rtl_cipher_create (rtl_Cipher_AlgorithmBF
, rtl_Cipher_ModeStream
);
483 OSL_ENSURE( aEncoder
, "Can't create encoder" );
487 OSL_ENSURE( aMasterPasswd
.size() == RTL_DIGEST_LENGTH_MD5
* 2, "Wrong master password format!" );
489 unsigned char code
[RTL_DIGEST_LENGTH_MD5
];
490 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
491 code
[ ind
] = static_cast<char>(o3tl::toUInt32(aMasterPasswd
.substr( ind
*2, 2 ), 16));
493 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
] = {0};
496 for( int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ind
++ )
498 auto tmp
= aIV
.substr( ind
*2, 2 );
499 iv
[ ind
] = static_cast<char>(rtl_ustr_toInt64_WithLength(tmp
.data(), 16, tmp
.size()));
503 rtlCipherError result
= rtl_cipher_init (
504 aEncoder
, rtl_Cipher_DirectionEncode
,
505 code
, RTL_DIGEST_LENGTH_MD5
, iv
, RTL_DIGEST_LENGTH_MD5
);
507 if( result
== rtl_Cipher_E_None
)
509 ::rtl::ByteSequence
resSeq(aSeq
.getLength()+1);
511 result
= rtl_cipher_encode ( aEncoder
, aSeq
.getStr(), aSeq
.getLength()+1,
512 reinterpret_cast<sal_uInt8
*>(resSeq
.getArray()), resSeq
.getLength() );
516 rtlCipherError result = rtl_cipher_init (
517 aEncoder, rtl_Cipher_DirectionDecode,
518 code, RTL_DIGEST_LENGTH_MD5, NULL, 0 );
521 if( result == rtl_Cipher_E_None )
523 OUString testOU = getAsciiLine( resSeq );
524 ::rtl::ByteSequence aSeq1 = getBufFromAsciiLine( testOU );
526 ::rtl::ByteSequence resSeq1( aSeq1.getLength() );
528 if( resSeq.getLength() == aSeq1.getLength() )
530 for( int ind = 0; ind < aSeq1.getLength(); ind++ )
531 if( resSeq[ind] != aSeq1[ind] )
535 result = rtl_cipher_decode ( aEncoder, (sal_uInt8*)aSeq1.getArray(), aSeq1.getLength(),
536 (sal_uInt8*)resSeq1.getArray(), resSeq1.getLength() );
538 OUString aPasswd( ( char* )resSeq1.getArray(), resSeq1.getLength(), RTL_TEXTENCODING_UTF8 );
542 rtl_cipher_destroy (aEncoder
);
544 if( result
== rtl_Cipher_E_None
)
545 return getAsciiLine( resSeq
);
549 rtl_cipher_destroy (aEncoder
);
554 OSL_FAIL( "No master password provided!" );
555 // throw special exception
558 // problems with encoding
559 OSL_FAIL( "Problem with encoding" );
560 throw RuntimeException("Can't encode!" );
563 void PasswordContainer::UpdateVector( const OUString
& aURL
, std::vector
< NamePasswordRecord
>& toUpdate
, NamePasswordRecord
const & aRecord
, bool writeFile
)
565 for (auto & aNPIter
: toUpdate
)
566 if( aNPIter
.GetUserName() == aRecord
.GetUserName() )
568 if( aRecord
.HasPasswords( MEMORY_RECORD
) )
569 aNPIter
.SetMemoryPasswords( aRecord
.GetMemoryPasswords() );
571 if( aRecord
.HasPasswords( PERSISTENT_RECORD
) )
573 aNPIter
.SetPersistentPasswords( aRecord
.GetPersistentPasswords(), aRecord
.GetPersistentIV() );
577 // the password must be already encoded
578 m_xStorageFile
->update( aURL
, aRecord
); // change existing ( aURL, aName ) record in the configfile
586 if( aRecord
.HasPasswords( PERSISTENT_RECORD
) && writeFile
)
588 // the password must be already encoded
589 m_xStorageFile
->update( aURL
, aRecord
); // add new aName to the existing url
592 toUpdate
.insert( toUpdate
.begin(), aRecord
);
596 UserRecord
PasswordContainer::CopyToUserRecord( const NamePasswordRecord
& aRecord
, bool& io_bTryToDecode
, const Reference
< XInteractionHandler
>& aHandler
)
598 ::std::vector
< OUString
> aPasswords
;
599 if( aRecord
.HasPasswords( MEMORY_RECORD
) )
600 aPasswords
= aRecord
.GetMemoryPasswords();
602 if( io_bTryToDecode
&& aRecord
.HasPasswords( PERSISTENT_RECORD
) )
606 ::std::vector
< OUString
> aDecodedPasswords
= DecodePasswords( aRecord
.GetPersistentPasswords(), aRecord
.GetPersistentIV(),
607 GetMasterPassword( aHandler
), css::task::PasswordRequestMode_PASSWORD_ENTER
);
608 aPasswords
.insert( aPasswords
.end(), aDecodedPasswords
.begin(), aDecodedPasswords
.end() );
610 catch( NoMasterException
& )
612 // if master password could not be detected the entry will be just ignored
613 io_bTryToDecode
= false;
617 return UserRecord( aRecord
.GetUserName(), comphelper::containerToSequence( aPasswords
) );
621 Sequence
< UserRecord
> PasswordContainer::CopyToUserRecordSequence( const std::vector
< NamePasswordRecord
>& original
, const Reference
< XInteractionHandler
>& aHandler
)
623 Sequence
< UserRecord
> aResult( original
.size() );
624 auto aResultRange
= asNonConstRange(aResult
);
626 bool bTryToDecode
= true;
628 for (auto const& aNPIter
: original
)
630 aResultRange
[nInd
] = CopyToUserRecord( aNPIter
, bTryToDecode
, aHandler
);
638 void SAL_CALL
PasswordContainer::add( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, const Reference
< XInteractionHandler
>& aHandler
)
640 std::unique_lock
aGuard( mMutex
);
642 PrivateAdd( Url
, UserName
, Passwords
, MEMORY_RECORD
, aHandler
);
646 void SAL_CALL
PasswordContainer::addPersistent( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, const Reference
< XInteractionHandler
>& aHandler
)
648 std::unique_lock
aGuard( mMutex
);
650 PrivateAdd( Url
, UserName
, Passwords
, PERSISTENT_RECORD
, aHandler
);
653 OUString
PasswordContainer::createIV()
655 rtlRandomPool randomPool
= mRandomPool
.get();
656 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
];
657 rtl_random_getBytes(randomPool
, iv
, RTL_DIGEST_LENGTH_MD5
);
658 OUStringBuffer aBuffer
;
659 for (sal_uInt8 i
: iv
)
661 aBuffer
.append(OUString::number(i
>> 4, 16) + OUString::number(i
& 15, 16));
663 return aBuffer
.makeStringAndClear();
666 void PasswordContainer::PrivateAdd( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, char Mode
, const Reference
< XInteractionHandler
>& aHandler
)
668 NamePasswordRecord
aRecord( UserName
);
669 ::std::vector
< OUString
> aStorePass
= comphelper::sequenceToContainer
< std::vector
<OUString
> >( Passwords
);
671 if( Mode
== PERSISTENT_RECORD
)
673 OUString sIV
= createIV();
674 OUString sEncodedPasswords
= EncodePasswords(aStorePass
, sIV
, GetMasterPassword(aHandler
));
675 aRecord
.SetPersistentPasswords(sEncodedPasswords
, sIV
);
677 else if( Mode
== MEMORY_RECORD
)
678 aRecord
.SetMemoryPasswords( std::move(aStorePass
) );
681 OSL_FAIL( "Unexpected persistence status!" );
685 if( !m_aContainer
.empty() )
687 PasswordMap::iterator aIter
= m_aContainer
.find( Url
);
689 if( aIter
!= m_aContainer
.end() )
691 UpdateVector( aIter
->first
, aIter
->second
, aRecord
, true );
696 std::vector
< NamePasswordRecord
> listToAdd( 1, aRecord
);
697 m_aContainer
.insert( PairUrlRecord( Url
, listToAdd
) );
699 if( Mode
== PERSISTENT_RECORD
&& m_xStorageFile
&& m_xStorageFile
->useStorage() )
700 m_xStorageFile
->update( Url
, aRecord
);
705 UrlRecord SAL_CALL
PasswordContainer::find( const OUString
& aURL
, const Reference
< XInteractionHandler
>& aHandler
)
707 return find( aURL
, u
"", false, aHandler
);
711 UrlRecord SAL_CALL
PasswordContainer::findForName( const OUString
& aURL
, const OUString
& aName
, const Reference
< XInteractionHandler
>& aHandler
)
713 return find( aURL
, aName
, true, aHandler
);
717 Sequence
< UserRecord
> PasswordContainer::FindUsr( const std::vector
< NamePasswordRecord
>& userlist
, std::u16string_view aName
, const Reference
< XInteractionHandler
>& aHandler
)
719 for (auto const& aNPIter
: userlist
)
721 if( aNPIter
.GetUserName() == aName
)
723 bool bTryToDecode
= true;
724 Sequence
< UserRecord
> aResult
{ CopyToUserRecord( aNPIter
, bTryToDecode
, aHandler
) };
730 return Sequence
< UserRecord
>();
734 bool PasswordContainer::createUrlRecord(
735 const PasswordMap::iterator
& rIter
,
737 std::u16string_view aName
,
738 const Reference
< XInteractionHandler
>& aHandler
,
743 Sequence
< UserRecord
> aUsrRec
744 = FindUsr( rIter
->second
, aName
, aHandler
);
745 if( aUsrRec
.hasElements() )
747 rRec
= UrlRecord( rIter
->first
, aUsrRec
);
755 CopyToUserRecordSequence( rIter
->second
, aHandler
) );
762 UrlRecord
PasswordContainer::find(
763 const OUString
& aURL
,
764 std::u16string_view aName
,
765 bool bName
, // only needed to support empty user names
766 const Reference
< XInteractionHandler
>& aHandler
)
768 std::unique_lock
aGuard( mMutex
);
770 if( !m_aContainer
.empty() && !aURL
.isEmpty() )
772 OUString
aUrl( aURL
);
774 // each iteration remove last '/...' section from the aUrl
775 // while it's possible, up to the most left '://'
778 // first look for <url>/somename and then look for <url>/somename/...
779 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
780 if( aIter
!= m_aContainer
.end() )
783 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
788 OUString
tmpUrl( aUrl
);
789 if ( !tmpUrl
.endsWith("/") )
792 aIter
= m_aContainer
.lower_bound( tmpUrl
);
793 if( aIter
!= m_aContainer
.end() && aIter
->first
.match( tmpUrl
) )
796 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
801 while( shorterUrl( aUrl
) && !aUrl
.isEmpty() );
807 OUString
PasswordContainer::GetDefaultMasterPassword()
809 OUStringBuffer aResult
;
810 for ( sal_Int32 nInd
= 0; nInd
< RTL_DIGEST_LENGTH_MD5
; nInd
++ )
811 aResult
.append("aa");
813 return aResult
.makeStringAndClear();
816 OUString
PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode
, const uno::Reference
< task::XInteractionHandler
>& xHandler
)
818 // empty string means that the call was cancelled or just failed
823 ::rtl::Reference
< MasterPasswordRequest_Impl
> xRequest
= new MasterPasswordRequest_Impl( aRMode
);
825 xHandler
->handle( xRequest
);
827 ::rtl::Reference
< ucbhelper::InteractionContinuation
> xSelection
= xRequest
->getSelection();
829 if ( xSelection
.is() )
831 Reference
< XInteractionAbort
> xAbort( xSelection
.get(), UNO_QUERY
);
834 const ::rtl::Reference
< ucbhelper::InteractionSupplyAuthentication
> & xSupp
835 = xRequest
->getAuthenticationSupplier();
837 aResult
= xSupp
->getPassword();
845 // Mangle the key to match an old bug
846 static OUString
ReencodeAsOldHash(std::u16string_view rPass
)
848 OUStringBuffer aBuffer
;
849 for (int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ++ind
)
851 auto tmp
= rPass
.substr(ind
* 2, 2);
852 unsigned char i
= static_cast<char>(rtl_ustr_toInt64_WithLength(tmp
.data(), 16, tmp
.size()));
853 aBuffer
.append(OUStringChar(static_cast< sal_Unicode
>('a' + (i
>> 4)))
854 + OUStringChar(static_cast< sal_Unicode
>('a' + (i
& 15))));
856 return aBuffer
.makeStringAndClear();
859 OUString
const & PasswordContainer::GetMasterPassword( const Reference
< XInteractionHandler
>& aHandler
)
861 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
862 if( !m_xStorageFile
|| !m_xStorageFile
->useStorage() )
863 throw NoMasterException("Password storing is not active!", Reference
< XInterface
>(), aRMode
);
865 if( m_aMasterPassword
.isEmpty() && aHandler
.is() )
867 OUString aEncodedMP
, aEncodedMPIV
;
868 bool bDefaultPassword
= false;
870 if( !m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
871 aRMode
= PasswordRequestMode_PASSWORD_CREATE
;
872 else if ( aEncodedMP
.isEmpty() )
874 m_aMasterPassword
= GetDefaultMasterPassword();
875 bDefaultPassword
= true;
878 if ( !bDefaultPassword
)
880 bool bAskAgain
= false;
884 OUString aPass
= RequestPasswordFromUser( aRMode
, aHandler
);
885 if ( !aPass
.isEmpty() )
887 if( aRMode
== PasswordRequestMode_PASSWORD_CREATE
)
889 m_aMasterPassword
= aPass
;
890 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
892 OUString sIV
= createIV();
893 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, sIV
, m_aMasterPassword
), sIV
);
897 if (m_xStorageFile
->getStorageVersion() == 0)
898 aPass
= ReencodeAsOldHash(aPass
);
900 std::vector
< OUString
> aRM( DecodePasswords( aEncodedMP
, aEncodedMPIV
, aPass
, aRMode
) );
901 if( aRM
.empty() || aPass
!= aRM
[0] )
904 aRMode
= PasswordRequestMode_PASSWORD_REENTER
;
907 m_aMasterPassword
= aPass
;
911 } while( bAskAgain
);
915 if ( m_aMasterPassword
.isEmpty() )
916 throw NoMasterException("No master password!", Reference
< XInterface
>(), aRMode
);
918 return m_aMasterPassword
;
922 void SAL_CALL
PasswordContainer::remove( const OUString
& aURL
, const OUString
& aName
)
924 std::unique_lock
aGuard( mMutex
);
926 OUString
aUrl( aURL
);
927 if( m_aContainer
.empty() )
930 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
932 if( aIter
== m_aContainer
.end() )
934 if( aUrl
.endsWith("/") )
935 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
939 aIter
= m_aContainer
.find( aUrl
);
942 if( aIter
== m_aContainer
.end() )
945 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
946 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
948 if (aNPIter
!= aIter
->second
.end())
950 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) && m_xStorageFile
)
951 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
953 // the iterator will not be used any more so it can be removed directly
954 aIter
->second
.erase( aNPIter
);
956 if( aIter
->second
.empty() )
957 m_aContainer
.erase( aIter
);
962 void SAL_CALL
PasswordContainer::removePersistent( const OUString
& aURL
, const OUString
& aName
)
964 std::unique_lock
aGuard( mMutex
);
966 OUString
aUrl( aURL
);
967 if( m_aContainer
.empty() )
970 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
972 if( aIter
== m_aContainer
.end() )
974 if( aUrl
.endsWith("/") )
975 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
979 aIter
= m_aContainer
.find( aUrl
);
982 if( aIter
== m_aContainer
.end() )
985 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
986 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
988 if (aNPIter
== aIter
->second
.end())
991 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
993 // TODO/LATER: should the password be converted to MemoryPassword?
994 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
996 if ( m_xStorageFile
)
997 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
1000 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1001 aIter
->second
.erase( aNPIter
);
1003 if( aIter
->second
.empty() )
1004 m_aContainer
.erase( aIter
);
1007 void SAL_CALL
PasswordContainer::removeAllPersistent()
1009 std::unique_lock
aGuard(mMutex
);
1010 removeAllPersistent(aGuard
);
1013 void PasswordContainer::removeAllPersistent(std::unique_lock
<std::mutex
>& /*rGuard*/)
1015 if( m_xStorageFile
)
1016 m_xStorageFile
->clear();
1018 for( PasswordMap::iterator aIter
= m_aContainer
.begin(); aIter
!= m_aContainer
.end(); )
1020 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= aIter
->second
.begin(); aNPIter
!= aIter
->second
.end(); )
1022 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1024 // TODO/LATER: should the password be converted to MemoryPassword?
1025 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1027 if ( m_xStorageFile
)
1028 m_xStorageFile
->remove( aIter
->first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1031 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1033 aNPIter
= aIter
->second
.erase(aNPIter
);
1039 if( aIter
->second
.empty() )
1041 aIter
= m_aContainer
.erase(aIter
);
1048 Sequence
< UrlRecord
> SAL_CALL
PasswordContainer::getAllPersistent( const Reference
< XInteractionHandler
>& xHandler
)
1050 std::unique_lock
aGuard( mMutex
);
1051 return getAllPersistent(aGuard
, xHandler
);
1054 Sequence
< UrlRecord
> PasswordContainer::getAllPersistent( std::unique_lock
<std::mutex
>& /*rGuard*/, const Reference
< XInteractionHandler
>& xHandler
)
1056 Sequence
< UrlRecord
> aResult
;
1058 for( const auto& rEntry
: m_aContainer
)
1060 Sequence
< UserRecord
> aUsers
;
1061 for (auto const& aNP
: rEntry
.second
)
1062 if( aNP
.HasPasswords( PERSISTENT_RECORD
) )
1064 sal_Int32 oldLen
= aUsers
.getLength();
1065 aUsers
.realloc( oldLen
+ 1 );
1066 aUsers
.getArray()[ oldLen
] = UserRecord( aNP
.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP
.GetPersistentPasswords(), aNP
.GetPersistentIV(),
1067 GetMasterPassword( xHandler
), css::task::PasswordRequestMode_PASSWORD_ENTER
) ) );
1070 if( aUsers
.hasElements() )
1072 sal_Int32 oldLen
= aResult
.getLength();
1073 aResult
.realloc( oldLen
+ 1 );
1074 aResult
.getArray()[ oldLen
] = UrlRecord( rEntry
.first
, aUsers
);
1081 sal_Bool SAL_CALL
PasswordContainer::authorizateWithMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1083 std::unique_lock
aGuard( mMutex
);
1084 return authorizateWithMasterPassword(aGuard
, xHandler
);
1087 bool PasswordContainer::authorizateWithMasterPassword( std::unique_lock
<std::mutex
>& /*rGuard*/, const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1089 bool bResult
= false;
1090 OUString aEncodedMP
, aEncodedMPIV
;
1091 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1093 // the method should fail if there is no master password
1094 if( m_xStorageFile
&& m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1096 if ( aEncodedMP
.isEmpty() )
1098 // this is a default master password
1099 // no UI is necessary
1104 if ( !xTmpHandler
.is() )
1106 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1107 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1108 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1111 if ( !m_aMasterPassword
.isEmpty() )
1113 // there is a password, it should be just rechecked
1114 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
1118 aPass
= RequestPasswordFromUser( aRMode
, xTmpHandler
);
1120 if (!aPass
.isEmpty() && m_xStorageFile
->getStorageVersion() == 0)
1122 aPass
= ReencodeAsOldHash(aPass
);
1125 bResult
= ( !aPass
.isEmpty() && aPass
== m_aMasterPassword
);
1126 aRMode
= PasswordRequestMode_PASSWORD_REENTER
; // further questions with error notification
1127 } while( !bResult
&& !aPass
.isEmpty() );
1133 // ask for the password, if user provide no correct password an exception will be thrown
1134 bResult
= !GetMasterPassword( xTmpHandler
).isEmpty();
1136 catch( uno::Exception
& )
1145 sal_Bool SAL_CALL
PasswordContainer::changeMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1147 bool bResult
= false;
1148 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1149 std::unique_lock
aGuard( mMutex
);
1151 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1153 if ( !xTmpHandler
.is() )
1155 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1156 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1157 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1160 bool bCanChangePassword
= true;
1161 // if there is already a stored master password it should be entered by the user before the change happen
1162 OUString aEncodedMP
, aEncodedMPIV
;
1163 if( !m_aMasterPassword
.isEmpty() || m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1164 bCanChangePassword
= authorizateWithMasterPassword( aGuard
, xTmpHandler
);
1166 if ( bCanChangePassword
)
1168 // ask for the new password, but do not set it
1169 OUString aPass
= RequestPasswordFromUser( PasswordRequestMode_PASSWORD_CREATE
, xTmpHandler
);
1171 if ( !aPass
.isEmpty() )
1173 // get all the persistent entries if it is possible
1174 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( aGuard
, uno::Reference
< task::XInteractionHandler
>() );
1176 // remove the master password and the entries persistence
1177 removeMasterPassword(aGuard
);
1179 // store the new master password
1180 m_aMasterPassword
= aPass
;
1181 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
1182 OUString aIV
= createIV();
1183 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, aIV
, m_aMasterPassword
), aIV
);
1185 // store all the entries with the new password
1186 for ( const auto& rURL
: aPersistent
)
1187 for ( const auto& rUser
: rURL
.UserList
)
1188 PrivateAdd( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
, PERSISTENT_RECORD
,
1189 uno::Reference
< task::XInteractionHandler
>() );
1199 void SAL_CALL
PasswordContainer::removeMasterPassword()
1201 std::unique_lock
aGuard(mMutex
);
1202 removeMasterPassword(aGuard
);
1205 void PasswordContainer::removeMasterPassword(std::unique_lock
<std::mutex
>& rGuard
)
1207 // remove all the stored passwords and the master password
1208 removeAllPersistent(rGuard
);
1210 if ( m_xStorageFile
)
1212 m_aMasterPassword
.clear();
1213 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
1217 sal_Bool SAL_CALL
PasswordContainer::hasMasterPassword( )
1219 std::unique_lock
aGuard( mMutex
);
1221 if ( !m_xStorageFile
)
1222 throw uno::RuntimeException();
1224 OUString aEncodedMP
, aEncodedMPIV
;
1225 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) );
1228 sal_Bool SAL_CALL
PasswordContainer::allowPersistentStoring( sal_Bool bAllow
)
1230 std::unique_lock
aGuard( mMutex
);
1232 if ( !m_xStorageFile
)
1233 throw uno::RuntimeException();
1236 removeMasterPassword(aGuard
);
1238 if (m_xStorageFile
->useStorage() == static_cast<bool>(bAllow
))
1241 m_xStorageFile
->setUseStorage( bAllow
);
1245 sal_Bool SAL_CALL
PasswordContainer::isPersistentStoringAllowed()
1247 std::unique_lock
aGuard( mMutex
);
1249 if ( !m_xStorageFile
)
1250 throw uno::RuntimeException();
1252 return m_xStorageFile
->useStorage();
1255 sal_Bool SAL_CALL
PasswordContainer::useDefaultMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1257 bool bResult
= false;
1258 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1259 std::unique_lock
aGuard( mMutex
);
1261 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1263 if ( !xTmpHandler
.is() )
1265 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1266 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1267 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1270 bool bCanChangePassword
= true;
1271 // if there is already a stored nondefault master password it should be entered by the user before the change happen
1272 OUString aEncodedMP
, aEncodedMPIV
;
1273 if( m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && !aEncodedMP
.isEmpty() )
1274 bCanChangePassword
= authorizateWithMasterPassword( aGuard
, xTmpHandler
);
1276 if ( bCanChangePassword
)
1278 // generate the default password
1279 OUString aPass
= GetDefaultMasterPassword();
1280 if ( !aPass
.isEmpty() )
1282 // get all the persistent entries if it is possible
1283 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( aGuard
, uno::Reference
< task::XInteractionHandler
>() );
1285 // remove the master password and the entries persistence
1286 removeMasterPassword(aGuard
);
1288 // store the empty string to flag the default master password
1289 m_aMasterPassword
= aPass
;
1290 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString(), true );
1292 // store all the entries with the new password
1293 for ( const auto& rURL
: aPersistent
)
1294 for ( const auto& rUser
: rURL
.UserList
)
1295 PrivateAdd( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
, PERSISTENT_RECORD
,
1296 uno::Reference
< task::XInteractionHandler
>() );
1307 sal_Bool SAL_CALL
PasswordContainer::isDefaultMasterPasswordUsed()
1309 std::unique_lock
aGuard( mMutex
);
1311 if ( !m_xStorageFile
)
1312 throw uno::RuntimeException();
1314 OUString aEncodedMP
, aEncodedMPIV
;
1315 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && aEncodedMP
.isEmpty() );
1319 void SAL_CALL
PasswordContainer::addUrl( const OUString
& Url
, sal_Bool MakePersistent
)
1321 mUrlContainer
.add( Url
, MakePersistent
);
1324 OUString SAL_CALL
PasswordContainer::findUrl( const OUString
& Url
)
1326 return mUrlContainer
.find( Url
);
1329 void SAL_CALL
PasswordContainer::removeUrl( const OUString
& Url
)
1331 mUrlContainer
.remove( Url
);
1334 uno::Sequence
< OUString
> SAL_CALL
PasswordContainer::getUrls( sal_Bool OnlyPersistent
)
1336 return mUrlContainer
.list( OnlyPersistent
);
1340 void PasswordContainer::Notify()
1342 std::unique_lock
aGuard( mMutex
);
1344 // remove the cached persistent values in the memory
1345 for( auto& rEntry
: m_aContainer
)
1347 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= rEntry
.second
.begin(); aNPIter
!= rEntry
.second
.end(); )
1349 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1351 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1353 if ( m_xStorageFile
)
1354 m_xStorageFile
->remove( rEntry
.first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1357 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1359 aNPIter
= rEntry
.second
.erase(aNPIter
);
1367 if( m_xStorageFile
)
1368 addon
= m_xStorageFile
->getInfo();
1370 for( const auto& rEntry
: addon
)
1372 PasswordMap::iterator aSearchIter
= m_aContainer
.find( rEntry
.first
);
1373 if( aSearchIter
!= m_aContainer
.end() )
1374 for (auto const& aNP
: rEntry
.second
)
1375 UpdateVector( aSearchIter
->first
, aSearchIter
->second
, aNP
, false );
1377 m_aContainer
.insert( PairUrlRecord( rEntry
.first
, rEntry
.second
) );
1381 OUString SAL_CALL
PasswordContainer::getImplementationName( )
1383 return "stardiv.svl.PasswordContainer";
1386 sal_Bool SAL_CALL
PasswordContainer::supportsService( const OUString
& ServiceName
)
1388 return cppu::supportsService( this, ServiceName
);
1391 Sequence
< OUString
> SAL_CALL
PasswordContainer::getSupportedServiceNames( )
1393 return { "com.sun.star.task.PasswordContainer" };
1396 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1397 svl_PasswordContainer_get_implementation(
1398 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1400 return cppu::acquire(new PasswordContainer(context
));
1404 MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode
)
1406 MasterPasswordRequest aRequest
;
1408 aRequest
.Classification
= InteractionClassification_ERROR
;
1409 aRequest
.Mode
= Mode
;
1411 setRequest( Any( aRequest
) );
1413 // Fill continuations...
1414 Sequence
< RememberAuthentication
> aRememberModes
{ RememberAuthentication_NO
};
1417 = new ::ucbhelper::InteractionSupplyAuthentication(
1419 false, // bCanSetRealm
1420 false, // bCanSetUserName
1421 true, // bCanSetPassword
1422 false, // bCanSetAccount
1423 aRememberModes
, // rRememberPasswordModes
1424 RememberAuthentication_NO
, // eDefaultRememberPasswordMode
1425 aRememberModes
, // rRememberAccountModes
1426 RememberAuthentication_NO
, // eDefaultRememberAccountMode
1427 false // bCanUseSystemCredentials
1431 Reference
< XInteractionContinuation
> > aContinuations
{
1432 new ::ucbhelper::InteractionAbort( this ),
1433 new ::ucbhelper::InteractionRetry( this ),
1437 setContinuations( aContinuations
);
1442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */