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 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 57 OUString BiffDetector_getImplementationName() 58 { 59 return CREATE_OUSTRING( "com.sun.star.comp.oox.xls.BiffDetector" ); 60 } 61 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 69 BiffDetector::BiffDetector( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) : 70 mxContext( rxContext, UNO_SET_THROW ) 71 { 72 } 73 74 BiffDetector::~BiffDetector() 75 { 76 } 77 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 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 179 OUString SAL_CALL BiffDetector::getImplementationName() throw( RuntimeException ) 180 { 181 return BiffDetector_getImplementationName(); 182 } 183 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 192 Sequence< OUString > SAL_CALL BiffDetector::getSupportedServiceNames() throw( RuntimeException ) 193 { 194 return BiffDetector_getSupportedServiceNames(); 195 } 196 197 // com.sun.star.document.XExtendedFilterDetect interface ---------------------- 198 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