1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
21 #include <xicontent.hxx>
22 #include <sfx2/objsh.hxx>
23 #include <sfx2/docfile.hxx>
24 #include <tools/urlobj.hxx>
25 #include <sfx2/linkmgr.hxx>
26 #include <svl/itemset.hxx>
27 #include <scitems.hxx>
28 #include <editeng/eeitem.hxx>
29 #include <svl/intitem.hxx>
30 #include <svl/stritem.hxx>
31 #include <editeng/flditem.hxx>
32 #include <editeng/editobj.hxx>
33 #include <unotools/charclass.hxx>
34 #include <stringutil.hxx>
35 #include <cellform.hxx>
36 #include <cellvalue.hxx>
37 #include <document.hxx>
38 #include <editutil.hxx>
39 #include <validat.hxx>
40 #include <patattr.hxx>
41 #include <docpool.hxx>
42 #include <rangenam.hxx>
43 #include <arealink.hxx>
44 #include <stlsheet.hxx>
45 #include <xlcontent.hxx>
46 #include <xlformula.hxx>
47 #include <xltracer.hxx>
48 #include <xistream.hxx>
49 #include <xihelper.hxx>
50 #include <xistyle.hxx>
51 #include <xiescher.hxx>
54 #include <excform.hxx>
55 #include <tabprotection.hxx>
56 #include <documentimport.hxx>
60 #include <oox/helper/helper.hxx>
61 #include <sal/log.hxx>
63 using ::com::sun::star::uno::Sequence
;
64 using ::std::unique_ptr
;
66 // Shared string table ========================================================
68 XclImpSst::XclImpSst( const XclImpRoot
& rRoot
) :
73 void XclImpSst::ReadSst( XclImpStream
& rStrm
)
76 sal_uInt32 nStrCount
= rStrm
.ReaduInt32();
77 auto nBytesAvailable
= rStrm
.GetRecLeft();
78 if (nStrCount
> nBytesAvailable
)
80 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount
<< " strings, but only " << nBytesAvailable
<< " bytes available, truncating");
81 nStrCount
= nBytesAvailable
;
84 maStrings
.reserve(nStrCount
);
85 while( (nStrCount
> 0) && rStrm
.IsValid() )
88 aString
.Read( rStrm
);
89 maStrings
.push_back( aString
);
94 const XclImpString
* XclImpSst::GetString( sal_uInt32 nSstIndex
) const
96 return (nSstIndex
< maStrings
.size()) ? &maStrings
[ nSstIndex
] : nullptr;
99 // Hyperlinks =================================================================
103 /** Reads character array and stores it into rString.
104 @param nChars Number of following characters (not byte count!).
105 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
106 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, sal_uInt32 nChars
, bool b16Bit
)
108 sal_uInt16 nReadChars
= ulimit_cast
< sal_uInt16
>( nChars
);
109 rString
+= rStrm
.ReadRawUniString( nReadChars
, b16Bit
);
110 // ignore remaining chars
111 std::size_t nIgnore
= nChars
- nReadChars
;
114 rStrm
.Ignore( nIgnore
);
117 /** Reads 32-bit string length and the character array and stores it into rString.
118 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
119 void lclAppendString32( OUString
& rString
, XclImpStream
& rStrm
, bool b16Bit
)
121 lclAppendString32( rString
, rStrm
, rStrm
.ReaduInt32(), b16Bit
);
124 /** Reads 32-bit string length and ignores following 16-bit character array. */
125 void lclIgnoreString32( XclImpStream
& rStrm
)
127 sal_uInt32 nChars
= rStrm
.ReaduInt32();
129 rStrm
.Ignore( nChars
);
132 /** Converts a path to an absolute path.
133 @param rPath The source path. The resulting path is returned here.
134 @param nLevel Number of parent directories to add in front of the path. */
135 void lclGetAbsPath( OUString
& rPath
, sal_uInt16 nLevel
, const SfxObjectShell
* pDocShell
)
137 OUStringBuffer aTmpStr
;
140 aTmpStr
.append( "../" );
143 aTmpStr
.append( rPath
);
147 bool bWasAbs
= false;
148 rPath
= pDocShell
->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr
.makeStringAndClear(), bWasAbs
).GetMainURL( INetURLObject::DecodeMechanism::NONE
);
149 // full path as stored in SvxURLField must be encoded
152 rPath
= aTmpStr
.makeStringAndClear();
155 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
156 void lclInsertUrl( XclImpRoot
& rRoot
, const OUString
& rUrl
, SCCOL nScCol
, SCROW nScRow
, SCTAB nScTab
)
158 ScDocumentImport
& rDoc
= rRoot
.GetDocImport();
159 ScAddress
aScPos( nScCol
, nScRow
, nScTab
);
160 ScRefCellValue
aCell(rDoc
.getDoc(), aScPos
);
161 switch( aCell
.meType
)
163 // #i54261# hyperlinks in string cells
164 case CELLTYPE_STRING
:
167 sal_uInt32 nNumFmt
= rDoc
.getDoc().GetNumberFormat(rDoc
.getDoc().GetNonThreadedContext(), aScPos
);
168 SvNumberFormatter
* pFormatter
= rDoc
.getDoc().GetFormatTable();
171 ScCellFormat::GetString(aCell
, nNumFmt
, aDisplText
, &pColor
, *pFormatter
, &rDoc
.getDoc());
172 if (aDisplText
.isEmpty())
175 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
176 SvxURLField
aUrlField( rUrl
, aDisplText
, SvxURLFormat::AppDefault
);
178 if( aCell
.meType
== CELLTYPE_EDIT
)
180 const EditTextObject
* pEditObj
= aCell
.mpEditText
;
181 rEE
.SetText( *pEditObj
);
182 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection( 0, 0, EE_PARA_ALL
, 0 ) );
186 rEE
.SetText( EMPTY_OUSTRING
);
187 rEE
.QuickInsertField( SvxFieldItem( aUrlField
, EE_FEATURE_FIELD
), ESelection() );
188 if( const ScPatternAttr
* pPattern
= rDoc
.getDoc().GetPattern( aScPos
.Col(), aScPos
.Row(), nScTab
) )
190 SfxItemSet
aItemSet( rEE
.GetEmptyItemSet() );
191 pPattern
->FillEditItemSet( &aItemSet
);
192 rEE
.QuickSetAttribs( aItemSet
, ESelection( 0, 0, EE_PARA_ALL
, 0 ) );
196 // The cell will own the text object instance.
197 rDoc
.setEditCell(aScPos
, rEE
.CreateTextObject());
202 // Handle other cell types e.g. formulas ( and ? ) that have associated
204 // Ideally all hyperlinks should be treated as below. For the moment,
205 // given the current absence of ods support lets 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.
212 SfxStringItem
aItem( ATTR_HYPERLINK
, rUrl
);
213 rDoc
.getDoc().ApplyAttr(nScCol
, nScRow
, nScTab
, aItem
);
221 void XclImpHyperlink::ReadHlink( XclImpStream
& rStrm
)
223 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
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
);
233 OUString
XclImpHyperlink::ReadEmbeddedData( XclImpStream
& rStrm
)
235 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
236 SfxObjectShell
* pDocShell
= rRoot
.GetDocShell();
238 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
243 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
245 OSL_ENSURE( aGuid
== XclTools::maGuidStdLink
, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
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
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
);
258 // URL fields are zero-terminated - do not let the stream replace them
259 // in the lclAppendString32() with the '?' character.
260 rStrm
.SetNulSubstChar( '\0' );
263 if( ::get_flag( nFlags
, EXC_HLINK_UNC
) )
265 xLongName
.reset( new OUString
);
266 lclAppendString32( *xLongName
, rStrm
, true );
267 lclGetAbsPath( *xLongName
, 0, pDocShell
);
270 else if( ::get_flag( nFlags
, EXC_HLINK_BODY
) )
274 if( aGuid
== XclTools::maGuidFileMoniker
)
276 sal_uInt16 nLevel
= rStrm
.ReaduInt16(); // counter for level to climb down in path
277 xShortName
.reset( new OUString
);
278 lclAppendString32( *xShortName
, rStrm
, false );
281 sal_uInt32 nStrLen
= rStrm
.ReaduInt32();
284 nStrLen
= rStrm
.ReaduInt32();
285 nStrLen
/= 2; // it's byte count here...
287 xLongName
.reset( new OUString
);
288 lclAppendString32( *xLongName
, rStrm
, nStrLen
, true );
289 lclGetAbsPath( *xLongName
, nLevel
, pDocShell
);
292 lclGetAbsPath( *xShortName
, nLevel
, pDocShell
);
294 else if( aGuid
== XclTools::maGuidUrlMoniker
)
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
);
305 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
310 if( ::get_flag( nFlags
, EXC_HLINK_MARK
) )
312 xTextMark
.reset( new OUString
);
313 lclAppendString32( *xTextMark
, rStrm
, true );
316 rStrm
.SetNulSubstChar(); // back to default
318 OSL_ENSURE( rStrm
.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
320 if (!xLongName
&& xShortName
.get())
321 xLongName
= std::move(xShortName
);
322 else if (!xLongName
&& xTextMark
.get())
323 xLongName
.reset( new OUString
);
329 if( xLongName
->isEmpty() )
331 sal_Int32 nSepPos
= xTextMark
->lastIndexOf( '!' );
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)
340 ScDocument
& rDoc
= rRoot
.GetDoc();
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, OUString( '.' ))));
348 xLongName
.reset( new OUString( *xLongName
+ "#" + *xTextMark
) );
350 return( *xLongName
);
352 return( OUString() );
355 void XclImpHyperlink::ConvertToValidTabName(OUString
& rUrl
)
357 sal_Int32 n
= rUrl
.getLength();
359 // Needs at least 4 characters.
363 // the 1st character must be '#'.
366 OUStringBuffer
aNewUrl("#");
367 OUStringBuffer aTabName
;
369 bool bInQuote
= false;
370 bool bQuoteTabName
= false;
371 for( sal_Int32 i
= 1; i
< n
; ++i
)
373 sal_Unicode c
= rUrl
[i
];
376 if (bInQuote
&& i
+1 < n
&& rUrl
[i
+1] == '\'')
378 // Two consecutive single quotes ('') signify a single literal
379 // quite. When this occurs, the whole table name needs to be
381 bQuoteTabName
= true;
382 aTabName
.append(c
).append(c
);
387 bInQuote
= !bInQuote
;
388 if (!bInQuote
&& !aTabName
.isEmpty())
392 aNewUrl
.append(aTabName
);
404 // It should be outside the quotes!
407 // All is good. Pass the new URL.
408 rUrl
= aNewUrl
.makeStringAndClear();
411 void XclImpHyperlink::InsertUrl( XclImpRoot
& rRoot
, const XclRange
& rXclRange
, const OUString
& rUrl
)
414 ConvertToValidTabName(aUrl
);
416 SCTAB nScTab
= rRoot
.GetCurrScTab();
417 ScRange
aScRange( ScAddress::UNINITIALIZED
);
418 if( rRoot
.GetAddressConverter().ConvertRange( aScRange
, rXclRange
, nScTab
, nScTab
, true ) )
420 SCCOL nScCol1
, nScCol2
;
421 SCROW nScRow1
, nScRow2
;
422 aScRange
.GetVars( nScCol1
, nScRow1
, nScTab
, nScCol2
, nScRow2
, nScTab
);
423 for( SCCOL nScCol
= nScCol1
; nScCol
<= nScCol2
; ++nScCol
)
424 for( SCROW nScRow
= nScRow1
; nScRow
<= nScRow2
; ++nScRow
)
425 lclInsertUrl( rRoot
, aUrl
, nScCol
, nScRow
, nScTab
);
429 // Label ranges ===============================================================
431 void XclImpLabelranges::ReadLabelranges( XclImpStream
& rStrm
)
433 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
434 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
436 ScDocument
& rDoc
= rRoot
.GetDoc();
437 SCTAB nScTab
= rRoot
.GetCurrScTab();
438 XclImpAddressConverter
& rAddrConv
= rRoot
.GetAddressConverter();
439 ScRangePairListRef xLabelRangesRef
;
441 XclRangeList aRowXclRanges
, aColXclRanges
;
442 rStrm
>> aRowXclRanges
>> aColXclRanges
;
445 ScRangeList aRowScRanges
;
446 rAddrConv
.ConvertRangeList( aRowScRanges
, aRowXclRanges
, nScTab
, false );
447 xLabelRangesRef
= rDoc
.GetRowNameRangesRef();
448 for ( size_t i
= 0, nRanges
= aRowScRanges
.size(); i
< nRanges
; ++i
)
450 const ScRange
& rScRange
= aRowScRanges
[ i
];
451 ScRange
aDataRange( rScRange
);
452 if( aDataRange
.aEnd
.Col() < rDoc
.MaxCol() )
454 aDataRange
.aStart
.SetCol( aDataRange
.aEnd
.Col() + 1 );
455 aDataRange
.aEnd
.SetCol( rDoc
.MaxCol() );
457 else if( aDataRange
.aStart
.Col() > 0 )
459 aDataRange
.aEnd
.SetCol( aDataRange
.aStart
.Col() - 1 );
460 aDataRange
.aStart
.SetCol( 0 );
462 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
465 // column label ranges
466 ScRangeList aColScRanges
;
467 rAddrConv
.ConvertRangeList( aColScRanges
, aColXclRanges
, nScTab
, false );
468 xLabelRangesRef
= rDoc
.GetColNameRangesRef();
470 for ( size_t i
= 0, nRanges
= aColScRanges
.size(); i
< nRanges
; ++i
)
472 const ScRange
& rScRange
= aColScRanges
[ i
];
473 ScRange
aDataRange( rScRange
);
474 if( aDataRange
.aEnd
.Row() < rDoc
.MaxRow() )
476 aDataRange
.aStart
.SetRow( aDataRange
.aEnd
.Row() + 1 );
477 aDataRange
.aEnd
.SetRow( rDoc
.MaxRow() );
479 else if( aDataRange
.aStart
.Row() > 0 )
481 aDataRange
.aEnd
.SetRow( aDataRange
.aStart
.Row() - 1 );
482 aDataRange
.aStart
.SetRow( 0 );
484 xLabelRangesRef
->Append( ScRangePair( rScRange
, aDataRange
) );
488 // Conditional formatting =====================================================
490 XclImpCondFormat::XclImpCondFormat( const XclImpRoot
& rRoot
, sal_uInt32 nFormatIndex
) :
492 mnFormatIndex( nFormatIndex
),
498 XclImpCondFormat::~XclImpCondFormat()
502 void XclImpCondFormat::ReadCondfmt( XclImpStream
& rStrm
)
504 OSL_ENSURE( !mnCondCount
, "XclImpCondFormat::ReadCondfmt - already initialized" );
505 XclRangeList aXclRanges
;
506 mnCondCount
= rStrm
.ReaduInt16();
509 GetAddressConverter().ConvertRangeList( maRanges
, aXclRanges
, GetCurrScTab(), true );
512 void XclImpCondFormat::ReadCF( XclImpStream
& rStrm
)
514 if( mnCondIndex
>= mnCondCount
)
516 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
520 // entire conditional format outside of valid range?
521 if( maRanges
.empty() )
524 sal_uInt8 nType
= rStrm
.ReaduInt8();
525 sal_uInt8 nOperator
= rStrm
.ReaduInt8();
526 sal_uInt16 nFmlaSize1
= rStrm
.ReaduInt16();
527 sal_uInt16 nFmlaSize2
= rStrm
.ReaduInt16();
528 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
529 rStrm
.Ignore( 2 ); //nFlagsExtended
531 // *** mode and comparison operator ***
533 ScConditionMode eMode
= ScConditionMode::NONE
;
536 case EXC_CF_TYPE_CELL
:
540 case EXC_CF_CMP_BETWEEN
: eMode
= ScConditionMode::Between
; break;
541 case EXC_CF_CMP_NOT_BETWEEN
: eMode
= ScConditionMode::NotBetween
; break;
542 case EXC_CF_CMP_EQUAL
: eMode
= ScConditionMode::Equal
; break;
543 case EXC_CF_CMP_NOT_EQUAL
: eMode
= ScConditionMode::NotEqual
; break;
544 case EXC_CF_CMP_GREATER
: eMode
= ScConditionMode::Greater
; break;
545 case EXC_CF_CMP_LESS
: eMode
= ScConditionMode::Less
; break;
546 case EXC_CF_CMP_GREATER_EQUAL
: eMode
= ScConditionMode::EqGreater
; break;
547 case EXC_CF_CMP_LESS_EQUAL
: eMode
= ScConditionMode::EqLess
; break;
550 "sc.filter", "unknown CF comparison " << nOperator
);
555 case EXC_CF_TYPE_FMLA
:
556 eMode
= ScConditionMode::Direct
;
560 SAL_INFO("sc.filter", "unknown CF mode " << nType
);
564 // *** create style sheet ***
566 OUString
aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex
, mnCondIndex
) );
567 SfxItemSet
& rStyleItemSet
= ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName
, true ).GetItemSet();
569 const XclImpPalette
& rPalette
= GetPalette();
573 if( get_flag( nFlags
, EXC_CF_BLOCK_NUMFMT
) )
575 XclImpNumFmtBuffer
& rNumFmtBuffer
= GetRoot().GetNumFmtBuffer();
576 bool bIFmt
= get_flag( nFlags
, EXC_CF_IFMT_USER
);
577 sal_uInt16 nFormat
= rNumFmtBuffer
.ReadCFFormat( rStrm
, bIFmt
);
578 rNumFmtBuffer
.FillToItemSet( rStyleItemSet
, nFormat
);
581 // *** font block ***
583 if( ::get_flag( nFlags
, EXC_CF_BLOCK_FONT
) )
585 XclImpFont
aFont( GetRoot() );
586 aFont
.ReadCFFontBlock( rStrm
);
587 aFont
.FillToItemSet( rStyleItemSet
, XclFontItemType::Cell
);
591 if( get_flag( nFlags
, EXC_CF_BLOCK_ALIGNMENT
) )
593 XclImpCellAlign aAlign
;
594 sal_uInt16
nAlign(0);
595 sal_uInt16
nAlignMisc(0);
596 nAlign
= rStrm
.ReaduInt16();
597 nAlignMisc
= rStrm
.ReaduInt16();
598 aAlign
.FillFromCF( nAlign
, nAlignMisc
);
599 aAlign
.FillToItemSet( rStyleItemSet
, nullptr );
603 // *** border block ***
605 if( ::get_flag( nFlags
, EXC_CF_BLOCK_BORDER
) )
607 sal_uInt16
nLineStyle(0);
608 sal_uInt32
nLineColor(0);
609 nLineStyle
= rStrm
.ReaduInt16();
610 nLineColor
= rStrm
.ReaduInt32();
613 XclImpCellBorder aBorder
;
614 aBorder
.FillFromCF8( nLineStyle
, nLineColor
, nFlags
);
615 aBorder
.FillToItemSet( rStyleItemSet
, rPalette
);
618 // *** pattern block ***
620 if( ::get_flag( nFlags
, EXC_CF_BLOCK_AREA
) )
622 sal_uInt16
nPattern(0), nColor(0);
623 nPattern
= rStrm
.ReaduInt16();
624 nColor
= rStrm
.ReaduInt16();
626 XclImpCellArea aArea
;
627 aArea
.FillFromCF8( nPattern
, nColor
, nFlags
);
628 aArea
.FillToItemSet( rStyleItemSet
, rPalette
);
631 if( get_flag( nFlags
, EXC_CF_BLOCK_PROTECTION
) )
633 sal_uInt16 nCellProt
;
634 nCellProt
= rStrm
.ReaduInt16();
635 XclImpCellProt aCellProt
;
636 aCellProt
.FillFromXF3(nCellProt
);
637 aCellProt
.FillToItemSet( rStyleItemSet
);
642 const ScAddress
& rPos
= maRanges
.front().aStart
; // assured above that maRanges is not empty
643 ExcelToSc
& rFmlaConv
= GetOldFmlaConverter();
645 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
648 std::unique_ptr
<ScTokenArray
> pTokArr
;
649 rFmlaConv
.Reset( rPos
);
650 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize1
, false, FT_CondFormat
);
651 // formula converter owns pTokArr -> create a copy of the token array
654 xTokArr1
= std::move( pTokArr
);
655 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1
);
659 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
662 std::unique_ptr
<ScTokenArray
> pTokArr
;
663 rFmlaConv
.Reset( rPos
);
664 rFmlaConv
.Convert( pTokArr
, rStrm
, nFmlaSize2
, false, FT_CondFormat
);
665 // formula converter owns pTokArr -> create a copy of the token array
668 xTokArr2
= std::move( pTokArr
);
669 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2
);
673 // *** create the Calc conditional formatting ***
675 const ScAddress
aPos(rPos
); //in case maRanges.Join invalidates it
677 if( !mxScCondFmt
.get() )
679 mxScCondFmt
.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
680 if(maRanges
.size() > 1)
681 maRanges
.Join(maRanges
[0], true);
682 mxScCondFmt
->SetRange(maRanges
);
685 ScCondFormatEntry
* pEntry
= new ScCondFormatEntry(eMode
, xTokArr1
.get(), xTokArr2
.get(), &GetDoc(), aPos
, aStyleName
);
686 mxScCondFmt
->AddEntry( pEntry
);
690 void XclImpCondFormat::Apply()
692 if( mxScCondFmt
.get() )
694 ScDocument
& rDoc
= GetDoc();
696 SCTAB nTab
= maRanges
.front().aStart
.Tab();
697 sal_uLong nKey
= rDoc
.AddCondFormat( mxScCondFmt
->Clone(), nTab
);
699 rDoc
.AddCondFormatData( maRanges
, nTab
, nKey
);
703 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot
& rRoot
) :
708 void XclImpCondFormatManager::ReadCondfmt( XclImpStream
& rStrm
)
710 XclImpCondFormat
* pFmt
= new XclImpCondFormat( GetRoot(), maCondFmtList
.size() );
711 pFmt
->ReadCondfmt( rStrm
);
712 maCondFmtList
.push_back( std::unique_ptr
<XclImpCondFormat
>(pFmt
) );
715 void XclImpCondFormatManager::ReadCF( XclImpStream
& rStrm
)
717 OSL_ENSURE( !maCondFmtList
.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
718 if( !maCondFmtList
.empty() )
719 maCondFmtList
.back()->ReadCF( rStrm
);
722 void XclImpCondFormatManager::Apply()
724 for( auto& rxFmt
: maCondFmtList
)
726 maCondFmtList
.clear();
729 // Data Validation ============================================================
731 XclImpValidationManager::DVItem::DVItem( const ScRangeList
& rRanges
, const ScValidationData
& rValidData
) :
732 maRanges(rRanges
), maValidData(rValidData
) {}
734 XclImpValidationManager::XclImpValidationManager( const XclImpRoot
& rRoot
) :
739 void XclImpValidationManager::ReadDval( XclImpStream
& rStrm
)
741 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
742 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
744 sal_uInt32
nObjId(0);
746 nObjId
= rStrm
.ReaduInt32();
747 if( nObjId
!= EXC_DVAL_NOOBJ
)
749 OSL_ENSURE( nObjId
<= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
750 rRoot
.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16
>( nObjId
) );
754 void XclImpValidationManager::ReadDV( XclImpStream
& rStrm
)
756 const XclImpRoot
& rRoot
= rStrm
.GetRoot();
757 OSL_ENSURE_BIFF( rRoot
.GetBiff() == EXC_BIFF8
);
759 ScDocument
& rDoc
= rRoot
.GetDoc();
760 SCTAB nScTab
= rRoot
.GetCurrScTab();
761 ExcelToSc
& rFmlaConv
= rRoot
.GetOldFmlaConverter();
764 sal_uInt32 nFlags
= rStrm
.ReaduInt32();
767 /* Empty strings are single NUL characters in Excel (string length is 1).
768 -> Do not let the stream replace them with '?' characters. */
769 rStrm
.SetNulSubstChar( '\0' );
770 OUString
aPromptTitle( rStrm
.ReadUniString() );
771 OUString
aErrorTitle( rStrm
.ReadUniString() );
772 OUString
aPromptMessage( rStrm
.ReadUniString() );
773 OUString
aErrorMessage( rStrm
.ReadUniString() );
774 rStrm
.SetNulSubstChar(); // back to default
777 if ( rStrm
.GetRecLeft() <= 8 )
778 // Not enough bytes left in the record. Bail out.
782 // string list is single tStr token with NUL separators -> replace them with LF
783 rStrm
.SetNulSubstChar( '\n' );
784 ::std::unique_ptr
< ScTokenArray
> xTokArr1
;
786 // We can't import the formula directly because we need the range
787 sal_uInt16 nLenFormula1
= rStrm
.ReaduInt16();
789 XclImpStreamPos aPosFormula1
;
790 rStrm
.StorePosition(aPosFormula1
);
791 rStrm
.Ignore(nLenFormula1
);
794 ::std::unique_ptr
< ScTokenArray
> xTokArr2
;
796 sal_uInt16 nLenFormula2
= rStrm
.ReaduInt16();
798 XclImpStreamPos aPosFormula2
;
799 rStrm
.StorePosition(aPosFormula2
);
800 rStrm
.Ignore(nLenFormula2
);
802 // read all cell ranges
803 XclRangeList aXclRanges
;
806 // convert to Calc range list
807 ScRangeList aScRanges
;
808 rRoot
.GetAddressConverter().ConvertRangeList( aScRanges
, aXclRanges
, nScTab
, true );
810 // only continue if there are valid ranges
811 if ( aScRanges
.empty() )
814 ScRange aCombinedRange
= aScRanges
.Combine();
816 XclImpStreamPos aCurrentPos
;
817 rStrm
.StorePosition(aCurrentPos
);
818 rStrm
.RestorePosition(aPosFormula1
);
819 if( nLenFormula1
> 0 )
821 std::unique_ptr
<ScTokenArray
> pTokArr
;
822 rFmlaConv
.Reset(aCombinedRange
.aStart
);
823 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula1
, false, FT_CondFormat
);
824 // formula converter owns pTokArr -> create a copy of the token array
826 xTokArr1
= std::move( pTokArr
);
828 rStrm
.SetNulSubstChar(); // back to default
829 if (nLenFormula2
> 0)
831 rStrm
.RestorePosition(aPosFormula2
);
832 std::unique_ptr
<ScTokenArray
> pTokArr
;
833 rFmlaConv
.Reset(aCombinedRange
.aStart
);
834 rFmlaConv
.Convert( pTokArr
, rStrm
, nLenFormula2
, false, FT_CondFormat
);
835 // formula converter owns pTokArr -> create a copy of the token array
837 xTokArr2
= std::move( pTokArr
);
840 rStrm
.RestorePosition(aCurrentPos
);
842 bool bIsValid
= true; // valid settings in flags field
844 ScValidationMode eValMode
= SC_VALID_ANY
;
845 switch( nFlags
& EXC_DV_MODE_MASK
)
847 case EXC_DV_MODE_ANY
: eValMode
= SC_VALID_ANY
; break;
848 case EXC_DV_MODE_WHOLE
: eValMode
= SC_VALID_WHOLE
; break;
849 case EXC_DV_MODE_DECIMAL
: eValMode
= SC_VALID_DECIMAL
; break;
850 case EXC_DV_MODE_LIST
: eValMode
= SC_VALID_LIST
; break;
851 case EXC_DV_MODE_DATE
: eValMode
= SC_VALID_DATE
; break;
852 case EXC_DV_MODE_TIME
: eValMode
= SC_VALID_TIME
; break;
853 case EXC_DV_MODE_TEXTLEN
: eValMode
= SC_VALID_TEXTLEN
; break;
854 case EXC_DV_MODE_CUSTOM
: eValMode
= SC_VALID_CUSTOM
; break;
855 default: bIsValid
= false;
857 rRoot
.GetTracer().TraceDVType(eValMode
== SC_VALID_CUSTOM
);
859 ScConditionMode eCondMode
= ScConditionMode::Between
;
860 switch( nFlags
& EXC_DV_COND_MASK
)
862 case EXC_DV_COND_BETWEEN
: eCondMode
= ScConditionMode::Between
; break;
863 case EXC_DV_COND_NOTBETWEEN
:eCondMode
= ScConditionMode::NotBetween
; break;
864 case EXC_DV_COND_EQUAL
: eCondMode
= ScConditionMode::Equal
; break;
865 case EXC_DV_COND_NOTEQUAL
: eCondMode
= ScConditionMode::NotEqual
; break;
866 case EXC_DV_COND_GREATER
: eCondMode
= ScConditionMode::Greater
; break;
867 case EXC_DV_COND_LESS
: eCondMode
= ScConditionMode::Less
; break;
868 case EXC_DV_COND_EQGREATER
: eCondMode
= ScConditionMode::EqGreater
; break;
869 case EXC_DV_COND_EQLESS
: eCondMode
= ScConditionMode::EqLess
; break;
870 default: bIsValid
= false;
874 // No valid validation found. Bail out.
877 // The default value for comparison is _BETWEEN. However, custom
878 // rules are a formula, and thus the comparator should be ignored
879 // and only a true or false from the formula is evaluated. In Calc,
880 // formulas use comparison SC_COND_DIRECT.
881 if( eValMode
== SC_VALID_CUSTOM
)
883 eCondMode
= ScConditionMode::Direct
;
886 // first range for base address for relative references
887 const ScRange
& rScRange
= aScRanges
.front(); // aScRanges is not empty
889 // process string list of a list validity (convert to list of string tokens)
890 if( xTokArr1
.get() && (eValMode
== SC_VALID_LIST
) && ::get_flag( nFlags
, EXC_DV_STRINGLIST
) )
891 XclTokenArrayHelper::ConvertStringToList(*xTokArr1
, rDoc
.GetSharedStringPool(), '\n');
894 std::make_unique
<DVItem
>(aScRanges
, ScValidationData(eValMode
, eCondMode
, xTokArr1
.get(), xTokArr2
.get(), &rDoc
, rScRange
.aStart
)));
895 DVItem
& rItem
= *maDVItems
.back();
897 rItem
.maValidData
.SetIgnoreBlank( ::get_flag( nFlags
, EXC_DV_IGNOREBLANK
) );
898 rItem
.maValidData
.SetListType( ::get_flagvalue( nFlags
, EXC_DV_SUPPRESSDROPDOWN
, css::sheet::TableValidationVisibility::INVISIBLE
, css::sheet::TableValidationVisibility::UNSORTED
) );
900 // *** prompt box ***
901 if( !aPromptTitle
.isEmpty() || !aPromptMessage
.isEmpty() )
903 // set any text stored in the record
904 rItem
.maValidData
.SetInput( aPromptTitle
, aPromptMessage
);
905 if( !::get_flag( nFlags
, EXC_DV_SHOWPROMPT
) )
906 rItem
.maValidData
.ResetInput();
910 ScValidErrorStyle eErrStyle
= SC_VALERR_STOP
;
911 switch( nFlags
& EXC_DV_ERROR_MASK
)
913 case EXC_DV_ERROR_WARNING
: eErrStyle
= SC_VALERR_WARNING
; break;
914 case EXC_DV_ERROR_INFO
: eErrStyle
= SC_VALERR_INFO
; break;
916 // set texts and error style
917 rItem
.maValidData
.SetError( aErrorTitle
, aErrorMessage
, eErrStyle
);
918 if( !::get_flag( nFlags
, EXC_DV_SHOWERROR
) )
919 rItem
.maValidData
.ResetError();
922 void XclImpValidationManager::Apply()
924 ScDocument
& rDoc
= GetRoot().GetDoc();
925 for (const auto& rxDVItem
: maDVItems
)
927 DVItem
& rItem
= *rxDVItem
;
929 sal_uLong nHandle
= rDoc
.AddValidationEntry( rItem
.maValidData
);
930 ScPatternAttr
aPattern( rDoc
.GetPool() );
931 aPattern
.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA
, nHandle
) );
934 for ( size_t i
= 0, nRanges
= rItem
.maRanges
.size(); i
< nRanges
; ++i
)
936 const ScRange
& rScRange
= rItem
.maRanges
[ i
];
937 rDoc
.ApplyPatternAreaTab( rScRange
.aStart
.Col(), rScRange
.aStart
.Row(),
938 rScRange
.aEnd
.Col(), rScRange
.aEnd
.Row(), rScRange
.aStart
.Tab(), aPattern
);
944 // Web queries ================================================================
946 XclImpWebQuery::XclImpWebQuery( const ScRange
& rDestRange
) :
947 maDestRange( rDestRange
),
948 meMode( xlWQUnknown
),
953 void XclImpWebQuery::ReadParamqry( XclImpStream
& rStrm
)
955 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
956 sal_uInt16 nType
= ::extract_value
< sal_uInt16
>( nFlags
, 0, 3 );
957 if( (nType
== EXC_PQRYTYPE_WEBQUERY
) && ::get_flag( nFlags
, EXC_PQRY_WEBQUERY
) )
959 if( ::get_flag( nFlags
, EXC_PQRY_TABLES
) )
961 meMode
= xlWQAllTables
;
962 maTables
= ScfTools::GetHTMLTablesName();
966 meMode
= xlWQDocument
;
967 maTables
= ScfTools::GetHTMLDocName();
972 void XclImpWebQuery::ReadWqstring( XclImpStream
& rStrm
)
974 maURL
= rStrm
.ReadUniString();
977 void XclImpWebQuery::ReadWqsettings( XclImpStream
& rStrm
)
980 sal_uInt16 nFlags
= rStrm
.ReaduInt16();
982 mnRefresh
= rStrm
.ReaduInt16();
984 if( ::get_flag( nFlags
, EXC_WQSETT_SPECTABLES
) && (meMode
== xlWQAllTables
) )
985 meMode
= xlWQSpecTables
;
988 void XclImpWebQuery::ReadWqtables( XclImpStream
& rStrm
)
990 if( meMode
== xlWQSpecTables
)
993 OUString
aTables( rStrm
.ReadUniString() );
995 const sal_Unicode cSep
= ';';
996 const OUString
aQuotedPairs( "\"\"" );
998 for ( sal_Int32 nStringIx
{aTables
.isEmpty() ? -1 : 0}; nStringIx
>=0; )
1000 OUString
aToken( ScStringUtil::GetQuotedToken( aTables
, 0, aQuotedPairs
, ',', nStringIx
) );
1001 sal_Int32 nTabNum
= CharClass::isAsciiNumeric( aToken
) ? aToken
.toInt32() : 0;
1003 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32
>( nTabNum
) ), cSep
);
1006 ScGlobal::EraseQuotes( aToken
, '"', false );
1007 if( !aToken
.isEmpty() )
1008 maTables
= ScGlobal::addToken( maTables
, ScfTools::GetNameFromHTMLName( aToken
), cSep
);
1014 void XclImpWebQuery::Apply( ScDocument
& rDoc
, const OUString
& rFilterName
)
1016 if( !maURL
.isEmpty() && (meMode
!= xlWQUnknown
) && rDoc
.GetDocumentShell() )
1018 ScAreaLink
* pLink
= new ScAreaLink( rDoc
.GetDocumentShell(),
1019 maURL
, rFilterName
, EMPTY_OUSTRING
, maTables
, maDestRange
, mnRefresh
* 60UL );
1020 rDoc
.GetLinkManager()->InsertFileLink( *pLink
, OBJECT_CLIENT_FILE
,
1021 maURL
, &rFilterName
, &maTables
);
1025 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot
& rRoot
) :
1030 void XclImpWebQueryBuffer::ReadQsi( XclImpStream
& rStrm
)
1032 if( GetBiff() == EXC_BIFF8
)
1035 OUString
aXclName( rStrm
.ReadUniString() );
1037 // #i64794# Excel replaces spaces with underscores
1038 aXclName
= aXclName
.replaceAll( " ", "_" );
1040 // find the defined name used in Calc
1041 if( const XclImpName
* pName
= GetNameManager().FindName( aXclName
, GetCurrScTab() ) )
1043 if( const ScRangeData
* pRangeData
= pName
->GetScRangeData() )
1046 if( pRangeData
->IsReference( aRange
) )
1047 maWQList
.emplace_back( aRange
);
1057 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream
& rStrm
)
1059 if (!maWQList
.empty())
1060 maWQList
.back().ReadParamqry( rStrm
);
1063 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream
& rStrm
)
1065 if (!maWQList
.empty())
1066 maWQList
.back().ReadWqstring( rStrm
);
1069 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream
& rStrm
)
1071 if (!maWQList
.empty())
1072 maWQList
.back().ReadWqsettings( rStrm
);
1075 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream
& rStrm
)
1077 if (!maWQList
.empty())
1078 maWQList
.back().ReadWqtables( rStrm
);
1081 void XclImpWebQueryBuffer::Apply()
1083 ScDocument
& rDoc
= GetDoc();
1084 for( auto& rQuery
: maWQList
)
1085 rQuery
.Apply( rDoc
, EXC_WEBQRY_FILTER
);
1088 // Decryption =================================================================
1092 XclImpDecrypterRef
lclReadFilepass5( XclImpStream
& rStrm
)
1094 XclImpDecrypterRef xDecr
;
1095 OSL_ENSURE( rStrm
.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1096 if( rStrm
.GetRecLeft() == 4 )
1098 sal_uInt16
nKey(0), nHash(0);
1099 nKey
= rStrm
.ReaduInt16();
1100 nHash
= rStrm
.ReaduInt16();
1101 xDecr
.reset( new XclImpBiff5Decrypter( nKey
, nHash
) );
1106 XclImpDecrypterRef
lclReadFilepass8_Standard( XclImpStream
& rStrm
)
1108 XclImpDecrypterRef xDecr
;
1109 OSL_ENSURE( rStrm
.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1110 if( rStrm
.GetRecLeft() == 48 )
1112 std::vector
<sal_uInt8
> aSalt(16);
1113 std::vector
<sal_uInt8
> aVerifier(16);
1114 std::vector
<sal_uInt8
> aVerifierHash(16);
1115 rStrm
.Read(aSalt
.data(), 16);
1116 rStrm
.Read(aVerifier
.data(), 16);
1117 rStrm
.Read(aVerifierHash
.data(), 16);
1118 xDecr
.reset(new XclImpBiff8StdDecrypter(aSalt
, aVerifier
, aVerifierHash
));
1123 XclImpDecrypterRef
lclReadFilepass8_Strong(XclImpStream
& rStream
)
1125 //It is possible there are other variants in existence but these
1126 //are the defaults I get with Excel 2013
1127 XclImpDecrypterRef xDecr
;
1129 msfilter::RC4EncryptionInfo info
;
1131 info
.header
.flags
= rStream
.ReaduInt32();
1132 if (oox::getFlag( info
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
1135 sal_uInt32 nHeaderSize
= rStream
.ReaduInt32();
1136 sal_uInt32 actualHeaderSize
= sizeof(info
.header
);
1138 if( nHeaderSize
< actualHeaderSize
)
1141 info
.header
.flags
= rStream
.ReaduInt32();
1142 info
.header
.sizeExtra
= rStream
.ReaduInt32();
1143 info
.header
.algId
= rStream
.ReaduInt32();
1144 info
.header
.algIdHash
= rStream
.ReaduInt32();
1145 info
.header
.keyBits
= rStream
.ReaduInt32();
1146 info
.header
.providedType
= rStream
.ReaduInt32();
1147 info
.header
.reserved1
= rStream
.ReaduInt32();
1148 info
.header
.reserved2
= rStream
.ReaduInt32();
1150 rStream
.Ignore(nHeaderSize
- actualHeaderSize
);
1152 info
.verifier
.saltSize
= rStream
.ReaduInt32();
1153 if (info
.verifier
.saltSize
!= msfilter::SALT_LENGTH
)
1155 rStream
.Read(&info
.verifier
.salt
, sizeof(info
.verifier
.salt
));
1156 rStream
.Read(&info
.verifier
.encryptedVerifier
, sizeof(info
.verifier
.encryptedVerifier
));
1158 info
.verifier
.encryptedVerifierHashSize
= rStream
.ReaduInt32();
1159 if (info
.verifier
.encryptedVerifierHashSize
!= RTL_DIGEST_LENGTH_SHA1
)
1161 rStream
.Read(&info
.verifier
.encryptedVerifierHash
, info
.verifier
.encryptedVerifierHashSize
);
1163 // check flags and algorithm IDs, required are AES128 and SHA-1
1164 if (!oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
1167 if (oox::getFlag(info
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
1170 if (info
.header
.algId
!= msfilter::ENCRYPT_ALGO_RC4
)
1173 // hash algorithm ID 0 defaults to SHA-1 too
1174 if (info
.header
.algIdHash
!= 0 && info
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
1177 xDecr
.reset(new XclImpBiff8CryptoAPIDecrypter(
1178 std::vector
<sal_uInt8
>(info
.verifier
.salt
,
1179 info
.verifier
.salt
+ SAL_N_ELEMENTS(info
.verifier
.salt
)),
1180 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifier
,
1181 info
.verifier
.encryptedVerifier
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifier
)),
1182 std::vector
<sal_uInt8
>(info
.verifier
.encryptedVerifierHash
,
1183 info
.verifier
.encryptedVerifierHash
+ SAL_N_ELEMENTS(info
.verifier
.encryptedVerifierHash
))));
1188 XclImpDecrypterRef
lclReadFilepass8( XclImpStream
& rStrm
)
1190 XclImpDecrypterRef xDecr
;
1192 sal_uInt16 nMode
= rStrm
.ReaduInt16();
1195 case EXC_FILEPASS_BIFF5
:
1196 xDecr
= lclReadFilepass5( rStrm
);
1199 case EXC_FILEPASS_BIFF8
:
1201 sal_uInt32 nVersion
= rStrm
.ReaduInt32();
1202 if (nVersion
== msfilter::VERSION_INFO_1997_FORMAT
)
1204 //A Version structure where Version.vMajor MUST be 0x0001,
1205 //and Version.vMinor MUST be 0x0001.
1206 xDecr
= lclReadFilepass8_Standard(rStrm
);
1208 else if (nVersion
== msfilter::VERSION_INFO_2007_FORMAT
||
1209 nVersion
== msfilter::VERSION_INFO_2007_FORMAT_SP2
)
1211 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1212 //Version.vMinor MUST be 0x0002.
1213 xDecr
= lclReadFilepass8_Strong(rStrm
);
1216 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1221 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1229 const ErrCode
& XclImpDecryptHelper::ReadFilepass( XclImpStream
& rStrm
)
1231 XclImpDecrypterRef xDecr
;
1232 rStrm
.DisableDecryption();
1234 // read the FILEPASS record and create a new decrypter object
1235 switch( rStrm
.GetRoot().GetBiff() )
1240 case EXC_BIFF5
: xDecr
= lclReadFilepass5( rStrm
); break;
1241 case EXC_BIFF8
: xDecr
= lclReadFilepass8( rStrm
); break;
1242 default: DBG_ERROR_BIFF();
1245 // set decrypter at import stream
1246 rStrm
.SetDecrypter( xDecr
);
1248 // request and verify a password (decrypter implements IDocPasswordVerifier)
1250 rStrm
.GetRoot().RequestEncryptionData( *xDecr
);
1252 // return error code (success, wrong password, etc.)
1253 return xDecr
? xDecr
->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT
;
1256 // Document protection ========================================================
1258 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot
& rRoot
) :
1259 XclImpRoot( rRoot
),
1261 mbDocProtect(false),
1266 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream
& rStrm
)
1268 mbDocProtect
= rStrm
.ReaduInt16() != 0;
1271 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream
& rStrm
)
1273 mbWinProtect
= rStrm
.ReaduInt16() != 0;
1276 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
)
1278 rStrm
.EnableDecryption();
1279 mnPassHash
= rStrm
.ReaduInt16();
1282 void XclImpDocProtectBuffer::Apply() const
1284 if (!mbDocProtect
&& !mbWinProtect
)
1285 // Excel requires either the structure or windows protection is set.
1286 // If neither is set then the document is not protected at all.
1289 unique_ptr
<ScDocProtection
> pProtect(new ScDocProtection
);
1290 pProtect
->setProtected(true);
1294 // 16-bit password hash.
1295 Sequence
<sal_Int8
> aPass
{sal_Int8(mnPassHash
>> 8), sal_Int8(mnPassHash
& 0xFF)};
1296 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1299 // document protection options
1300 pProtect
->setOption(ScDocProtection::STRUCTURE
, mbDocProtect
);
1301 pProtect
->setOption(ScDocProtection::WINDOWS
, mbWinProtect
);
1303 GetDoc().SetDocProtection(pProtect
.get());
1306 // Sheet Protection ===========================================================
1308 XclImpSheetProtectBuffer::Sheet::Sheet() :
1310 mnPasswordHash(0x0000),
1315 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet
& r
) :
1316 mbProtected(r
.mbProtected
),
1317 mnPasswordHash(r
.mnPasswordHash
),
1318 mnOptions(r
.mnOptions
)
1322 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot
& rRoot
) :
1327 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream
& rStrm
, SCTAB nTab
)
1329 if ( rStrm
.ReaduInt16() )
1331 Sheet
* pSheet
= GetSheetItem(nTab
);
1333 pSheet
->mbProtected
= true;
1337 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream
& rStrm
, SCTAB nTab
)
1339 // The flag size specifies the size of bytes that follows that stores
1340 // feature data. If -1 it depends on the feature type imported earlier.
1341 // For enhanced protection data, the size is always 4. For the most xls
1342 // documents out there this value is almost always -1.
1343 sal_Int32 nFlagSize
= rStrm
.ReadInt32();
1344 if (nFlagSize
!= -1)
1347 // There are actually 4 bytes to read, but the upper 2 bytes currently
1348 // don't store any bits.
1349 sal_uInt16 nOptions
= rStrm
.ReaduInt16();
1351 Sheet
* pSheet
= GetSheetItem(nTab
);
1353 pSheet
->mnOptions
= nOptions
;
1356 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection
& rProt
, SCTAB nTab
)
1358 Sheet
* pSheet
= GetSheetItem(nTab
);
1360 pSheet
->maEnhancedProtections
.push_back( rProt
);
1363 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream
& rStrm
, SCTAB nTab
)
1365 sal_uInt16 nHash
= rStrm
.ReaduInt16();
1366 Sheet
* pSheet
= GetSheetItem(nTab
);
1368 pSheet
->mnPasswordHash
= nHash
;
1371 void XclImpSheetProtectBuffer::Apply() const
1373 for (const auto& [rTab
, rSheet
] : maProtectedSheets
)
1375 if (!rSheet
.mbProtected
)
1376 // This sheet is (for whatever reason) not protected.
1379 unique_ptr
<ScTableProtection
> pProtect(new ScTableProtection
);
1380 pProtect
->setProtected(true);
1382 // 16-bit hash password
1383 const sal_uInt16 nHash
= rSheet
.mnPasswordHash
;
1386 Sequence
<sal_Int8
> aPass
{sal_Int8(nHash
>> 8), sal_Int8(nHash
& 0xFF)};
1387 pProtect
->setPasswordHash(aPass
, PASSHASH_XL
);
1390 // sheet protection options
1391 const sal_uInt16 nOptions
= rSheet
.mnOptions
;
1392 pProtect
->setOption( ScTableProtection::OBJECTS
, (nOptions
& 0x0001) );
1393 pProtect
->setOption( ScTableProtection::SCENARIOS
, (nOptions
& 0x0002) );
1394 pProtect
->setOption( ScTableProtection::FORMAT_CELLS
, (nOptions
& 0x0004) );
1395 pProtect
->setOption( ScTableProtection::FORMAT_COLUMNS
, (nOptions
& 0x0008) );
1396 pProtect
->setOption( ScTableProtection::FORMAT_ROWS
, (nOptions
& 0x0010) );
1397 pProtect
->setOption( ScTableProtection::INSERT_COLUMNS
, (nOptions
& 0x0020) );
1398 pProtect
->setOption( ScTableProtection::INSERT_ROWS
, (nOptions
& 0x0040) );
1399 pProtect
->setOption( ScTableProtection::INSERT_HYPERLINKS
, (nOptions
& 0x0080) );
1400 pProtect
->setOption( ScTableProtection::DELETE_COLUMNS
, (nOptions
& 0x0100) );
1401 pProtect
->setOption( ScTableProtection::DELETE_ROWS
, (nOptions
& 0x0200) );
1402 pProtect
->setOption( ScTableProtection::SELECT_LOCKED_CELLS
, (nOptions
& 0x0400) );
1403 pProtect
->setOption( ScTableProtection::SORT
, (nOptions
& 0x0800) );
1404 pProtect
->setOption( ScTableProtection::AUTOFILTER
, (nOptions
& 0x1000) );
1405 pProtect
->setOption( ScTableProtection::PIVOT_TABLES
, (nOptions
& 0x2000) );
1406 pProtect
->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS
, (nOptions
& 0x4000) );
1408 // Enhanced protection containing editable ranges and permissions.
1409 pProtect
->setEnhancedProtection( rSheet
.maEnhancedProtections
);
1411 // all done. now commit.
1412 GetDoc().SetTabProtection(rTab
, pProtect
.get());
1416 XclImpSheetProtectBuffer::Sheet
* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab
)
1418 ProtectedSheetMap::iterator itr
= maProtectedSheets
.find(nTab
);
1419 if (itr
== maProtectedSheets
.end())
1422 if ( !maProtectedSheets
.emplace( nTab
, Sheet() ).second
)
1425 itr
= maProtectedSheets
.find(nTab
);
1428 return &itr
->second
;
1431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */