xref: /core/sc/source/filter/excel/xicontent.cxx (revision 6376fe01859a14a22b2601ff04691ceb894c33d4)
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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
21 #include <utility>
22 #include <xicontent.hxx>
23 #include <sfx2/objsh.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <tools/urlobj.hxx>
26 #include <sfx2/linkmgr.hxx>
27 #include <svl/itemset.hxx>
28 #include <scitems.hxx>
29 #include <editeng/eeitem.hxx>
30 #include <svl/intitem.hxx>
31 #include <svl/stritem.hxx>
32 #include <editeng/flditem.hxx>
33 #include <editeng/editobj.hxx>
34 #include <unotools/charclass.hxx>
35 #include <comphelper/configuration.hxx>
36 #include <stringutil.hxx>
37 #include <cellform.hxx>
38 #include <cellvalue.hxx>
39 #include <document.hxx>
40 #include <editutil.hxx>
41 #include <validat.hxx>
42 #include <patattr.hxx>
43 #include <docpool.hxx>
44 #include <docsh.hxx>
45 #include <rangenam.hxx>
46 #include <arealink.hxx>
47 #include <stlsheet.hxx>
48 #include <xlcontent.hxx>
49 #include <xlformula.hxx>
50 #include <xltracer.hxx>
51 #include <xistream.hxx>
52 #include <xihelper.hxx>
53 #include <xistyle.hxx>
54 #include <xiescher.hxx>
55 #include <xiname.hxx>
56 
57 #include <excform.hxx>
58 #include <tabprotection.hxx>
59 #include <documentimport.hxx>
60 
61 #include <memory>
62 #include <oox/helper/helper.hxx>
63 #include <sal/log.hxx>
64 
65 using ::com::sun::star::uno::Sequence;
66 
67 // Shared string table ========================================================
68 
XclImpSst(const XclImpRoot & rRoot)69 XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
70     XclImpRoot( rRoot )
71 {
72 }
73 
ReadSst(XclImpStream & rStrm)74 void XclImpSst::ReadSst( XclImpStream& rStrm )
75 {
76     rStrm.Ignore( 4 );
77     sal_uInt32 nStrCount = rStrm.ReaduInt32();
78     auto nBytesAvailable = rStrm.GetRecLeft();
79     if (nStrCount > nBytesAvailable)
80     {
81         SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
82         nStrCount = nBytesAvailable;
83     }
84     maStrings.clear();
85     maStrings.reserve(nStrCount);
86     while( (nStrCount > 0) && rStrm.IsValid() )
87     {
88         XclImpString aString;
89         aString.Read( rStrm );
90         maStrings.push_back(std::move(aString));
91         --nStrCount;
92     }
93 }
94 
GetString(sal_uInt32 nSstIndex) const95 const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
96 {
97     return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
98 }
99 
100 // Hyperlinks =================================================================
101 
102 namespace {
103 
104 /** Reads character array and stores it into rString.
105     @param nChars  Number of following characters (not byte count!).
106     @param b16Bit  true = 16-bit characters, false = 8-bit characters. */
lclAppendString32(OUString & rString,XclImpStream & rStrm,sal_uInt32 nChars,bool b16Bit)107 void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
108 {
109     sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
110     rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
111     // ignore remaining chars
112     std::size_t nIgnore = nChars - nReadChars;
113     if( b16Bit )
114         nIgnore *= 2;
115     rStrm.Ignore( nIgnore );
116 }
117 
118 /** Reads 32-bit string length and the character array and stores it into rString.
119     @param b16Bit  true = 16-bit characters, false = 8-bit characters. */
lclAppendString32(OUString & rString,XclImpStream & rStrm,bool b16Bit)120 void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
121 {
122     lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
123 }
124 
125 /** Reads 32-bit string length and ignores following 16-bit character array. */
lclIgnoreString32(XclImpStream & rStrm)126 void lclIgnoreString32( XclImpStream& rStrm )
127 {
128     sal_uInt32 nChars = rStrm.ReaduInt32();
129     nChars *= 2;
130     rStrm.Ignore( nChars );
131 }
132 
133 /** Converts a path to an absolute path.
134     @param rPath  The source path. The resulting path is returned here.
135     @param nLevel  Number of parent directories to add in front of the path. */
lclGetAbsPath(OUString & rPath,sal_uInt16 nLevel,const SfxObjectShell * pDocShell)136 void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
137 {
138     OUStringBuffer aTmpStr;
139     while( nLevel )
140     {
141         aTmpStr.append( "../" );
142         --nLevel;
143     }
144     aTmpStr.append( rPath );
145 
146     if( pDocShell )
147     {
148         bool bWasAbs = false;
149         rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
150         // full path as stored in SvxURLField must be encoded
151     }
152     else
153         rPath = aTmpStr.makeStringAndClear();
154 }
155 
156 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
lclInsertUrl(XclImpRoot & rRoot,const OUString & rUrl,SCCOL nScCol,SCROW nScRow,SCTAB nScTab)157 void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
158 {
159     ScDocumentImport& rDoc = rRoot.GetDocImport();
160     ScAddress aScPos( nScCol, nScRow, nScTab );
161     ScRefCellValue aCell(rDoc.getDoc(), aScPos);
162     switch( aCell.getType() )
163     {
164         // #i54261# hyperlinks in string cells
165         case CELLTYPE_STRING:
166         case CELLTYPE_EDIT:
167         {
168             ScInterpreterContext& rContext = rDoc.getDoc().GetNonThreadedContext();
169             sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rContext, aScPos);
170             const Color* pColor;
171             OUString aDisplText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, &rContext, rDoc.getDoc());
172             if (aDisplText.isEmpty())
173                 aDisplText = rUrl;
174 
175             ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
176             SvxURLField aUrlField( rUrl, aDisplText, SvxURLFormat::AppDefault );
177 
178             if( aCell.getType() == CELLTYPE_EDIT )
179             {
180                 const EditTextObject* pEditObj = aCell.getEditText();
181                 rEE.SetTextCurrentDefaults( *pEditObj );
182                 rEE.QuickInsertField(SvxFieldItem(aUrlField, EE_FEATURE_FIELD), ESelection::All());
183             }
184             else
185             {
186                 rEE.SetTextCurrentDefaults( OUString() );
187                 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
188                 if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
189                 {
190                     SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
191                     pPattern->FillEditItemSet( &aItemSet );
192                     rEE.QuickSetAttribs(aItemSet, ESelection::All());
193                 }
194             }
195 
196             // The cell will own the text object instance.
197             rDoc.setEditCell(aScPos, rEE.CreateTextObject());
198         }
199         break;
200 
201         default:
202         // Handle other cell types e.g. formulas ( and ? ) that have associated
203         // hyperlinks.
204         // Ideally all hyperlinks should be treated  as below. For the moment,
205         // given the current absence of ods support let's just handle what we
206         // previously didn't handle the new way.
207         // Unfortunately we won't be able to preserve such hyperlinks when
208         // saving to ods. Note: when we are able to save such hyperlinks to ods
209         // we should handle *all* imported hyperlinks as below ( e.g. as cell
210         // attribute ) for better interoperability.
211         {
212             SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
213             rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
214             break;
215         }
216     }
217 }
218 
219 } // namespace
220 
ReadHlink(XclImpStream & rStrm)221 void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
222 {
223     XclRange aXclRange( ScAddress::UNINITIALIZED );
224     rStrm >> aXclRange;
225     // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
226     aXclRange.maFirst.mnCol &= 0xFF;
227     aXclRange.maLast.mnCol &= 0xFF;
228     OUString aString = ReadEmbeddedData( rStrm );
229     if ( !aString.isEmpty() )
230         rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
231 }
232 
ReadEmbeddedData(XclImpStream & rStrm)233 OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
234 {
235     const XclImpRoot& rRoot = rStrm.GetRoot();
236     SfxObjectShell* pDocShell = rRoot.GetDocShell();
237 
238     OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
239 
240     XclGuid aGuid;
241     rStrm >> aGuid;
242     rStrm.Ignore( 4 );
243     sal_uInt32 nFlags = rStrm.ReaduInt32();
244 
245     OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
246 
247     std::unique_ptr< OUString > xLongName;    // link / file name
248     std::unique_ptr< OUString > xShortName;   // 8.3-representation of file name
249     std::unique_ptr< OUString > xTextMark;    // text mark
250 
251     // description (ignore)
252     if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
253         lclIgnoreString32( rStrm );
254     // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
255     if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
256         lclIgnoreString32( rStrm );
257 
258     // URL fields are zero-terminated - do not let the stream replace them
259     // in the lclAppendString32() with the '?' character.
260     rStrm.SetNulSubstChar( '\0' );
261 
262     // UNC path
263     if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
264     {
265         xLongName.reset( new OUString );
266         lclAppendString32( *xLongName, rStrm, true );
267         lclGetAbsPath( *xLongName, 0, pDocShell );
268     }
269     // file link or URL
270     else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
271     {
272         rStrm >> aGuid;
273 
274         if( aGuid == XclTools::maGuidFileMoniker )
275         {
276             sal_uInt16 nLevel = rStrm.ReaduInt16(); // counter for level to climb down in path
277             xShortName.reset( new OUString );
278             lclAppendString32( *xShortName, rStrm, false );
279             rStrm.Ignore( 24 );
280 
281             sal_uInt32 nStrLen = rStrm.ReaduInt32();
282             if( nStrLen )
283             {
284                 nStrLen = rStrm.ReaduInt32();
285                 nStrLen /= 2;       // it's byte count here...
286                 rStrm.Ignore( 2 );
287                 xLongName.reset( new OUString );
288                 lclAppendString32( *xLongName, rStrm, nStrLen, true );
289                 lclGetAbsPath( *xLongName, nLevel, pDocShell );
290             }
291             else
292                 lclGetAbsPath( *xShortName, nLevel, pDocShell );
293         }
294         else if( aGuid == XclTools::maGuidUrlMoniker )
295         {
296             sal_uInt32 nStrLen = rStrm.ReaduInt32();
297             nStrLen /= 2;       // it's byte count here...
298             xLongName.reset( new OUString );
299             lclAppendString32( *xLongName, rStrm, nStrLen, true );
300             if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
301                 lclGetAbsPath( *xLongName, 0, pDocShell );
302         }
303         else
304         {
305             OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
306         }
307     }
308 
309     // text mark
310     if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
311     {
312         xTextMark.reset( new OUString );
313         lclAppendString32( *xTextMark, rStrm, true );
314     }
315 
316     rStrm.SetNulSubstChar();    // back to default
317 
318     OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
319 
320     if (!xLongName && xShortName)
321         xLongName = std::move(xShortName);
322     else if (!xLongName && xTextMark)
323         xLongName.reset( new OUString );
324 
325     if (xLongName)
326     {
327         if (xTextMark)
328         {
329             if( xLongName->isEmpty() )
330             {
331                 sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
332                 if( nSepPos > 0 )
333                 {
334                     // Do not attempt to blindly convert '#SheetName!A1' to
335                     // '#SheetName.A1', it can be #SheetName!R1C1 as well.
336                     // Hyperlink handler has to handle all, but prefer
337                     // '#SheetName.A1' if possible.
338                     if (nSepPos < xTextMark->getLength() - 1)
339                     {
340                         ScDocument& rDoc = rRoot.GetDoc();
341                         ScRange aRange;
342                         if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), rDoc, formula::FormulaGrammar::CONV_XL_R1C1)
343                                         & ScRefFlags::VALID) == ScRefFlags::ZERO)
344                             xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ))));
345                     }
346                 }
347             }
348             xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
349         }
350         return( *xLongName );
351     }
352     return( OUString() );
353 }
354 
ConvertToValidTabName(OUString & rUrl)355 void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
356 {
357     sal_Int32 n = rUrl.getLength();
358     if (n < 4)
359         // Needs at least 4 characters.
360         return;
361 
362     if (rUrl[0] != '#')
363         // the 1st character must be '#'.
364         return;
365 
366     OUStringBuffer aNewUrl("#");
367     OUStringBuffer aTabName;
368 
369     bool bInQuote = false;
370     bool bQuoteTabName = false;
371     for( sal_Int32 i = 1; i < n; ++i )
372     {
373         sal_Unicode c = rUrl[i];
374         if (c == '\'')
375         {
376             if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
377             {
378                 // Two consecutive single quotes ('') signify a single literal
379                 // quite.  When this occurs, the whole table name needs to be
380                 // quoted.
381                 bQuoteTabName = true;
382                 aTabName.append(OUStringChar(c) + OUStringChar(c));
383                 ++i;
384                 continue;
385             }
386 
387             bInQuote = !bInQuote;
388             if (!bInQuote && !aTabName.isEmpty())
389             {
390                 if (bQuoteTabName)
391                     aNewUrl.append("'");
392                 aNewUrl.append(aTabName);
393                 if (bQuoteTabName)
394                     aNewUrl.append("'");
395             }
396         }
397         else if (bInQuote)
398             aTabName.append(c);
399         else
400             aNewUrl.append(c);
401     }
402 
403     if (bInQuote)
404         // It should be outside the quotes!
405         return;
406 
407     // All is good.  Pass the new URL.
408     rUrl = aNewUrl.makeStringAndClear();
409 }
410 
InsertUrl(XclImpRoot & rRoot,const XclRange & rXclRange,const OUString & rUrl)411 void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
412 {
413     OUString aUrl(rUrl);
414     ConvertToValidTabName(aUrl);
415 
416     SCTAB nScTab = rRoot.GetCurrScTab();
417     ScRange aScRange( ScAddress::UNINITIALIZED );
418     if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
419     {
420         SCCOL nScCol1, nScCol2;
421         SCROW nScRow1, nScRow2;
422         aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
423 
424         if (comphelper::IsFuzzing())
425         {
426             SCROW nRows = nScRow2 - nScRow1;
427             if (nRows > 1024)
428             {
429                 SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2 << " to " << nScRow1 + 1024);
430                 nScRow2 = nScRow1 + 1024;
431             }
432         }
433 
434         for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
435             for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
436                 lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
437     }
438 }
439 
440 // Label ranges ===============================================================
441 
ReadLabelranges(XclImpStream & rStrm)442 void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
443 {
444     const XclImpRoot& rRoot = rStrm.GetRoot();
445     OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
446 
447     ScDocument& rDoc = rRoot.GetDoc();
448     SCTAB nScTab = rRoot.GetCurrScTab();
449     XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
450     ScRangePairListRef xLabelRangesRef;
451 
452     XclRangeList aRowXclRanges, aColXclRanges;
453     rStrm >> aRowXclRanges >> aColXclRanges;
454 
455     // row label ranges
456     ScRangeList aRowScRanges;
457     rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
458     xLabelRangesRef = rDoc.GetRowNameRangesRef();
459     for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
460     {
461         const ScRange & rScRange = aRowScRanges[ i ];
462         ScRange aDataRange( rScRange );
463         if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
464         {
465             aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
466             aDataRange.aEnd.SetCol( rDoc.MaxCol() );
467         }
468         else if( aDataRange.aStart.Col() > 0 )
469         {
470             aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
471             aDataRange.aStart.SetCol( 0 );
472         }
473         xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
474     }
475 
476     // column label ranges
477     ScRangeList aColScRanges;
478     rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
479     xLabelRangesRef = rDoc.GetColNameRangesRef();
480 
481     for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
482     {
483         const ScRange & rScRange = aColScRanges[ i ];
484         ScRange aDataRange( rScRange );
485         if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
486         {
487             aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
488             aDataRange.aEnd.SetRow( rDoc.MaxRow() );
489         }
490         else if( aDataRange.aStart.Row() > 0 )
491         {
492             aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
493             aDataRange.aStart.SetRow( 0 );
494         }
495         xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
496     }
497 }
498 
499 // Conditional formatting =====================================================
500 
XclImpCondFormat(const XclImpRoot & rRoot,sal_uInt32 nFormatIndex)501 XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
502     XclImpRoot( rRoot ),
503     mnFormatIndex( nFormatIndex ),
504     mnCondCount( 0 ),
505     mnCondIndex( 0 )
506 {
507 }
508 
~XclImpCondFormat()509 XclImpCondFormat::~XclImpCondFormat()
510 {
511 }
512 
ReadCondfmt(XclImpStream & rStrm)513 void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
514 {
515     OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
516     XclRangeList aXclRanges;
517     mnCondCount = rStrm.ReaduInt16();
518     rStrm.Ignore( 10 );
519     rStrm >> aXclRanges;
520     GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
521 }
522 
ReadCF(XclImpStream & rStrm)523 void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
524 {
525     if( mnCondIndex >= mnCondCount )
526     {
527         OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
528         return;
529     }
530 
531     // entire conditional format outside of valid range?
532     if( maRanges.empty() )
533         return;
534 
535     sal_uInt8 nType = rStrm.ReaduInt8();
536     sal_uInt8 nOperator = rStrm.ReaduInt8();
537     sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
538     sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
539     sal_uInt32 nFlags = rStrm.ReaduInt32();
540     rStrm.Ignore( 2 ); //nFlagsExtended
541 
542     // *** mode and comparison operator ***
543 
544     ScConditionMode eMode = ScConditionMode::NONE;
545     switch( nType )
546     {
547         case EXC_CF_TYPE_CELL:
548         {
549             switch( nOperator )
550             {
551                 case EXC_CF_CMP_BETWEEN:        eMode = ScConditionMode::Between;    break;
552                 case EXC_CF_CMP_NOT_BETWEEN:    eMode = ScConditionMode::NotBetween; break;
553                 case EXC_CF_CMP_EQUAL:          eMode = ScConditionMode::Equal;      break;
554                 case EXC_CF_CMP_NOT_EQUAL:      eMode = ScConditionMode::NotEqual;   break;
555                 case EXC_CF_CMP_GREATER:        eMode = ScConditionMode::Greater;    break;
556                 case EXC_CF_CMP_LESS:           eMode = ScConditionMode::Less;       break;
557                 case EXC_CF_CMP_GREATER_EQUAL:  eMode = ScConditionMode::EqGreater;  break;
558                 case EXC_CF_CMP_LESS_EQUAL:     eMode = ScConditionMode::EqLess;     break;
559                 default:
560                     SAL_INFO(
561                         "sc.filter", "unknown CF comparison " << nOperator);
562             }
563         }
564         break;
565 
566         case EXC_CF_TYPE_FMLA:
567             eMode = ScConditionMode::Direct;
568         break;
569 
570         default:
571             SAL_INFO("sc.filter", "unknown CF mode " << nType);
572             return;
573     }
574 
575     // *** create style sheet ***
576 
577     OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
578     SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
579 
580     const XclImpPalette& rPalette = GetPalette();
581 
582     // number format
583 
584     if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
585     {
586         XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
587         bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
588         sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
589         rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
590     }
591 
592     // *** font block ***
593 
594     if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
595     {
596         XclImpFont aFont( GetRoot() );
597         aFont.ReadCFFontBlock( rStrm );
598         aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
599     }
600 
601     // alignment
602     if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
603     {
604         XclImpCellAlign aAlign;
605         sal_uInt16 nAlign(0);
606         sal_uInt16 nAlignMisc(0);
607         nAlign = rStrm.ReaduInt16();
608         nAlignMisc = rStrm.ReaduInt16();
609         aAlign.FillFromCF( nAlign, nAlignMisc );
610         aAlign.FillToItemSet( rStyleItemSet, nullptr );
611         rStrm.Ignore(4);
612     }
613 
614     // *** border block ***
615 
616     if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
617     {
618         sal_uInt16 nLineStyle(0);
619         sal_uInt32 nLineColor(0);
620         nLineStyle = rStrm.ReaduInt16();
621         nLineColor = rStrm.ReaduInt32();
622         rStrm.Ignore( 2 );
623 
624         XclImpCellBorder aBorder;
625         aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
626         aBorder.FillToItemSet( rStyleItemSet, rPalette );
627     }
628 
629     // *** pattern block ***
630 
631     if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
632     {
633         sal_uInt16 nPattern(0), nColor(0);
634         nPattern = rStrm.ReaduInt16();
635         nColor = rStrm.ReaduInt16();
636 
637         XclImpCellArea aArea;
638         aArea.FillFromCF8( nPattern, nColor, nFlags );
639         aArea.FillToItemSet( rStyleItemSet, rPalette );
640     }
641 
642     if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
643     {
644         sal_uInt16 nCellProt;
645         nCellProt = rStrm.ReaduInt16();
646         XclImpCellProt aCellProt;
647         aCellProt.FillFromXF3(nCellProt);
648         aCellProt.FillToItemSet( rStyleItemSet );
649     }
650 
651     // *** formulas ***
652 
653     const ScAddress& rPos = maRanges.front().aStart;    // assured above that maRanges is not empty
654     ExcelToSc& rFmlaConv = GetOldFmlaConverter();
655 
656     std::unique_ptr< ScTokenArray > xTokArr1;
657     if( nFmlaSize1 > 0 )
658     {
659         std::unique_ptr<ScTokenArray> pTokArr;
660         rFmlaConv.Reset( rPos );
661         rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
662         // formula converter owns pTokArr -> create a copy of the token array
663         if( pTokArr )
664         {
665             xTokArr1 = std::move( pTokArr );
666             GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
667         }
668     }
669 
670     std::unique_ptr< ScTokenArray > xTokArr2;
671     if( nFmlaSize2 > 0 )
672     {
673         std::unique_ptr<ScTokenArray> pTokArr;
674         rFmlaConv.Reset( rPos );
675         rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
676         // formula converter owns pTokArr -> create a copy of the token array
677         if( pTokArr )
678         {
679             xTokArr2 = std::move( pTokArr );
680             GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
681         }
682     }
683 
684     // *** create the Calc conditional formatting ***
685 
686     const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
687 
688     if( !mxScCondFmt )
689     {
690         mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, GetDoc() ) );
691         if(maRanges.size() > 1)
692             maRanges.Join(maRanges[0], true);
693         mxScCondFmt->SetRange(maRanges);
694     }
695 
696     ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), GetDoc(), aPos, aStyleName);
697     mxScCondFmt->AddEntry( pEntry );
698     ++mnCondIndex;
699 }
700 
Apply()701 void XclImpCondFormat::Apply()
702 {
703     if( mxScCondFmt )
704     {
705         ScDocument& rDoc = GetDoc();
706 
707         SCTAB nTab = maRanges.front().aStart.Tab();
708         sal_uInt32 nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
709 
710         rDoc.AddCondFormatData( maRanges, nTab, nKey );
711     }
712 }
713 
XclImpCondFormatManager(const XclImpRoot & rRoot)714 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
715     XclImpRoot( rRoot )
716 {
717 }
718 
ReadCondfmt(XclImpStream & rStrm)719 void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
720 {
721     XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
722     pFmt->ReadCondfmt( rStrm );
723     maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
724 }
725 
ReadCF(XclImpStream & rStrm)726 void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
727 {
728     OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
729     if( !maCondFmtList.empty() )
730         maCondFmtList.back()->ReadCF( rStrm );
731 }
732 
Apply()733 void XclImpCondFormatManager::Apply()
734 {
735     for( auto& rxFmt : maCondFmtList )
736         rxFmt->Apply();
737     maCondFmtList.clear();
738 }
739 
740 // Data Validation ============================================================
741 
DVItem(ScRangeList aRanges,const ScValidationData & rValidData)742 XclImpValidationManager::DVItem::DVItem( ScRangeList aRanges, const ScValidationData& rValidData ) :
743     maRanges(std::move(aRanges)), maValidData(rValidData) {}
744 
XclImpValidationManager(const XclImpRoot & rRoot)745 XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
746     XclImpRoot( rRoot )
747 {
748 }
749 
ReadDval(XclImpStream & rStrm)750 void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
751 {
752     const XclImpRoot& rRoot = rStrm.GetRoot();
753     OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
754 
755     sal_uInt32 nObjId(0);
756     rStrm.Ignore( 10 );
757     nObjId = rStrm.ReaduInt32();
758     if( nObjId != EXC_DVAL_NOOBJ )
759     {
760         OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
761         rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
762     }
763 }
764 
ReadDV(XclImpStream & rStrm)765 void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
766 {
767     const XclImpRoot& rRoot = rStrm.GetRoot();
768     OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
769 
770     ScDocument& rDoc = rRoot.GetDoc();
771     SCTAB nScTab = rRoot.GetCurrScTab();
772     ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
773 
774     // flags
775     sal_uInt32 nFlags = rStrm.ReaduInt32();
776 
777     // message strings
778     /*  Empty strings are single NUL characters in Excel (string length is 1).
779         -> Do not let the stream replace them with '?' characters. */
780     rStrm.SetNulSubstChar( '\0' );
781     OUString aPromptTitle(   rStrm.ReadUniString() );
782     OUString aErrorTitle(    rStrm.ReadUniString() );
783     OUString aPromptMessage( rStrm.ReadUniString() );
784     OUString aErrorMessage(  rStrm.ReadUniString() );
785     rStrm.SetNulSubstChar();    // back to default
786 
787     // formula(s)
788     if ( rStrm.GetRecLeft() <= 8 )
789         // Not enough bytes left in the record.  Bail out.
790         return;
791 
792     // first formula
793     // string list is single tStr token with NUL separators -> replace them with LF
794     rStrm.SetNulSubstChar( '\n' );
795     std::unique_ptr< ScTokenArray > xTokArr1;
796 
797     // We can't import the formula directly because we need the range
798     sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
799     rStrm.Ignore( 2 );
800     XclImpStreamPos aPosFormula1;
801     rStrm.StorePosition(aPosFormula1);
802     rStrm.Ignore(nLenFormula1);
803 
804     // second formula
805     std::unique_ptr< ScTokenArray > xTokArr2;
806 
807     sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
808     rStrm.Ignore( 2 );
809     XclImpStreamPos aPosFormula2;
810     rStrm.StorePosition(aPosFormula2);
811     rStrm.Ignore(nLenFormula2);
812 
813     // read all cell ranges
814     XclRangeList aXclRanges;
815     rStrm >> aXclRanges;
816 
817     // convert to Calc range list
818     ScRangeList aScRanges;
819     rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
820 
821     // only continue if there are valid ranges
822     if ( aScRanges.empty() )
823         return;
824 
825     ScRange aCombinedRange = aScRanges.Combine();
826 
827     XclImpStreamPos aCurrentPos;
828     rStrm.StorePosition(aCurrentPos);
829     rStrm.RestorePosition(aPosFormula1);
830     if( nLenFormula1 > 0 )
831     {
832         std::unique_ptr<ScTokenArray> pTokArr;
833         rFmlaConv.Reset(aCombinedRange.aStart);
834         rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
835         // formula converter owns pTokArr -> create a copy of the token array
836         if( pTokArr )
837             xTokArr1 = std::move( pTokArr );
838     }
839     rStrm.SetNulSubstChar();    // back to default
840     if (nLenFormula2 > 0)
841     {
842         rStrm.RestorePosition(aPosFormula2);
843         std::unique_ptr<ScTokenArray> pTokArr;
844         rFmlaConv.Reset(aCombinedRange.aStart);
845         rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
846         // formula converter owns pTokArr -> create a copy of the token array
847         if( pTokArr )
848             xTokArr2 = std::move( pTokArr );
849     }
850 
851     rStrm.RestorePosition(aCurrentPos);
852 
853     bool bIsValid = true;   // valid settings in flags field
854 
855     ScValidationMode eValMode = SC_VALID_ANY;
856     switch( nFlags & EXC_DV_MODE_MASK )
857     {
858         case EXC_DV_MODE_ANY:       eValMode = SC_VALID_ANY;        break;
859         case EXC_DV_MODE_WHOLE:     eValMode = SC_VALID_WHOLE;      break;
860         case EXC_DV_MODE_DECIMAL:   eValMode = SC_VALID_DECIMAL;    break;
861         case EXC_DV_MODE_LIST:      eValMode = SC_VALID_LIST;       break;
862         case EXC_DV_MODE_DATE:      eValMode = SC_VALID_DATE;       break;
863         case EXC_DV_MODE_TIME:      eValMode = SC_VALID_TIME;       break;
864         case EXC_DV_MODE_TEXTLEN:   eValMode = SC_VALID_TEXTLEN;    break;
865         case EXC_DV_MODE_CUSTOM:    eValMode = SC_VALID_CUSTOM;     break;
866         default:                    bIsValid = false;
867     }
868     rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
869 
870     ScConditionMode eCondMode = ScConditionMode::Between;
871     switch( nFlags & EXC_DV_COND_MASK )
872     {
873         case EXC_DV_COND_BETWEEN:   eCondMode = ScConditionMode::Between;    break;
874         case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
875         case EXC_DV_COND_EQUAL:     eCondMode = ScConditionMode::Equal;      break;
876         case EXC_DV_COND_NOTEQUAL:  eCondMode = ScConditionMode::NotEqual;   break;
877         case EXC_DV_COND_GREATER:   eCondMode = ScConditionMode::Greater;    break;
878         case EXC_DV_COND_LESS:      eCondMode = ScConditionMode::Less;       break;
879         case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater;  break;
880         case EXC_DV_COND_EQLESS:    eCondMode = ScConditionMode::EqLess;     break;
881         default:                    bIsValid = false;
882     }
883 
884     if ( !bIsValid )
885         // No valid validation found.  Bail out.
886         return;
887 
888     // The default value for comparison is _BETWEEN. However, custom
889     // rules are a formula, and thus the comparator should be ignored
890     // and only a true or false from the formula is evaluated. In Calc,
891     // formulas use comparison SC_COND_DIRECT.
892     if( eValMode == SC_VALID_CUSTOM )
893     {
894         eCondMode = ScConditionMode::Direct;
895     }
896 
897     // first range for base address for relative references
898     const ScRange& rScRange = aScRanges.front();    // aScRanges is not empty
899 
900     // process string list of a list validity (convert to list of string tokens)
901     if( xTokArr1 && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
902         XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
903 
904     maDVItems.push_back(
905         std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), rDoc, rScRange.aStart)));
906     DVItem& rItem = *maDVItems.back();
907 
908     rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
909     rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
910 
911     // *** prompt box ***
912     if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
913     {
914         // set any text stored in the record
915         rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
916         if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
917             rItem.maValidData.ResetInput();
918     }
919 
920     // *** error box ***
921     ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
922     switch( nFlags & EXC_DV_ERROR_MASK )
923     {
924         case EXC_DV_ERROR_WARNING:  eErrStyle = SC_VALERR_WARNING;  break;
925         case EXC_DV_ERROR_INFO:     eErrStyle = SC_VALERR_INFO;     break;
926     }
927     // set texts and error style
928     rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
929     if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
930         rItem.maValidData.ResetError();
931 }
932 
Apply()933 void XclImpValidationManager::Apply()
934 {
935     const bool bFuzzing = comphelper::IsFuzzing();
936     size_t nPatterns = 0;
937 
938     ScDocument& rDoc = GetRoot().GetDoc();
939     for (const auto& rxDVItem : maDVItems)
940     {
941         DVItem& rItem = *rxDVItem;
942         // set the handle ID
943         sal_uInt32 nHandle = rDoc.AddValidationEntry( rItem.maValidData );
944         ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
945         aPattern.ItemSetPut(SfxUInt32Item(ATTR_VALIDDATA, nHandle));
946 
947         // apply all ranges
948         for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i, ++nPatterns )
949         {
950             const ScRange & rScRange = rItem.maRanges[ i ];
951             rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
952                 rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
953             if (bFuzzing && nPatterns >= 128)
954             {
955                 SAL_WARN("sc.filter", "for fuzzing performance, abandoned pattern after " << nPatterns << " insertions");
956                 break;
957             }
958         }
959     }
960     maDVItems.clear();
961 }
962 
963 // Web queries ================================================================
964 
XclImpWebQuery(const ScRange & rDestRange)965 XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
966     maDestRange( rDestRange ),
967     meMode( xlWQUnknown ),
968     mnRefresh( 0 )
969 {
970 }
971 
ReadParamqry(XclImpStream & rStrm)972 void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
973 {
974     sal_uInt16 nFlags = rStrm.ReaduInt16();
975     sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
976     if( !((nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY )) )
977         return;
978 
979     if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
980     {
981         meMode = xlWQAllTables;
982         maTables = ScfTools::GetHTMLTablesName();
983     }
984     else
985     {
986         meMode = xlWQDocument;
987         maTables = ScfTools::GetHTMLDocName();
988     }
989 }
990 
ReadWqstring(XclImpStream & rStrm)991 void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
992 {
993     maURL = rStrm.ReadUniString();
994 }
995 
ReadWqsettings(XclImpStream & rStrm)996 void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
997 {
998     rStrm.Ignore( 10 );
999     sal_uInt16 nFlags = rStrm.ReaduInt16();
1000     rStrm.Ignore( 10 );
1001     mnRefresh = rStrm.ReaduInt16();
1002 
1003     if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
1004         meMode = xlWQSpecTables;
1005 }
1006 
ReadWqtables(XclImpStream & rStrm)1007 void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
1008 {
1009     if( meMode != xlWQSpecTables )
1010         return;
1011 
1012     rStrm.Ignore( 4 );
1013     OUString aTables( rStrm.ReadUniString() );
1014 
1015     const sal_Unicode cSep = ';';
1016     static constexpr OUStringLiteral aQuotedPairs( u"\"\"" );
1017     maTables.clear();
1018     for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
1019     {
1020         OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
1021         sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
1022         if( nTabNum > 0 )
1023             maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
1024         else
1025         {
1026             ScGlobal::EraseQuotes( aToken, '"', false );
1027             if( !aToken.isEmpty() )
1028                 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
1029         }
1030     }
1031 }
1032 
Apply(ScDocument & rDoc,const OUString & rFilterName)1033 void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
1034 {
1035     if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
1036     {
1037         ScAreaLink* pLink = new ScAreaLink( *rDoc.GetDocumentShell(),
1038             maURL, rFilterName, OUString(), maTables, maDestRange, mnRefresh * 60UL );
1039         rDoc.GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile,
1040             maURL, &rFilterName, &maTables );
1041     }
1042 }
1043 
XclImpWebQueryBuffer(const XclImpRoot & rRoot)1044 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
1045     XclImpRoot( rRoot )
1046 {
1047 }
1048 
ReadQsi(XclImpStream & rStrm)1049 void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
1050 {
1051     if( GetBiff() == EXC_BIFF8 )
1052     {
1053         rStrm.Ignore( 10 );
1054         OUString aXclName( rStrm.ReadUniString() );
1055 
1056         // #i64794# Excel replaces spaces with underscores
1057         aXclName = aXclName.replaceAll( " ", "_" );
1058 
1059         // find the defined name used in Calc
1060         if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
1061         {
1062             if( const ScRangeData* pRangeData = pName->GetScRangeData() )
1063             {
1064                 ScRange aRange;
1065                 if( pRangeData->IsReference( aRange ) )
1066                     maWQList.emplace_back( aRange );
1067             }
1068         }
1069     }
1070     else
1071     {
1072         DBG_ERROR_BIFF();
1073     }
1074 }
1075 
ReadParamqry(XclImpStream & rStrm)1076 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
1077 {
1078     if (!maWQList.empty())
1079         maWQList.back().ReadParamqry( rStrm );
1080 }
1081 
ReadWqstring(XclImpStream & rStrm)1082 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
1083 {
1084     if (!maWQList.empty())
1085         maWQList.back().ReadWqstring( rStrm );
1086 }
1087 
ReadWqsettings(XclImpStream & rStrm)1088 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
1089 {
1090     if (!maWQList.empty())
1091         maWQList.back().ReadWqsettings( rStrm );
1092 }
1093 
ReadWqtables(XclImpStream & rStrm)1094 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
1095 {
1096     if (!maWQList.empty())
1097         maWQList.back().ReadWqtables( rStrm );
1098 }
1099 
Apply()1100 void XclImpWebQueryBuffer::Apply()
1101 {
1102     ScDocument& rDoc = GetDoc();
1103     for( auto& rQuery : maWQList )
1104         rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
1105 }
1106 
1107 // Decryption =================================================================
1108 
1109 namespace {
1110 
lclReadFilepass5(XclImpStream & rStrm)1111 XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
1112 {
1113     XclImpDecrypterRef xDecr;
1114     OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1115     if( rStrm.GetRecLeft() == 4 )
1116     {
1117         sal_uInt16 nKey(0), nHash(0);
1118         nKey = rStrm.ReaduInt16();
1119         nHash = rStrm.ReaduInt16();
1120         xDecr = std::make_shared<XclImpBiff5Decrypter>( nKey, nHash );
1121     }
1122     return xDecr;
1123 }
1124 
lclReadFilepass8_Standard(XclImpStream & rStrm)1125 XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
1126 {
1127     XclImpDecrypterRef xDecr;
1128     OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1129     if( rStrm.GetRecLeft() == 48 )
1130     {
1131         std::vector<sal_uInt8> aSalt(16);
1132         std::vector<sal_uInt8> aVerifier(16);
1133         std::vector<sal_uInt8> aVerifierHash(16);
1134         rStrm.Read(aSalt.data(), 16);
1135         rStrm.Read(aVerifier.data(), 16);
1136         rStrm.Read(aVerifierHash.data(), 16);
1137         xDecr = std::make_shared<XclImpBiff8StdDecrypter>(std::move(aSalt), std::move(aVerifier), std::move(aVerifierHash));
1138     }
1139     return xDecr;
1140 }
1141 
lclReadFilepass8_Strong(XclImpStream & rStream)1142 XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
1143 {
1144     //It is possible there are other variants in existence but these
1145     //are the defaults I get with Excel 2013
1146     XclImpDecrypterRef xDecr;
1147 
1148     msfilter::RC4EncryptionInfo info;
1149 
1150     info.header.flags = rStream.ReaduInt32();
1151     if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
1152         return xDecr;
1153 
1154     sal_uInt32 nHeaderSize = rStream.ReaduInt32();
1155     sal_uInt32 actualHeaderSize = sizeof(info.header);
1156 
1157     if( nHeaderSize < actualHeaderSize )
1158         return xDecr;
1159 
1160     info.header.flags = rStream.ReaduInt32();
1161     info.header.sizeExtra = rStream.ReaduInt32();
1162     info.header.algId = rStream.ReaduInt32();
1163     info.header.algIdHash = rStream.ReaduInt32();
1164     info.header.keyBits = rStream.ReaduInt32();
1165     info.header.providedType = rStream.ReaduInt32();
1166     info.header.reserved1 = rStream.ReaduInt32();
1167     info.header.reserved2 = rStream.ReaduInt32();
1168 
1169     rStream.Ignore(nHeaderSize - actualHeaderSize);
1170 
1171     info.verifier.saltSize = rStream.ReaduInt32();
1172     if (info.verifier.saltSize != msfilter::SALT_LENGTH)
1173         return xDecr;
1174     rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
1175     rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
1176 
1177     info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
1178     if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
1179         return xDecr;
1180     rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
1181 
1182     // check flags and algorithm IDs, required are AES128 and SHA-1
1183     if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
1184         return xDecr;
1185 
1186     if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
1187         return xDecr;
1188 
1189     if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
1190         return xDecr;
1191 
1192     // hash algorithm ID 0 defaults to SHA-1 too
1193     if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
1194         return xDecr;
1195 
1196     xDecr = std::make_shared<XclImpBiff8CryptoAPIDecrypter>(
1197         std::vector<sal_uInt8>(info.verifier.salt,
1198             info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
1199         std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
1200             info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
1201         std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
1202             info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash)));
1203 
1204     return xDecr;
1205 }
1206 
lclReadFilepass8(XclImpStream & rStrm)1207 XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
1208 {
1209     XclImpDecrypterRef xDecr;
1210 
1211     sal_uInt16 nMode = rStrm.ReaduInt16();
1212     switch( nMode )
1213     {
1214         case EXC_FILEPASS_BIFF5:
1215             xDecr = lclReadFilepass5( rStrm );
1216         break;
1217 
1218         case EXC_FILEPASS_BIFF8:
1219         {
1220             sal_uInt32 nVersion = rStrm.ReaduInt32();
1221             if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
1222             {
1223                 //A Version structure where Version.vMajor MUST be 0x0001,
1224                 //and Version.vMinor MUST be 0x0001.
1225                 xDecr = lclReadFilepass8_Standard(rStrm);
1226             }
1227             else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
1228                      nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
1229             {
1230                 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1231                 //Version.vMinor MUST be 0x0002.
1232                 xDecr = lclReadFilepass8_Strong(rStrm);
1233             }
1234             else
1235                 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1236         }
1237         break;
1238 
1239         default:
1240             OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1241     }
1242 
1243     return xDecr;
1244 }
1245 
1246 } // namespace
1247 
ReadFilepass(XclImpStream & rStrm)1248 const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
1249 {
1250     XclImpDecrypterRef xDecr;
1251     rStrm.DisableDecryption();
1252 
1253     // read the FILEPASS record and create a new decrypter object
1254     switch( rStrm.GetRoot().GetBiff() )
1255     {
1256         case EXC_BIFF2:
1257         case EXC_BIFF3:
1258         case EXC_BIFF4:
1259         case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm );  break;
1260         case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm );  break;
1261         default:        DBG_ERROR_BIFF();
1262     };
1263 
1264     // set decrypter at import stream
1265     rStrm.SetDecrypter( xDecr );
1266 
1267     // request and verify a password (decrypter implements IDocPasswordVerifier)
1268     if( xDecr )
1269         rStrm.GetRoot().RequestEncryptionData( *xDecr );
1270 
1271     // return error code (success, wrong password, etc.)
1272     return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
1273 }
1274 
1275 // Document protection ========================================================
1276 
XclImpDocProtectBuffer(const XclImpRoot & rRoot)1277 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
1278     XclImpRoot( rRoot ),
1279     mnPassHash(0x0000),
1280     mbDocProtect(false),
1281     mbWinProtect(false)
1282 {
1283 }
1284 
ReadDocProtect(XclImpStream & rStrm)1285 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
1286 {
1287     mbDocProtect = rStrm.ReaduInt16() != 0;
1288 }
1289 
ReadWinProtect(XclImpStream & rStrm)1290 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
1291 {
1292     mbWinProtect = rStrm.ReaduInt16() != 0;
1293 }
1294 
ReadPasswordHash(XclImpStream & rStrm)1295 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
1296 {
1297     rStrm.EnableDecryption();
1298     mnPassHash = rStrm.ReaduInt16();
1299 }
1300 
Apply() const1301 void XclImpDocProtectBuffer::Apply() const
1302 {
1303     if (!mbDocProtect && !mbWinProtect)
1304         // Excel requires either the structure or windows protection is set.
1305         // If neither is set then the document is not protected at all.
1306         return;
1307 
1308     std::unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
1309     pProtect->setProtected(true);
1310 
1311     if (mnPassHash)
1312     {
1313         // 16-bit password hash.
1314         Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
1315         pProtect->setPasswordHash(aPass, PASSHASH_XL);
1316     }
1317 
1318     // document protection options
1319     pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
1320     pProtect->setOption(ScDocProtection::WINDOWS,   mbWinProtect);
1321 
1322     GetDoc().SetDocProtection(pProtect.get());
1323 }
1324 
1325 // Sheet Protection ===========================================================
1326 
Sheet()1327 XclImpSheetProtectBuffer::Sheet::Sheet() :
1328     mbProtected(false),
1329     mnPasswordHash(0x0000),
1330     mnOptions(0x4400)
1331 {
1332 }
1333 
Sheet(const Sheet & r)1334 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
1335     mbProtected(r.mbProtected),
1336     mnPasswordHash(r.mnPasswordHash),
1337     mnOptions(r.mnOptions)
1338 {
1339 }
1340 
XclImpSheetProtectBuffer(const XclImpRoot & rRoot)1341 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
1342     XclImpRoot( rRoot )
1343 {
1344 }
1345 
ReadProtect(XclImpStream & rStrm,SCTAB nTab)1346 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
1347 {
1348     if ( rStrm.ReaduInt16() )
1349     {
1350         Sheet* pSheet = GetSheetItem(nTab);
1351         if (pSheet)
1352             pSheet->mbProtected = true;
1353     }
1354 }
1355 
ReadOptions(XclImpStream & rStrm,SCTAB nTab)1356 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
1357 {
1358     // The flag size specifies the size of bytes that follows that stores
1359     // feature data.  If -1 it depends on the feature type imported earlier.
1360     // For enhanced protection data, the size is always 4.  For the most xls
1361     // documents out there this value is almost always -1.
1362     sal_Int32 nFlagSize = rStrm.ReadInt32();
1363     if (nFlagSize != -1)
1364         return;
1365 
1366     // There are actually 4 bytes to read, but the upper 2 bytes currently
1367     // don't store any bits.
1368     sal_uInt16 nOptions = rStrm.ReaduInt16();
1369 
1370     Sheet* pSheet = GetSheetItem(nTab);
1371     if (pSheet)
1372         pSheet->mnOptions = nOptions;
1373 }
1374 
AppendEnhancedProtection(const ScEnhancedProtection & rProt,SCTAB nTab)1375 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
1376 {
1377     Sheet* pSheet = GetSheetItem(nTab);
1378     if (pSheet)
1379         pSheet->maEnhancedProtections.push_back( rProt);
1380 }
1381 
ReadPasswordHash(XclImpStream & rStrm,SCTAB nTab)1382 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
1383 {
1384     sal_uInt16 nHash = rStrm.ReaduInt16();
1385     Sheet* pSheet = GetSheetItem(nTab);
1386     if (pSheet)
1387         pSheet->mnPasswordHash = nHash;
1388 }
1389 
Apply() const1390 void XclImpSheetProtectBuffer::Apply() const
1391 {
1392     for (const auto& [rTab, rSheet] : maProtectedSheets)
1393     {
1394         if (!rSheet.mbProtected)
1395             // This sheet is (for whatever reason) not protected.
1396             continue;
1397 
1398         std::unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
1399         pProtect->setProtected(true);
1400 
1401         // 16-bit hash password
1402         const sal_uInt16 nHash = rSheet.mnPasswordHash;
1403         if (nHash)
1404         {
1405             Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
1406             pProtect->setPasswordHash(aPass, PASSHASH_XL);
1407         }
1408 
1409         // sheet protection options
1410         const sal_uInt16 nOptions = rSheet.mnOptions;
1411         pProtect->setOption( ScTableProtection::OBJECTS,               (nOptions & 0x0001) );
1412         pProtect->setOption( ScTableProtection::SCENARIOS,             (nOptions & 0x0002) );
1413         pProtect->setOption( ScTableProtection::FORMAT_CELLS,          (nOptions & 0x0004) );
1414         pProtect->setOption( ScTableProtection::FORMAT_COLUMNS,        (nOptions & 0x0008) );
1415         pProtect->setOption( ScTableProtection::FORMAT_ROWS,           (nOptions & 0x0010) );
1416         pProtect->setOption( ScTableProtection::INSERT_COLUMNS,        (nOptions & 0x0020) );
1417         pProtect->setOption( ScTableProtection::INSERT_ROWS,           (nOptions & 0x0040) );
1418         pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS,     (nOptions & 0x0080) );
1419         pProtect->setOption( ScTableProtection::DELETE_COLUMNS,        (nOptions & 0x0100) );
1420         pProtect->setOption( ScTableProtection::DELETE_ROWS,           (nOptions & 0x0200) );
1421         pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS,   (nOptions & 0x0400) );
1422         pProtect->setOption( ScTableProtection::SORT,                  (nOptions & 0x0800) );
1423         pProtect->setOption( ScTableProtection::AUTOFILTER,            (nOptions & 0x1000) );
1424         pProtect->setOption( ScTableProtection::PIVOT_TABLES,          (nOptions & 0x2000) );
1425         pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
1426 
1427         // Enhanced protection containing editable ranges and permissions.
1428         pProtect->setEnhancedProtection( std::vector(rSheet.maEnhancedProtections) );
1429 
1430         // all done.  now commit.
1431         GetDoc().SetTabProtection(rTab, pProtect.get());
1432     }
1433 }
1434 
GetSheetItem(SCTAB nTab)1435 XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
1436 {
1437     ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
1438     if (itr == maProtectedSheets.end())
1439     {
1440         // new sheet
1441         if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
1442             return nullptr;
1443 
1444         itr = maProtectedSheets.find(nTab);
1445     }
1446 
1447     return &itr->second;
1448 }
1449 
1450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1451