use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xicontent.cxx
blobcd284c32ee1a210de8425a21ea0df9128827367f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <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>
57 #include <excform.hxx>
58 #include <tabprotection.hxx>
59 #include <documentimport.hxx>
61 #include <memory>
62 #include <oox/helper/helper.hxx>
63 #include <sal/log.hxx>
65 using ::com::sun::star::uno::Sequence;
66 using ::std::unique_ptr;
68 // Shared string table ========================================================
70 XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
71 XclImpRoot( rRoot )
75 void XclImpSst::ReadSst( XclImpStream& rStrm )
77 rStrm.Ignore( 4 );
78 sal_uInt32 nStrCount = rStrm.ReaduInt32();
79 auto nBytesAvailable = rStrm.GetRecLeft();
80 if (nStrCount > nBytesAvailable)
82 SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
83 nStrCount = nBytesAvailable;
85 maStrings.clear();
86 maStrings.reserve(nStrCount);
87 while( (nStrCount > 0) && rStrm.IsValid() )
89 XclImpString aString;
90 aString.Read( rStrm );
91 maStrings.push_back( aString );
92 --nStrCount;
96 const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
98 return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
101 // Hyperlinks =================================================================
103 namespace {
105 /** Reads character array and stores it into rString.
106 @param nChars Number of following characters (not byte count!).
107 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
108 void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
110 sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
111 rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
112 // ignore remaining chars
113 std::size_t nIgnore = nChars - nReadChars;
114 if( b16Bit )
115 nIgnore *= 2;
116 rStrm.Ignore( nIgnore );
119 /** Reads 32-bit string length and the character array and stores it into rString.
120 @param b16Bit true = 16-bit characters, false = 8-bit characters. */
121 void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
123 lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
126 /** Reads 32-bit string length and ignores following 16-bit character array. */
127 void lclIgnoreString32( XclImpStream& rStrm )
129 sal_uInt32 nChars = rStrm.ReaduInt32();
130 nChars *= 2;
131 rStrm.Ignore( nChars );
134 /** Converts a path to an absolute path.
135 @param rPath The source path. The resulting path is returned here.
136 @param nLevel Number of parent directories to add in front of the path. */
137 void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
139 OUStringBuffer aTmpStr;
140 while( nLevel )
142 aTmpStr.append( "../" );
143 --nLevel;
145 aTmpStr.append( rPath );
147 if( pDocShell )
149 bool bWasAbs = false;
150 rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
151 // full path as stored in SvxURLField must be encoded
153 else
154 rPath = aTmpStr.makeStringAndClear();
157 /** Inserts the URL into a text cell. Does not modify value or formula cells. */
158 void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
160 ScDocumentImport& rDoc = rRoot.GetDocImport();
161 ScAddress aScPos( nScCol, nScRow, nScTab );
162 ScRefCellValue aCell(rDoc.getDoc(), aScPos);
163 switch( aCell.getType() )
165 // #i54261# hyperlinks in string cells
166 case CELLTYPE_STRING:
167 case CELLTYPE_EDIT:
169 ScInterpreterContext& rContext = rDoc.getDoc().GetNonThreadedContext();
170 sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rContext, aScPos);
171 const Color* pColor;
172 OUString aDisplText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, &rContext, rDoc.getDoc());
173 if (aDisplText.isEmpty())
174 aDisplText = rUrl;
176 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
177 SvxURLField aUrlField( rUrl, aDisplText, SvxURLFormat::AppDefault );
179 if( aCell.getType() == CELLTYPE_EDIT )
181 const EditTextObject* pEditObj = aCell.getEditText();
182 rEE.SetTextCurrentDefaults( *pEditObj );
183 rEE.QuickInsertField(SvxFieldItem(aUrlField, EE_FEATURE_FIELD), ESelection::All());
185 else
187 rEE.SetTextCurrentDefaults( OUString() );
188 rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
189 if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
191 SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
192 pPattern->FillEditItemSet( &aItemSet );
193 rEE.QuickSetAttribs(aItemSet, ESelection::All());
197 // The cell will own the text object instance.
198 rDoc.setEditCell(aScPos, rEE.CreateTextObject());
200 break;
202 default:
203 // Handle other cell types e.g. formulas ( and ? ) that have associated
204 // hyperlinks.
205 // Ideally all hyperlinks should be treated as below. For the moment,
206 // given the current absence of ods support let's just handle what we
207 // previously didn't handle the new way.
208 // Unfortunately we won't be able to preserve such hyperlinks when
209 // saving to ods. Note: when we are able to save such hyperlinks to ods
210 // we should handle *all* imported hyperlinks as below ( e.g. as cell
211 // attribute ) for better interoperability.
213 SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
214 rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
215 break;
220 } // namespace
222 void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
224 XclRange aXclRange( ScAddress::UNINITIALIZED );
225 rStrm >> aXclRange;
226 // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
227 aXclRange.maFirst.mnCol &= 0xFF;
228 aXclRange.maLast.mnCol &= 0xFF;
229 OUString aString = ReadEmbeddedData( rStrm );
230 if ( !aString.isEmpty() )
231 rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
234 OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
236 const XclImpRoot& rRoot = rStrm.GetRoot();
237 SfxObjectShell* pDocShell = rRoot.GetDocShell();
239 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
241 XclGuid aGuid;
242 rStrm >> aGuid;
243 rStrm.Ignore( 4 );
244 sal_uInt32 nFlags = rStrm.ReaduInt32();
246 OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
248 ::std::unique_ptr< OUString > xLongName; // link / file name
249 ::std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
250 ::std::unique_ptr< OUString > xTextMark; // text mark
252 // description (ignore)
253 if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
254 lclIgnoreString32( rStrm );
255 // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
256 if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
257 lclIgnoreString32( rStrm );
259 // URL fields are zero-terminated - do not let the stream replace them
260 // in the lclAppendString32() with the '?' character.
261 rStrm.SetNulSubstChar( '\0' );
263 // UNC path
264 if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
266 xLongName.reset( new OUString );
267 lclAppendString32( *xLongName, rStrm, true );
268 lclGetAbsPath( *xLongName, 0, pDocShell );
270 // file link or URL
271 else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
273 rStrm >> aGuid;
275 if( aGuid == XclTools::maGuidFileMoniker )
277 sal_uInt16 nLevel = rStrm.ReaduInt16(); // counter for level to climb down in path
278 xShortName.reset( new OUString );
279 lclAppendString32( *xShortName, rStrm, false );
280 rStrm.Ignore( 24 );
282 sal_uInt32 nStrLen = rStrm.ReaduInt32();
283 if( nStrLen )
285 nStrLen = rStrm.ReaduInt32();
286 nStrLen /= 2; // it's byte count here...
287 rStrm.Ignore( 2 );
288 xLongName.reset( new OUString );
289 lclAppendString32( *xLongName, rStrm, nStrLen, true );
290 lclGetAbsPath( *xLongName, nLevel, pDocShell );
292 else
293 lclGetAbsPath( *xShortName, nLevel, pDocShell );
295 else if( aGuid == XclTools::maGuidUrlMoniker )
297 sal_uInt32 nStrLen = rStrm.ReaduInt32();
298 nStrLen /= 2; // it's byte count here...
299 xLongName.reset( new OUString );
300 lclAppendString32( *xLongName, rStrm, nStrLen, true );
301 if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
302 lclGetAbsPath( *xLongName, 0, pDocShell );
304 else
306 OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
310 // text mark
311 if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
313 xTextMark.reset( new OUString );
314 lclAppendString32( *xTextMark, rStrm, true );
317 rStrm.SetNulSubstChar(); // back to default
319 OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
321 if (!xLongName && xShortName)
322 xLongName = std::move(xShortName);
323 else if (!xLongName && xTextMark)
324 xLongName.reset( new OUString );
326 if (xLongName)
328 if (xTextMark)
330 if( xLongName->isEmpty() )
332 sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
333 if( nSepPos > 0 )
335 // Do not attempt to blindly convert '#SheetName!A1' to
336 // '#SheetName.A1', it can be #SheetName!R1C1 as well.
337 // Hyperlink handler has to handle all, but prefer
338 // '#SheetName.A1' if possible.
339 if (nSepPos < xTextMark->getLength() - 1)
341 ScDocument& rDoc = rRoot.GetDoc();
342 ScRange aRange;
343 if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), rDoc, formula::FormulaGrammar::CONV_XL_R1C1)
344 & ScRefFlags::VALID) == ScRefFlags::ZERO)
345 xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ))));
349 xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
351 return( *xLongName );
353 return( OUString() );
356 void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
358 sal_Int32 n = rUrl.getLength();
359 if (n < 4)
360 // Needs at least 4 characters.
361 return;
363 if (rUrl[0] != '#')
364 // the 1st character must be '#'.
365 return;
367 OUStringBuffer aNewUrl("#");
368 OUStringBuffer aTabName;
370 bool bInQuote = false;
371 bool bQuoteTabName = false;
372 for( sal_Int32 i = 1; i < n; ++i )
374 sal_Unicode c = rUrl[i];
375 if (c == '\'')
377 if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
379 // Two consecutive single quotes ('') signify a single literal
380 // quite. When this occurs, the whole table name needs to be
381 // quoted.
382 bQuoteTabName = true;
383 aTabName.append(OUStringChar(c) + OUStringChar(c));
384 ++i;
385 continue;
388 bInQuote = !bInQuote;
389 if (!bInQuote && !aTabName.isEmpty())
391 if (bQuoteTabName)
392 aNewUrl.append("'");
393 aNewUrl.append(aTabName);
394 if (bQuoteTabName)
395 aNewUrl.append("'");
398 else if (bInQuote)
399 aTabName.append(c);
400 else
401 aNewUrl.append(c);
404 if (bInQuote)
405 // It should be outside the quotes!
406 return;
408 // All is good. Pass the new URL.
409 rUrl = aNewUrl.makeStringAndClear();
412 void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
414 OUString aUrl(rUrl);
415 ConvertToValidTabName(aUrl);
417 SCTAB nScTab = rRoot.GetCurrScTab();
418 ScRange aScRange( ScAddress::UNINITIALIZED );
419 if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
421 SCCOL nScCol1, nScCol2;
422 SCROW nScRow1, nScRow2;
423 aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
425 if (comphelper::IsFuzzing())
427 SCROW nRows = nScRow2 - nScRow1;
428 if (nRows > 1024)
430 SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2 << " to " << nScRow1 + 1024);
431 nScRow2 = nScRow1 + 1024;
435 for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
436 for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
437 lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
441 // Label ranges ===============================================================
443 void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
445 const XclImpRoot& rRoot = rStrm.GetRoot();
446 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
448 ScDocument& rDoc = rRoot.GetDoc();
449 SCTAB nScTab = rRoot.GetCurrScTab();
450 XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
451 ScRangePairListRef xLabelRangesRef;
453 XclRangeList aRowXclRanges, aColXclRanges;
454 rStrm >> aRowXclRanges >> aColXclRanges;
456 // row label ranges
457 ScRangeList aRowScRanges;
458 rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
459 xLabelRangesRef = rDoc.GetRowNameRangesRef();
460 for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
462 const ScRange & rScRange = aRowScRanges[ i ];
463 ScRange aDataRange( rScRange );
464 if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
466 aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
467 aDataRange.aEnd.SetCol( rDoc.MaxCol() );
469 else if( aDataRange.aStart.Col() > 0 )
471 aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
472 aDataRange.aStart.SetCol( 0 );
474 xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
477 // column label ranges
478 ScRangeList aColScRanges;
479 rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
480 xLabelRangesRef = rDoc.GetColNameRangesRef();
482 for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
484 const ScRange & rScRange = aColScRanges[ i ];
485 ScRange aDataRange( rScRange );
486 if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
488 aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
489 aDataRange.aEnd.SetRow( rDoc.MaxRow() );
491 else if( aDataRange.aStart.Row() > 0 )
493 aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
494 aDataRange.aStart.SetRow( 0 );
496 xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
500 // Conditional formatting =====================================================
502 XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
503 XclImpRoot( rRoot ),
504 mnFormatIndex( nFormatIndex ),
505 mnCondCount( 0 ),
506 mnCondIndex( 0 )
510 XclImpCondFormat::~XclImpCondFormat()
514 void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
516 OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
517 XclRangeList aXclRanges;
518 mnCondCount = rStrm.ReaduInt16();
519 rStrm.Ignore( 10 );
520 rStrm >> aXclRanges;
521 GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
524 void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
526 if( mnCondIndex >= mnCondCount )
528 OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
529 return;
532 // entire conditional format outside of valid range?
533 if( maRanges.empty() )
534 return;
536 sal_uInt8 nType = rStrm.ReaduInt8();
537 sal_uInt8 nOperator = rStrm.ReaduInt8();
538 sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
539 sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
540 sal_uInt32 nFlags = rStrm.ReaduInt32();
541 rStrm.Ignore( 2 ); //nFlagsExtended
543 // *** mode and comparison operator ***
545 ScConditionMode eMode = ScConditionMode::NONE;
546 switch( nType )
548 case EXC_CF_TYPE_CELL:
550 switch( nOperator )
552 case EXC_CF_CMP_BETWEEN: eMode = ScConditionMode::Between; break;
553 case EXC_CF_CMP_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
554 case EXC_CF_CMP_EQUAL: eMode = ScConditionMode::Equal; break;
555 case EXC_CF_CMP_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
556 case EXC_CF_CMP_GREATER: eMode = ScConditionMode::Greater; break;
557 case EXC_CF_CMP_LESS: eMode = ScConditionMode::Less; break;
558 case EXC_CF_CMP_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
559 case EXC_CF_CMP_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
560 default:
561 SAL_INFO(
562 "sc.filter", "unknown CF comparison " << nOperator);
565 break;
567 case EXC_CF_TYPE_FMLA:
568 eMode = ScConditionMode::Direct;
569 break;
571 default:
572 SAL_INFO("sc.filter", "unknown CF mode " << nType);
573 return;
576 // *** create style sheet ***
578 OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
579 SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
581 const XclImpPalette& rPalette = GetPalette();
583 // number format
585 if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
587 XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
588 bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
589 sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
590 rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
593 // *** font block ***
595 if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
597 XclImpFont aFont( GetRoot() );
598 aFont.ReadCFFontBlock( rStrm );
599 aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
602 // alignment
603 if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
605 XclImpCellAlign aAlign;
606 sal_uInt16 nAlign(0);
607 sal_uInt16 nAlignMisc(0);
608 nAlign = rStrm.ReaduInt16();
609 nAlignMisc = rStrm.ReaduInt16();
610 aAlign.FillFromCF( nAlign, nAlignMisc );
611 aAlign.FillToItemSet( rStyleItemSet, nullptr );
612 rStrm.Ignore(4);
615 // *** border block ***
617 if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
619 sal_uInt16 nLineStyle(0);
620 sal_uInt32 nLineColor(0);
621 nLineStyle = rStrm.ReaduInt16();
622 nLineColor = rStrm.ReaduInt32();
623 rStrm.Ignore( 2 );
625 XclImpCellBorder aBorder;
626 aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
627 aBorder.FillToItemSet( rStyleItemSet, rPalette );
630 // *** pattern block ***
632 if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
634 sal_uInt16 nPattern(0), nColor(0);
635 nPattern = rStrm.ReaduInt16();
636 nColor = rStrm.ReaduInt16();
638 XclImpCellArea aArea;
639 aArea.FillFromCF8( nPattern, nColor, nFlags );
640 aArea.FillToItemSet( rStyleItemSet, rPalette );
643 if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
645 sal_uInt16 nCellProt;
646 nCellProt = rStrm.ReaduInt16();
647 XclImpCellProt aCellProt;
648 aCellProt.FillFromXF3(nCellProt);
649 aCellProt.FillToItemSet( rStyleItemSet );
652 // *** formulas ***
654 const ScAddress& rPos = maRanges.front().aStart; // assured above that maRanges is not empty
655 ExcelToSc& rFmlaConv = GetOldFmlaConverter();
657 ::std::unique_ptr< ScTokenArray > xTokArr1;
658 if( nFmlaSize1 > 0 )
660 std::unique_ptr<ScTokenArray> pTokArr;
661 rFmlaConv.Reset( rPos );
662 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
663 // formula converter owns pTokArr -> create a copy of the token array
664 if( pTokArr )
666 xTokArr1 = std::move( pTokArr );
667 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
671 ::std::unique_ptr< ScTokenArray > xTokArr2;
672 if( nFmlaSize2 > 0 )
674 std::unique_ptr<ScTokenArray> pTokArr;
675 rFmlaConv.Reset( rPos );
676 rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
677 // formula converter owns pTokArr -> create a copy of the token array
678 if( pTokArr )
680 xTokArr2 = std::move( pTokArr );
681 GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
685 // *** create the Calc conditional formatting ***
687 const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
689 if( !mxScCondFmt )
691 mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
692 if(maRanges.size() > 1)
693 maRanges.Join(maRanges[0], true);
694 mxScCondFmt->SetRange(maRanges);
697 ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), GetDoc(), aPos, aStyleName);
698 mxScCondFmt->AddEntry( pEntry );
699 ++mnCondIndex;
702 void XclImpCondFormat::Apply()
704 if( mxScCondFmt )
706 ScDocument& rDoc = GetDoc();
708 SCTAB nTab = maRanges.front().aStart.Tab();
709 sal_uInt32 nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
711 rDoc.AddCondFormatData( maRanges, nTab, nKey );
715 XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
716 XclImpRoot( rRoot )
720 void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
722 XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
723 pFmt->ReadCondfmt( rStrm );
724 maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
727 void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
729 OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
730 if( !maCondFmtList.empty() )
731 maCondFmtList.back()->ReadCF( rStrm );
734 void XclImpCondFormatManager::Apply()
736 for( auto& rxFmt : maCondFmtList )
737 rxFmt->Apply();
738 maCondFmtList.clear();
741 // Data Validation ============================================================
743 XclImpValidationManager::DVItem::DVItem( ScRangeList aRanges, const ScValidationData& rValidData ) :
744 maRanges(std::move(aRanges)), maValidData(rValidData) {}
746 XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
747 XclImpRoot( rRoot )
751 void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
753 const XclImpRoot& rRoot = rStrm.GetRoot();
754 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
756 sal_uInt32 nObjId(0);
757 rStrm.Ignore( 10 );
758 nObjId = rStrm.ReaduInt32();
759 if( nObjId != EXC_DVAL_NOOBJ )
761 OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
762 rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
766 void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
768 const XclImpRoot& rRoot = rStrm.GetRoot();
769 OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
771 ScDocument& rDoc = rRoot.GetDoc();
772 SCTAB nScTab = rRoot.GetCurrScTab();
773 ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
775 // flags
776 sal_uInt32 nFlags = rStrm.ReaduInt32();
778 // message strings
779 /* Empty strings are single NUL characters in Excel (string length is 1).
780 -> Do not let the stream replace them with '?' characters. */
781 rStrm.SetNulSubstChar( '\0' );
782 OUString aPromptTitle( rStrm.ReadUniString() );
783 OUString aErrorTitle( rStrm.ReadUniString() );
784 OUString aPromptMessage( rStrm.ReadUniString() );
785 OUString aErrorMessage( rStrm.ReadUniString() );
786 rStrm.SetNulSubstChar(); // back to default
788 // formula(s)
789 if ( rStrm.GetRecLeft() <= 8 )
790 // Not enough bytes left in the record. Bail out.
791 return;
793 // first formula
794 // string list is single tStr token with NUL separators -> replace them with LF
795 rStrm.SetNulSubstChar( '\n' );
796 ::std::unique_ptr< ScTokenArray > xTokArr1;
798 // We can't import the formula directly because we need the range
799 sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
800 rStrm.Ignore( 2 );
801 XclImpStreamPos aPosFormula1;
802 rStrm.StorePosition(aPosFormula1);
803 rStrm.Ignore(nLenFormula1);
805 // second formula
806 ::std::unique_ptr< ScTokenArray > xTokArr2;
808 sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
809 rStrm.Ignore( 2 );
810 XclImpStreamPos aPosFormula2;
811 rStrm.StorePosition(aPosFormula2);
812 rStrm.Ignore(nLenFormula2);
814 // read all cell ranges
815 XclRangeList aXclRanges;
816 rStrm >> aXclRanges;
818 // convert to Calc range list
819 ScRangeList aScRanges;
820 rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
822 // only continue if there are valid ranges
823 if ( aScRanges.empty() )
824 return;
826 ScRange aCombinedRange = aScRanges.Combine();
828 XclImpStreamPos aCurrentPos;
829 rStrm.StorePosition(aCurrentPos);
830 rStrm.RestorePosition(aPosFormula1);
831 if( nLenFormula1 > 0 )
833 std::unique_ptr<ScTokenArray> pTokArr;
834 rFmlaConv.Reset(aCombinedRange.aStart);
835 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
836 // formula converter owns pTokArr -> create a copy of the token array
837 if( pTokArr )
838 xTokArr1 = std::move( pTokArr );
840 rStrm.SetNulSubstChar(); // back to default
841 if (nLenFormula2 > 0)
843 rStrm.RestorePosition(aPosFormula2);
844 std::unique_ptr<ScTokenArray> pTokArr;
845 rFmlaConv.Reset(aCombinedRange.aStart);
846 rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
847 // formula converter owns pTokArr -> create a copy of the token array
848 if( pTokArr )
849 xTokArr2 = std::move( pTokArr );
852 rStrm.RestorePosition(aCurrentPos);
854 bool bIsValid = true; // valid settings in flags field
856 ScValidationMode eValMode = SC_VALID_ANY;
857 switch( nFlags & EXC_DV_MODE_MASK )
859 case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
860 case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
861 case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
862 case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
863 case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
864 case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
865 case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
866 case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
867 default: bIsValid = false;
869 rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
871 ScConditionMode eCondMode = ScConditionMode::Between;
872 switch( nFlags & EXC_DV_COND_MASK )
874 case EXC_DV_COND_BETWEEN: eCondMode = ScConditionMode::Between; break;
875 case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
876 case EXC_DV_COND_EQUAL: eCondMode = ScConditionMode::Equal; break;
877 case EXC_DV_COND_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
878 case EXC_DV_COND_GREATER: eCondMode = ScConditionMode::Greater; break;
879 case EXC_DV_COND_LESS: eCondMode = ScConditionMode::Less; break;
880 case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
881 case EXC_DV_COND_EQLESS: eCondMode = ScConditionMode::EqLess; break;
882 default: bIsValid = false;
885 if ( !bIsValid )
886 // No valid validation found. Bail out.
887 return;
889 // The default value for comparison is _BETWEEN. However, custom
890 // rules are a formula, and thus the comparator should be ignored
891 // and only a true or false from the formula is evaluated. In Calc,
892 // formulas use comparison SC_COND_DIRECT.
893 if( eValMode == SC_VALID_CUSTOM )
895 eCondMode = ScConditionMode::Direct;
898 // first range for base address for relative references
899 const ScRange& rScRange = aScRanges.front(); // aScRanges is not empty
901 // process string list of a list validity (convert to list of string tokens)
902 if( xTokArr1 && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
903 XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
905 maDVItems.push_back(
906 std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), rDoc, rScRange.aStart)));
907 DVItem& rItem = *maDVItems.back();
909 rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
910 rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
912 // *** prompt box ***
913 if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
915 // set any text stored in the record
916 rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
917 if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
918 rItem.maValidData.ResetInput();
921 // *** error box ***
922 ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
923 switch( nFlags & EXC_DV_ERROR_MASK )
925 case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
926 case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
928 // set texts and error style
929 rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
930 if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
931 rItem.maValidData.ResetError();
934 void XclImpValidationManager::Apply()
936 const bool bFuzzing = comphelper::IsFuzzing();
937 size_t nPatterns = 0;
939 ScDocument& rDoc = GetRoot().GetDoc();
940 for (const auto& rxDVItem : maDVItems)
942 DVItem& rItem = *rxDVItem;
943 // set the handle ID
944 sal_uInt32 nHandle = rDoc.AddValidationEntry( rItem.maValidData );
945 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
946 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
948 // apply all ranges
949 for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i, ++nPatterns )
951 const ScRange & rScRange = rItem.maRanges[ i ];
952 rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
953 rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
954 if (bFuzzing && nPatterns >= 128)
956 SAL_WARN("sc.filter", "for fuzzing performance, abandoned pattern after " << nPatterns << " insertions");
957 break;
961 maDVItems.clear();
964 // Web queries ================================================================
966 XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
967 maDestRange( rDestRange ),
968 meMode( xlWQUnknown ),
969 mnRefresh( 0 )
973 void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
975 sal_uInt16 nFlags = rStrm.ReaduInt16();
976 sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
977 if( !((nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY )) )
978 return;
980 if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
982 meMode = xlWQAllTables;
983 maTables = ScfTools::GetHTMLTablesName();
985 else
987 meMode = xlWQDocument;
988 maTables = ScfTools::GetHTMLDocName();
992 void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
994 maURL = rStrm.ReadUniString();
997 void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
999 rStrm.Ignore( 10 );
1000 sal_uInt16 nFlags = rStrm.ReaduInt16();
1001 rStrm.Ignore( 10 );
1002 mnRefresh = rStrm.ReaduInt16();
1004 if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
1005 meMode = xlWQSpecTables;
1008 void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
1010 if( meMode != xlWQSpecTables )
1011 return;
1013 rStrm.Ignore( 4 );
1014 OUString aTables( rStrm.ReadUniString() );
1016 const sal_Unicode cSep = ';';
1017 static constexpr OUStringLiteral aQuotedPairs( u"\"\"" );
1018 maTables.clear();
1019 for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
1021 OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
1022 sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
1023 if( nTabNum > 0 )
1024 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
1025 else
1027 ScGlobal::EraseQuotes( aToken, '"', false );
1028 if( !aToken.isEmpty() )
1029 maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
1034 void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
1036 if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
1038 ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
1039 maURL, rFilterName, OUString(), maTables, maDestRange, mnRefresh * 60UL );
1040 rDoc.GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile,
1041 maURL, &rFilterName, &maTables );
1045 XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
1046 XclImpRoot( rRoot )
1050 void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
1052 if( GetBiff() == EXC_BIFF8 )
1054 rStrm.Ignore( 10 );
1055 OUString aXclName( rStrm.ReadUniString() );
1057 // #i64794# Excel replaces spaces with underscores
1058 aXclName = aXclName.replaceAll( " ", "_" );
1060 // find the defined name used in Calc
1061 if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
1063 if( const ScRangeData* pRangeData = pName->GetScRangeData() )
1065 ScRange aRange;
1066 if( pRangeData->IsReference( aRange ) )
1067 maWQList.emplace_back( aRange );
1071 else
1073 DBG_ERROR_BIFF();
1077 void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
1079 if (!maWQList.empty())
1080 maWQList.back().ReadParamqry( rStrm );
1083 void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
1085 if (!maWQList.empty())
1086 maWQList.back().ReadWqstring( rStrm );
1089 void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
1091 if (!maWQList.empty())
1092 maWQList.back().ReadWqsettings( rStrm );
1095 void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
1097 if (!maWQList.empty())
1098 maWQList.back().ReadWqtables( rStrm );
1101 void XclImpWebQueryBuffer::Apply()
1103 ScDocument& rDoc = GetDoc();
1104 for( auto& rQuery : maWQList )
1105 rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
1108 // Decryption =================================================================
1110 namespace {
1112 XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
1114 XclImpDecrypterRef xDecr;
1115 OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
1116 if( rStrm.GetRecLeft() == 4 )
1118 sal_uInt16 nKey(0), nHash(0);
1119 nKey = rStrm.ReaduInt16();
1120 nHash = rStrm.ReaduInt16();
1121 xDecr = std::make_shared<XclImpBiff5Decrypter>( nKey, nHash );
1123 return xDecr;
1126 XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
1128 XclImpDecrypterRef xDecr;
1129 OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
1130 if( rStrm.GetRecLeft() == 48 )
1132 std::vector<sal_uInt8> aSalt(16);
1133 std::vector<sal_uInt8> aVerifier(16);
1134 std::vector<sal_uInt8> aVerifierHash(16);
1135 rStrm.Read(aSalt.data(), 16);
1136 rStrm.Read(aVerifier.data(), 16);
1137 rStrm.Read(aVerifierHash.data(), 16);
1138 xDecr = std::make_shared<XclImpBiff8StdDecrypter>(std::move(aSalt), std::move(aVerifier), std::move(aVerifierHash));
1140 return xDecr;
1143 XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
1145 //It is possible there are other variants in existence but these
1146 //are the defaults I get with Excel 2013
1147 XclImpDecrypterRef xDecr;
1149 msfilter::RC4EncryptionInfo info;
1151 info.header.flags = rStream.ReaduInt32();
1152 if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
1153 return xDecr;
1155 sal_uInt32 nHeaderSize = rStream.ReaduInt32();
1156 sal_uInt32 actualHeaderSize = sizeof(info.header);
1158 if( nHeaderSize < actualHeaderSize )
1159 return xDecr;
1161 info.header.flags = rStream.ReaduInt32();
1162 info.header.sizeExtra = rStream.ReaduInt32();
1163 info.header.algId = rStream.ReaduInt32();
1164 info.header.algIdHash = rStream.ReaduInt32();
1165 info.header.keyBits = rStream.ReaduInt32();
1166 info.header.providedType = rStream.ReaduInt32();
1167 info.header.reserved1 = rStream.ReaduInt32();
1168 info.header.reserved2 = rStream.ReaduInt32();
1170 rStream.Ignore(nHeaderSize - actualHeaderSize);
1172 info.verifier.saltSize = rStream.ReaduInt32();
1173 if (info.verifier.saltSize != msfilter::SALT_LENGTH)
1174 return xDecr;
1175 rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
1176 rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
1178 info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
1179 if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
1180 return xDecr;
1181 rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
1183 // check flags and algorithm IDs, required are AES128 and SHA-1
1184 if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
1185 return xDecr;
1187 if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
1188 return xDecr;
1190 if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
1191 return xDecr;
1193 // hash algorithm ID 0 defaults to SHA-1 too
1194 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
1195 return xDecr;
1197 xDecr = std::make_shared<XclImpBiff8CryptoAPIDecrypter>(
1198 std::vector<sal_uInt8>(info.verifier.salt,
1199 info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
1200 std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
1201 info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
1202 std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
1203 info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash)));
1205 return xDecr;
1208 XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
1210 XclImpDecrypterRef xDecr;
1212 sal_uInt16 nMode = rStrm.ReaduInt16();
1213 switch( nMode )
1215 case EXC_FILEPASS_BIFF5:
1216 xDecr = lclReadFilepass5( rStrm );
1217 break;
1219 case EXC_FILEPASS_BIFF8:
1221 sal_uInt32 nVersion = rStrm.ReaduInt32();
1222 if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
1224 //A Version structure where Version.vMajor MUST be 0x0001,
1225 //and Version.vMinor MUST be 0x0001.
1226 xDecr = lclReadFilepass8_Standard(rStrm);
1228 else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
1229 nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
1231 //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
1232 //Version.vMinor MUST be 0x0002.
1233 xDecr = lclReadFilepass8_Strong(rStrm);
1235 else
1236 OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
1238 break;
1240 default:
1241 OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
1244 return xDecr;
1247 } // namespace
1249 const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
1251 XclImpDecrypterRef xDecr;
1252 rStrm.DisableDecryption();
1254 // read the FILEPASS record and create a new decrypter object
1255 switch( rStrm.GetRoot().GetBiff() )
1257 case EXC_BIFF2:
1258 case EXC_BIFF3:
1259 case EXC_BIFF4:
1260 case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
1261 case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
1262 default: DBG_ERROR_BIFF();
1265 // set decrypter at import stream
1266 rStrm.SetDecrypter( xDecr );
1268 // request and verify a password (decrypter implements IDocPasswordVerifier)
1269 if( xDecr )
1270 rStrm.GetRoot().RequestEncryptionData( *xDecr );
1272 // return error code (success, wrong password, etc.)
1273 return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
1276 // Document protection ========================================================
1278 XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
1279 XclImpRoot( rRoot ),
1280 mnPassHash(0x0000),
1281 mbDocProtect(false),
1282 mbWinProtect(false)
1286 void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
1288 mbDocProtect = rStrm.ReaduInt16() != 0;
1291 void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
1293 mbWinProtect = rStrm.ReaduInt16() != 0;
1296 void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
1298 rStrm.EnableDecryption();
1299 mnPassHash = rStrm.ReaduInt16();
1302 void XclImpDocProtectBuffer::Apply() const
1304 if (!mbDocProtect && !mbWinProtect)
1305 // Excel requires either the structure or windows protection is set.
1306 // If neither is set then the document is not protected at all.
1307 return;
1309 unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
1310 pProtect->setProtected(true);
1312 if (mnPassHash)
1314 // 16-bit password hash.
1315 Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
1316 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1319 // document protection options
1320 pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
1321 pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
1323 GetDoc().SetDocProtection(pProtect.get());
1326 // Sheet Protection ===========================================================
1328 XclImpSheetProtectBuffer::Sheet::Sheet() :
1329 mbProtected(false),
1330 mnPasswordHash(0x0000),
1331 mnOptions(0x4400)
1335 XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
1336 mbProtected(r.mbProtected),
1337 mnPasswordHash(r.mnPasswordHash),
1338 mnOptions(r.mnOptions)
1342 XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
1343 XclImpRoot( rRoot )
1347 void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
1349 if ( rStrm.ReaduInt16() )
1351 Sheet* pSheet = GetSheetItem(nTab);
1352 if (pSheet)
1353 pSheet->mbProtected = true;
1357 void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
1359 // The flag size specifies the size of bytes that follows that stores
1360 // feature data. If -1 it depends on the feature type imported earlier.
1361 // For enhanced protection data, the size is always 4. For the most xls
1362 // documents out there this value is almost always -1.
1363 sal_Int32 nFlagSize = rStrm.ReadInt32();
1364 if (nFlagSize != -1)
1365 return;
1367 // There are actually 4 bytes to read, but the upper 2 bytes currently
1368 // don't store any bits.
1369 sal_uInt16 nOptions = rStrm.ReaduInt16();
1371 Sheet* pSheet = GetSheetItem(nTab);
1372 if (pSheet)
1373 pSheet->mnOptions = nOptions;
1376 void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
1378 Sheet* pSheet = GetSheetItem(nTab);
1379 if (pSheet)
1380 pSheet->maEnhancedProtections.push_back( rProt);
1383 void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
1385 sal_uInt16 nHash = rStrm.ReaduInt16();
1386 Sheet* pSheet = GetSheetItem(nTab);
1387 if (pSheet)
1388 pSheet->mnPasswordHash = nHash;
1391 void XclImpSheetProtectBuffer::Apply() const
1393 for (const auto& [rTab, rSheet] : maProtectedSheets)
1395 if (!rSheet.mbProtected)
1396 // This sheet is (for whatever reason) not protected.
1397 continue;
1399 unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
1400 pProtect->setProtected(true);
1402 // 16-bit hash password
1403 const sal_uInt16 nHash = rSheet.mnPasswordHash;
1404 if (nHash)
1406 Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
1407 pProtect->setPasswordHash(aPass, PASSHASH_XL);
1410 // sheet protection options
1411 const sal_uInt16 nOptions = rSheet.mnOptions;
1412 pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
1413 pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
1414 pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
1415 pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
1416 pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
1417 pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
1418 pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
1419 pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
1420 pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
1421 pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
1422 pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
1423 pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
1424 pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
1425 pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
1426 pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
1428 // Enhanced protection containing editable ranges and permissions.
1429 pProtect->setEnhancedProtection( std::vector(rSheet.maEnhancedProtections) );
1431 // all done. now commit.
1432 GetDoc().SetTabProtection(rTab, pProtect.get());
1436 XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
1438 ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
1439 if (itr == maProtectedSheets.end())
1441 // new sheet
1442 if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
1443 return nullptr;
1445 itr = maProtectedSheets.find(nTab);
1448 return &itr->second;
1451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */