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 "lotfilter.hxx"
21 #include <lotimpop.hxx>
22 #include <osl/mutex.hxx>
23 #include <sal/log.hxx>
24 
25 #include <document.hxx>
26 #include <formulacell.hxx>
27 #include <global.hxx>
28 
29 #include <root.hxx>
30 #include <lotfntbf.hxx>
31 #include <lotform.hxx>
32 #include <tool.h>
33 #include <namebuff.hxx>
34 #include <lotattr.hxx>
35 #include <stringutil.hxx>
36 #include <config_fuzzers.h>
37 
38 #include <unotools/configmgr.hxx>
39 
LOTUS_ROOT(ScDocument * pDocP,rtl_TextEncoding eQ)40 LOTUS_ROOT::LOTUS_ROOT( ScDocument* pDocP, rtl_TextEncoding eQ )
41     :
42         pDoc( pDocP),
43         maRangeNames(),
44         eCharsetQ( eQ),
45         eFirstType( Lotus123Typ::X),
46         eActType( Lotus123Typ::X),
47         pRngNmBffWK3( new RangeNameBufferWK3() ),
48         maAttrTable( this )
49 {
50 }
51 
~LOTUS_ROOT()52 LOTUS_ROOT::~LOTUS_ROOT()
53 {
54 }
55 
56 static osl::Mutex aLotImpSemaphore;
57 
ImportLotus(LotusContext & rContext,SvStream & aStream,ScDocument * pDoc,rtl_TextEncoding eQ)58 ImportLotus::ImportLotus(LotusContext &rContext, SvStream& aStream, ScDocument* pDoc, rtl_TextEncoding eQ)
59     : ImportTyp(pDoc, eQ)
60     , pIn(&aStream)
61     , aConv(rContext, *pIn, pDoc->GetSharedStringPool(), eQ, false)
62     , nTab(0)
63     , nExtTab(0)
64 {
65     // good point to start locking of import lotus
66     aLotImpSemaphore.acquire();
67 
68     rContext.pLotusRoot = new LOTUS_ROOT(pDoc, eQ);
69 }
70 
~ImportLotus()71 ImportLotus::~ImportLotus()
72 {
73     LotusContext &rContext = aConv.getContext();
74     delete rContext.pLotusRoot;
75     rContext.pLotusRoot = nullptr;
76 
77     // no need 4 pLotusRoot anymore
78     aLotImpSemaphore.release();
79 }
80 
Bof()81 void ImportLotus::Bof()
82 {
83     sal_uInt16  nFileCode, nFileSub, nSaveCnt;
84     sal_uInt8   nMajorId, nMinorId, nFlags;
85 
86     Read( nFileCode );
87     Read( nFileSub );
88     LotusContext &rContext = aConv.getContext();
89     Read( rContext.pLotusRoot->aActRange );
90     Read( nSaveCnt );
91     Read( nMajorId );
92     Read( nMinorId );
93     Skip( 1 );
94     Read( nFlags );
95 
96     if( nFileSub == 0x0004 )
97     {
98         if( nFileCode == 0x1000 )
99         {// <= WK3
100             rContext.pLotusRoot->eFirstType = rContext.pLotusRoot->eActType = Lotus123Typ::WK3;
101         }
102         else if( nFileCode == 0x1002 )
103         {// WK4
104             rContext.pLotusRoot->eFirstType = rContext.pLotusRoot->eActType = Lotus123Typ::WK4;
105         }
106     }
107 }
108 
BofFm3()109 bool ImportLotus::BofFm3()
110 {
111     sal_uInt16  nFileCode, nFileSub;
112 
113     Read( nFileCode );
114     Read( nFileSub );
115 
116     return ( nFileCode == 0x8007 && ( nFileSub == 0x0000 || nFileSub == 0x00001 ) );
117 }
118 
Columnwidth(sal_uInt16 nRecLen)119 void ImportLotus::Columnwidth( sal_uInt16 nRecLen )
120 {
121     SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Columnwidth(): Record too short!" );
122 
123     sal_uInt8    nLTab, nWindow2;
124     sal_uInt16    nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
125 
126     Read( nLTab );
127     Read( nWindow2 );
128 
129     if( !pD->HasTable( static_cast<SCTAB> (nLTab) ) )
130         pD->MakeTable( static_cast<SCTAB> (nLTab) );
131 
132     if( !nWindow2 )
133     {
134         Skip( 2 );
135 
136         sal_uInt8   nCol, nSpaces;
137 
138         while( nCnt )
139         {
140             Read( nCol );
141             Read( nSpaces );
142             // Attention: ambiguous Correction factor!
143             pD->SetColWidth( static_cast<SCCOL> (nCol), static_cast<SCTAB> (nLTab), static_cast<sal_uInt16>( TWIPS_PER_CHAR * 1.28 * nSpaces ) );
144 
145             nCnt--;
146         }
147     }
148 }
149 
Hiddencolumn(sal_uInt16 nRecLen)150 void ImportLotus::Hiddencolumn( sal_uInt16 nRecLen )
151 {
152     SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Hiddencolumn(): Record too short!" );
153 
154     sal_uInt8    nLTab, nWindow2;
155     sal_uInt16    nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
156 
157     Read( nLTab );
158     Read( nWindow2 );
159 
160     if( !nWindow2 )
161     {
162         Skip( 2 );
163 
164         sal_uInt8   nCol;
165 
166         while( nCnt )
167         {
168             Read( nCol );
169 
170             pD->SetColHidden(static_cast<SCCOL>(nCol), static_cast<SCCOL>(nCol), static_cast<SCTAB>(nLTab), true);
171             nCnt--;
172         }
173     }
174 }
175 
Userrange()176 void ImportLotus::Userrange()
177 {
178     sal_uInt16      nRangeType;
179     ScRange     aScRange;
180 
181     Read( nRangeType );
182 
183     sal_Char aBuffer[ 17 ];
184     pIn->ReadBytes(aBuffer, 16);
185     aBuffer[ 16 ] = 0;
186     OUString      aName( aBuffer, strlen(aBuffer), eQuellChar );
187 
188     Read( aScRange );
189 
190     LotusContext &rContext = aConv.getContext();
191     rContext.pLotusRoot->pRngNmBffWK3->Add( aName, aScRange );
192 }
193 
Errcell()194 void ImportLotus::Errcell()
195 {
196     ScAddress   aA;
197 
198     Read( aA );
199 
200     ScSetStringParam aParam;
201     aParam.setTextInput();
202     pD->EnsureTable(aA.Tab());
203     pD->SetString(aA, "#ERR!", &aParam);
204 }
205 
Nacell()206 void ImportLotus::Nacell()
207 {
208     ScAddress   aA;
209 
210     Read( aA );
211 
212     ScSetStringParam aParam;
213     aParam.setTextInput();
214     pD->EnsureTable(aA.Tab());
215     pD->SetString(aA, "#NA!", &aParam);
216 }
217 
Labelcell()218 void ImportLotus::Labelcell()
219 {
220     ScAddress   aA;
221     OUString    aLabel;
222     sal_Char    cAlign;
223 
224     Read( aA );
225     Read( cAlign );
226     Read( aLabel );
227 
228     ScSetStringParam aParam;
229     aParam.setTextInput();
230     pD->EnsureTable(aA.Tab());
231     pD->SetString(aA, aLabel, &aParam);
232 }
233 
Numbercell()234 void ImportLotus::Numbercell()
235 {
236     ScAddress   aAddr;
237     double      fVal;
238 
239     Read( aAddr );
240     Read( fVal );
241 
242     pD->EnsureTable(aAddr.Tab());
243     pD->SetValue(aAddr, fVal);
244 }
245 
Smallnumcell()246 void ImportLotus::Smallnumcell()
247 {
248     ScAddress   aAddr;
249     sal_Int16       nVal;
250 
251     Read( aAddr );
252     Read( nVal );
253 
254     pD->EnsureTable(aAddr.Tab());
255     pD->SetValue(aAddr, SnumToDouble(nVal));
256 }
257 
Formulacell(sal_uInt16 n)258 void ImportLotus::Formulacell( sal_uInt16 n )
259 {
260     SAL_WARN_IF( !pIn, "sc.filter", "-ImportLotus::Formulacell(): Null-Stream!" );
261 
262     ScAddress           aAddr;
263 
264     Read( aAddr );
265     Skip( 10 );
266 
267     n -= std::min<sal_uInt16>(n, 14);
268 
269     std::unique_ptr<ScTokenArray> pErg;
270     sal_Int32 nRest = n;
271 
272     aConv.Reset( aAddr );
273     aConv.SetWK3();
274     aConv.Convert( pErg, nRest );
275     if (!aConv.good())
276         return;
277 
278     ScFormulaCell* pCell = pErg ? new ScFormulaCell(pD, aAddr, std::move(pErg)) : new ScFormulaCell(pD, aAddr);
279     pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
280     pD->EnsureTable(aAddr.Tab());
281     pD->SetFormulaCell(aAddr, pCell);
282 }
283 
Read(OUString & r)284 void ImportLotus::Read( OUString &r )
285 {
286     ScfTools::AppendCString( *pIn, r, eQuellChar );
287 }
288 
RowPresentation(sal_uInt16 nRecLen)289 void ImportLotus::RowPresentation( sal_uInt16 nRecLen )
290 {
291     SAL_WARN_IF( nRecLen < 5, "sc.filter", "*ImportLotus::RowPresentation(): Record too short!" );
292 
293     sal_uInt8    nLTab, nFlags;
294     sal_uInt16  nRow, nHeight;
295     sal_uInt16    nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 8;
296 
297     Read( nLTab );
298     Skip( 1 );
299 
300     while( nCnt )
301     {
302         Read( nRow );
303         Read( nHeight );
304         Skip( 2 );
305         Read( nFlags );
306         Skip( 1 );
307 
308         if( nFlags & 0x02 )     // Fixed / Stretch to fit fonts
309         {   // fixed
310             // Height in Lotus in 1/32 Points
311             nHeight *= 20;  // -> 32 * TWIPS
312             nHeight /= 32;  // -> TWIPS
313 
314             pD->SetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), pD->GetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab) ) | CRFlags::ManualSize );
315 
316             pD->SetRowHeight( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), nHeight );
317         }
318 
319         nCnt--;
320     }
321 }
322 
NamedSheet()323 void ImportLotus::NamedSheet()
324 {
325     sal_uInt16 nTmpTab(0);
326     Read(nTmpTab);
327     OUString aName;
328     Read(aName);
329 
330     SCTAB nLTab(SanitizeTab(static_cast<SCTAB>(nTmpTab)));
331 #if ENABLE_FUZZERS
332     //ofz#14167 arbitrary sheet limit to make fuzzing useful
333     if (nLTab > 5)
334         nLTab = 5;
335 #endif
336 
337     if (pD->HasTable(nLTab))
338         pD->RenameTab(nLTab, aName);
339     else
340         pD->InsertTab(nLTab, aName);
341 }
342 
Font_Face()343 void ImportLotus::Font_Face()
344 {
345     sal_uInt8   nNum;
346     OUString    aName;
347 
348     Read( nNum );
349 
350     if( nNum >= LotusFontBuffer::nSize )
351         return;     // nonsense
352 
353     Read( aName );
354 
355     LotusContext &rContext = aConv.getContext();
356     rContext.pLotusRoot->maFontBuff.SetName( nNum, aName );
357 }
358 
Font_Type()359 void ImportLotus::Font_Type()
360 {
361     LotusContext &rContext = aConv.getContext();
362     for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
363     {
364         sal_uInt16 nType;
365         Read( nType );
366         rContext.pLotusRoot->maFontBuff.SetType( nCnt, nType );
367     }
368 }
369 
Font_Ysize()370 void ImportLotus::Font_Ysize()
371 {
372     LotusContext &rContext = aConv.getContext();
373     for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
374     {
375         sal_uInt16 nSize;
376         Read( nSize );
377         rContext.pLotusRoot->maFontBuff.SetHeight( nCnt, nSize );
378     }
379 }
380 
Row_(const sal_uInt16 nRecLen)381 void ImportLotus::Row_( const sal_uInt16 nRecLen )
382 {
383     SAL_WARN_IF( nExtTab < 0, "sc.filter", "*ImportLotus::Row_(): not possible!" );
384 
385     sal_uInt16            nCntDwn = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 5;
386     SCCOL           nColCnt = 0;
387     sal_uInt8           nRepeats;
388     LotAttrWK3      aAttr;
389 
390     bool            bCenter = false;
391     SCCOL           nCenterStart = 0, nCenterEnd = 0;
392 
393     sal_uInt16 nTmpRow(0);
394     Read(nTmpRow);
395     SCROW nRow(SanitizeRow(static_cast<SCROW>(nTmpRow)));
396     sal_uInt16 nHeight(0);
397     Read(nHeight);
398 
399     nHeight &= 0x0FFF;
400     nHeight *= 22;
401 
402     SCTAB nDestTab(static_cast<SCTAB>(nExtTab));
403 
404     if( nHeight )
405         pD->SetRowHeight(nRow, nDestTab, nHeight);
406 
407     LotusContext &rContext = aConv.getContext();
408     while( nCntDwn )
409     {
410         Read( aAttr );
411         Read( nRepeats );
412 
413         if( aAttr.HasStyles() )
414             rContext.pLotusRoot->maAttrTable.SetAttr(
415                 nColCnt, static_cast<SCCOL> ( nColCnt + nRepeats ), nRow, aAttr );
416 
417         // Do this here and NOT in class LotAttrTable, as we only add attributes if the other
418         // attributes are set
419         //  -> for Center-Attribute default is centered
420         if( aAttr.IsCentered() )
421         {
422             if( bCenter )
423             {
424                 if (pD->HasData(nColCnt, nRow, nDestTab))
425                 {
426                     // new Center after previous Center
427                     pD->DoMerge(nDestTab, nCenterStart, nRow, nCenterEnd, nRow);
428                     nCenterStart = nColCnt;
429                 }
430             }
431             else
432             {
433                 // fully new Center
434                 bCenter = true;
435                 nCenterStart = nColCnt;
436             }
437             nCenterEnd = nColCnt + static_cast<SCCOL>(nRepeats);
438         }
439         else
440         {
441             if( bCenter )
442             {
443                 // possibly reset old Center
444                 pD->DoMerge(nDestTab, nCenterStart, nRow, nCenterEnd, nRow);
445                 bCenter = false;
446             }
447         }
448 
449         nColCnt = nColCnt + static_cast<SCCOL>(nRepeats);
450         nColCnt++;
451 
452         nCntDwn--;
453     }
454 
455     if( bCenter )
456         // possibly reset old Center
457         pD->DoMerge(nDestTab, nCenterStart, nRow, nCenterEnd, nRow);
458 }
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
460