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