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 <XclImpChangeTrack.hxx>
21 #include <sot/storage.hxx>
22 #include <svl/zforlist.hxx>
23 #include <svl/sharedstringpool.hxx>
24 #include <sal/log.hxx>
25 #include <chgviset.hxx>
26 #include <formulacell.hxx>
27 #include <chgtrack.hxx>
28 #include <xihelper.hxx>
29 #include <xilink.hxx>
30 #include <externalrefmgr.hxx>
31 #include <document.hxx>
32 #include <documentimport.hxx>
33 #include <excdefs.hxx>
34 
35 // class XclImpChangeTrack
36 
XclImpChangeTrack(const XclImpRoot & rRoot,const XclImpStream & rBookStrm)37 XclImpChangeTrack::XclImpChangeTrack( const XclImpRoot& rRoot, const XclImpStream& rBookStrm ) :
38     XclImpRoot( rRoot ),
39     aRecHeader(),
40     sOldUsername(),
41     nTabIdCount( 0 ),
42     bGlobExit( false ),
43     eNestedMode( nmBase )
44 {
45     // Verify that the User Names stream exists before going any further. Excel adds both
46     // "Revision Log" and "User Names" streams when Change Tracking is active but the Revision log
47     // remains if Change Tracking is turned off.
48     tools::SvRef<SotStorageStream> xUserStrm = OpenStream( EXC_STREAM_USERNAMES );
49     if( !xUserStrm.is() )
50         return;
51 
52     xInStrm = OpenStream( EXC_STREAM_REVLOG );
53     if( xInStrm.is() )
54     {
55         xInStrm->Seek( STREAM_SEEK_TO_END );
56         sal_uInt64 const nStreamLen = xInStrm->Tell();
57         if( (xInStrm->GetErrorCode() == ERRCODE_NONE) && (nStreamLen != STREAM_SEEK_TO_END) )
58         {
59             xInStrm->Seek( STREAM_SEEK_TO_BEGIN );
60             pStrm.reset( new XclImpStream( *xInStrm, GetRoot() ) );
61             pStrm->CopyDecrypterFrom( rBookStrm );
62             pChangeTrack.reset(new ScChangeTrack( &GetDoc() ));
63 
64             sOldUsername = pChangeTrack->GetUser();
65             pChangeTrack->SetUseFixDateTime( true );
66 
67             ReadRecords();
68         }
69     }
70 }
71 
~XclImpChangeTrack()72 XclImpChangeTrack::~XclImpChangeTrack()
73 {
74     pChangeTrack.reset();
75     pStrm.reset();
76 }
77 
DoAcceptRejectAction(ScChangeAction * pAction)78 void XclImpChangeTrack::DoAcceptRejectAction( ScChangeAction* pAction )
79 {
80     if( !pAction ) return;
81     switch( aRecHeader.nAccept )
82     {
83         case EXC_CHTR_ACCEPT:
84             pChangeTrack->Accept( pAction );
85         break;
86         case EXC_CHTR_REJECT:
87         break;
88     }
89 }
90 
DoAcceptRejectAction(sal_uInt32 nFirst,sal_uInt32 nLast)91 void XclImpChangeTrack::DoAcceptRejectAction( sal_uInt32 nFirst, sal_uInt32 nLast )
92 {
93     for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; nIndex++ )
94         DoAcceptRejectAction( pChangeTrack->GetAction( nIndex ) );
95 }
96 
DoInsertRange(const ScRange & rRange,bool bEndOfList)97 void XclImpChangeTrack::DoInsertRange( const ScRange& rRange, bool bEndOfList )
98 {
99     sal_uInt32 nFirst = pChangeTrack->GetActionMax() + 1;
100     pChangeTrack->AppendInsert(rRange, bEndOfList);
101     sal_uInt32 nLast = pChangeTrack->GetActionMax();
102     DoAcceptRejectAction( nFirst, nLast );
103 }
104 
DoDeleteRange(const ScRange & rRange)105 void XclImpChangeTrack::DoDeleteRange( const ScRange& rRange )
106 {
107     sal_uLong nFirst, nLast;
108     pChangeTrack->AppendDeleteRange( rRange,  &GetDoc() , nFirst, nLast );
109     DoAcceptRejectAction( nFirst, nLast );
110 }
111 
ReadTabNum()112 SCTAB XclImpChangeTrack::ReadTabNum()
113 {
114     return static_cast<SCTAB>(GetTabInfo().GetCurrentIndex(
115                 pStrm->ReaduInt16(), nTabIdCount ));
116 }
117 
ReadDateTime(DateTime & rDateTime)118 void XclImpChangeTrack::ReadDateTime( DateTime& rDateTime )
119 {
120     sal_uInt16 nYear;
121     sal_uInt8 nMonth, nDay, nHour, nMin, nSec;
122 
123     nYear = pStrm->ReaduInt16();
124     nMonth = pStrm->ReaduInt8();
125     nDay = pStrm->ReaduInt8();
126     nHour = pStrm->ReaduInt8();
127     nMin = pStrm->ReaduInt8();
128     nSec = pStrm->ReaduInt8();
129 
130     rDateTime.SetYear( nYear );
131     rDateTime.SetMonth( nMonth );
132     rDateTime.SetDay( nDay );
133     rDateTime.SetHour( nHour );
134     rDateTime.SetMin( nMin );
135     rDateTime.SetSec( nSec );
136     rDateTime.SetNanoSec( 0 );
137 }
138 
CheckRecord(sal_uInt16 nOpCode)139 bool XclImpChangeTrack::CheckRecord( sal_uInt16 nOpCode )
140 {
141     if( (nOpCode != EXC_CHTR_OP_UNKNOWN) && (aRecHeader.nOpCode != nOpCode) )
142     {
143         OSL_FAIL( "XclImpChangeTrack::CheckRecord - unknown action" );
144         return false;
145     }
146     return aRecHeader.nIndex != 0;
147 }
148 
Read3DTabRefInfo(SCTAB & rFirstTab,SCTAB & rLastTab,ExcelToSc8::ExternalTabInfo & rExtInfo)149 void XclImpChangeTrack::Read3DTabRefInfo( SCTAB& rFirstTab, SCTAB& rLastTab, ExcelToSc8::ExternalTabInfo& rExtInfo )
150 {
151     if( LookAtuInt8() == 0x01 )
152     {
153         rExtInfo.mbExternal = false;
154         // internal ref - read tab num and return sc tab num (position in TABID list)
155         pStrm->Ignore( 3 );
156         rFirstTab = static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) );
157         sal_uInt8 nFillByte = pStrm->ReaduInt8();
158         rLastTab = (nFillByte == 0x00) ?
159             static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) ) : rFirstTab;
160     }
161     else
162     {
163         // external ref - read doc and tab name and find sc tab num
164         // - URL
165         OUString aEncUrl( pStrm->ReadUniString() );
166         OUString aUrl;
167         bool bSelf;
168         XclImpUrlHelper::DecodeUrl( aUrl, bSelf, GetRoot(), aEncUrl );
169         pStrm->Ignore( 1 );
170         // - sheet name, always separated from URL
171         OUString aTabName( pStrm->ReadUniString() );
172         pStrm->Ignore( 1 );
173 
174         rExtInfo.mbExternal = true;
175         ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
176         pRefMgr->convertToAbsName(aUrl);
177         rExtInfo.mnFileId = pRefMgr->getExternalFileId(aUrl);
178         rExtInfo.maTabName = aTabName;
179         rFirstTab = rLastTab = 0;
180     }
181 }
182 
ReadFormula(std::unique_ptr<ScTokenArray> & rpTokenArray,const ScAddress & rPosition)183 void XclImpChangeTrack::ReadFormula( std::unique_ptr<ScTokenArray>& rpTokenArray, const ScAddress& rPosition )
184 {
185     sal_uInt16 nFmlSize = pStrm->ReaduInt16();
186 
187     // create a memory stream and copy the formula to be able to read simultaneously
188     // the formula and the additional 3D tab ref data following the formula
189     // here we have to simulate an Excel record to be able to use an XclImpStream...
190     // 2do: remove the stream member from formula converter and add it as a parameter
191     // to the Convert() routine (to prevent the construction/destruction of the
192     // converter in each formula)
193     SvMemoryStream aMemStrm;
194     aMemStrm.WriteUInt16( 0x0001 ).WriteUInt16( nFmlSize );
195     size_t nRead = pStrm->CopyToStream( aMemStrm, nFmlSize );
196 
197     // survive reading invalid streams!
198     // if we can't read as many bytes as required just don't use them and
199     // assume that this part is broken
200     if(nRead != nFmlSize)
201     {
202         rpTokenArray = nullptr;
203         pStrm->Ignore(1);
204         return;
205     }
206 
207     XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
208     aFmlaStrm.StartNextRecord();
209     XclImpChTrFmlConverter aFmlConv( GetRoot(), *this );
210 
211     // read the formula, 3D tab refs from extended data
212     std::unique_ptr<ScTokenArray> pArray;
213     aFmlConv.Reset( rPosition );
214     bool bOK = (aFmlConv.Convert( pArray, aFmlaStrm, nFmlSize, false ) == ConvErr::OK);   // JEG : Check This
215     rpTokenArray = (bOK && pArray) ? std::move( pArray ) : nullptr;
216     pStrm->Ignore( 1 );
217 }
218 
ReadCell(ScCellValue & rCell,sal_uInt32 & rFormat,sal_uInt16 nFlags,const ScAddress & rPosition)219 void XclImpChangeTrack::ReadCell(
220     ScCellValue& rCell, sal_uInt32& rFormat, sal_uInt16 nFlags, const ScAddress& rPosition )
221 {
222     rCell.clear();
223     rFormat = 0;
224     switch( nFlags & EXC_CHTR_TYPE_MASK )
225     {
226         case EXC_CHTR_TYPE_EMPTY:
227         break;
228         case EXC_CHTR_TYPE_RK:
229         {
230             double fValue = XclTools::GetDoubleFromRK( pStrm->ReadInt32() );
231             if( pStrm->IsValid() )
232             {
233                 rCell.meType = CELLTYPE_VALUE;
234                 rCell.mfValue = fValue;
235             }
236         }
237         break;
238         case EXC_CHTR_TYPE_DOUBLE:
239         {
240             double fValue = pStrm->ReadDouble();
241             if( pStrm->IsValid() )
242             {
243                 rCell.meType = CELLTYPE_VALUE;
244                 rCell.mfValue = fValue;
245             }
246         }
247         break;
248         case EXC_CHTR_TYPE_STRING:
249         {
250             OUString sString = pStrm->ReadUniString();
251             if( pStrm->IsValid() )
252             {
253                 rCell.meType = CELLTYPE_STRING;
254                 rCell.mpString = new svl::SharedString(GetDoc().GetSharedStringPool().intern(sString));
255             }
256         }
257         break;
258         case EXC_CHTR_TYPE_BOOL:
259         {
260             double fValue = static_cast<double>(pStrm->ReaduInt16() != 0);
261             if( pStrm->IsValid() )
262             {
263                 rCell.meType = CELLTYPE_VALUE;
264                 rCell.mfValue = fValue;
265                 rFormat = GetFormatter().GetStandardFormat( SvNumFormatType::LOGICAL, ScGlobal::eLnge );
266             }
267         }
268         break;
269         case EXC_CHTR_TYPE_FORMULA:
270         {
271             std::unique_ptr<ScTokenArray> pTokenArray;
272             ReadFormula( pTokenArray, rPosition );
273             if( pStrm->IsValid() && pTokenArray )
274             {
275                 rCell.meType = CELLTYPE_FORMULA;
276                 rCell.mpFormula = new ScFormulaCell(&GetDoc(), rPosition, std::move(pTokenArray));
277             }
278         }
279         break;
280         default:
281             OSL_FAIL( "XclImpChangeTrack::ReadCell - unknown data type" );
282     }
283 }
284 
ReadChTrInsert()285 void XclImpChangeTrack::ReadChTrInsert()
286 {
287     *pStrm >> aRecHeader;
288     if( CheckRecord( EXC_CHTR_OP_UNKNOWN ) )
289     {
290         if( (aRecHeader.nOpCode != EXC_CHTR_OP_INSROW) &&
291             (aRecHeader.nOpCode != EXC_CHTR_OP_INSCOL) &&
292             (aRecHeader.nOpCode != EXC_CHTR_OP_DELROW) &&
293             (aRecHeader.nOpCode != EXC_CHTR_OP_DELCOL) )
294         {
295             OSL_FAIL( "XclImpChangeTrack::ReadChTrInsert - unknown action" );
296             return;
297         }
298 
299         ScRange aRange;
300         aRange.aStart.SetTab( ReadTabNum() );
301         aRange.aEnd.SetTab( aRange.aStart.Tab() );
302         sal_uInt16 nFlags = pStrm->ReaduInt16();
303         bool bEndOfList = (nFlags & 0x0001); // row auto-inserted at the bottom.
304         Read2DRange( aRange );
305 
306         if( aRecHeader.nOpCode & EXC_CHTR_OP_COLFLAG )
307             aRange.aEnd.SetRow( GetDocImport().getDoc().MaxRow() );
308         else
309             aRange.aEnd.SetCol( GetDocImport().getDoc().MaxCol() );
310 
311         bool bValid = pStrm->IsValid();
312         if( FoundNestedMode() )
313             ReadNestedRecords();
314 
315         if( bValid )
316         {
317             if( aRecHeader.nOpCode & EXC_CHTR_OP_DELFLAG )
318                 DoDeleteRange( aRange );
319             else
320                 DoInsertRange(aRange, bEndOfList);
321         }
322     }
323 }
324 
ReadChTrInfo()325 void XclImpChangeTrack::ReadChTrInfo()
326 {
327     pStrm->DisableDecryption();
328     pStrm->Ignore( 32 );
329     OUString sUsername( pStrm->ReadUniString() );
330     if( !pStrm->IsValid() ) return;
331 
332     if( !sUsername.isEmpty() )
333         pChangeTrack->SetUser( sUsername );
334     pStrm->Seek( 148 );
335     if( !pStrm->IsValid() ) return;
336 
337     DateTime aDateTime( DateTime::EMPTY );
338     ReadDateTime( aDateTime );
339     if( pStrm->IsValid() )
340         pChangeTrack->SetFixDateTimeLocal( aDateTime );
341 }
342 
ReadChTrCellContent()343 void XclImpChangeTrack::ReadChTrCellContent()
344 {
345     *pStrm >> aRecHeader;
346     if( CheckRecord( EXC_CHTR_OP_CELL ) )
347     {
348         ScAddress aPosition;
349         SCTAB nTab = ReadTabNum();
350         aPosition.SetTab( nTab );
351         sal_uInt16 nValueType;
352         nValueType = pStrm->ReaduInt16();
353         sal_uInt16 nOldValueType = (nValueType >> 3) & EXC_CHTR_TYPE_MASK;
354         sal_uInt16 nNewValueType = nValueType & EXC_CHTR_TYPE_MASK;
355         pStrm->Ignore( 2 );
356         Read2DAddress( aPosition );
357         sal_uInt16 nOldSize;
358         nOldSize = pStrm->ReaduInt16();
359         SAL_WARN_IF( (nOldSize == 0) != (nOldValueType == EXC_CHTR_TYPE_EMPTY),
360             "sc.filter",
361             "XclImpChangeTrack::ReadChTrCellContent - old value mismatch" );
362         pStrm->Ignore( 4 );
363         switch( nValueType & EXC_CHTR_TYPE_FORMATMASK )
364         {
365             case 0x0000:                            break;
366             case 0x1100:    pStrm->Ignore( 16 );    break;
367             case 0x1300:    pStrm->Ignore( 8 );     break;
368             default:        OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - unknown format info" );
369         }
370 
371         ScCellValue aOldCell;
372         ScCellValue aNewCell;
373         sal_uInt32 nOldFormat;
374         sal_uInt32 nNewFormat;
375         ReadCell(aOldCell, nOldFormat, nOldValueType, aPosition);
376         ReadCell(aNewCell, nNewFormat, nNewValueType, aPosition);
377         if( !pStrm->IsValid() || (pStrm->GetRecLeft() > 0) )
378         {
379             OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - bytes left, action ignored" );
380             aOldCell.clear();
381             aNewCell.clear();
382         }
383         else
384         {
385             ScChangeActionContent* pNewAction =
386                 pChangeTrack->AppendContentOnTheFly(aPosition, aOldCell, aNewCell, nOldFormat, nNewFormat);
387             DoAcceptRejectAction( pNewAction );
388         }
389     }
390 }
391 
ReadChTrTabId()392 void XclImpChangeTrack::ReadChTrTabId()
393 {
394     if( nTabIdCount == 0 )  // read only 1st time, otherwise calculated by <ReadChTrInsertTab()>
395         nTabIdCount = static_cast< sal_uInt16 >( pStrm->GetRecLeft() >> 1 );
396 }
397 
ReadChTrMoveRange()398 void XclImpChangeTrack::ReadChTrMoveRange()
399 {
400     *pStrm >> aRecHeader;
401     if( CheckRecord( EXC_CHTR_OP_MOVE ) )
402     {
403         ScRange aSourceRange;
404         ScRange aDestRange;
405         aDestRange.aStart.SetTab( ReadTabNum() );
406         aDestRange.aEnd.SetTab( aDestRange.aStart.Tab() );
407         Read2DRange( aSourceRange );
408         Read2DRange( aDestRange );
409         aSourceRange.aStart.SetTab( ReadTabNum() );
410         aSourceRange.aEnd.SetTab( aSourceRange.aStart.Tab() );
411 
412         bool bValid = pStrm->IsValid();
413         if( FoundNestedMode() )
414             ReadNestedRecords();
415 
416         if( bValid )
417         {
418             pChangeTrack->AppendMove( aSourceRange, aDestRange, nullptr );
419             DoAcceptRejectAction( pChangeTrack->GetLast() );
420         }
421     }
422 }
423 
ReadChTrInsertTab()424 void XclImpChangeTrack::ReadChTrInsertTab()
425 {
426     *pStrm >> aRecHeader;
427     if( CheckRecord( EXC_CHTR_OP_INSTAB ) )
428     {
429         SCTAB nTab = ReadTabNum();
430         if( pStrm->IsValid() )
431         {
432             nTabIdCount++;
433             DoInsertRange(ScRange(0, 0, nTab, GetDocImport().getDoc().MaxCol(), GetDocImport().getDoc().MaxRow(), nTab), false);
434         }
435     }
436 }
437 
InitNestedMode()438 void XclImpChangeTrack::InitNestedMode()
439 {
440     OSL_ENSURE( eNestedMode == nmBase, "XclImpChangeTrack::InitNestedMode - unexpected nested mode" );
441     if( eNestedMode == nmBase )
442         eNestedMode = nmFound;
443 }
444 
ReadNestedRecords()445 void XclImpChangeTrack::ReadNestedRecords()
446 {
447     OSL_ENSURE( eNestedMode == nmFound, "XclImpChangeTrack::StartNestedMode - missing nested mode" );
448     if( eNestedMode == nmFound )
449     {
450         eNestedMode = nmNested;
451         ReadRecords();
452     }
453 }
454 
EndNestedMode()455 bool XclImpChangeTrack::EndNestedMode()
456 {
457     OSL_ENSURE( eNestedMode != nmBase, "XclImpChangeTrack::EndNestedMode - missing nested mode" );
458     bool bReturn = (eNestedMode == nmNested);
459     eNestedMode = nmBase;
460     return bReturn;
461 }
462 
ReadRecords()463 void XclImpChangeTrack::ReadRecords()
464 {
465     bool bExitLoop = false;
466 
467     while( !bExitLoop && !bGlobExit && pStrm->StartNextRecord() )
468     {
469         switch( pStrm->GetRecId() )
470         {
471             case 0x000A:    bGlobExit = true;               break;
472             case 0x0137:    ReadChTrInsert();               break;
473             case 0x0138:    ReadChTrInfo();                 break;
474             case 0x013B:    ReadChTrCellContent();          break;
475             case 0x013D:    ReadChTrTabId();                break;
476             case 0x0140:    ReadChTrMoveRange();            break;
477             case 0x014D:    ReadChTrInsertTab();            break;
478             case 0x014E:
479             case 0x0150:    InitNestedMode();               break;
480             case 0x014F:
481             case 0x0151:    bExitLoop = EndNestedMode();    break;
482         }
483     }
484 }
485 
Apply()486 void XclImpChangeTrack::Apply()
487 {
488     if( pChangeTrack )
489     {
490         pChangeTrack->SetUser( sOldUsername );
491         pChangeTrack->SetUseFixDateTime( false );
492 
493         GetDoc().SetChangeTrack( std::move(pChangeTrack) );
494 
495         ScChangeViewSettings aSettings;
496         aSettings.SetShowChanges( true );
497         GetDoc().SetChangeViewSettings( aSettings );
498     }
499 }
500 
501 // class XclImpChTrFmlConverter
502 
XclImpChTrFmlConverter(XclImpRoot & rRoot,XclImpChangeTrack & rXclChTr)503 XclImpChTrFmlConverter::XclImpChTrFmlConverter(
504     XclImpRoot& rRoot, XclImpChangeTrack& rXclChTr ) :
505     ExcelToSc8( rRoot ),
506     rChangeTrack( rXclChTr ) {}
507 
~XclImpChTrFmlConverter()508 XclImpChTrFmlConverter::~XclImpChTrFmlConverter()
509 {
510 }
511 
512 // virtual, called from ExcToSc8::Convert()
Read3DTabReference(sal_uInt16,SCTAB & rFirstTab,SCTAB & rLastTab,ExternalTabInfo & rExtInfo)513 bool XclImpChTrFmlConverter::Read3DTabReference( sal_uInt16 /*nIxti*/, SCTAB& rFirstTab, SCTAB& rLastTab,
514                                                  ExternalTabInfo& rExtInfo )
515 {
516     rChangeTrack.Read3DTabRefInfo( rFirstTab, rLastTab, rExtInfo );
517     return true;
518 }
519 
520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
521