/src/libreoffice/sc/source/ui/unoobj/exceldetect.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | */ |
9 | | |
10 | | #include "exceldetect.hxx" |
11 | | |
12 | | #include <com/sun/star/io/XInputStream.hpp> |
13 | | #include <com/sun/star/ucb/ContentCreationException.hpp> |
14 | | #include <com/sun/star/uno/XComponentContext.hpp> |
15 | | #include <cppuhelper/supportsservice.hxx> |
16 | | |
17 | | #include <sfx2/docfile.hxx> |
18 | | #include <unotools/mediadescriptor.hxx> |
19 | | #include <sot/storage.hxx> |
20 | | #include <comphelper/diagnose_ex.hxx> |
21 | | #include <comphelper/sequenceashashmap.hxx> |
22 | | |
23 | | #include <global.hxx> |
24 | | |
25 | | using namespace com::sun::star; |
26 | | |
27 | 0 | ScExcelBiffDetect::ScExcelBiffDetect() {} |
28 | 0 | ScExcelBiffDetect::~ScExcelBiffDetect() {} |
29 | | |
30 | | OUString ScExcelBiffDetect::getImplementationName() |
31 | 0 | { |
32 | 0 | return u"com.sun.star.comp.calc.ExcelBiffFormatDetector"_ustr; |
33 | 0 | } |
34 | | |
35 | | sal_Bool ScExcelBiffDetect::supportsService( const OUString& aName ) |
36 | 0 | { |
37 | 0 | return cppu::supportsService(this, aName); |
38 | 0 | } |
39 | | |
40 | | uno::Sequence<OUString> ScExcelBiffDetect::getSupportedServiceNames() |
41 | 0 | { |
42 | 0 | return { u"com.sun.star.frame.ExtendedTypeDetection"_ustr }; |
43 | 0 | } |
44 | | |
45 | | namespace { |
46 | | |
47 | | bool hasStream(const uno::Reference<io::XInputStream>& xInStream, const OUString& rName) |
48 | 0 | { |
49 | 0 | SfxMedium aMedium; |
50 | 0 | aMedium.UseInteractionHandler(false); |
51 | 0 | aMedium.setStreamToLoadFrom(xInStream, true); |
52 | 0 | SvStream* pStream = aMedium.GetInStream(); |
53 | 0 | if (!pStream) |
54 | 0 | return false; |
55 | | |
56 | 0 | sal_uInt64 const nSize = pStream->TellEnd(); |
57 | 0 | pStream->Seek(0); |
58 | |
|
59 | 0 | if (!nSize) |
60 | 0 | { |
61 | | // 0-size stream. Failed. |
62 | 0 | return false; |
63 | 0 | } |
64 | | |
65 | 0 | try |
66 | 0 | { |
67 | 0 | rtl::Reference<SotStorage> xStorage = new SotStorage(pStream, false); |
68 | 0 | if (!xStorage.is() || xStorage->GetError()) |
69 | 0 | return false; |
70 | 0 | return xStorage->IsStream(rName); |
71 | 0 | } |
72 | 0 | catch (const css::ucb::ContentCreationException &) |
73 | 0 | { |
74 | 0 | TOOLS_WARN_EXCEPTION("sc", "hasStream"); |
75 | 0 | } |
76 | | |
77 | 0 | return false; |
78 | 0 | } |
79 | | |
80 | | /** |
81 | | * We detect BIFF 2, 3 and 4 file types together since the only thing that |
82 | | * set them apart is the BOF ID. |
83 | | */ |
84 | | bool isExcel40(const uno::Reference<io::XInputStream>& xInStream) |
85 | 0 | { |
86 | 0 | SfxMedium aMedium; |
87 | 0 | aMedium.UseInteractionHandler(false); |
88 | 0 | aMedium.setStreamToLoadFrom(xInStream, true); |
89 | 0 | SvStream* pStream = aMedium.GetInStream(); |
90 | 0 | if (!pStream) |
91 | 0 | return false; |
92 | | |
93 | 0 | sal_uInt64 const nSize = pStream->TellEnd(); |
94 | 0 | pStream->Seek(0); |
95 | |
|
96 | 0 | if (nSize < 4) |
97 | 0 | return false; |
98 | | |
99 | 0 | sal_uInt16 nBofId, nBofSize; |
100 | 0 | pStream->ReadUInt16( nBofId ).ReadUInt16( nBofSize ); |
101 | |
|
102 | 0 | switch (nBofId) |
103 | 0 | { |
104 | 0 | case 0x0009: // Excel 2.1 worksheet (BIFF 2) |
105 | 0 | case 0x0209: // Excel 3.0 worksheet (BIFF 3) |
106 | 0 | case 0x0409: // Excel 4.0 worksheet (BIFF 4) |
107 | 0 | case 0x0809: // Excel 5.0 worksheet (BIFF 5), some apps create such files (fdo#70100) |
108 | 0 | break; |
109 | 0 | default: |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | 0 | if (nBofSize < 4 || 16 < nBofSize) |
114 | | // BOF record must be sized between 4 and 16 for BIFF 2, 3 and 4. |
115 | 0 | return false; |
116 | | |
117 | 0 | sal_uInt64 const nPos = pStream->Tell(); |
118 | 0 | if (nSize - nPos < nBofSize) |
119 | | // BOF record doesn't have required bytes. |
120 | 0 | return false; |
121 | | |
122 | 0 | return true; |
123 | 0 | } |
124 | | |
125 | | bool isTemplate(std::u16string_view rType) |
126 | 0 | { |
127 | 0 | return rType.find(u"_VorlageTemplate") != std::u16string_view::npos; |
128 | 0 | } |
129 | | |
130 | | } |
131 | | |
132 | | OUString ScExcelBiffDetect::detect( uno::Sequence<beans::PropertyValue>& lDescriptor ) |
133 | 0 | { |
134 | 0 | comphelper::SequenceAsHashMap aMediaDesc(lDescriptor); |
135 | 0 | OUString aType; |
136 | 0 | aMediaDesc[utl::MediaDescriptor::PROP_TYPENAME] >>= aType; |
137 | 0 | if (aType.isEmpty()) |
138 | | // Type is not given. We can't proceed. |
139 | 0 | return OUString(); |
140 | | |
141 | 0 | utl::MediaDescriptor::addInputStream(aMediaDesc); |
142 | 0 | uno::Reference<io::XInputStream> xInStream(aMediaDesc[utl::MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY); |
143 | 0 | if (!xInStream.is()) |
144 | | // No input stream. |
145 | 0 | return OUString(); |
146 | | |
147 | 0 | if (aType == "calc_MS_Excel_97" || aType == "calc_MS_Excel_97_VorlageTemplate") |
148 | 0 | { |
149 | | // See if this stream is an Excel 97/XP/2003 (BIFF8) stream. |
150 | 0 | if (!hasStream(xInStream, u"Workbook"_ustr)) |
151 | | // BIFF8 is expected to contain a stream named "Workbook". |
152 | 0 | return OUString(); |
153 | | |
154 | 0 | aMediaDesc[utl::MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? SC_XL97TMPL_FILTER_NAME : SC_XL97_FILTER_NAME; |
155 | 0 | } |
156 | | |
157 | 0 | else if (aType == "calc_MS_Excel_95" || aType == "calc_MS_Excel_95_VorlageTemplate") |
158 | 0 | { |
159 | | // See if this stream is an Excel 95 (BIFF5) stream. |
160 | 0 | if (!hasStream(xInStream, u"Book"_ustr)) |
161 | 0 | return OUString(); |
162 | | |
163 | 0 | aMediaDesc[utl::MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? SC_XL95TMPL_FILTER_NAME : SC_XL95_FILTER_NAME; |
164 | 0 | } |
165 | | |
166 | 0 | else if (aType == "calc_MS_Excel_5095" || aType == "calc_MS_Excel_5095_VorlageTemplate") |
167 | 0 | { |
168 | | // See if this stream is an Excel 5.0/95 stream. |
169 | 0 | if (!hasStream(xInStream, u"Book"_ustr)) |
170 | 0 | return OUString(); |
171 | | |
172 | 0 | aMediaDesc[utl::MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? SC_XL5TMPL_FILTER_NAME : SC_XL5_FILTER_NAME; |
173 | 0 | } |
174 | | |
175 | 0 | else if (aType == "calc_MS_Excel_40" || aType == "calc_MS_Excel_40_VorlageTemplate") |
176 | 0 | { |
177 | | // See if this stream is an Excel 4.0 stream. |
178 | 0 | if (!isExcel40(xInStream)) |
179 | 0 | return OUString(); |
180 | | |
181 | 0 | aMediaDesc[utl::MediaDescriptor::PROP_FILTERNAME] <<= isTemplate(aType) ? SC_XL4TMPL_FILTER_NAME : SC_XL4_FILTER_NAME; |
182 | 0 | } |
183 | | |
184 | 0 | else |
185 | | // Nothing to detect. |
186 | 0 | return OUString(); |
187 | | |
188 | 0 | aMediaDesc >> lDescriptor; |
189 | 0 | return aType; |
190 | 0 | } |
191 | | |
192 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* |
193 | | com_sun_star_comp_calc_ExcelBiffFormatDetector_get_implementation(css::uno::XComponentContext* /*context*/, |
194 | | css::uno::Sequence<css::uno::Any> const &) |
195 | 0 | { |
196 | 0 | return cppu::acquire(new ScExcelBiffDetect); |
197 | 0 | } |
198 | | |
199 | | |
200 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |