1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_desktop.hxx"
26 
27 #include "dp_misc.h"
28 #include "dp_ucb.h"
29 #include "dp_persmap.h"
30 #include "rtl/strbuf.hxx"
31 #include "rtl/ustrbuf.hxx"
32 #include "osl/file.hxx"
33 #include "osl/thread.h"
34 
35 
36 using namespace ::com::sun::star;
37 using namespace ::com::sun::star::uno;
38 using namespace ::rtl;
39 using ::osl::File;
40 
41 namespace dp_misc
42 {
43 
44 //______________________________________________________________________________
45 void PersistentMap::throw_rtexc( int err, char const * pmsg ) const
46 {
47     OUStringBuffer buf;
48     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("[") );
49     buf.append( m_sysPath );
50     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("] Berkeley Db error (") );
51     buf.append( static_cast<sal_Int32>(err) );
52     buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("): ") );
53     if (pmsg == 0)
54         pmsg = DbEnv::strerror(err);
55     const OString msg(pmsg);
56     buf.append( OUString( msg.getStr(), msg.getLength(),
57                           osl_getThreadTextEncoding() ) );
58     const OUString msg_(buf.makeStringAndClear());
59     OSL_ENSURE( 0, rtl::OUStringToOString(
60                     msg_, RTL_TEXTENCODING_UTF8 ).getStr() );
61     throw RuntimeException( msg_, Reference<XInterface>() );
62 }
63 
64 //______________________________________________________________________________
65 PersistentMap::~PersistentMap()
66 {
67     try {
68         m_db.close(0);
69     }
70     catch (DbException & exc) {
71         (void) exc; // avoid warnings
72         OSL_ENSURE( 0, DbEnv::strerror( exc.get_errno() ) );
73     }
74 }
75 
76 //______________________________________________________________________________
77 PersistentMap::PersistentMap( OUString const & url_, bool readOnly )
78     : m_db( 0, 0 )
79 {
80     try {
81         OUString url( expandUnoRcUrl(url_) );
82         if ( File::getSystemPathFromFileURL( url, m_sysPath ) != File::E_None )
83         {
84             OSL_ASSERT( false );
85         }
86         OString cstr_sysPath(
87             OUStringToOString( m_sysPath, RTL_TEXTENCODING_UTF8 ) );
88         char const * pcstr_sysPath = cstr_sysPath.getStr();
89 
90         u_int32_t flags = DB_CREATE;
91         if (readOnly) {
92             flags = DB_RDONLY;
93             if (! create_ucb_content(
94                     0, url,
95                     Reference<com::sun::star::ucb::XCommandEnvironment>(),
96                     false /* no throw */ )) {
97                 // ignore non-existent file in read-only mode: simulate empty db
98                 pcstr_sysPath = 0;
99                 flags = DB_CREATE;
100             }
101         }
102 
103         int err = m_db.open(
104             // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
105             0, pcstr_sysPath, 0, DB_HASH, flags/* | DB_THREAD*/, 0664 /* fs mode */ );
106         if (err != 0)
107             throw_rtexc(err);
108     }
109     catch (DbException & exc) {
110         throw_rtexc( exc.get_errno(), exc.what() );
111     }
112 }
113 
114 //______________________________________________________________________________
115 PersistentMap::PersistentMap()
116     : m_db( 0, 0 )
117 {
118     try {
119         // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
120         int err = m_db.open( 0, 0, 0, DB_HASH, DB_CREATE/* | DB_THREAD*/, 0 );
121         if (err != 0)
122             throw_rtexc(err);
123     }
124     catch (DbException & exc) {
125         throw_rtexc( exc.get_errno(), exc.what() );
126     }
127 }
128 
129 //______________________________________________________________________________
130 bool PersistentMap::has( OString const & key ) const
131 {
132     return get( 0, key );
133 }
134 
135 //______________________________________________________________________________
136 bool PersistentMap::get( OString * value, OString const & key ) const
137 {
138     try {
139         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
140         Dbt dbData;
141         int err = m_db.get( 0, &dbKey, &dbData, 0 );
142         if (err == DB_NOTFOUND)
143             return false;
144         if (err == 0) {
145             if (value != 0) {
146                 *value = OString(
147                     static_cast< sal_Char const * >(dbData.get_data()),
148                     dbData.get_size() );
149             }
150             return true;
151         }
152         throw_rtexc(err);
153     }
154     catch (DbException & exc) {
155         throw_rtexc( exc.get_errno(), exc.what() );
156     }
157     return false; // avoiding warning
158 }
159 
160 //______________________________________________________________________________
161 void PersistentMap::put( OString const & key, OString const & value )
162 {
163     try {
164         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
165         Dbt dbData( const_cast< sal_Char * >(
166                         value.getStr()), value.getLength() );
167         int err = m_db.put( 0, &dbKey, &dbData, 0 );
168         if (err == 0) {
169 #if OSL_DEBUG_LEVEL > 0
170             OString v;
171             OSL_ASSERT( get( &v, key ) );
172             OSL_ASSERT( v.equals( value ) );
173 #endif
174             err = m_db.sync(0);
175         }
176         if (err != 0)
177             throw_rtexc(err);
178     }
179     catch (DbException & exc) {
180         throw_rtexc( exc.get_errno(), exc.what() );
181     }
182 }
183 
184 //______________________________________________________________________________
185 bool PersistentMap::erase( OString const & key, bool flush_immediately )
186 {
187     try {
188         Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
189         int err = m_db.del( &dbKey, 0 );
190         if (err == 0) {
191             if (flush_immediately) {
192                 err = m_db.sync(0);
193                 if (err != 0)
194                     throw_rtexc(err);
195             }
196             return true;
197         }
198         if (err == DB_NOTFOUND)
199             return false;
200         throw_rtexc(err);
201     }
202     catch (DbException & exc) {
203         throw_rtexc( exc.get_errno(), exc.what() );
204     }
205     return false; // avoiding warning
206 }
207 
208 //______________________________________________________________________________
209 t_string2string_map PersistentMap::getEntries() const
210 {
211     try {
212         Dbc * pcurs = 0;
213         int err = m_db.cursor( 0, &pcurs, 0 );
214         if (err != 0)
215             throw_rtexc(err);
216 
217         t_string2string_map ret;
218         for (;;) {
219             Dbt dbKey, dbData;
220             err = pcurs->get( &dbKey, &dbData, DB_NEXT );
221             if (err == DB_NOTFOUND)
222                 break;
223             if (err != 0)
224                 throw_rtexc(err);
225 
226             ::std::pair<t_string2string_map::iterator, bool > insertion(
227                 ret.insert( t_string2string_map::value_type(
228                                 t_string2string_map::value_type(
229                                     OString( static_cast< sal_Char const * >(
230                                                  dbKey.get_data()),
231                                              dbKey.get_size() ),
232                                     OString( static_cast< sal_Char const * >(
233                                                  dbData.get_data()),
234                                              dbData.get_size() ) ) ) ) );
235             OSL_ASSERT( insertion.second );
236         }
237         err = pcurs->close();
238         if (err != 0)
239             throw_rtexc(err);
240         return ret;
241     }
242     catch (DbException & exc) {
243         throw_rtexc( exc.get_errno(), exc.what() );
244     }
245     return t_string2string_map(); // avoiding warning
246 }
247 
248 }
249 
250