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 unsigned char iv
[RTL_DIGEST_LENGTH_MD5
];
656 if (rtl_random_getBytes(nullptr, iv
, RTL_DIGEST_LENGTH_MD5
) != rtl_Random_E_None
)
658 throw uno::RuntimeException("rtl_random_getBytes failed");
660 OUStringBuffer aBuffer
;
661 for (sal_uInt8 i
: iv
)
663 aBuffer
.append(OUString::number(i
>> 4, 16) + OUString::number(i
& 15, 16));
665 return aBuffer
.makeStringAndClear();
668 void PasswordContainer::PrivateAdd( const OUString
& Url
, const OUString
& UserName
, const Sequence
< OUString
>& Passwords
, char Mode
, const Reference
< XInteractionHandler
>& aHandler
)
670 NamePasswordRecord
aRecord( UserName
);
671 ::std::vector
< OUString
> aStorePass
= comphelper::sequenceToContainer
< std::vector
<OUString
> >( Passwords
);
673 if( Mode
== PERSISTENT_RECORD
)
675 OUString sIV
= createIV();
676 OUString sEncodedPasswords
= EncodePasswords(aStorePass
, sIV
, GetMasterPassword(aHandler
));
677 aRecord
.SetPersistentPasswords(sEncodedPasswords
, sIV
);
679 else if( Mode
== MEMORY_RECORD
)
680 aRecord
.SetMemoryPasswords( std::move(aStorePass
) );
683 OSL_FAIL( "Unexpected persistence status!" );
687 if( !m_aContainer
.empty() )
689 PasswordMap::iterator aIter
= m_aContainer
.find( Url
);
691 if( aIter
!= m_aContainer
.end() )
693 UpdateVector( aIter
->first
, aIter
->second
, aRecord
, true );
698 std::vector
< NamePasswordRecord
> listToAdd( 1, aRecord
);
699 m_aContainer
.insert( PairUrlRecord( Url
, listToAdd
) );
701 if( Mode
== PERSISTENT_RECORD
&& m_xStorageFile
&& m_xStorageFile
->useStorage() )
702 m_xStorageFile
->update( Url
, aRecord
);
707 UrlRecord SAL_CALL
PasswordContainer::find( const OUString
& aURL
, const Reference
< XInteractionHandler
>& aHandler
)
709 return find( aURL
, u
"", false, aHandler
);
713 UrlRecord SAL_CALL
PasswordContainer::findForName( const OUString
& aURL
, const OUString
& aName
, const Reference
< XInteractionHandler
>& aHandler
)
715 return find( aURL
, aName
, true, aHandler
);
719 Sequence
< UserRecord
> PasswordContainer::FindUsr( const std::vector
< NamePasswordRecord
>& userlist
, std::u16string_view aName
, const Reference
< XInteractionHandler
>& aHandler
)
721 for (auto const& aNPIter
: userlist
)
723 if( aNPIter
.GetUserName() == aName
)
725 bool bTryToDecode
= true;
726 Sequence
< UserRecord
> aResult
{ CopyToUserRecord( aNPIter
, bTryToDecode
, aHandler
) };
732 return Sequence
< UserRecord
>();
736 bool PasswordContainer::createUrlRecord(
737 const PasswordMap::iterator
& rIter
,
739 std::u16string_view aName
,
740 const Reference
< XInteractionHandler
>& aHandler
,
745 Sequence
< UserRecord
> aUsrRec
746 = FindUsr( rIter
->second
, aName
, aHandler
);
747 if( aUsrRec
.hasElements() )
749 rRec
= UrlRecord( rIter
->first
, aUsrRec
);
757 CopyToUserRecordSequence( rIter
->second
, aHandler
) );
764 UrlRecord
PasswordContainer::find(
765 const OUString
& aURL
,
766 std::u16string_view aName
,
767 bool bName
, // only needed to support empty user names
768 const Reference
< XInteractionHandler
>& aHandler
)
770 std::unique_lock
aGuard( mMutex
);
772 if( !m_aContainer
.empty() && !aURL
.isEmpty() )
774 OUString
aUrl( aURL
);
776 // each iteration remove last '/...' section from the aUrl
777 // while it's possible, up to the most left '://'
780 // first look for <url>/somename and then look for <url>/somename/...
781 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
782 if( aIter
!= m_aContainer
.end() )
785 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
790 OUString
tmpUrl( aUrl
);
791 if ( !tmpUrl
.endsWith("/") )
794 aIter
= m_aContainer
.lower_bound( tmpUrl
);
795 if( aIter
!= m_aContainer
.end() && aIter
->first
.match( tmpUrl
) )
798 if ( createUrlRecord( aIter
, bName
, aName
, aHandler
, aRec
) )
803 while( shorterUrl( aUrl
) && !aUrl
.isEmpty() );
809 OUString
PasswordContainer::GetDefaultMasterPassword()
811 OUStringBuffer aResult
;
812 for ( sal_Int32 nInd
= 0; nInd
< RTL_DIGEST_LENGTH_MD5
; nInd
++ )
813 aResult
.append("aa");
815 return aResult
.makeStringAndClear();
818 OUString
PasswordContainer::RequestPasswordFromUser( PasswordRequestMode aRMode
, const uno::Reference
< task::XInteractionHandler
>& xHandler
)
820 // empty string means that the call was cancelled or just failed
825 ::rtl::Reference
< MasterPasswordRequest_Impl
> xRequest
= new MasterPasswordRequest_Impl( aRMode
);
827 xHandler
->handle( xRequest
);
829 ::rtl::Reference
< ucbhelper::InteractionContinuation
> xSelection
= xRequest
->getSelection();
831 if ( xSelection
.is() )
833 Reference
< XInteractionAbort
> xAbort( xSelection
.get(), UNO_QUERY
);
836 const ::rtl::Reference
< ucbhelper::InteractionSupplyAuthentication
> & xSupp
837 = xRequest
->getAuthenticationSupplier();
839 aResult
= xSupp
->getPassword();
847 // Mangle the key to match an old bug
848 static OUString
ReencodeAsOldHash(std::u16string_view rPass
)
850 OUStringBuffer aBuffer
;
851 for (int ind
= 0; ind
< RTL_DIGEST_LENGTH_MD5
; ++ind
)
853 auto tmp
= rPass
.substr(ind
* 2, 2);
854 unsigned char i
= static_cast<char>(rtl_ustr_toInt64_WithLength(tmp
.data(), 16, tmp
.size()));
855 aBuffer
.append(OUStringChar(static_cast< sal_Unicode
>('a' + (i
>> 4)))
856 + OUStringChar(static_cast< sal_Unicode
>('a' + (i
& 15))));
858 return aBuffer
.makeStringAndClear();
861 OUString
const & PasswordContainer::GetMasterPassword( const Reference
< XInteractionHandler
>& aHandler
)
863 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
864 if( !m_xStorageFile
|| !m_xStorageFile
->useStorage() )
865 throw NoMasterException("Password storing is not active!", Reference
< XInterface
>(), aRMode
);
867 if( m_aMasterPassword
.isEmpty() && aHandler
.is() )
869 OUString aEncodedMP
, aEncodedMPIV
;
870 bool bDefaultPassword
= false;
872 if( !m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
873 aRMode
= PasswordRequestMode_PASSWORD_CREATE
;
874 else if ( aEncodedMP
.isEmpty() )
876 m_aMasterPassword
= GetDefaultMasterPassword();
877 bDefaultPassword
= true;
880 if ( !bDefaultPassword
)
882 bool bAskAgain
= false;
886 OUString aPass
= RequestPasswordFromUser( aRMode
, aHandler
);
887 if ( !aPass
.isEmpty() )
889 if( aRMode
== PasswordRequestMode_PASSWORD_CREATE
)
891 m_aMasterPassword
= aPass
;
892 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
894 OUString sIV
= createIV();
895 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, sIV
, m_aMasterPassword
), sIV
);
899 if (m_xStorageFile
->getStorageVersion() == 0)
900 aPass
= ReencodeAsOldHash(aPass
);
902 std::vector
< OUString
> aRM( DecodePasswords( aEncodedMP
, aEncodedMPIV
, aPass
, aRMode
) );
903 if( aRM
.empty() || aPass
!= aRM
[0] )
906 aRMode
= PasswordRequestMode_PASSWORD_REENTER
;
909 m_aMasterPassword
= aPass
;
913 } while( bAskAgain
);
917 if ( m_aMasterPassword
.isEmpty() )
918 throw NoMasterException("No master password!", Reference
< XInterface
>(), aRMode
);
920 return m_aMasterPassword
;
924 void SAL_CALL
PasswordContainer::remove( const OUString
& aURL
, const OUString
& aName
)
926 std::unique_lock
aGuard( mMutex
);
928 OUString
aUrl( aURL
);
929 if( m_aContainer
.empty() )
932 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
934 if( aIter
== m_aContainer
.end() )
936 if( aUrl
.endsWith("/") )
937 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
941 aIter
= m_aContainer
.find( aUrl
);
944 if( aIter
== m_aContainer
.end() )
947 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
948 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
950 if (aNPIter
!= aIter
->second
.end())
952 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) && m_xStorageFile
)
953 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
955 // the iterator will not be used any more so it can be removed directly
956 aIter
->second
.erase( aNPIter
);
958 if( aIter
->second
.empty() )
959 m_aContainer
.erase( aIter
);
964 void SAL_CALL
PasswordContainer::removePersistent( const OUString
& aURL
, const OUString
& aName
)
966 std::unique_lock
aGuard( mMutex
);
968 OUString
aUrl( aURL
);
969 if( m_aContainer
.empty() )
972 PasswordMap::iterator aIter
= m_aContainer
.find( aUrl
);
974 if( aIter
== m_aContainer
.end() )
976 if( aUrl
.endsWith("/") )
977 aUrl
= aUrl
.copy( 0, aUrl
.getLength() - 1 );
981 aIter
= m_aContainer
.find( aUrl
);
984 if( aIter
== m_aContainer
.end() )
987 auto aNPIter
= std::find_if(aIter
->second
.begin(), aIter
->second
.end(),
988 [&aName
](const NamePasswordRecord
& rNPRecord
) { return rNPRecord
.GetUserName() == aName
; });
990 if (aNPIter
== aIter
->second
.end())
993 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
995 // TODO/LATER: should the password be converted to MemoryPassword?
996 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
998 if ( m_xStorageFile
)
999 m_xStorageFile
->remove( aURL
, aName
); // remove record ( aURL, aName )
1002 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1003 aIter
->second
.erase( aNPIter
);
1005 if( aIter
->second
.empty() )
1006 m_aContainer
.erase( aIter
);
1009 void SAL_CALL
PasswordContainer::removeAllPersistent()
1011 std::unique_lock
aGuard(mMutex
);
1012 removeAllPersistent(aGuard
);
1015 void PasswordContainer::removeAllPersistent(std::unique_lock
<std::mutex
>& /*rGuard*/)
1017 if( m_xStorageFile
)
1018 m_xStorageFile
->clear();
1020 for( PasswordMap::iterator aIter
= m_aContainer
.begin(); aIter
!= m_aContainer
.end(); )
1022 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= aIter
->second
.begin(); aNPIter
!= aIter
->second
.end(); )
1024 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1026 // TODO/LATER: should the password be converted to MemoryPassword?
1027 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1029 if ( m_xStorageFile
)
1030 m_xStorageFile
->remove( aIter
->first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1033 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1035 aNPIter
= aIter
->second
.erase(aNPIter
);
1041 if( aIter
->second
.empty() )
1043 aIter
= m_aContainer
.erase(aIter
);
1050 Sequence
< UrlRecord
> SAL_CALL
PasswordContainer::getAllPersistent( const Reference
< XInteractionHandler
>& xHandler
)
1052 std::unique_lock
aGuard( mMutex
);
1053 return getAllPersistent(aGuard
, xHandler
);
1056 Sequence
< UrlRecord
> PasswordContainer::getAllPersistent( std::unique_lock
<std::mutex
>& /*rGuard*/, const Reference
< XInteractionHandler
>& xHandler
)
1058 Sequence
< UrlRecord
> aResult
;
1060 for( const auto& rEntry
: m_aContainer
)
1062 Sequence
< UserRecord
> aUsers
;
1063 for (auto const& aNP
: rEntry
.second
)
1064 if( aNP
.HasPasswords( PERSISTENT_RECORD
) )
1066 sal_Int32 oldLen
= aUsers
.getLength();
1067 aUsers
.realloc( oldLen
+ 1 );
1068 aUsers
.getArray()[ oldLen
] = UserRecord( aNP
.GetUserName(), comphelper::containerToSequence( DecodePasswords( aNP
.GetPersistentPasswords(), aNP
.GetPersistentIV(),
1069 GetMasterPassword( xHandler
), css::task::PasswordRequestMode_PASSWORD_ENTER
) ) );
1072 if( aUsers
.hasElements() )
1074 sal_Int32 oldLen
= aResult
.getLength();
1075 aResult
.realloc( oldLen
+ 1 );
1076 aResult
.getArray()[ oldLen
] = UrlRecord( rEntry
.first
, aUsers
);
1083 sal_Bool SAL_CALL
PasswordContainer::authorizateWithMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1085 std::unique_lock
aGuard( mMutex
);
1086 return authorizateWithMasterPassword(aGuard
, xHandler
);
1089 bool PasswordContainer::authorizateWithMasterPassword( std::unique_lock
<std::mutex
>& /*rGuard*/, const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1091 bool bResult
= false;
1092 OUString aEncodedMP
, aEncodedMPIV
;
1093 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1095 // the method should fail if there is no master password
1096 if( m_xStorageFile
&& m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1098 if ( aEncodedMP
.isEmpty() )
1100 // this is a default master password
1101 // no UI is necessary
1106 if ( !xTmpHandler
.is() )
1108 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1109 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1110 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1113 if ( !m_aMasterPassword
.isEmpty() )
1115 // there is a password, it should be just rechecked
1116 PasswordRequestMode aRMode
= PasswordRequestMode_PASSWORD_ENTER
;
1120 aPass
= RequestPasswordFromUser( aRMode
, xTmpHandler
);
1122 if (!aPass
.isEmpty() && m_xStorageFile
->getStorageVersion() == 0)
1124 aPass
= ReencodeAsOldHash(aPass
);
1127 bResult
= ( !aPass
.isEmpty() && aPass
== m_aMasterPassword
);
1128 aRMode
= PasswordRequestMode_PASSWORD_REENTER
; // further questions with error notification
1129 } while( !bResult
&& !aPass
.isEmpty() );
1135 // ask for the password, if user provide no correct password an exception will be thrown
1136 bResult
= !GetMasterPassword( xTmpHandler
).isEmpty();
1138 catch( uno::Exception
& )
1147 sal_Bool SAL_CALL
PasswordContainer::changeMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1149 bool bResult
= false;
1150 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1151 std::unique_lock
aGuard( mMutex
);
1153 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1155 if ( !xTmpHandler
.is() )
1157 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1158 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1159 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1162 bool bCanChangePassword
= true;
1163 // if there is already a stored master password it should be entered by the user before the change happen
1164 OUString aEncodedMP
, aEncodedMPIV
;
1165 if( !m_aMasterPassword
.isEmpty() || m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) )
1166 bCanChangePassword
= authorizateWithMasterPassword( aGuard
, xTmpHandler
);
1168 if ( bCanChangePassword
)
1170 // ask for the new password, but do not set it
1171 OUString aPass
= RequestPasswordFromUser( PasswordRequestMode_PASSWORD_CREATE
, xTmpHandler
);
1173 if ( !aPass
.isEmpty() )
1175 // get all the persistent entries if it is possible
1176 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( aGuard
, uno::Reference
< task::XInteractionHandler
>() );
1178 // remove the master password and the entries persistence
1179 removeMasterPassword(aGuard
);
1181 // store the new master password
1182 m_aMasterPassword
= aPass
;
1183 std::vector
< OUString
> aMaster( 1, m_aMasterPassword
);
1184 OUString aIV
= createIV();
1185 m_xStorageFile
->setEncodedMasterPassword(EncodePasswords(aMaster
, aIV
, m_aMasterPassword
), aIV
);
1187 // store all the entries with the new password
1188 for ( const auto& rURL
: aPersistent
)
1189 for ( const auto& rUser
: rURL
.UserList
)
1190 PrivateAdd( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
, PERSISTENT_RECORD
,
1191 uno::Reference
< task::XInteractionHandler
>() );
1201 void SAL_CALL
PasswordContainer::removeMasterPassword()
1203 std::unique_lock
aGuard(mMutex
);
1204 removeMasterPassword(aGuard
);
1207 void PasswordContainer::removeMasterPassword(std::unique_lock
<std::mutex
>& rGuard
)
1209 // remove all the stored passwords and the master password
1210 removeAllPersistent(rGuard
);
1212 if ( m_xStorageFile
)
1214 m_aMasterPassword
.clear();
1215 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString() ); // let the master password be removed from configuration
1219 sal_Bool SAL_CALL
PasswordContainer::hasMasterPassword( )
1221 std::unique_lock
aGuard( mMutex
);
1223 if ( !m_xStorageFile
)
1224 throw uno::RuntimeException();
1226 OUString aEncodedMP
, aEncodedMPIV
;
1227 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) );
1230 sal_Bool SAL_CALL
PasswordContainer::allowPersistentStoring( sal_Bool bAllow
)
1232 std::unique_lock
aGuard( mMutex
);
1234 if ( !m_xStorageFile
)
1235 throw uno::RuntimeException();
1238 removeMasterPassword(aGuard
);
1240 if (m_xStorageFile
->useStorage() == static_cast<bool>(bAllow
))
1243 m_xStorageFile
->setUseStorage( bAllow
);
1247 sal_Bool SAL_CALL
PasswordContainer::isPersistentStoringAllowed()
1249 std::unique_lock
aGuard( mMutex
);
1251 if ( !m_xStorageFile
)
1252 throw uno::RuntimeException();
1254 return m_xStorageFile
->useStorage();
1257 sal_Bool SAL_CALL
PasswordContainer::useDefaultMasterPassword( const uno::Reference
< task::XInteractionHandler
>& xHandler
)
1259 bool bResult
= false;
1260 uno::Reference
< task::XInteractionHandler
> xTmpHandler
= xHandler
;
1261 std::unique_lock
aGuard( mMutex
);
1263 if ( m_xStorageFile
&& m_xStorageFile
->useStorage() )
1265 if ( !xTmpHandler
.is() )
1267 uno::Reference
< lang::XMultiServiceFactory
> xFactory( mComponent
, uno::UNO_QUERY_THROW
);
1268 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getComponentContext(xFactory
) );
1269 xTmpHandler
.set( InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
1272 bool bCanChangePassword
= true;
1273 // if there is already a stored nondefault master password it should be entered by the user before the change happen
1274 OUString aEncodedMP
, aEncodedMPIV
;
1275 if( m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && !aEncodedMP
.isEmpty() )
1276 bCanChangePassword
= authorizateWithMasterPassword( aGuard
, xTmpHandler
);
1278 if ( bCanChangePassword
)
1280 // generate the default password
1281 OUString aPass
= GetDefaultMasterPassword();
1282 if ( !aPass
.isEmpty() )
1284 // get all the persistent entries if it is possible
1285 const Sequence
< UrlRecord
> aPersistent
= getAllPersistent( aGuard
, uno::Reference
< task::XInteractionHandler
>() );
1287 // remove the master password and the entries persistence
1288 removeMasterPassword(aGuard
);
1290 // store the empty string to flag the default master password
1291 m_aMasterPassword
= aPass
;
1292 m_xStorageFile
->setEncodedMasterPassword( OUString(), OUString(), true );
1294 // store all the entries with the new password
1295 for ( const auto& rURL
: aPersistent
)
1296 for ( const auto& rUser
: rURL
.UserList
)
1297 PrivateAdd( rURL
.Url
, rUser
.UserName
, rUser
.Passwords
, PERSISTENT_RECORD
,
1298 uno::Reference
< task::XInteractionHandler
>() );
1309 sal_Bool SAL_CALL
PasswordContainer::isDefaultMasterPasswordUsed()
1311 std::unique_lock
aGuard( mMutex
);
1313 if ( !m_xStorageFile
)
1314 throw uno::RuntimeException();
1316 OUString aEncodedMP
, aEncodedMPIV
;
1317 return ( m_xStorageFile
->useStorage() && m_xStorageFile
->getEncodedMasterPassword( aEncodedMP
, aEncodedMPIV
) && aEncodedMP
.isEmpty() );
1321 void SAL_CALL
PasswordContainer::addUrl( const OUString
& Url
, sal_Bool MakePersistent
)
1323 mUrlContainer
.add( Url
, MakePersistent
);
1326 OUString SAL_CALL
PasswordContainer::findUrl( const OUString
& Url
)
1328 return mUrlContainer
.find( Url
);
1331 void SAL_CALL
PasswordContainer::removeUrl( const OUString
& Url
)
1333 mUrlContainer
.remove( Url
);
1336 uno::Sequence
< OUString
> SAL_CALL
PasswordContainer::getUrls( sal_Bool OnlyPersistent
)
1338 return mUrlContainer
.list( OnlyPersistent
);
1342 void PasswordContainer::Notify()
1344 std::unique_lock
aGuard( mMutex
);
1346 // remove the cached persistent values in the memory
1347 for( auto& rEntry
: m_aContainer
)
1349 for( std::vector
< NamePasswordRecord
>::iterator aNPIter
= rEntry
.second
.begin(); aNPIter
!= rEntry
.second
.end(); )
1351 if( aNPIter
->HasPasswords( PERSISTENT_RECORD
) )
1353 aNPIter
->RemovePasswords( PERSISTENT_RECORD
);
1355 if ( m_xStorageFile
)
1356 m_xStorageFile
->remove( rEntry
.first
, aNPIter
->GetUserName() ); // remove record ( aURL, aName )
1359 if( !aNPIter
->HasPasswords( MEMORY_RECORD
) )
1361 aNPIter
= rEntry
.second
.erase(aNPIter
);
1369 if( m_xStorageFile
)
1370 addon
= m_xStorageFile
->getInfo();
1372 for( const auto& rEntry
: addon
)
1374 PasswordMap::iterator aSearchIter
= m_aContainer
.find( rEntry
.first
);
1375 if( aSearchIter
!= m_aContainer
.end() )
1376 for (auto const& aNP
: rEntry
.second
)
1377 UpdateVector( aSearchIter
->first
, aSearchIter
->second
, aNP
, false );
1379 m_aContainer
.insert( PairUrlRecord( rEntry
.first
, rEntry
.second
) );
1383 OUString SAL_CALL
PasswordContainer::getImplementationName( )
1385 return "stardiv.svl.PasswordContainer";
1388 sal_Bool SAL_CALL
PasswordContainer::supportsService( const OUString
& ServiceName
)
1390 return cppu::supportsService( this, ServiceName
);
1393 Sequence
< OUString
> SAL_CALL
PasswordContainer::getSupportedServiceNames( )
1395 return { "com.sun.star.task.PasswordContainer" };
1398 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1399 svl_PasswordContainer_get_implementation(
1400 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1402 return cppu::acquire(new PasswordContainer(context
));
1406 MasterPasswordRequest_Impl::MasterPasswordRequest_Impl( PasswordRequestMode Mode
)
1408 MasterPasswordRequest aRequest
;
1410 aRequest
.Classification
= InteractionClassification_ERROR
;
1411 aRequest
.Mode
= Mode
;
1413 setRequest( Any( aRequest
) );
1415 // Fill continuations...
1416 Sequence
< RememberAuthentication
> aRememberModes
{ RememberAuthentication_NO
};
1419 = new ::ucbhelper::InteractionSupplyAuthentication(
1421 false, // bCanSetRealm
1422 false, // bCanSetUserName
1423 true, // bCanSetPassword
1424 false, // bCanSetAccount
1425 aRememberModes
, // rRememberPasswordModes
1426 RememberAuthentication_NO
, // eDefaultRememberPasswordMode
1427 aRememberModes
, // rRememberAccountModes
1428 RememberAuthentication_NO
, // eDefaultRememberAccountMode
1429 false // bCanUseSystemCredentials
1433 Reference
< XInteractionContinuation
> > aContinuations
{
1434 new ::ucbhelper::InteractionAbort( this ),
1435 new ::ucbhelper::InteractionRetry( this ),
1439 setContinuations( aContinuations
);
1444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */