1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
3 * Version: MPL 2.0 / LGPLv2.1+
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 * Major Contributor(s):
10 * Copyright (C) 2009, 2011 Alonso Laurent (alonso@loria.fr)
11 * Copyright (C) 2006, 2007 Andrew Ziem
12 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
14 * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
15 *
16 * For minor contributions see the git repository.
17 *
18 * Alternatively, the contents of this file may be used under the terms
19 * of the GNU Lesser General Public License Version 2.1 or later
20 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
21 * applicable instead of those above.
22 *
23 * For further information visit http://libwps.sourceforge.net
24 */
25
26 /*
27 * freely inspired from istorage :
28 * ------------------------------------------------------------
29 * Generic OLE Zones furnished with a copy of the file header
30 *
31 * Compound Storage (32 bit version)
32 * Storage implementation
33 *
34 * This file contains the compound file implementation
35 * of the storage interface.
36 *
37 * Copyright 1999 Francis Beaudet
38 * Copyright 1999 Sylvain St-Germain
39 * Copyright 1999 Thuy Nguyen
40 * Copyright 2005 Mike McCormack
41 *
42 * This library is free software; you can redistribute it and/or
43 * modify it under the terms of the GNU Lesser General Public
44 * License as published by the Free Software Foundation; either
45 * version 2.1 of the License, or (at your option) any later version.
46 *
47 * This library is distributed in the hope that it will be useful,
48 * but WITHOUT ANY WARRANTY; without even the implied warranty of
49 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
50 * Lesser General Public License for more details.
51 *
52 * You should have received a copy of the GNU Lesser General Public
53 * License along with this library; if not, write to the Free Software
54 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
55 *
56 */
57
58 /*
59 * NOTES
60 * The compound file implementation of IStorage used for create
61 * and manage substorages and streams within a storage object
62 * residing in a compound file object.
63 *
64 * ------------------------------------------------------------
65 */
66
67 #include <cstdlib>
68 #include <cstring>
69 #include <map>
70 #include <sstream>
71 #include <string>
72
73 #include <librevenge/librevenge.h>
74
75 #include "WPSHeader.h"
76 #include "WPSPosition.h"
77
78 #include "WPSOLEParser.h"
79
80 using namespace libwps;
81
82 //////////////////////////////////////////////////
83 // internal structure
84 //////////////////////////////////////////////////
85 namespace WPSOLEParserInternal
86 {
87 /** Internal: internal method to compobj definition */
88 class CompObj
89 {
90 public:
91 //! the constructor
CompObj()92 CompObj()
93 : m_mapCls()
94 {
95 initCLSMap();
96 }
97
98 /** return the CLS Name corresponding to an identifier */
getCLSName(unsigned long v)99 char const *getCLSName(unsigned long v)
100 {
101 if (m_mapCls.find(v) == m_mapCls.end()) return nullptr;
102 return m_mapCls[v];
103 }
104
105 protected:
106 /** map CLSId <-> name */
107 std::map<unsigned long, char const *> m_mapCls;
108
109 /** initialise a map CLSId <-> name */
initCLSMap()110 void initCLSMap()
111 {
112 // source: binfilter/bf_so3/source/inplace/embobj.cxx
113 m_mapCls[0x00000319]="Picture"; // addon Enhanced Metafile ( find in some file)
114
115 m_mapCls[0x00021290]="MSClipArtGalley2";
116 m_mapCls[0x000212F0]="MSWordArt"; // or MSWordArt.2
117 m_mapCls[0x00021302]="MSWorksREVENGEoc"; // addon
118
119 // MS Apps
120 m_mapCls[0x00030000]= "ExcelWorksheet";
121 m_mapCls[0x00030001]= "ExcelChart";
122 m_mapCls[0x00030002]= "ExcelMacrosheet";
123 m_mapCls[0x00030003]= "WordDocument";
124 m_mapCls[0x00030004]= "MSPowerPoint";
125 m_mapCls[0x00030005]= "MSPowerPointSho";
126 m_mapCls[0x00030006]= "MSGraph";
127 m_mapCls[0x00030007]= "MSDraw"; // find also ca003 ?
128 m_mapCls[0x00030008]= "Note-It";
129 m_mapCls[0x00030009]= "WordArt";
130 m_mapCls[0x0003000a]= "PBrush";
131 m_mapCls[0x0003000b]= "Equation"; // "Microsoft Equation Editor"
132 m_mapCls[0x0003000c]= "Package";
133 m_mapCls[0x0003000d]= "SoundRec";
134 m_mapCls[0x0003000e]= "MPlayer";
135 // MS Demos
136 m_mapCls[0x0003000f]= "ServerDemo"; // "OLE 1.0 Server Demo"
137 m_mapCls[0x00030010]= "Srtest"; // "OLE 1.0 Test Demo"
138 m_mapCls[0x00030011]= "SrtInv"; // "OLE 1.0 Inv Demo"
139 m_mapCls[0x00030012]= "OleDemo"; //"OLE 1.0 Demo"
140
141 // Coromandel / Dorai Swamy / 718-793-7963
142 m_mapCls[0x00030013]= "CoromandelIntegra";
143 m_mapCls[0x00030014]= "CoromandelObjServer";
144
145 // 3-d Visions Corp / Peter Hirsch / 310-325-1339
146 m_mapCls[0x00030015]= "StanfordGraphics";
147
148 // Deltapoint / Nigel Hearne / 408-648-4000
149 m_mapCls[0x00030016]= "DGraphCHART";
150 m_mapCls[0x00030017]= "DGraphDATA";
151
152 // Corel / Richard V. Woodend / 613-728-8200 x1153
153 m_mapCls[0x00030018]= "PhotoPaint"; // "Corel PhotoPaint"
154 m_mapCls[0x00030019]= "CShow"; // "Corel Show"
155 m_mapCls[0x0003001a]= "CorelChart";
156 m_mapCls[0x0003001b]= "CDraw"; // "Corel Draw"
157
158 // Inset Systems / Mark Skiba / 203-740-2400
159 m_mapCls[0x0003001c]= "HJWIN1.0"; // "Inset Systems"
160
161 // Mark V Systems / Mark McGraw / 818-995-7671
162 m_mapCls[0x0003001d]= "ObjMakerOLE"; // "MarkV Systems Object Maker"
163
164 // IdentiTech / Mike Gilger / 407-951-9503
165 m_mapCls[0x0003001e]= "FYI"; // "IdentiTech FYI"
166 m_mapCls[0x0003001f]= "FYIView"; // "IdentiTech FYI Viewer"
167
168 // Inventa Corporation / Balaji Varadarajan / 408-987-0220
169 m_mapCls[0x00030020]= "Stickynote";
170
171 // ShapeWare Corp. / Lori Pearce / 206-467-6723
172 m_mapCls[0x00030021]= "ShapewareVISIO10";
173 m_mapCls[0x00030022]= "ImportServer"; // "Spaheware Import Server"
174
175 // test app SrTest
176 m_mapCls[0x00030023]= "SrvrTest"; // "OLE 1.0 Server Test"
177
178 // test app ClTest. Doesn't really work as a server but is in reg db
179 m_mapCls[0x00030025]= "Cltest"; // "OLE 1.0 Client Test"
180
181 // Microsoft ClipArt Gallery Sherry Larsen-Holmes
182 m_mapCls[0x00030026]= "MS_ClipArt_Gallery";
183 // Microsoft Project Cory Reina
184 m_mapCls[0x00030027]= "MSProject";
185
186 // Microsoft Works Chart
187 m_mapCls[0x00030028]= "MSWorksChart";
188
189 // Microsoft Works Spreadsheet
190 m_mapCls[0x00030029]= "MSWorksSpreadsheet";
191
192 // AFX apps - Dean McCrory
193 m_mapCls[0x0003002A]= "MinSvr"; // "AFX Mini Server"
194 m_mapCls[0x0003002B]= "HierarchyList"; // "AFX Hierarchy List"
195 m_mapCls[0x0003002C]= "BibRef"; // "AFX BibRef"
196 m_mapCls[0x0003002D]= "MinSvrMI"; // "AFX Mini Server MI"
197 m_mapCls[0x0003002E]= "TestServ"; // "AFX Test Server"
198
199 // Ami Pro
200 m_mapCls[0x0003002F]= "AmiProDocument";
201
202 // WordPerfect Presentations For Windows
203 m_mapCls[0x00030030]= "WPGraphics";
204 m_mapCls[0x00030031]= "WPCharts";
205
206 // MicroGrafx Charisma
207 m_mapCls[0x00030032]= "Charisma";
208 m_mapCls[0x00030033]= "Charisma_30"; // v 3.0
209 m_mapCls[0x00030034]= "CharPres_30"; // v 3.0 Pres
210 // MicroGrafx Draw
211 m_mapCls[0x00030035]= "Draw"; //"MicroGrafx Draw"
212 // MicroGrafx Designer
213 m_mapCls[0x00030036]= "Designer_40"; // "MicroGrafx Designer 4.0"
214
215 // STAR DIVISION
216 //m_mapCls[0x000424CA]= "StarMath"; // "StarMath 1.0"
217 m_mapCls[0x00043AD2]= "FontWork"; // "Star FontWork"
218 //m_mapCls[0x000456EE]= "StarMath2"; // "StarMath 2.0"
219 }
220 };
221
222 /** Internal: internal method to keep ole definition */
223 struct OleDef
224 {
OleDefWPSOLEParserInternal::OleDef225 OleDef()
226 : m_id(-1)
227 , m_base("")
228 , m_dir("")
229 , m_name("") { }
230 int m_id /**main id*/;
231 std::string m_base/** the base name*/, m_dir/**the directory*/, m_name/**the complete name*/;
232 };
233
234 /** Internal: the state of a WPSOLEParser */
235 struct State
236 {
237 //! constructor
StateWPSOLEParserInternal::State238 State(libwps_tools_win::Font::Type fontType, std::function<int(std::string const &)> const &dirToIdFunc)
239 : m_fontType(fontType)
240 , m_directoryToIdFunction(dirToIdFunc)
241 , m_metaData()
242 , m_unknownOLEs()
243 , m_idToObjectMap()
244 , m_compObjIdName()
245 {
246 }
247 //! the default font type
248 libwps_tools_win::Font::Type m_fontType;
249 //! the function used to convert a directory name in a id
250 std::function<int(std::string const &)> m_directoryToIdFunction;
251 //! the meta data
252 librevenge::RVNGPropertyList m_metaData;
253 //! list of ole which can not be parsed
254 std::vector<std::string> m_unknownOLEs;
255 //! map id to object
256 std::map<int, WPSEmbeddedObject> m_idToObjectMap;
257
258 //! a smart ptr used to stored the list of compobj id->name
259 std::shared_ptr<WPSOLEParserInternal::CompObj> m_compObjIdName;
260 };
261 }
262
263 // constructor/destructor
WPSOLEParser(const std::string & mainName,libwps_tools_win::Font::Type fontType,std::function<int (std::string const &)> const & dirToIdFunc)264 WPSOLEParser::WPSOLEParser(const std::string &mainName, libwps_tools_win::Font::Type fontType,
265 std::function<int(std::string const &)> const &dirToIdFunc)
266 : m_avoidOLE(mainName)
267 , m_state(new WPSOLEParserInternal::State(fontType, dirToIdFunc))
268 {
269 }
270
~WPSOLEParser()271 WPSOLEParser::~WPSOLEParser()
272 {
273 }
274
275
getNotParse() const276 std::vector<std::string> const &WPSOLEParser::getNotParse() const
277 {
278 return m_state->m_unknownOLEs;
279 }
280 //! returns the list of data positions which have been read
getObjectsMap() const281 std::map<int,WPSEmbeddedObject> const &WPSOLEParser::getObjectsMap() const
282 {
283 return m_state->m_idToObjectMap;
284 }
updateMetaData(librevenge::RVNGPropertyList & metaData) const285 void WPSOLEParser::updateMetaData(librevenge::RVNGPropertyList &metaData) const
286 {
287 librevenge::RVNGPropertyList::Iter i(m_state->m_metaData);
288 for (i.rewind(); i.next();)
289 {
290 if (!metaData[i.key()])
291 metaData.insert(i.key(),i()->clone());
292 }
293 }
294 // parsing
getIdFromDirectory(std::string const & dirName)295 int WPSOLEParser::getIdFromDirectory(std::string const &dirName)
296 {
297 // try to retrieve the identificator stored in the directory
298 // MatOST/MatadorObject1/ -> 1, -1
299 // Object 2/ -> 2, -1
300 auto dir=dirName+'/';
301 auto pos = dir.find('/');
302 while (pos != std::string::npos)
303 {
304 if (pos >= 1 && dir[pos-1] >= '0' && dir[pos-1] <= '9')
305 {
306 auto idP = pos-1;
307 while (idP >=1 && dir[idP-1] >= '0' && dir[idP-1] <= '9')
308 idP--;
309 int val = std::atoi(dir.substr(idP, idP-pos).c_str());
310 return val;
311 }
312 pos = dir.find('/', pos+1);
313 }
314 WPS_DEBUG_MSG(("WPSOLEParser::getIdFromDirectory: can not find id for %s\n", dirName.c_str()));
315 return -1;
316 }
317
parse(RVNGInputStreamPtr file)318 bool WPSOLEParser::parse(RVNGInputStreamPtr file)
319 {
320 if (!m_state->m_compObjIdName)
321 m_state->m_compObjIdName.reset(new WPSOLEParserInternal::CompObj);
322
323 m_state->m_unknownOLEs.resize(0);
324 m_state->m_idToObjectMap.clear();
325
326 if (!file || !file->isStructured()) return false;
327
328 //
329 // we begin by grouping the Ole by their potential main id
330 //
331 std::multimap<int, WPSOLEParserInternal::OleDef> listsById;
332 std::vector<int> listIds;
333 unsigned numSubStreams = file->subStreamCount();
334 for (unsigned i = 0; i < numSubStreams; ++i)
335 {
336 char const *nm=file->subStreamName(i);
337 if (!nm) continue;
338 std::string name(nm);
339 if (name.empty() || name[name.length()-1]=='/') continue;
340 // separated the directory and the name
341 // MatOST/MatadorObject1/Ole10Native
342 // -> dir="MatOST/MatadorObject1", base="Ole10Native"
343 auto pos = name.find_last_of('/');
344
345 std::string dir, base;
346 if (pos == std::string::npos) base = name;
347 else if (pos == 0) base = name.substr(1);
348 else
349 {
350 dir = name.substr(0,pos);
351 base = name.substr(pos+1);
352 }
353 if (dir == "" && base == m_avoidOLE) continue;
354
355 #define PRINT_OLE_NAME
356 #ifdef PRINT_OLE_NAME
357 WPS_DEBUG_MSG(("WPSOLEParser::parse: find OLEName=%s\n", name.c_str()));
358 #endif
359 WPSOLEParserInternal::OleDef data;
360 data.m_name = name;
361 data.m_base = base;
362 data.m_dir = dir;
363 data.m_id = m_state->m_directoryToIdFunction(dir);
364 if (listsById.find(data.m_id) == listsById.end())
365 listIds.push_back(data.m_id);
366 listsById.insert(std::multimap<int, WPSOLEParserInternal::OleDef>::value_type(data.m_id, data));
367 }
368
369 for (auto id : listIds)
370 {
371 auto pos = listsById.lower_bound(id);
372
373 // try to find a representation for each id
374 // FIXME: maybe we must also find some for each subid
375 WPSEmbeddedObject pict;
376
377 while (pos != listsById.end())
378 {
379 auto const &dOle = pos->second;
380 if (pos->first != id) break;
381 ++pos;
382
383 RVNGInputStreamPtr ole(file->getSubStreamByName(dOle.m_name.c_str()));
384 if (!ole)
385 {
386 WPS_DEBUG_MSG(("WPSOLEParser: error: can not find OLE part: \"%s\"\n", dOle.m_name.c_str()));
387 continue;
388 }
389
390 libwps::DebugFile asciiFile(ole);
391 asciiFile.open(dOle.m_name);
392 bool ok = true;
393
394 try
395 {
396 librevenge::RVNGPropertyList pList;
397 if (readMM(ole, dOle.m_base, asciiFile));
398 else if (readSummaryInformation(ole, dOle.m_base, dOle.m_dir.empty() ? m_state->m_metaData : pList, asciiFile));
399 else if (readObjInfo(ole, dOle.m_base, asciiFile));
400 else if (readOle(ole, dOle.m_base, asciiFile));
401 else if (readMN0AndCheckWKS(ole, dOle.m_base, pict, asciiFile));
402 else if (isOlePres(ole, dOle.m_base) && readOlePres(ole, pict, asciiFile));
403 else if (isOle10Native(ole, dOle.m_base) && readOle10Native(ole, pict, asciiFile));
404 else if (readCompObj(ole, dOle.m_base, asciiFile));
405 else if (readContents(ole, dOle.m_base, pict, asciiFile));
406 else if (readCONTENTS(ole, dOle.m_base, pict, asciiFile));
407 else
408 ok = false;
409 }
410 catch (...)
411 {
412 ok = false;
413 }
414 if (!ok)
415 m_state->m_unknownOLEs.push_back(dOle.m_name);
416 asciiFile.reset();
417 }
418
419 if (!pict.isEmpty())
420 m_state->m_idToObjectMap[id]=pict;
421 }
422
423 return true;
424 }
425
426
427
428 ////////////////////////////////////////
429 //
430 // small structure
431 //
432 ////////////////////////////////////////
readOle(RVNGInputStreamPtr & ip,std::string const & oleName,libwps::DebugFile & ascii)433 bool WPSOLEParser::readOle(RVNGInputStreamPtr &ip, std::string const &oleName,
434 libwps::DebugFile &ascii)
435 {
436 if (!ip.get()) return false;
437
438 if (strcmp("Ole",oleName.c_str()) != 0) return false;
439
440 if (ip->seek(20, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 20) return false;
441
442 ip->seek(0, librevenge::RVNG_SEEK_SET);
443
444 int val[20];
445 for (int &i : val)
446 {
447 i = libwps::read8(ip);
448 if (i < -10 || i > 10) return false;
449 }
450
451 libwps::DebugStream f;
452 f << "@@Ole: ";
453 // always 1, 0, 2, 0*
454 for (int i = 0; i < 20; i++)
455 if (val[i]) f << "f" << i << "=" << val[i] << ",";
456 ascii.addPos(0);
457 ascii.addNote(f.str().c_str());
458
459 if (!ip->isEnd())
460 {
461 ascii.addPos(20);
462 ascii.addNote("@@Ole:###");
463 }
464
465 return true;
466 }
467
readObjInfo(RVNGInputStreamPtr & input,std::string const & oleName,libwps::DebugFile & ascii)468 bool WPSOLEParser::readObjInfo(RVNGInputStreamPtr &input, std::string const &oleName,
469 libwps::DebugFile &ascii)
470 {
471 if (strcmp(oleName.c_str(),"ObjInfo") != 0) return false;
472
473 input->seek(14, librevenge::RVNG_SEEK_SET);
474 if (input->tell() != 6 || !input->isEnd()) return false;
475
476 input->seek(0, librevenge::RVNG_SEEK_SET);
477 libwps::DebugStream f;
478 f << "@@ObjInfo:";
479
480 // always 0, 3, 4 ?
481 for (int i = 0; i < 3; i++) f << libwps::read16(input) << ",";
482
483 ascii.addPos(0);
484 ascii.addNote(f.str().c_str());
485
486 return true;
487 }
488
readMM(RVNGInputStreamPtr & input,std::string const & oleName,libwps::DebugFile & ascii)489 bool WPSOLEParser::readMM(RVNGInputStreamPtr &input, std::string const &oleName,
490 libwps::DebugFile &ascii)
491 {
492 if (strcmp(oleName.c_str(),"MM") != 0) return false;
493
494 input->seek(14, librevenge::RVNG_SEEK_SET);
495 if (input->tell() != 14 || !input->isEnd()) return false;
496
497 input->seek(0, librevenge::RVNG_SEEK_SET);
498 int entete = libwps::readU16(input);
499 if (entete != 0x444e)
500 {
501 if (entete == 0x4e44)
502 {
503 WPS_DEBUG_MSG(("WPSOLEParser::readMM: ERROR: endian mode probably bad, potentially bad PC/Mac mode detection.\n"));
504 }
505 return false;
506 }
507 libwps::DebugStream f;
508 f << "@@MM:";
509
510 int val[6];
511 for (int &i : val)
512 i = libwps::read16(input);
513
514 switch (val[5])
515 {
516 case 0:
517 f << "conversion,";
518 break;
519 case 2:
520 f << "Works3,";
521 break;
522 case 4:
523 f << "Works4,";
524 break;
525 default:
526 f << "version=unknown,";
527 break;
528 }
529
530 // 1, 0, 0, 0, 0 : Mac file
531 // 0, 1, 0, [0,1,2,4,6], 0 : Pc file
532 for (int i = 0; i < 5; i++)
533 {
534 if ((i%2)!=1 && val[i]) f << "###";
535 f << val[i] << ",";
536 }
537
538 ascii.addPos(0);
539 ascii.addNote(f.str().c_str());
540
541 return true;
542 }
543
544
readCompObj(RVNGInputStreamPtr & ip,std::string const & oleName,libwps::DebugFile & ascii)545 bool WPSOLEParser::readCompObj(RVNGInputStreamPtr &ip, std::string const &oleName, libwps::DebugFile &ascii)
546 {
547 if (strncmp(oleName.c_str(), "CompObj", 7) != 0) return false;
548
549 // check minimal size
550 const int minSize = 12 + 14+ 16 + 12; // size of header, clsid, footer, 3 string size
551 if (ip->seek(minSize,librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != minSize) return false;
552
553 libwps::DebugStream f;
554 f << "@@CompObj(Header): ";
555 ip->seek(0,librevenge::RVNG_SEEK_SET);
556
557 for (int i = 0; i < 6; i++)
558 {
559 auto val = long(libwps::readU16(ip));
560 f << val << ", ";
561 }
562
563 ascii.addPos(0);
564 ascii.addNote(f.str().c_str());
565
566 ascii.addPos(12);
567 // the clsid
568 unsigned long clsData[4]; // ushort n1, n2, n3, b8, ... b15
569 for (unsigned long &i : clsData)
570 i = libwps::readU32(ip);
571
572 f.str("");
573 f << "@@CompObj(CLSID):";
574 if (clsData[1] == 0 && clsData[2] == 0xC0 && clsData[3] == 0x46000000L)
575 {
576 // normally, a referenced object
577 char const *clsName = m_state->m_compObjIdName->getCLSName(clsData[0]);
578 if (clsName)
579 f << "'" << clsName << "'";
580 else
581 {
582 WPS_DEBUG_MSG(("WPSOLEParser::readCompObj: unknown clsid=%lx\n", clsData[0]));
583 f << "unknCLSID='" << std::hex << clsData[0] << "'";
584 }
585 }
586 else
587 {
588 /* I found:
589 c1dbcd28e20ace11a29a00aa004a1a72 for MSWorks.Table
590 c2dbcd28e20ace11a29a00aa004a1a72 for Microsoft Works/MSWorksREVENGEoc
591 a3bcb394c2bd1b10a18306357c795b37 for Microsoft Drawing 1.01/MSDraw.1.01
592 b25aa40e0a9ed111a40700c04fb932ba for Quill96 Story Group Class ( basic MSWorks doc?)
593 796827ed8bc9d111a75f00c04fb9667b for MSWorks4Sheet
594 */
595 f << "data0=(" << std::hex << clsData[0] << "," << clsData[1] << "), "
596 << "data1=(" << clsData[2] << "," << clsData[3] << ")";
597 }
598 ascii.addNote(f.str().c_str());
599 f << std::dec;
600 for (int ch = 0; ch < 3; ch++)
601 {
602 long actPos = ip->tell();
603 long sz = libwps::read32(ip);
604 bool waitNumber = sz == -1;
605 if (waitNumber) sz = 4;
606 if (sz < 0 || ip->seek(actPos+4+sz,librevenge::RVNG_SEEK_SET) != 0 ||
607 ip->tell() != actPos+4+sz) return false;
608 ip->seek(actPos+4,librevenge::RVNG_SEEK_SET);
609
610 std::string st;
611 if (waitNumber)
612 {
613 f.str("");
614 f << libwps::read32(ip) << "[val*]";
615 st = f.str();
616 }
617 else
618 {
619 for (int i = 0; i < sz; i++)
620 st += char(libwps::readU8(ip));
621 }
622
623 f.str("");
624 f << "@@CompObj:";
625 switch (ch)
626 {
627 case 0:
628 f << "UserType=";
629 break;
630 case 1:
631 f << "ClipName=";
632 break;
633 case 2:
634 f << "ProgIdName=";
635 break;
636 default:
637 break;
638 }
639 f << st;
640 ascii.addPos(actPos);
641 ascii.addNote(f.str().c_str());
642 }
643
644 if (ip->isEnd()) return true;
645
646 long actPos = ip->tell();
647 long nbElt = 4;
648 if (ip->seek(actPos+16,librevenge::RVNG_SEEK_SET) != 0 ||
649 ip->tell() != actPos+16)
650 {
651 if ((ip->tell()-actPos)%4) return false;
652 nbElt = (ip->tell()-actPos)/4;
653 }
654
655 f.str("");
656 f << "@@CompObj(Footer): " << std::hex;
657 ip->seek(actPos,librevenge::RVNG_SEEK_SET);
658 for (int i = 0; i < nbElt; i++)
659 f << libwps::readU32(ip) << ",";
660 ascii.addPos(actPos);
661 ascii.addNote(f.str().c_str());
662
663 ascii.addPos(ip->tell());
664
665 return true;
666 }
667
668 //////////////////////////////////////////////////
669 //
670 // OlePres001 seems to contained standart picture file and size
671 // extract the picture if it is possible
672 //
673 //////////////////////////////////////////////////
674
isOlePres(RVNGInputStreamPtr & ip,std::string const & oleName)675 bool WPSOLEParser::isOlePres(RVNGInputStreamPtr &ip, std::string const &oleName)
676 {
677 if (!ip.get()) return false;
678
679 if (strncmp("OlePres",oleName.c_str(),7) != 0) return false;
680
681 if (ip->seek(40, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 40) return false;
682
683 ip->seek(0, librevenge::RVNG_SEEK_SET);
684 for (int i= 0; i < 2; i++)
685 {
686 long val = libwps::read32(ip);
687 if (val < -10 || val > 10) return false;
688 }
689
690 long actPos = ip->tell();
691 int hSize = libwps::read32(ip);
692 if (hSize < 4) return false;
693 if (ip->seek(actPos+hSize+28, librevenge::RVNG_SEEK_SET) != 0
694 || ip->tell() != actPos+hSize+28)
695 return false;
696
697 ip->seek(actPos+hSize, librevenge::RVNG_SEEK_SET);
698 for (int i= 3; i < 7; i++)
699 {
700 long val = libwps::read32(ip);
701 if (val < -10 || val > 10)
702 {
703 if (i != 5 || val > 256) return false;
704 }
705 }
706
707 ip->seek(8, librevenge::RVNG_SEEK_CUR);
708 long size = libwps::read32(ip);
709
710 if (size <= 0) return ip->isEnd();
711
712 actPos = ip->tell();
713 if (ip->seek(actPos+size, librevenge::RVNG_SEEK_SET) != 0
714 || ip->tell() != actPos+size)
715 return false;
716
717 return true;
718 }
719
readOlePres(RVNGInputStreamPtr & ip,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)720 bool WPSOLEParser::readOlePres(RVNGInputStreamPtr &ip, WPSEmbeddedObject &obj, libwps::DebugFile &ascii)
721 {
722 if (!isOlePres(ip, "OlePres")) return false;
723
724 libwps::DebugStream f;
725 f << "@@OlePress(header): ";
726 ip->seek(0,librevenge::RVNG_SEEK_SET);
727 for (int i = 0; i < 2; i++)
728 {
729 long val = libwps::read32(ip);
730 f << val << ", ";
731 }
732
733 long actPos = ip->tell();
734 int hSize = libwps::read32(ip);
735 if (hSize < 4) return false;
736 f << "hSize = " << hSize;
737 ascii.addPos(0);
738 ascii.addNote(f.str().c_str());
739
740 long endHPos = actPos+hSize;
741 if (hSize > 4) // CHECKME
742 {
743 bool ok = true;
744 f.str("");
745 f << "@@OlePress(headerA): ";
746 if (hSize < 14) ok = false;
747 else
748 {
749 // 12,21,32|48,0
750 for (int i = 0; i < 4; i++) f << libwps::read16(ip) << ",";
751 // 3 name of creator
752 for (int ch=0; ch < 3; ch++)
753 {
754 std::string str;
755 bool find = false;
756 while (ip->tell() < endHPos)
757 {
758 unsigned char c = libwps::readU8(ip);
759 if (c == 0)
760 {
761 find = true;
762 break;
763 }
764 str += char(c);
765 }
766 if (!find)
767 {
768 ok = false;
769 break;
770 }
771 f << ", name" << ch << "=" << str;
772 }
773 if (ok) ok = ip->tell() == endHPos;
774 }
775 // FIXME, normally they remain only a few bits (size unknown)
776 if (!ok) f << "###";
777 ascii.addPos(actPos);
778 ascii.addNote(f.str().c_str());
779 }
780 if (ip->seek(endHPos+28, librevenge::RVNG_SEEK_SET) != 0
781 || ip->tell() != endHPos+28)
782 return false;
783
784 ip->seek(endHPos, librevenge::RVNG_SEEK_SET);
785
786 actPos = ip->tell();
787 f.str("");
788 f << "@@OlePress(headerB): ";
789 for (int i = 3; i < 7; i++)
790 {
791 long val = libwps::read32(ip);
792 f << val << ", ";
793 }
794 // dim in TWIP ?
795 auto extendX = long(libwps::readU32(ip));
796 auto extendY = long(libwps::readU32(ip));
797 if (extendX > 0 && extendY > 0 && obj.m_size!=Vec2f())
798 obj.m_size=Vec2f(float(extendX)/1440.f, float(extendY)/1440.f);
799 long fSize = libwps::read32(ip);
800 f << "extendX="<< extendX << ", extendY=" << extendY << ", fSize=" << fSize;
801
802 ascii.addPos(actPos);
803 ascii.addNote(f.str().c_str());
804
805 if (fSize == 0) return ip->isEnd();
806
807 librevenge::RVNGBinaryData data;
808 if (!libwps::readData(ip, static_cast<unsigned long>(fSize), data)) return false;
809 obj.add(data);
810 #ifdef DEBUG_WITH_FILES
811 std::stringstream s;
812 static int num=0;
813 s << "OlePress" << num++;
814 libwps::Debug::dumpFile(data, s.str().c_str());
815 #endif
816 if (!ip->isEnd())
817 {
818 ascii.addPos(ip->tell());
819 ascii.addNote("@@OlePress###");
820 }
821
822 ascii.skipZone(36+hSize,36+hSize+fSize-1);
823 return true;
824 }
825
826 //////////////////////////////////////////////////
827 //
828 // Ole10Native: basic Windows picture, with no size
829 // - in general used to store a bitmap
830 //
831 //////////////////////////////////////////////////
832
isOle10Native(RVNGInputStreamPtr & ip,std::string const & oleName)833 bool WPSOLEParser::isOle10Native(RVNGInputStreamPtr &ip, std::string const &oleName)
834 {
835 if (strncmp("Ole10Native",oleName.c_str(),11) != 0) return false;
836
837 if (ip->seek(4, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4) return false;
838
839 ip->seek(0, librevenge::RVNG_SEEK_SET);
840 long size = libwps::read32(ip);
841
842 if (size <= 0) return false;
843 if (ip->seek(4+size, librevenge::RVNG_SEEK_SET) != 0 || ip->tell() != 4+size)
844 return false;
845
846 return true;
847 }
848
readOle10Native(RVNGInputStreamPtr & ip,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)849 bool WPSOLEParser::readOle10Native(RVNGInputStreamPtr &ip, WPSEmbeddedObject &obj, libwps::DebugFile &ascii)
850 {
851 if (!isOle10Native(ip, "Ole10Native")) return false;
852
853 libwps::DebugStream f;
854 f << "@@Ole10Native(Header): ";
855 ip->seek(0,librevenge::RVNG_SEEK_SET);
856 long fSize = libwps::read32(ip);
857 f << "fSize=" << fSize;
858
859 ascii.addPos(0);
860 ascii.addNote(f.str().c_str());
861
862 librevenge::RVNGBinaryData data;
863 if (!libwps::readData(ip, static_cast<unsigned long>(fSize), data)) return false;
864 obj.add(data);
865 #ifdef DEBUG_WITH_FILES
866 std::stringstream s;
867 static int num=0;
868 s << "Ole10_" << num++ << ".bmp";
869 libwps::Debug::dumpFile(data, s.str().c_str());
870 #endif
871
872 if (!ip->isEnd())
873 {
874 ascii.addPos(ip->tell());
875 ascii.addNote("@@Ole10Native###");
876 }
877 ascii.skipZone(4,4+fSize-1);
878 return true;
879 }
880
881 ////////////////////////////////////////////////////////////////
882 //
883 // In general a picture : a PNG, an JPEG, a basic metafile,
884 // find also a MSDraw.1.01 picture (with first bytes 0x78563412="xV4") or WordArt,
885 // ( with first bytes "WordArt" ) which are not sucefull read
886 // (can probably contain a list of data, but do not know how to
887 // detect that)
888 //
889 // To check: does this is related to MSO_BLIPTYPE ?
890 // or OO/filter/sources/msfilter/msdffimp.cxx ?
891 //
892 ////////////////////////////////////////////////////////////////
readContents(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)893 bool WPSOLEParser::readContents(RVNGInputStreamPtr &input,
894 std::string const &oleName,
895 WPSEmbeddedObject &obj,
896 libwps::DebugFile &ascii)
897 {
898 if (strcmp(oleName.c_str(),"Contents") != 0) return false;
899
900 libwps::DebugStream f;
901 input->seek(0, librevenge::RVNG_SEEK_SET);
902 f << "@@Contents:";
903
904 bool ok = true;
905 // bdbox 0 : size in the file ?
906 int dim[2];
907 dim[0] = libwps::read32(input);
908 if (dim[0]==0x12345678)
909 {
910 WPS_DEBUG_MSG(("WPSOLEParser: warning: find a MSDraw picture, ignored\n"));
911 ascii.addPos(0);
912 ascii.addNote("Entries(MSDraw):");
913 return false;
914 }
915 dim[1] = libwps::read32(input);
916 f << "bdbox0=(" << dim[0] << "," << dim[1]<<"),";
917 for (int i = 0; i < 3; i++)
918 {
919 /* 0,{10|21|75|101|116}x2 */
920 auto val = long(libwps::readU32(input));
921 if (val < 1000)
922 f << val << ",";
923 else
924 f << std::hex << "0x" << val << std::dec << ",";
925 if (val > 0x10000) ok=false;
926 }
927 // new bdbox : size of the picture ?
928 int naturalSize[2];
929 naturalSize[0] = libwps::read32(input);
930 naturalSize[1] = libwps::read32(input);
931 f << std::dec << "bdbox1=(" << naturalSize[0] << "," << naturalSize[1]<<"),";
932 f << "unk=" << libwps::readU32(input) << ","; // 24 or 32
933 if (input->isEnd())
934 {
935 WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents header length\n"));
936 return false;
937 }
938 long actPos = input->tell();
939 auto size = long(libwps::readU32(input));
940 if (size <= 0) ok = false;
941 if (ok)
942 {
943 input->seek(actPos+size+4, librevenge::RVNG_SEEK_SET);
944 if (input->tell() != actPos+size+4 || !input->isEnd())
945 {
946 ok = false;
947 WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents unexpected file size=%ld\n",
948 size));
949 }
950 }
951
952 if (!ok) f << "###";
953 f << "dataSize=" << size;
954
955 ascii.addPos(0);
956 ascii.addNote(f.str().c_str());
957
958 input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
959
960 if (ok)
961 {
962 librevenge::RVNGBinaryData data;
963 if (libwps::readData(input,static_cast<unsigned long>(size), data))
964 {
965 obj.add(data);
966 ascii.skipZone(actPos+4, actPos+size+4-1);
967 #ifdef DEBUG_WITH_FILES
968 std::stringstream s;
969 static int fileId=0;
970 s << oleName << ++fileId << "cntents.pict";
971 libwps::Debug::dumpFile(data, s.str().c_str());
972 #endif
973 }
974 else
975 {
976 input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
977 ok = false;
978 }
979 }
980 if (ok)
981 {
982 if (dim[0] > 0 && dim[0] < 3000 && dim[1] > 0 && dim[1] < 3000 && obj.m_size!=Vec2f())
983 obj.m_size=Vec2f(float(dim[0])/72.f,float(dim[1])/72.f);
984 else
985 {
986 WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents odd size : %d %d\n", dim[0], dim[1]));
987 }
988 if (naturalSize[0] > 0 && naturalSize[0] < 5000 &&
989 naturalSize[1] > 0 && naturalSize[1] < 5000 && obj.m_size!=Vec2f())
990 obj.m_size=Vec2f(float(dim[0])/72.f,float(dim[1])/72.f);
991 else
992 {
993 WPS_DEBUG_MSG(("WPSOLEParser: warning: Contents odd naturalsize : %d %d\n", naturalSize[0], naturalSize[1]));
994 }
995 }
996 if (!input->isEnd())
997 {
998 ascii.addPos(actPos);
999 ascii.addNote("@@Contents:###");
1000 }
1001
1002 if (!ok)
1003 {
1004 WPS_DEBUG_MSG(("WPSOLEParser: warning: read ole Contents: failed\n"));
1005 }
1006 return ok;
1007 }
1008
1009 ////////////////////////////////////////////////////////////////
1010 //
1011 // Another different type of contents (this time in majuscule)
1012 // we seem to contain the header of a EMF and then the EMF file
1013 //
1014 ////////////////////////////////////////////////////////////////
readCONTENTS(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile & ascii)1015 bool WPSOLEParser::readCONTENTS(RVNGInputStreamPtr &input,
1016 std::string const &oleName,
1017 WPSEmbeddedObject &obj,
1018 libwps::DebugFile &ascii)
1019 {
1020 if (strcmp(oleName.c_str(),"CONTENTS") != 0) return false;
1021
1022 libwps::DebugStream f;
1023
1024 input->seek(0, librevenge::RVNG_SEEK_SET);
1025 f << "@@CONTENTS:";
1026
1027 auto hSize = long(libwps::readU32(input));
1028 if (input->isEnd()) return false;
1029 f << "hSize=" << std::hex << hSize << std::dec;
1030
1031 if (hSize <= 52 || input->seek(hSize+8,librevenge::RVNG_SEEK_SET) != 0
1032 || input->tell() != hSize+8)
1033 {
1034 WPS_DEBUG_MSG(("WPSOLEParser: warning: CONTENTS headerSize=%ld\n",
1035 hSize));
1036 return false;
1037 }
1038
1039 // minimal checking of the "copied" header
1040 input->seek(4, librevenge::RVNG_SEEK_SET);
1041 auto type = long(libwps::readU32(input));
1042 if (type < 0 || type > 4) return false;
1043 auto newSize = long(libwps::readU32(input));
1044
1045 f << ", type=" << type;
1046 if (newSize < 8) return false;
1047
1048 if (newSize != hSize) // can sometimes happen, pb after a conversion ?
1049 f << ", ###newSize=" << std::hex << newSize << std::dec;
1050
1051 // checkme: two bdbox, in document then data : units ?
1052 // Maybe first in POINT, second in TWIP ?
1053 for (int st = 0; st < 2 ; st++)
1054 {
1055 int dim[4];
1056 for (int &i : dim) i = libwps::read32(input);
1057
1058 bool ok = dim[0] >= 0 && dim[2] > dim[0] && dim[1] >= 0 && dim[3] > dim[2];
1059 if (ok && st==0 && obj.m_size==Vec2f())
1060 obj.m_size=Vec2f(float(dim[2]-dim[0])/72.f, float(dim[3]-dim[1])/72.f);
1061 if (st==0) f << ", bdbox(Text)";
1062 else f << ", bdbox(Data)";
1063 if (!ok) f << "###";
1064 f << "=(" << dim[0] << "x" << dim[1] << "<->" << dim[2] << "x" << dim[3] << ")";
1065 }
1066 char dataType[5];
1067 for (int i = 0; i < 4; i++) dataType[i] = char(libwps::readU8(input));
1068 dataType[4] = '\0';
1069 f << ",typ=\""<<dataType<<"\""; // always " EMF" ?
1070
1071 for (int i = 0; i < 2; i++) // always id0=0, id1=1 ?
1072 {
1073 int val = libwps::readU16(input);
1074 if (val) f << ",id"<< i << "=" << val;
1075 }
1076 auto dataLength = long(libwps::readU32(input));
1077 f << ",length=" << dataLength+hSize;
1078
1079 ascii.addPos(0);
1080 ascii.addNote(f.str().c_str());
1081
1082 ascii.addPos(input->tell());
1083 f.str("");
1084 f << "@@CONTENTS(2)";
1085 for (int i = 0; i < 12 && 4*i+52 < hSize; i++)
1086 {
1087 // f0=7,f1=1,f5=500,f6=320,f7=1c4,f8=11a
1088 // or f0=a,f1=1,f2=2,f3=6c,f5=480,f6=360,f7=140,f8=f0
1089 // or f0=61,f1=1,f2=2,f3=58,f5=280,f6=1e0,f7=a9,f8=7f
1090 // f3=some header sub size ? f5/f6 and f7/f8 two other bdbox ?
1091 auto val = long(libwps::readU32(input));
1092 if (val) f << std::hex << ",f" << i << "=" << val;
1093 }
1094 for (int i = 0; 2*i+100 < hSize; i++)
1095 {
1096 // g0=e3e3,g1=6,g2=4e6e,g3=4
1097 // g0=e200,g1=4,g2=a980,g3=3,g4=4c,g5=50
1098 // ---
1099 long val = libwps::readU16(input);
1100 if (val) f << std::hex << ",g" << i << "=" << val;
1101 }
1102 ascii.addNote(f.str().c_str());
1103
1104 if (dataLength <= 0 || input->seek(hSize+4+dataLength,librevenge::RVNG_SEEK_SET) != 0
1105 || input->tell() != hSize+4+dataLength || !input->isEnd())
1106 {
1107 WPS_DEBUG_MSG(("WPSOLEParser: warning: CONTENTS unexpected file length=%ld\n",
1108 dataLength));
1109 return false;
1110 }
1111
1112 input->seek(4+hSize, librevenge::RVNG_SEEK_SET);
1113 librevenge::RVNGBinaryData data;
1114 if (!libwps::readDataToEnd(input, data)) return false;
1115 obj.add(data);
1116 #ifdef DEBUG_WITH_FILES
1117 std::stringstream s;
1118 static int fileId=0;
1119 s << oleName << ++fileId << "Contents.pict";
1120 libwps::Debug::dumpFile(data, s.str().c_str());
1121 #endif
1122
1123 ascii.skipZone(hSize+4, input->tell()-1);
1124 return true;
1125 }
1126
1127 ////////////////////////////////////////////////////////////////
1128 //
1129 // the MN0 subdirectory
1130 //
1131 ////////////////////////////////////////////////////////////////
readMN0AndCheckWKS(RVNGInputStreamPtr & input,std::string const & oleName,WPSEmbeddedObject & obj,libwps::DebugFile &)1132 bool WPSOLEParser::readMN0AndCheckWKS(RVNGInputStreamPtr &input, std::string const &oleName,
1133 WPSEmbeddedObject &obj, libwps::DebugFile &/*ascii*/)
1134 {
1135 if (strcmp(oleName.c_str(),"MN0") != 0) return false;
1136 std::unique_ptr<WPSHeader> header(WPSHeader::constructHeader(input));
1137 if (!header) return false;
1138 bool ok=header->getKind()==WPS_SPREADSHEET;
1139 if (!ok) return false;
1140
1141 input->seek(0, librevenge::RVNG_SEEK_SET);
1142 librevenge::RVNGBinaryData data;
1143 if (!libwps::readDataToEnd(input, data))
1144 return false;
1145 obj.add(data,"image/wks-ods");
1146 #ifdef DEBUG_WITH_FILES
1147 std::stringstream s;
1148 static int fileId=0;
1149 s << oleName << ++fileId << ".wks";
1150 libwps::Debug::dumpFile(data, s.str().c_str());
1151 #endif
1152 return true;
1153 }
1154
1155 ////////////////////////////////////////////////////////////
1156 //
1157 // Summary function
1158 //
1159 ////////////////////////////////////////////////////////////
readSummaryInformation(RVNGInputStreamPtr input,std::string const & oleName,librevenge::RVNGPropertyList & pList,libwps::DebugFile & ascii) const1160 bool WPSOLEParser::readSummaryInformation(RVNGInputStreamPtr input, std::string const &oleName,
1161 librevenge::RVNGPropertyList &pList, libwps::DebugFile &ascii) const
1162 {
1163 if (oleName!="SummaryInformation") return false;
1164 input->seek(0, librevenge::RVNG_SEEK_END);
1165 long endPos=input->tell();
1166 input->seek(0, librevenge::RVNG_SEEK_SET);
1167 libwps::DebugStream f;
1168 f << "Entries(SumInfo):";
1169 auto val=int(libwps::readU16(input));
1170 if (endPos<48 || val!=0xfffe)
1171 {
1172 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: header seems bad\n"));
1173 f << "###";
1174 ascii.addPos(0);
1175 ascii.addNote(f.str().c_str());
1176 return true;
1177 }
1178 for (int i=0; i<11; ++i) // f1=1, f2=0-2
1179 {
1180 val=int(libwps::readU16(input));
1181 if (val) f << "f" << i << "=" << val << ",";
1182 }
1183 unsigned long lVal=libwps::readU32(input);
1184 if (lVal==0 || lVal>15) // find 1 or 2 sections, unsure about the maximum numbers
1185 {
1186 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: summary info is bad\n"));
1187 f << "###sumInfo=" << std::hex << lVal << std::dec << ",";
1188 ascii.addPos(0);
1189 ascii.addNote(f.str().c_str());
1190 return true;
1191 }
1192 auto numSection=int(lVal);
1193 if (numSection!=1)
1194 f << "num[section]=" << numSection << ",";
1195 for (int i=0; i<4; ++i)
1196 {
1197 val=int(libwps::readU32(input));
1198 static int const expected[]= {int(0xf29f85e0),0x10684ff9,0x891ab,int(0xd9b3272b)};
1199 if (val==expected[i]) continue;
1200 f << "#fmid" << i << "=" << std::hex << val << std::dec << ",";
1201 static bool first=true;
1202 if (first)
1203 {
1204 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: fmid is bad\n"));
1205 first=false;
1206 }
1207 }
1208 auto decal=int(libwps::readU32(input));
1209 if (decal<0x30 || endPos<decal)
1210 {
1211 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: decal is bad\n"));
1212 f << "decal=" << val << ",";
1213 ascii.addPos(0);
1214 ascii.addNote(f.str().c_str());
1215 return true;
1216
1217 }
1218 ascii.addPos(0);
1219 ascii.addNote(f.str().c_str());
1220 if (decal!=0x30)
1221 {
1222 ascii.addPos(0x30);
1223 ascii.addNote("_");
1224 input->seek(decal, librevenge::RVNG_SEEK_SET);
1225 }
1226
1227 for (int sect=0; sect<numSection; ++sect)
1228 {
1229 long pos=input->tell();
1230 f.str("");
1231 f << "SumInfo-A:";
1232 auto pSectSize=long(libwps::readU32(input));
1233 long endSect=pos+pSectSize;
1234 auto N=int(libwps::readU32(input));
1235 f << "N=" << N << ",";
1236 if (pSectSize<0 || endPos-pos<pSectSize || (pSectSize-8)/8<N)
1237 {
1238 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: psetstruct is bad\n"));
1239 f << "###";
1240 ascii.addPos(pos);
1241 ascii.addNote(f.str().c_str());
1242 return true;
1243 }
1244 f << "[";
1245 std::map<long,int> posToTypeMap;
1246 for (int i=0; i<N; ++i)
1247 {
1248 auto type=int(libwps::readU32(input));
1249 auto depl=int(libwps::readU32(input));
1250 if (depl<=0) continue;
1251 f << std::hex << depl << std::dec << ":" << type << ",";
1252 if ((depl-8)/8<N || depl>pSectSize-4 || posToTypeMap.find(pos+depl)!=posToTypeMap.end())
1253 {
1254 f << "###";
1255 continue;
1256 }
1257 posToTypeMap[pos+depl]=type;
1258 }
1259 f << "],";
1260 ascii.addPos(pos);
1261 ascii.addNote(f.str().c_str());
1262
1263 for (auto it=posToTypeMap.begin(); it!=posToTypeMap.end(); ++it)
1264 {
1265 pos=it->first;
1266 auto nextIt=it;
1267 if (++nextIt!=posToTypeMap.end()) endPos=nextIt->first;
1268 input->seek(pos, librevenge::RVNG_SEEK_SET);
1269 f.str("");
1270 f << "SumInfo-B" << it->second << ":";
1271 auto type=int(libwps::readU32(input));
1272 if (sect==0 && it->second==1 && type==2)
1273 {
1274 long value=-1;
1275 if (readSummaryPropertyLong(input,endPos,type,value,f) && value>=0 && value<10000) // 10000 is mac
1276 f << "encoding,"; // USEME: never seems actually
1277 }
1278 else if (sect==0 && type==0x1e && ((it->second>=2 && it->second<=6) || it->second==8))
1279 {
1280 librevenge::RVNGString text;
1281 if (readSummaryPropertyString(input, endPos, type, text, f) && !text.empty())
1282 {
1283 static char const *attribNames[] =
1284 {
1285 "", "", "dc:title", "dc:subject", "meta:initial-creator",
1286 "meta:keywords", "dc:description"/*comment*/, "", "dc:creator"
1287 };
1288 pList.insert(attribNames[it->second], text);
1289 }
1290 }
1291 else if (!readSummaryProperty(input, endPos, type, ascii, f))
1292 {
1293 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryInformation: find unknown type\n"));
1294 f << "##type=" << std::hex << type << std::dec << ",";
1295 }
1296 if (input->tell()!=endPos && input->tell()!=pos)
1297 ascii.addDelimiter(input->tell(),'|');
1298 ascii.addPos(pos);
1299 ascii.addNote(f.str().c_str());
1300 }
1301 input->seek(endSect, librevenge::RVNG_SEEK_SET);
1302 }
1303 return true;
1304 }
1305
readSummaryPropertyString(RVNGInputStreamPtr input,long endPos,int type,librevenge::RVNGString & string,libwps::DebugStream & f) const1306 bool WPSOLEParser::readSummaryPropertyString(RVNGInputStreamPtr input, long endPos, int type,
1307 librevenge::RVNGString &string, libwps::DebugStream &f) const
1308 {
1309 if (!input) return false;
1310 long pos=input->tell();
1311 string.clear();
1312 auto sSz=long(libwps::readU32(input));
1313 if (sSz<0 || (endPos-pos-4)<sSz || pos+4+sSz>endPos)
1314 {
1315 WPS_DEBUG_MSG(("WPSOLEParser::readSummaryPropertyString: string size is bad\n"));
1316 f << "##stringSz=" << sSz << ",";
1317 return false;
1318 }
1319 std::string text("");
1320 for (long c=0; c < sSz; ++c)
1321 {
1322 auto ch=static_cast<unsigned char>(libwps::readU8(input));
1323 if (ch)
1324 text+=char(ch);
1325 else if (c+1!=sSz)
1326 text+="##";
1327 }
1328 f << text;
1329 if (!text.empty())
1330 string=libwps_tools_win::Font::unicodeString(text, m_state->m_fontType);
1331 if (type==0x1f && (sSz%4))
1332 input->seek(sSz%4, librevenge::RVNG_SEEK_CUR);
1333 return true;
1334 }
1335
readSummaryPropertyLong(RVNGInputStreamPtr input,long endPos,int type,long & value,libwps::DebugStream & f)1336 bool WPSOLEParser::readSummaryPropertyLong(RVNGInputStreamPtr input, long endPos, int type, long &value,
1337 libwps::DebugStream &f)
1338 {
1339 if (!input) return false;
1340 long pos=input->tell();
1341 switch (type)
1342 {
1343 case 2: // int
1344 case 0x12: // uint
1345 if (pos+2>endPos)
1346 return false;
1347 value=type==2 ? long(libwps::read16(input)) : long(libwps::readU16(input));
1348 break;
1349 case 3: // int
1350 case 9: // uint
1351 if (pos+4>endPos)
1352 return false;
1353 value=type==3 ? long(libwps::read32(input)) : long(libwps::readU32(input));
1354 break;
1355 default:
1356 return false;
1357 }
1358 f << "val=" << value << ",";
1359 return true;
1360 }
1361
readSummaryProperty(RVNGInputStreamPtr input,long endPos,int type,libwps::DebugFile & ascii,libwps::DebugStream & f) const1362 bool WPSOLEParser::readSummaryProperty(RVNGInputStreamPtr input, long endPos, int type,
1363 libwps::DebugFile &ascii, libwps::DebugStream &f) const
1364 {
1365 if (!input) return false;
1366 long pos=input->tell();
1367 // see propread.cxx
1368 if (type&0x1000)
1369 {
1370 auto N=int(libwps::readU32(input));
1371 f << "N=" << N << ",";
1372 f << "[";
1373 for (int n=0; n<N; ++n)
1374 {
1375 pos=input->tell();
1376 f << "[";
1377 if (!readSummaryProperty(input, endPos, type&0xFFF, ascii, f))
1378 {
1379 input->seek(pos, librevenge::RVNG_SEEK_SET);
1380 return false;
1381 }
1382 f << "],";
1383 }
1384 f << "],";
1385 return true;
1386 }
1387 switch (type)
1388 {
1389 case 0x10: // int1
1390 case 0x11: // uint1
1391 if (pos+1>endPos)
1392 return false;
1393 f << "val=" << char(libwps::readU8(input));
1394 break;
1395 case 2: // int
1396 case 0xb: // bool
1397 case 0x12: // uint
1398 if (pos+2>endPos)
1399 return false;
1400 if (type==2)
1401 f << "val=" << int(libwps::read16(input)) << ",";
1402 else if (type==0x12)
1403 f << "val=" << int(libwps::readU16(input)) << ",";
1404 else if (libwps::readU16(input))
1405 f << "true,";
1406 break;
1407 case 3: // int
1408 case 4: // float
1409 case 9: // uint
1410 if (pos+4>endPos)
1411 return false;
1412 if (type==3)
1413 f << "val=" << int(libwps::read32(input)) << ",";
1414 else if (type==9)
1415 f << "val=" << int(libwps::readU32(input)) << ",";
1416 else
1417 f << "val[fl4]=" << std::hex << libwps::readU32(input) << std::dec << ",";
1418 break;
1419 case 5: // double
1420 case 6:
1421 case 7:
1422 case 20:
1423 case 21:
1424 case 0x40:
1425 if (pos+8>endPos)
1426 return false;
1427 ascii.addDelimiter(input->tell(),'|');
1428 if (type==5)
1429 f << "double,";
1430 else if (type==6)
1431 f << "cy,";
1432 else if (type==7)
1433 f << "date,";
1434 else if (type==20)
1435 f << "long,";
1436 else if (type==21)
1437 f << "ulong,";
1438 else
1439 f << "fileTime,"; // readme 8 byte
1440 input->seek(pos+8, librevenge::RVNG_SEEK_SET);
1441 break;
1442 case 0xc: // variant
1443 if (pos+4>endPos)
1444 return false;
1445 type=int(libwps::readU32(input));
1446 return readSummaryProperty(input, endPos, type, ascii, f);
1447 // case 20: int64
1448 // case 21: uint64
1449 case 8:
1450 case 0x1e:
1451 case 0x1f:
1452 {
1453 librevenge::RVNGString string;
1454 if (!readSummaryPropertyString(input, endPos, type, string, f))
1455 return false;
1456 break;
1457 }
1458 case 0x41:
1459 case 0x46:
1460 case 0x47:
1461 {
1462 if (pos+4>endPos)
1463 return false;
1464 f << (type==0x41 ? "blob" : type==0x46 ? "blob[object]" : "clipboard") << ",";
1465 auto dSz=long(libwps::readU32(input));
1466 if (dSz<0 || pos+4+dSz>endPos)
1467 return false;
1468 if (dSz)
1469 {
1470 ascii.skipZone(pos+4, pos+4+dSz-1);
1471 input->seek(dSz, librevenge::RVNG_SEEK_CUR);
1472 }
1473 break;
1474 }
1475 /* todo type==0x47, vtcf clipboard */
1476 default:
1477 return false;
1478 }
1479 return true;
1480 }
1481 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
1482