1 /* This file is part of the wvWare 2 project
2 Copyright (C) 2001-2003 Werner Trobin <trobin@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 Boston, MA 02111-1307, USA.
17 */
18
19 #include "olestorage.h"
20 #include "olestream.h"
21 #include "wvlog.h"
22
23 #include <gsf/gsf-input-memory.h>
24 #include <gsf/gsf-infile.h>
25 #include <gsf/gsf-infile-msole.h>
26 #include <gsf/gsf-output-stdio.h>
27 #include <gsf/gsf-outfile.h>
28 #include <gsf/gsf-outfile-msole.h>
29 #include <gsf/gsf-utils.h>
30
31 using namespace wvWare;
32
OLEStorage()33 OLEStorage::OLEStorage() : m_inputFile( 0 ), m_outputFile( 0 ),
34 m_fileName( "" ), m_buffer( 0 ), m_buflen( 0 )
35 {
36 // According to Dom it's no problem to call that function multiple times
37 gsf_init();
38 }
39
OLEStorage(const std::string & fileName)40 OLEStorage::OLEStorage( const std::string& fileName ) : m_inputFile( 0 ),
41 m_outputFile( 0 ), m_fileName( fileName ), m_buffer( 0 ), m_buflen( 0 )
42 {
43 // According to Dom it's no problem to call that function multiple times
44 gsf_init();
45 }
46
OLEStorage(const unsigned char * buffer,size_t buflen)47 OLEStorage::OLEStorage( const unsigned char* buffer, size_t buflen ) :
48 m_inputFile( 0 ), m_outputFile( 0 ), m_fileName( "" ), m_buffer( buffer ), m_buflen( buflen )
49 {
50 // According to Dom it's no problem to call that function multiple times
51 gsf_init();
52 }
53
~OLEStorage()54 OLEStorage::~OLEStorage()
55 {
56 close(); // just in case
57 // According to Dom it's no problem to call that function multiple times
58 gsf_shutdown();
59 }
60
setName(const std::string & fileName)61 void OLEStorage::setName( const std::string& fileName )
62 {
63 // Don't set a new name if we already have an open storage or a buffer
64 if ( m_inputFile || m_outputFile || m_buffer )
65 return;
66 m_fileName = fileName;
67 }
68
setBuffer(const unsigned char * buffer,size_t buflen)69 void OLEStorage::setBuffer( const unsigned char* buffer, size_t buflen )
70 {
71 // Don't set a new buffer if we already have an open storage or a filename
72 if ( m_inputFile || m_outputFile || !m_fileName.empty() )
73 return;
74 m_buffer = buffer;
75 m_buflen = buflen;
76 }
77
open(Mode mode)78 bool OLEStorage::open( Mode mode )
79 {
80 // We already have an open storage, return as appropriate
81 if ( m_inputFile && mode == ReadOnly )
82 return true;
83 else if ( m_outputFile && mode == WriteOnly )
84 return true;
85 else if ( m_inputFile || m_outputFile )
86 return false;
87
88 if ( m_fileName.empty() && ( mode == WriteOnly || !m_buffer ) )
89 return false;
90
91 GError* err = 0;
92
93 if ( mode == ReadOnly ) {
94 GsfInput* input;
95 if ( m_buffer )
96 input = GSF_INPUT( gsf_input_memory_new( m_buffer, m_buflen, false ) );
97 else
98 input = GSF_INPUT( gsf_input_mmap_new( m_fileName.c_str(), &err ) );
99
100 if ( !input ) {
101 if ( !err )
102 return false;
103 wvlog << m_fileName << " error: " << err->message << std::endl;
104 g_error_free( err );
105 return false;
106 }
107
108 m_inputFile = GSF_INFILE( gsf_infile_msole_new( input, &err ) );
109 g_object_unref( G_OBJECT( input ) );
110 if ( !m_inputFile ) {
111 if ( !err )
112 return false;
113 wvlog << m_fileName << " Not an OLE file: " << err->message << std::endl;
114 g_error_free( err );
115 return false;
116 }
117 }
118 else {
119 GsfOutput* output = GSF_OUTPUT( gsf_output_stdio_new( m_fileName.c_str(), &err ) );
120 if ( !output ) {
121 if ( !err )
122 return false;
123 wvlog << m_fileName << " error: " << err->message << std::endl;
124 g_error_free( err );
125 return false;
126 }
127
128 m_outputFile = GSF_OUTFILE( gsf_outfile_msole_new( output ) );
129 g_object_unref( G_OBJECT(output) );
130 }
131 return true;
132 }
133
close()134 void OLEStorage::close()
135 {
136 // check if we still have some open streams and close them properly
137 std::list<OLEStream*>::const_iterator it = m_streams.begin();
138 std::list<OLEStream*>::const_iterator end = m_streams.end();
139 while ( it != end ) {
140 OLEStream* stream( *it );
141 ++it; // first advance the iterator, as the stream will remove itself from the list
142 delete stream;
143 wvlog << "Warning: Closing a stream you didn't delete." << std::endl;
144 }
145 m_streams.clear(); // should be a no-op
146
147 if ( m_inputFile ) {
148 g_object_unref( G_OBJECT( m_inputFile ) );
149 m_inputFile = 0;
150 }
151
152 if ( m_outputFile ) {
153 gsf_output_close( reinterpret_cast<GsfOutput*>( m_outputFile ) );
154 g_object_unref( G_OBJECT( m_outputFile ) );
155 m_outputFile = 0;
156 }
157 }
158
isValid() const159 bool OLEStorage::isValid() const
160 {
161 return m_inputFile || m_outputFile;
162 }
163
listDirectory()164 std::list<std::string> OLEStorage::listDirectory()
165 {
166 std::list<std::string> ret;
167
168 if ( m_outputFile || !m_inputFile ) // outfiles don't seem to support that feature :-(
169 return ret;
170
171 GsfInfile* currentDir( m_inputFile );
172 if ( !m_path.empty() )
173 currentDir = m_path.back().infile;
174
175 int numChildren = gsf_infile_num_children( currentDir );
176 for ( int i = 0; i < numChildren; ++i ) {
177 GsfInput* entry( gsf_infile_child_by_index( currentDir, i ) );
178 const char* name( gsf_input_name( entry ) );
179 ret.push_back( name != 0 ? name : "[unnamed]" );
180 g_object_unref( G_OBJECT( entry ) );
181 }
182 return ret;
183 }
184
enterDirectory(const std::string & directory)185 bool OLEStorage::enterDirectory( const std::string& directory )
186 {
187 if ( m_inputFile ) {
188 GsfInfile* currentDir( m_inputFile );
189 if ( !m_path.empty() )
190 currentDir = m_path.back().infile;
191
192 GsfInput* dir( gsf_infile_child_by_name( currentDir, directory.c_str() ) );
193
194 if ( dir && GSF_IS_INFILE( dir ) &&
195 gsf_infile_num_children( GSF_INFILE( dir ) ) >= 0 ) {
196 m_path.push_back( GSF_INFILE( dir ) );
197 return true;
198 }
199 }
200 else if ( m_outputFile ) {
201 GsfOutfile* currentDir( m_outputFile );
202 if ( !m_path.empty() )
203 currentDir = m_path.back().outfile;
204
205 GsfOutput* newDir( gsf_outfile_new_child( currentDir, directory.c_str(), TRUE ) );
206 if ( newDir ) {
207 m_path.push_back( GSF_OUTFILE( newDir ) );
208 return true;
209 }
210 }
211 return false; // no file opened
212 }
213
leaveDirectory()214 void OLEStorage::leaveDirectory()
215 {
216 if ( !m_path.empty() ) {
217 if ( m_inputFile )
218 g_object_unref( G_OBJECT( m_path.back().infile ) );
219 else if ( m_outputFile ) {
220 gsf_output_close( reinterpret_cast<GsfOutput*>( m_path.back().outfile ) );
221 g_object_unref( G_OBJECT( m_path.back().outfile ) );
222 }
223 m_path.pop_back();
224 }
225 }
226
setPath(const std::string & path)227 bool OLEStorage::setPath( const std::string& path )
228 {
229 // Save the old path
230 std::deque<Directory> oldPath;
231 oldPath.swap( m_path );
232
233 std::string tmp;
234 std::string::size_type start = 1, end = 0;
235 bool success = true;
236
237 if ( path[ 0 ] != '/' )
238 start = 0;
239
240 while ( start < path.length() && success ) {
241 end = path.find_first_of( '/', start );
242 if ( end != std::string::npos ) {
243 tmp = path.substr( start, end - start );
244 start = end + 1;
245 }
246 else {
247 tmp = path.substr( start );
248 start = std::string::npos;
249 }
250 if ( !enterDirectory( tmp ) )
251 success = false;
252 }
253
254 // If we were successful, we save the current m_path in
255 // the oldPath, and free m_path using leaveDirectory
256 if ( success )
257 oldPath.swap( m_path );
258 while ( !m_path.empty() )
259 leaveDirectory();
260
261 // Now restore the correct path in m_path
262 oldPath.swap( m_path );
263 return success;
264 }
265
path() const266 std::string OLEStorage::path() const
267 {
268 std::deque<Directory>::const_iterator it( m_path.begin() );
269 std::deque<Directory>::const_iterator end( m_path.end() );
270 std::string p( "/" );
271 for ( ; it != end; ++it ) {
272 const char* name = 0;
273 if ( m_inputFile )
274 name = gsf_input_name( GSF_INPUT( ( *it ).infile ) ); // it->infile doesn't work with gcc 2.95.2
275 else if ( m_outputFile )
276 name = gsf_output_name( GSF_OUTPUT( ( *it ).outfile ) ); // it->outfile doesn't work with gcc 2.95.2
277
278 if ( name ) {
279 p.append( name );
280 p.push_back( '/' );
281 }
282 }
283 return p;
284 }
285
createStreamReader(const std::string & stream)286 OLEStreamReader* OLEStorage::createStreamReader( const std::string& stream )
287 {
288 if ( !m_inputFile )
289 return 0;
290
291 GsfInfile* currentDir( m_inputFile );
292 if ( !m_path.empty() )
293 currentDir = m_path.back().infile;
294 GsfInput* input( gsf_infile_child_by_name( currentDir, stream.c_str() ) );
295
296 if ( !input )
297 return 0;
298
299 OLEStreamReader* reader( new OLEStreamReader( input, this ) );
300 m_streams.push_back( reader );
301 return reader;
302 }
303
createStreamWriter(const std::string & stream)304 OLEStreamWriter* OLEStorage::createStreamWriter( const std::string& stream )
305 {
306 if ( !m_outputFile )
307 return 0;
308 // Don't try to confuse our highly intelligent path system :p
309 if ( stream.find('/') != std::string::npos ) {
310 wvlog << "Warning: You tried to create a stream with a '/' in its name." << std::endl;
311 return 0;
312 }
313
314 GsfOutfile* currentDir( m_outputFile );
315 if ( !m_path.empty() )
316 currentDir = m_path.back().outfile;
317 GsfOutput* output( gsf_outfile_new_child( currentDir, stream.c_str(), FALSE ) );
318
319 if ( !output )
320 return 0;
321
322 OLEStreamWriter* writer( new OLEStreamWriter( output, this ) );
323 m_streams.push_back( writer );
324 return writer;
325 }
326
streamDestroyed(OLEStream * stream)327 void OLEStorage::streamDestroyed( OLEStream* stream )
328 {
329 m_streams.remove( stream );
330 }
331