Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */