xref: /trunk/main/oox/source/xls/biffdetector.cxx (revision ca5ec200)
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 #include "oox/xls/biffdetector.hxx"
25 
26 #include <algorithm>
27 #include <com/sun/star/io/XInputStream.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <comphelper/mediadescriptor.hxx>
30 #include <rtl/strbuf.hxx>
31 #include "oox/helper/binaryinputstream.hxx"
32 #include "oox/ole/olestorage.hxx"
33 
34 namespace oox {
35 namespace xls {
36 
37 // ============================================================================
38 
39 using namespace ::com::sun::star::beans;
40 using namespace ::com::sun::star::io;
41 using namespace ::com::sun::star::lang;
42 using namespace ::com::sun::star::uno;
43 
44 using ::comphelper::MediaDescriptor;
45 using ::rtl::OStringBuffer;
46 using ::rtl::OUString;
47 
48 // ============================================================================
49 
BiffDetector_getSupportedServiceNames()50 Sequence< OUString > BiffDetector_getSupportedServiceNames()
51 {
52     Sequence< OUString > aServiceNames( 1 );
53     aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" );
54     return aServiceNames;
55 }
56 
BiffDetector_getImplementationName()57 OUString BiffDetector_getImplementationName()
58 {
59     return CREATE_OUSTRING( "com.sun.star.comp.oox.xls.BiffDetector" );
60 }
61 
BiffDetector_createInstance(const Reference<XComponentContext> & rxContext)62 Reference< XInterface > SAL_CALL BiffDetector_createInstance( const Reference< XComponentContext >& rxContext ) throw( Exception )
63 {
64     return static_cast< ::cppu::OWeakObject* >( new BiffDetector( rxContext ) );
65 }
66 
67 // ============================================================================
68 
BiffDetector(const Reference<XComponentContext> & rxContext)69 BiffDetector::BiffDetector( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
70     mxContext( rxContext, UNO_SET_THROW )
71 {
72 }
73 
~BiffDetector()74 BiffDetector::~BiffDetector()
75 {
76 }
77 
detectStreamBiffVersion(BinaryInputStream & rInStream)78 /*static*/ BiffType BiffDetector::detectStreamBiffVersion( BinaryInputStream& rInStream )
79 {
80     BiffType eBiff = BIFF_UNKNOWN;
81     if( !rInStream.isEof() && rInStream.isSeekable() && (rInStream.size() > 4) )
82     {
83         sal_Int64 nOldPos = rInStream.tell();
84         rInStream.seekToStart();
85         sal_uInt16 nBofId, nBofSize;
86         rInStream >> nBofId >> nBofSize;
87 
88         if( (4 <= nBofSize) && (nBofSize <= 16) && (rInStream.tell() + nBofSize <= rInStream.size()) )
89         {
90             switch( nBofId )
91             {
92                 case BIFF2_ID_BOF:
93                     eBiff = BIFF2;
94                 break;
95                 case BIFF3_ID_BOF:
96                     eBiff = BIFF3;
97                 break;
98                 case BIFF4_ID_BOF:
99                     eBiff = BIFF4;
100                 break;
101                 case BIFF5_ID_BOF:
102                 {
103                     if( 6 <= nBofSize )
104                     {
105                         sal_uInt16 nVersion;
106                         rInStream >> nVersion;
107                         // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
108                         switch( nVersion & 0xFF00 )
109                         {
110                             case 0:                 eBiff = BIFF5;  break;  // #i44031# #i62752#
111                             case BIFF_BOF_BIFF2:    eBiff = BIFF2;  break;
112                             case BIFF_BOF_BIFF3:    eBiff = BIFF3;  break;
113                             case BIFF_BOF_BIFF4:    eBiff = BIFF4;  break;
114                             case BIFF_BOF_BIFF5:    eBiff = BIFF5;  break;
115                             case BIFF_BOF_BIFF8:    eBiff = BIFF8;  break;
116                             default:    OSL_ENSURE( false,
117                                 OStringBuffer( "lclDetectStreamBiffVersion - unknown BIFF version: 0x" ).
118                                 append( static_cast< sal_Int32 >( nVersion ), 16 ).getStr() );
119                         }
120                     }
121                 }
122                 break;
123                 // else do nothing, no BIFF stream
124             }
125         }
126         rInStream.seek( nOldPos );
127     }
128     return eBiff;
129 }
130 
detectStorageBiffVersion(OUString & orWorkbookStreamName,const StorageRef & rxStorage)131 /*static*/ BiffType BiffDetector::detectStorageBiffVersion( OUString& orWorkbookStreamName, const StorageRef& rxStorage )
132 {
133     static const OUString saBookName = CREATE_OUSTRING( "Book" );
134     static const OUString saWorkbookName = CREATE_OUSTRING( "Workbook" );
135 
136     BiffType eBiff = BIFF_UNKNOWN;
137     if( rxStorage.get() )
138     {
139         if( rxStorage->isStorage() )
140         {
141             // try to open the "Book" stream
142             BinaryXInputStream aBookStrm5( rxStorage->openInputStream( saBookName ), true );
143             BiffType eBookStrm5Biff = detectStreamBiffVersion( aBookStrm5 );
144 
145             // try to open the "Workbook" stream
146             BinaryXInputStream aBookStrm8( rxStorage->openInputStream( saWorkbookName ), true );
147             BiffType eBookStrm8Biff = detectStreamBiffVersion( aBookStrm8 );
148 
149             // decide which stream to use
150             if( (eBookStrm8Biff != BIFF_UNKNOWN) && ((eBookStrm5Biff == BIFF_UNKNOWN) || (eBookStrm8Biff > eBookStrm5Biff)) )
151             {
152                 /*  Only "Workbook" stream exists; or both streams exist, and
153                     "Workbook" has higher BIFF version than "Book" stream. */
154                 eBiff = eBookStrm8Biff;
155                 orWorkbookStreamName = saWorkbookName;
156             }
157             else if( eBookStrm5Biff != BIFF_UNKNOWN )
158             {
159                 /*  Only "Book" stream exists; or both streams exist, and
160                     "Book" has higher BIFF version than "Workbook" stream. */
161                 eBiff = eBookStrm5Biff;
162                 orWorkbookStreamName = saBookName;
163             }
164         }
165         else
166         {
167             // no storage, try plain input stream from medium (even for BIFF5+)
168             BinaryXInputStream aStrm( rxStorage->openInputStream( OUString() ), false );
169             eBiff = detectStreamBiffVersion( aStrm );
170             orWorkbookStreamName = OUString();
171         }
172     }
173 
174     return eBiff;
175 }
176 
177 // com.sun.star.lang.XServiceInfo interface -----------------------------------
178 
getImplementationName()179 OUString SAL_CALL BiffDetector::getImplementationName() throw( RuntimeException )
180 {
181     return BiffDetector_getImplementationName();
182 }
183 
supportsService(const OUString & rService)184 sal_Bool SAL_CALL BiffDetector::supportsService( const OUString& rService ) throw( RuntimeException )
185 {
186     const Sequence< OUString > aServices = BiffDetector_getSupportedServiceNames();
187     const OUString* pArray = aServices.getConstArray();
188     const OUString* pArrayEnd = pArray + aServices.getLength();
189     return ::std::find( pArray, pArrayEnd, rService ) != pArrayEnd;
190 }
191 
getSupportedServiceNames()192 Sequence< OUString > SAL_CALL BiffDetector::getSupportedServiceNames() throw( RuntimeException )
193 {
194     return BiffDetector_getSupportedServiceNames();
195 }
196 
197 // com.sun.star.document.XExtendedFilterDetect interface ----------------------
198 
detect(Sequence<PropertyValue> & rDescriptor)199 OUString SAL_CALL BiffDetector::detect( Sequence< PropertyValue >& rDescriptor ) throw( RuntimeException )
200 {
201     OUString aTypeName;
202 
203     MediaDescriptor aDescriptor( rDescriptor );
204     aDescriptor.addInputStream();
205 
206     Reference< XInputStream > xInStrm( aDescriptor[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY_THROW );
207     StorageRef xStorage( new ::oox::ole::OleStorage( mxContext, xInStrm, true ) );
208 
209     OUString aWorkbookName;
210     switch( detectStorageBiffVersion( aWorkbookName, xStorage ) )
211     {
212         case BIFF2:
213         case BIFF3:
214         case BIFF4: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_40" );  break;
215         case BIFF5: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_95" );  break;
216         case BIFF8: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_97" );  break;
217         default:;
218     }
219 
220     return aTypeName;
221 }
222 
223 // ============================================================================
224 
225 } // namespace xls
226 } // namespace oox
227