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