1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_desktop.hxx"
30 
31 #include "dp_misc.h"
32 #include "dp_ucb.h"
33 #include "dp_persmap.h"
34 #include "rtl/strbuf.hxx"
35 #include "rtl/ustrbuf.hxx"
36 #include "osl/file.hxx"
37 #include "osl/thread.h"
38 
39 
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::uno;
42 using namespace ::rtl;
43 using ::osl::File;
44 
45 namespace dp_misc
46 {
47 
48 //______________________________________________________________________________
49 void PersistentMap::throw_rtexc( int err, char const * pmsg ) const
50 {
51     OUStringBuffer buf;
52     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("[") );
53     buf.append( m_sysPath );
54     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("] Berkeley Db error (") );
55     buf.append( static_cast<sal_Int32>(err) );
56     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("): ") );
57     if (pmsg == 0)
58         pmsg = DbEnv::strerror(err);
59     const OString msg(pmsg);
60     buf.append( OUString( msg.getStr(), msg.getLength(),
61                           osl_getThreadTextEncoding() ) );
62     const OUString msg_(buf.makeStringAndClear());
63     OSL_ENSURE( 0, rtl::OUStringToOString(
64                     msg_, RTL_TEXTENCODING_UTF8 ).getStr() );
65     throw RuntimeException( msg_, Reference<XInterface>() );
66 }
67 
68 //______________________________________________________________________________
69 PersistentMap::~PersistentMap()
70 {
71     try {
72         m_db.close(0);
73     }
74     catch (DbException & exc) {
75         (void) exc; // avoid warnings
76         OSL_ENSURE( 0, DbEnv::strerror( exc.get_errno() ) );
77     }
78 }
79 
80 //______________________________________________________________________________
81 PersistentMap::PersistentMap( OUString const & url_, bool readOnly )
82     : m_db( 0, 0 )
83 {
84     try {
85         OUString url( expandUnoRcUrl(url_) );
86         if ( File::getSystemPathFromFileURL( url, m_sysPath ) != File::E_None )
87         {
88             OSL_ASSERT( false );
89         }
90         OString cstr_sysPath(
91             OUStringToOString( m_sysPath, RTL_TEXTENCODING_UTF8 ) );
92         char const * pcstr_sysPath = cstr_sysPath.getStr();
93 
94         u_int32_t flags = DB_CREATE;
95         if (readOnly) {
96             flags = DB_RDONLY;
97             if (! create_ucb_content(
98                     0, url,
99                     Reference<com::sun::star::ucb::XCommandEnvironment>(),
100                     false /* no throw */ )) {
101                 // ignore non-existent file in read-only mode: simulate empty db
102                 pcstr_sysPath = 0;
103                 flags = DB_CREATE;
104             }
105         }
106 
107         int err = m_db.open(
108             // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
109             0, pcstr_sysPath, 0, DB_HASH, flags/* | DB_THREAD*/, 0664 /* fs mode */ );
110         if (err != 0)
111             throw_rtexc(err);
112     }
113     catch (DbException & exc) {
114         throw_rtexc( exc.get_errno(), exc.what() );
115     }
116 }
117 
118 //______________________________________________________________________________
119 PersistentMap::PersistentMap()
120     : m_db( 0, 0 )
121 {
122     try {
123         // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
124         int err = m_db.open( 0, 0, 0, DB_HASH, DB_CREATE/* | DB_THREAD*/, 0 );
125         if (err != 0)
126             throw_rtexc(err);
127     }
128     catch (DbException & exc) {
129         throw_rtexc( exc.get_errno(), exc.what() );
130     }
131 }
132 
133 //______________________________________________________________________________
134 bool PersistentMap::has( OString const & key ) const
135 {
136     return get( 0, key );
137 }
138 
139 //______________________________________________________________________________
140 bool PersistentMap::get( OString * value, OString const & key ) const
141 {
142     try {
143         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
144         Dbt dbData;
145         int err = m_db.get( 0, &dbKey, &dbData, 0 );
146         if (err == DB_NOTFOUND)
147             return false;
148         if (err == 0) {
149             if (value != 0) {
150                 *value = OString(
151                     static_cast< sal_Char const * >(dbData.get_data()),
152                     dbData.get_size() );
153             }
154             return true;
155         }
156         throw_rtexc(err);
157     }
158     catch (DbException & exc) {
159         throw_rtexc( exc.get_errno(), exc.what() );
160     }
161     return false; // avoiding warning
162 }
163 
164 //______________________________________________________________________________
165 void PersistentMap::put( OString const & key, OString const & value )
166 {
167     try {
168         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
169         Dbt dbData( const_cast< sal_Char * >(
170                         value.getStr()), value.getLength() );
171         int err = m_db.put( 0, &dbKey, &dbData, 0 );
172         if (err == 0) {
173 #if OSL_DEBUG_LEVEL > 0
174             OString v;
175             OSL_ASSERT( get( &v, key ) );
176             OSL_ASSERT( v.equals( value ) );
177 #endif
178             err = m_db.sync(0);
179         }
180         if (err != 0)
181             throw_rtexc(err);
182     }
183     catch (DbException & exc) {
184         throw_rtexc( exc.get_errno(), exc.what() );
185     }
186 }
187 
188 //______________________________________________________________________________
189 bool PersistentMap::erase( OString const & key, bool flush_immediately )
190 {
191     try {
192         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
193         int err = m_db.del( &dbKey, 0 );
194         if (err == 0) {
195             if (flush_immediately) {
196                 err = m_db.sync(0);
197                 if (err != 0)
198                     throw_rtexc(err);
199             }
200             return true;
201         }
202         if (err == DB_NOTFOUND)
203             return false;
204         throw_rtexc(err);
205     }
206     catch (DbException & exc) {
207         throw_rtexc( exc.get_errno(), exc.what() );
208     }
209     return false; // avoiding warning
210 }
211 
212 //______________________________________________________________________________
213 t_string2string_map PersistentMap::getEntries() const
214 {
215     try {
216         Dbc * pcurs = 0;
217         int err = m_db.cursor( 0, &pcurs, 0 );
218         if (err != 0)
219             throw_rtexc(err);
220 
221         t_string2string_map ret;
222         for (;;) {
223             Dbt dbKey, dbData;
224             err = pcurs->get( &dbKey, &dbData, DB_NEXT );
225             if (err == DB_NOTFOUND)
226                 break;
227             if (err != 0)
228                 throw_rtexc(err);
229 
230             ::std::pair<t_string2string_map::iterator, bool > insertion(
231                 ret.insert( t_string2string_map::value_type(
232                                 t_string2string_map::value_type(
233                                     OString( static_cast< sal_Char const * >(
234                                                  dbKey.get_data()),
235                                              dbKey.get_size() ),
236                                     OString( static_cast< sal_Char const * >(
237                                                  dbData.get_data()),
238                                              dbData.get_size() ) ) ) ) );
239             OSL_ASSERT( insertion.second );
240         }
241         err = pcurs->close();
242         if (err != 0)
243             throw_rtexc(err);
244         return ret;
245     }
246     catch (DbException & exc) {
247         throw_rtexc( exc.get_errno(), exc.what() );
248     }
249     return t_string2string_map(); // avoiding warning
250 }
251 
252 }
253 
254