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 .
21 #include "dp_persmap.h"
22 #include "rtl/strbuf.hxx"
24 #ifndef DISABLE_BDB2PMAP
28 using namespace ::rtl
;
30 // the persistent map is used to manage a handful of key-value string pairs
31 // this implementation replaces a rather heavy-weight berkeleydb integration
33 // the file backing up a persistent map consists of line pairs with
34 // - a key string (encoded with chars 0x00..0x0F being escaped)
35 // - a value string (encoded with chars 0x00..0x0F being escaped)
40 static const char PmapMagic
[4] = {'P','m','p','1'};
42 PersistentMap::PersistentMap( OUString
const & url_
, bool readOnly
)
43 : m_MapFile( expandUnoRcUrl(url_
) )
44 , m_bReadOnly( readOnly
)
46 , m_bToBeCreated( !readOnly
)
49 #ifndef DISABLE_BDB2PMAP
50 m_MapFileName
= expandUnoRcUrl( url_
);
55 PersistentMap::PersistentMap()
56 : m_MapFile( OUString() )
57 , m_bReadOnly( false )
59 , m_bToBeCreated( false )
63 PersistentMap::~PersistentMap()
72 // replace 0x00..0x0F with "%0".."%F"
73 // replace "%" with "%%"
74 static OString
encodeString( const OString
& rStr
)
76 const sal_Char
* pChar
= rStr
.getStr();
77 const sal_Int32 nLen
= rStr
.getLength();
79 // short circuit for the simple non-encoded case
82 const unsigned char c
= (unsigned char) *(pChar
++);
91 // escape chars 0x00..0x0F with "%0".."%F"
92 OStringBuffer
aEncStr( nLen
+ 32);
93 aEncStr
.append( pChar
- (nLen
-i
), nLen
- i
);
96 unsigned char c
= (unsigned char) *(pChar
++);
100 c
+= (c
<= 0x09) ? '0' : 'A'-10;
102 aEncStr
.append( '%');
106 return aEncStr
.makeStringAndClear();
109 // replace "%0".."%F" with 0x00..0x0F
110 // replace "%%" with "%"
111 static OString
decodeString( const sal_Char
* pEncChars
, int nLen
)
113 const char* pChar
= pEncChars
;
115 // short circuit for the simple non-encoded case
117 if( *(pChar
++) == '%')
120 return OString( pEncChars
, nLen
);
122 // replace escaped chars with their decoded counterparts
123 OStringBuffer
aDecStr( nLen
);
125 for( i
= nLen
; --i
>= 0;)
127 sal_Char c
= *(pChar
++);
128 // handle escaped character
134 if( ('0' <= c
) && (c
<= '9'))
138 OSL_ASSERT( ('A' <= c
) && (c
<= 'F'));
145 return aDecStr
.makeStringAndClear();
148 bool PersistentMap::open()
150 // open the existing file
151 sal_uInt32 nOpenFlags
= osl_File_OpenFlag_Read
;
153 nOpenFlags
|= osl_File_OpenFlag_Write
;
155 const osl::File::RC rcOpen
= m_MapFile
.open( nOpenFlags
);
156 m_bIsOpen
= (rcOpen
== osl::File::E_None
);
158 // or create later if needed
159 m_bToBeCreated
&= (rcOpen
== osl::File::E_NOENT
) && !m_bIsOpen
;
161 #ifndef DISABLE_BDB2PMAP
167 return m_bToBeCreated
;
172 //______________________________________________________________________________
173 bool PersistentMap::readAll()
175 // prepare for re-reading the map-file
176 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
180 // read header and check magic
181 char aHeaderBytes
[ sizeof(PmapMagic
)];
182 sal_uInt64 nBytesRead
= 0;
183 m_MapFile
.read( aHeaderBytes
, sizeof(aHeaderBytes
), nBytesRead
);
184 OSL_ASSERT( nBytesRead
== sizeof(aHeaderBytes
));
185 if( nBytesRead
!= sizeof(aHeaderBytes
))
187 // check header magic
188 for( int i
= 0; i
< (int)sizeof(PmapMagic
); ++i
)
189 if( aHeaderBytes
[i
] != PmapMagic
[i
])
192 // read key value pairs and add them to the map
193 ByteSequence aKeyLine
;
194 ByteSequence aValLine
;
197 // read key-value line pair
198 // an empty key name indicates the end of the line pairs
199 if( m_MapFile
.readLine( aKeyLine
) != osl::File::E_None
)
201 if( !aKeyLine
.getLength())
203 if( m_MapFile
.readLine( aValLine
) != osl::File::E_None
)
205 // decode key and value strings
206 const OString aKeyName
= decodeString( (sal_Char
*)aKeyLine
.getConstArray(), aKeyLine
.getLength());
207 const OString aValName
= decodeString( (sal_Char
*)aValLine
.getConstArray(), aValLine
.getLength());
208 // insert key-value pair into map
209 add( aKeyName
, aValName
);
210 // check end-of-file status
211 sal_Bool bIsEOF
= true;
212 if( m_MapFile
.isEndOfFile( &bIsEOF
) != osl::File::E_None
)
222 void PersistentMap::flush()
226 OSL_ASSERT( !m_bReadOnly
);
227 if( m_bToBeCreated
&& !m_entries
.empty())
229 const sal_uInt32 nOpenFlags
= osl_File_OpenFlag_Read
| osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
;
230 const osl::File::RC rcOpen
= m_MapFile
.open( nOpenFlags
);
231 m_bIsOpen
= (rcOpen
== osl::File::E_None
);
232 m_bToBeCreated
= !m_bIsOpen
;
237 // write header magic
238 const osl::FileBase::RC nRes
= m_MapFile
.setPos( osl_Pos_Absolut
, 0);
240 sal_uInt64 nBytesWritten
= 0;
241 m_MapFile
.write( PmapMagic
, sizeof(PmapMagic
), nBytesWritten
);
243 // write key value pairs
244 t_string2string_map::const_iterator it
= m_entries
.begin();
245 for(; it
!= m_entries
.end(); ++it
) {
246 // write line for key
247 const OString aKeyString
= encodeString( (*it
).first
);
248 const sal_Int32 nKeyLen
= aKeyString
.getLength();
249 m_MapFile
.write( aKeyString
.getStr(), nKeyLen
, nBytesWritten
);
250 OSL_ASSERT( nKeyLen
== (sal_Int32
)nBytesWritten
);
251 m_MapFile
.write( "\n", 1, nBytesWritten
);
252 // write line for value
253 const OString
& rValString
= encodeString( (*it
).second
);
254 const sal_Int32 nValLen
= rValString
.getLength();
255 m_MapFile
.write( rValString
.getStr(), nValLen
, nBytesWritten
);
256 OSL_ASSERT( nValLen
== (sal_Int32
)nBytesWritten
);
257 m_MapFile
.write( "\n", 1, nBytesWritten
);
260 // write a file delimiter (an empty key-string)
261 m_MapFile
.write( "\n", 1, nBytesWritten
);
262 // truncate file here
263 sal_uInt64 nNewFileSize
;
264 if( m_MapFile
.getPos( nNewFileSize
) == osl::File::E_None
)
265 m_MapFile
.setSize( nNewFileSize
);
268 // the in-memory map now matches to the file on disk
272 bool PersistentMap::has( OString
const & key
) const
274 return get( NULL
, key
);
277 bool PersistentMap::get( OString
* value
, OString
const & key
) const
279 t_string2string_map::const_iterator it
= m_entries
.find( key
);
280 if( it
== m_entries
.end())
287 void PersistentMap::add( OString
const & key
, OString
const & value
)
291 typedef std::pair
<t_string2string_map::iterator
,bool> InsertRC
;
292 InsertRC r
= m_entries
.insert( t_string2string_map::value_type(key
,value
));
293 m_bIsDirty
= r
.second
;
296 //______________________________________________________________________________
297 void PersistentMap::put( OString
const & key
, OString
const & value
)
300 // HACK: flush now as the extension manager does not seem
301 // to properly destruct this object in some situations
306 bool PersistentMap::erase( OString
const & key
, bool flush_immediately
)
310 size_t nCount
= m_entries
.erase( key
);
314 if( flush_immediately
)
319 t_string2string_map
PersistentMap::getEntries() const
321 // TODO: return by const reference instead?
325 #ifndef DISABLE_BDB2PMAP
326 bool PersistentMap::importFromBDB()
331 // get the name of its BDB counterpart
332 OUString aDBName
= m_MapFileName
;
333 if( !aDBName
.endsWith( ".pmap" ))
335 aDBName
= aDBName
.replaceAt( aDBName
.getLength()-5, 5, ".db");
337 // open the corresponding BDB file for reading
338 osl::File
aDBFile( aDBName
);
339 osl::File::RC rc
= aDBFile
.open( osl_File_OpenFlag_Read
);
340 if( rc
!= osl::File::E_None
)
342 sal_uInt64 nFileSize
= 0;
343 if( aDBFile
.getSize( nFileSize
) != osl::File::E_None
)
347 std::vector
<sal_uInt8
> aRawBDB( nFileSize
);
348 for( sal_uInt64 nOfs
= 0; nOfs
< nFileSize
;) {
349 sal_uInt64 nBytesRead
= 0;
350 rc
= aDBFile
.read( (void*)&aRawBDB
[nOfs
], nFileSize
- nOfs
, nBytesRead
);
351 if( (rc
!= osl::File::E_None
) || !nBytesRead
)
356 // check BDB file header for non_encrypted Hash format v4..9
357 if( nFileSize
< 0x0100)
359 if( aRawBDB
[24] != 0) // only not-encrypted migration
361 if( aRawBDB
[25] != 8) // we expect a P_HASHMETA page
363 const bool bLE
= (aRawBDB
[12]==0x61 && aRawBDB
[13]==0x15 && aRawBDB
[14]==0x06);
364 const bool bBE
= (aRawBDB
[15]==0x61 && aRawBDB
[14]==0x15 && aRawBDB
[13]==0x06);
367 if( (aRawBDB
[16] < 4) || (9 < aRawBDB
[16])) // version
369 const sal_uInt64 nPgSize
= bLE
370 ? (aRawBDB
[20] + (aRawBDB
[21]<<8) + (aRawBDB
[22]<<16) + (aRawBDB
[23]<<24))
371 : (aRawBDB
[23] + (aRawBDB
[22]<<8) + (aRawBDB
[21]<<16) + (aRawBDB
[20]<<24));
372 const int nPgCount
= nFileSize
/ nPgSize
;
373 if( nPgCount
* nPgSize
!= nFileSize
)
376 // find PackageManager's new_style entries
377 // using a simple heuristic for BDB_Hash pages
379 for( int nPgNo
= 1; nPgNo
< nPgCount
; ++nPgNo
) {
380 // parse the next _db_page
381 const sal_uInt8
* const pPage
= &aRawBDB
[ nPgNo
* nPgSize
];
382 const sal_uInt8
* const pEnd
= pPage
+ nPgSize
;
383 const int nHfOffset
= bLE
? (pPage
[22] + (pPage
[23]<<8)) : (pPage
[23] + (pPage
[22]<<8));
386 const sal_uInt8
* pCur
= pPage
+ nHfOffset
;
387 // iterate through the entries
388 for(; pCur
< pEnd
; ++pCur
) {
391 // get the value-candidate
392 const sal_uInt8
* pVal
= pCur
+ 1;
393 while( ++pCur
< pEnd
)
394 if( (*pCur
< ' ') || ((*pCur
> 0x7F) && (*pCur
!= 0xFF)))
398 if( (pCur
[0] != 0x01) || (pCur
[1] != 0xFF))
400 const OString
aVal( (sal_Char
*)pVal
, pCur
- pVal
);
401 // get the key-candidate
402 const sal_uInt8
* pKey
= pCur
+ 1;
403 while( ++pCur
< pEnd
)
404 if( (*pCur
< ' ') || ((*pCur
> 0x7F) && (*pCur
!= 0xFF)))
406 if( (pCur
< pEnd
) && (*pCur
> 0x01))
408 const OString
aKey( (sal_Char
*)pKey
, pCur
- pKey
);
409 --pCur
; // prepare for next round by rewinding to end of key-string
411 // add the key/value pair
417 return (nEntryCount
> 0);
419 #endif // DISABLE_BDB2PMAP
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */