Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: KML Driver
4 : * Purpose: Class for reading, parsing and handling a kmlfile.
5 : * Author: Jens Oberender, j.obi@troja.net
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2007, Jens Oberender
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 : #include "kmlnode.h"
14 : #include "kml.h"
15 :
16 : #include <cstring>
17 : #include <cstdio>
18 : #include <exception>
19 : #include <iostream>
20 : #include <string>
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_error.h"
24 : #ifdef HAVE_EXPAT
25 : #include "expat.h"
26 : #endif
27 :
28 : constexpr int PARSER_BUF_SIZE = 8192;
29 :
30 : KML::KML() = default;
31 :
32 26 : KML::~KML()
33 : {
34 26 : if (nullptr != pKMLFile_)
35 26 : VSIFCloseL(pKMLFile_);
36 26 : CPLFree(papoLayers_);
37 :
38 26 : delete poTrunk_;
39 26 : }
40 :
41 26 : bool KML::open(const char *pszFilename)
42 : {
43 26 : if (nullptr != pKMLFile_)
44 0 : VSIFCloseL(pKMLFile_);
45 :
46 26 : pKMLFile_ = VSIFOpenL(pszFilename, "r");
47 26 : return pKMLFile_ != nullptr;
48 : }
49 :
50 25 : bool KML::parse()
51 : {
52 25 : if (nullptr == pKMLFile_)
53 : {
54 0 : sError_ = "No file given";
55 0 : return false;
56 : }
57 :
58 25 : if (poTrunk_ != nullptr)
59 : {
60 0 : delete poTrunk_;
61 0 : poTrunk_ = nullptr;
62 : }
63 :
64 25 : if (poCurrent_ != nullptr)
65 : {
66 0 : delete poCurrent_;
67 0 : poCurrent_ = nullptr;
68 : }
69 :
70 25 : XML_Parser oParser = OGRCreateExpatXMLParser();
71 25 : XML_SetUserData(oParser, this);
72 25 : XML_SetElementHandler(oParser, startElement, endElement);
73 25 : XML_SetCharacterDataHandler(oParser, dataHandler);
74 25 : oCurrentParser = oParser;
75 25 : nWithoutEventCounter = 0;
76 :
77 25 : int nDone = 0;
78 25 : unsigned nLen = 0;
79 50 : std::vector<char> aBuf(PARSER_BUF_SIZE);
80 25 : bool bError = false;
81 :
82 45 : do
83 : {
84 70 : nDataHandlerCounter = 0;
85 70 : nLen = static_cast<unsigned>(
86 70 : VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
87 70 : nDone = nLen < aBuf.size();
88 70 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
89 : {
90 1 : CPLError(CE_Failure, CPLE_AppDefined,
91 : "XML parsing of KML file failed: %s",
92 : OGRExpatGetParseError(oParser));
93 1 : bError = true;
94 1 : break;
95 : }
96 69 : nWithoutEventCounter++;
97 69 : } while (!nDone && nLen > 0 && nWithoutEventCounter < 10);
98 :
99 25 : XML_ParserFree(oParser);
100 25 : VSIRewindL(pKMLFile_);
101 :
102 25 : if (nWithoutEventCounter == 10)
103 : {
104 0 : CPLError(CE_Failure, CPLE_AppDefined,
105 : "Too much data inside one element. File probably corrupted");
106 0 : bError = true;
107 : }
108 :
109 25 : if (bError)
110 : {
111 1 : if (poCurrent_ != nullptr)
112 : {
113 0 : while (poCurrent_)
114 : {
115 0 : KMLNode *poTemp = poCurrent_->getParent();
116 0 : delete poCurrent_;
117 0 : poCurrent_ = poTemp;
118 : }
119 : // No need to destroy poTrunk_ : it has been destroyed in
120 : // the last iteration
121 : }
122 : else
123 : {
124 : // Case of invalid content after closing element matching
125 : // first <kml> element
126 1 : delete poTrunk_;
127 : }
128 1 : poTrunk_ = nullptr;
129 1 : return false;
130 : }
131 :
132 24 : poCurrent_ = nullptr;
133 24 : return true;
134 : }
135 :
136 26 : void KML::checkValidity()
137 : {
138 26 : if (poTrunk_ != nullptr)
139 : {
140 0 : delete poTrunk_;
141 0 : poTrunk_ = nullptr;
142 : }
143 :
144 26 : if (poCurrent_ != nullptr)
145 : {
146 0 : delete poCurrent_;
147 0 : poCurrent_ = nullptr;
148 : }
149 :
150 26 : if (pKMLFile_ == nullptr)
151 : {
152 0 : sError_ = "No file given";
153 1 : return;
154 : }
155 :
156 26 : XML_Parser oParser = OGRCreateExpatXMLParser();
157 26 : XML_SetUserData(oParser, this);
158 26 : XML_SetElementHandler(oParser, startElementValidate, nullptr);
159 26 : XML_SetCharacterDataHandler(oParser, dataHandlerValidate);
160 26 : int nCount = 0;
161 :
162 26 : oCurrentParser = oParser;
163 :
164 26 : int nDone = 0;
165 26 : unsigned nLen = 0;
166 52 : std::vector<char> aBuf(PARSER_BUF_SIZE);
167 :
168 : // Parses the file until we find the first element.
169 0 : do
170 : {
171 26 : nDataHandlerCounter = 0;
172 26 : nLen = static_cast<unsigned>(
173 26 : VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
174 26 : nDone = nLen < aBuf.size();
175 26 : if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
176 : {
177 1 : if (nLen <= PARSER_BUF_SIZE - 1)
178 1 : aBuf[nLen] = 0;
179 : else
180 0 : aBuf[PARSER_BUF_SIZE - 1] = 0;
181 2 : if (strstr(aBuf.data(), "<?xml") &&
182 1 : (strstr(aBuf.data(), "<kml") ||
183 0 : (strstr(aBuf.data(), "<Document") &&
184 0 : strstr(aBuf.data(), "/kml/2."))))
185 : {
186 1 : CPLError(CE_Failure, CPLE_AppDefined,
187 : "XML parsing of KML file failed: %s",
188 : OGRExpatGetParseError(oParser));
189 : }
190 :
191 1 : validity = KML_VALIDITY_INVALID;
192 1 : XML_ParserFree(oParser);
193 1 : VSIRewindL(pKMLFile_);
194 1 : return;
195 : }
196 :
197 25 : nCount++;
198 : /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */
199 : /* is KML or not, we give up and fail silently */
200 25 : } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN &&
201 : nCount < 50);
202 :
203 25 : XML_ParserFree(oParser);
204 25 : VSIRewindL(pKMLFile_);
205 25 : poCurrent_ = nullptr;
206 : }
207 :
208 5680 : void XMLCALL KML::startElement(void *pUserData, const char *pszName,
209 : const char **ppszAttr)
210 : {
211 5680 : KML *poKML = static_cast<KML *>(pUserData);
212 : try
213 : {
214 5680 : poKML->nWithoutEventCounter = 0;
215 :
216 5680 : const char *pszColumn = strchr(pszName, ':');
217 5680 : if (pszColumn)
218 5 : pszName = pszColumn + 1;
219 :
220 11335 : if (poKML->poTrunk_ == nullptr ||
221 11310 : (poKML->poCurrent_ != nullptr &&
222 5655 : poKML->poCurrent_->getName().compare("description") != 0))
223 : {
224 5675 : if (poKML->nDepth_ == 1024)
225 : {
226 0 : CPLError(CE_Failure, CPLE_AppDefined,
227 : "Too big depth level (%d) while parsing KML.",
228 : poKML->nDepth_);
229 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
230 0 : return;
231 : }
232 :
233 5675 : KMLNode *poMynew = new KMLNode();
234 5675 : poMynew->setName(pszName);
235 5675 : poMynew->setLevel(poKML->nDepth_);
236 :
237 7126 : for (int i = 0; ppszAttr[i]; i += 2)
238 : {
239 1451 : Attribute *poAtt = new Attribute();
240 1451 : poAtt->sName = ppszAttr[i];
241 1451 : poAtt->sValue = ppszAttr[i + 1];
242 1451 : poMynew->addAttribute(poAtt);
243 : }
244 :
245 5675 : if (poKML->poTrunk_ == nullptr)
246 25 : poKML->poTrunk_ = poMynew;
247 5675 : if (poKML->poCurrent_ != nullptr)
248 5650 : poMynew->setParent(poKML->poCurrent_);
249 5675 : poKML->poCurrent_ = poMynew;
250 :
251 5675 : poKML->nDepth_++;
252 : }
253 5 : else if (poKML->poCurrent_ != nullptr)
254 : {
255 10 : std::string sNewContent = "<";
256 5 : sNewContent += pszName;
257 6 : for (int i = 0; ppszAttr[i]; i += 2)
258 : {
259 1 : sNewContent += " ";
260 1 : sNewContent += ppszAttr[i];
261 1 : sNewContent += "=\"";
262 1 : sNewContent += ppszAttr[i + 1];
263 1 : sNewContent += "\"";
264 : }
265 5 : sNewContent += ">";
266 5 : if (poKML->poCurrent_->numContent() == 0)
267 0 : poKML->poCurrent_->addContent(sNewContent);
268 : else
269 5 : poKML->poCurrent_->appendContent(sNewContent);
270 : }
271 : }
272 0 : catch (const std::exception &ex)
273 : {
274 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
275 0 : ex.what());
276 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
277 : }
278 : }
279 :
280 2069 : void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName,
281 : const char **ppszAttr)
282 : {
283 2069 : KML *poKML = static_cast<KML *>(pUserData);
284 :
285 2069 : if (poKML->validity != KML_VALIDITY_UNKNOWN)
286 2043 : return;
287 :
288 26 : poKML->validity = KML_VALIDITY_INVALID;
289 :
290 26 : const char *pszColumn = strchr(pszName, ':');
291 26 : if (pszColumn)
292 1 : pszName = pszColumn + 1;
293 :
294 26 : if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0)
295 : {
296 : // Check all Attributes
297 55 : for (int i = 0; ppszAttr[i]; i += 2)
298 : {
299 : // Find the namespace and determine the KML version
300 29 : if (strcmp(ppszAttr[i], "xmlns") == 0)
301 : {
302 : // Is it KML 2.2?
303 24 : if ((strcmp(ppszAttr[i + 1],
304 24 : "http://earth.google.com/kml/2.2") == 0) ||
305 24 : (strcmp(ppszAttr[i + 1],
306 : "http://www.opengis.net/kml/2.2") == 0))
307 : {
308 10 : poKML->validity = KML_VALIDITY_VALID;
309 10 : poKML->sVersion_ = "2.2";
310 : }
311 14 : else if (strcmp(ppszAttr[i + 1],
312 : "http://earth.google.com/kml/2.1") == 0)
313 : {
314 14 : poKML->validity = KML_VALIDITY_VALID;
315 14 : poKML->sVersion_ = "2.1";
316 : }
317 0 : else if (strcmp(ppszAttr[i + 1],
318 : "http://earth.google.com/kml/2.0") == 0)
319 : {
320 0 : poKML->validity = KML_VALIDITY_VALID;
321 0 : poKML->sVersion_ = "2.0";
322 : }
323 : else
324 : {
325 0 : CPLDebug("KML",
326 : "Unhandled xmlns value : %s. Going on though...",
327 0 : ppszAttr[i]);
328 0 : poKML->validity = KML_VALIDITY_VALID;
329 0 : poKML->sVersion_ = "?";
330 : }
331 : }
332 : }
333 :
334 26 : if (poKML->validity == KML_VALIDITY_INVALID)
335 : {
336 2 : CPLDebug("KML", "Did not find xmlns attribute in <kml> element. "
337 : "Going on though...");
338 2 : poKML->validity = KML_VALIDITY_VALID;
339 2 : poKML->sVersion_ = "?";
340 : }
341 : }
342 : }
343 :
344 7667 : void XMLCALL KML::dataHandlerValidate(void *pUserData,
345 : const char * /* pszData */,
346 : int /* nLen */)
347 : {
348 7667 : KML *poKML = static_cast<KML *>(pUserData);
349 :
350 7667 : poKML->nDataHandlerCounter++;
351 7667 : if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
352 : {
353 0 : CPLError(CE_Failure, CPLE_AppDefined,
354 : "File probably corrupted (million laugh pattern)");
355 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
356 : }
357 7667 : }
358 :
359 5680 : void XMLCALL KML::endElement(void *pUserData, const char *pszName)
360 : {
361 5680 : KML *poKML = static_cast<KML *>(pUserData);
362 :
363 : try
364 : {
365 5680 : poKML->nWithoutEventCounter = 0;
366 :
367 5680 : const char *pszColumn = strchr(pszName, ':');
368 5680 : if (pszColumn)
369 5 : pszName = pszColumn + 1;
370 :
371 11360 : if (poKML->poCurrent_ != nullptr &&
372 5680 : poKML->poCurrent_->getName().compare(pszName) == 0)
373 : {
374 5675 : poKML->nDepth_--;
375 5675 : KMLNode *poTmp = poKML->poCurrent_;
376 : // Split the coordinates
377 5943 : if (poKML->poCurrent_->getName().compare("coordinates") == 0 &&
378 268 : poKML->poCurrent_->numContent() == 1)
379 : {
380 526 : const std::string sData = poKML->poCurrent_->getContent(0);
381 263 : std::size_t nPos = 0;
382 263 : const std::size_t nLength = sData.length();
383 263 : const char *pszData = sData.c_str();
384 : while (true)
385 : {
386 : // Cut off whitespaces
387 33868 : while (nPos < nLength &&
388 33605 : (pszData[nPos] == ' ' || pszData[nPos] == '\n' ||
389 2106 : pszData[nPos] == '\r' || pszData[nPos] == '\t'))
390 31499 : nPos++;
391 :
392 2369 : if (nPos == nLength)
393 263 : break;
394 :
395 2106 : const std::size_t nPosBegin = nPos;
396 2106 : size_t nContentSize = 0;
397 :
398 : // Get content
399 81109 : while (nPos < nLength && pszData[nPos] != ' ' &&
400 162068 : pszData[nPos] != '\n' && pszData[nPos] != '\r' &&
401 79090 : pszData[nPos] != '\t')
402 : {
403 79090 : nContentSize++;
404 79090 : nPos++;
405 : }
406 :
407 2106 : if (nContentSize > 0)
408 : {
409 4212 : std::string sTmp(pszData + nPosBegin, nContentSize);
410 2106 : poKML->poCurrent_->addContent(sTmp);
411 : }
412 2106 : }
413 263 : if (poKML->poCurrent_->numContent() > 1)
414 263 : poKML->poCurrent_->deleteContent(0);
415 : }
416 5412 : else if (poKML->poCurrent_->numContent() == 1)
417 : {
418 9960 : const std::string sData = poKML->poCurrent_->getContent(0);
419 9960 : std::string sDataWithoutNL;
420 4980 : std::size_t nPos = 0;
421 4980 : const std::size_t nLength = sData.length();
422 4980 : const char *pszData = sData.c_str();
423 4980 : std::size_t nLineStartPos = 0;
424 4980 : bool bLineStart = true;
425 :
426 : // Re-assemble multi-line content by removing leading spaces for
427 : // each line. I am not sure why we do that. Should we preserve
428 : // content as such?
429 173887 : while (nPos < nLength)
430 : {
431 168907 : const char ch = pszData[nPos];
432 168907 : if (bLineStart &&
433 11829 : (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'))
434 80494 : nLineStartPos++;
435 88413 : else if (ch == '\n' || ch == '\r')
436 : {
437 1244 : if (!bLineStart)
438 : {
439 : std::string sTmp(pszData + nLineStartPos,
440 1244 : nPos - nLineStartPos);
441 1244 : if (!sDataWithoutNL.empty())
442 1111 : sDataWithoutNL += '\n';
443 1244 : sDataWithoutNL += sTmp;
444 1244 : bLineStart = true;
445 : }
446 1244 : nLineStartPos = nPos + 1;
447 : }
448 : else
449 : {
450 87169 : bLineStart = false;
451 : }
452 168907 : nPos++;
453 : }
454 :
455 4980 : if (nLineStartPos > 0)
456 : {
457 1861 : if (nLineStartPos < nPos)
458 : {
459 : std::string sTmp(pszData + nLineStartPos,
460 242 : nPos - nLineStartPos);
461 121 : if (!sDataWithoutNL.empty())
462 121 : sDataWithoutNL += '\n';
463 121 : sDataWithoutNL += sTmp;
464 : }
465 :
466 1861 : poKML->poCurrent_->deleteContent(0);
467 1861 : poKML->poCurrent_->addContent(sDataWithoutNL);
468 : }
469 : }
470 :
471 5675 : if (poKML->poCurrent_->getParent() != nullptr)
472 5650 : poKML->poCurrent_ = poKML->poCurrent_->getParent();
473 : else
474 25 : poKML->poCurrent_ = nullptr;
475 :
476 5675 : if (!poKML->isHandled(pszName))
477 : {
478 3553 : CPLDebug("KML", "Not handled: %s", pszName);
479 3553 : delete poTmp;
480 3553 : if (poKML->poCurrent_ == poTmp)
481 0 : poKML->poCurrent_ = nullptr;
482 3553 : if (poKML->poTrunk_ == poTmp)
483 0 : poKML->poTrunk_ = nullptr;
484 : }
485 : else
486 : {
487 2122 : if (poKML->poCurrent_ != nullptr)
488 2097 : poKML->poCurrent_->addChildren(poTmp);
489 : }
490 : }
491 5 : else if (poKML->poCurrent_ != nullptr)
492 : {
493 10 : std::string sNewContent = "</";
494 5 : sNewContent += pszName;
495 5 : sNewContent += ">";
496 5 : if (poKML->poCurrent_->numContent() == 0)
497 0 : poKML->poCurrent_->addContent(sNewContent);
498 : else
499 5 : poKML->poCurrent_->appendContent(sNewContent);
500 : }
501 : }
502 0 : catch (const std::exception &ex)
503 : {
504 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
505 0 : ex.what());
506 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
507 : }
508 5680 : }
509 :
510 24279 : void XMLCALL KML::dataHandler(void *pUserData, const char *pszData, int nLen)
511 : {
512 24279 : KML *poKML = static_cast<KML *>(pUserData);
513 :
514 24279 : poKML->nWithoutEventCounter = 0;
515 :
516 24279 : if (nLen < 1 || poKML->poCurrent_ == nullptr)
517 0 : return;
518 :
519 24279 : poKML->nDataHandlerCounter++;
520 24279 : if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
521 : {
522 0 : CPLError(CE_Failure, CPLE_AppDefined,
523 : "File probably corrupted (million laugh pattern)");
524 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
525 : }
526 :
527 : try
528 : {
529 48558 : std::string sData(pszData, nLen);
530 :
531 24279 : if (poKML->poCurrent_->numContent() == 0)
532 5243 : poKML->poCurrent_->addContent(sData);
533 : else
534 19036 : poKML->poCurrent_->appendContent(sData);
535 : }
536 0 : catch (const std::exception &ex)
537 : {
538 0 : CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
539 0 : ex.what());
540 0 : XML_StopParser(poKML->oCurrentParser, XML_FALSE);
541 : }
542 : }
543 :
544 26 : bool KML::isValid()
545 : {
546 26 : checkValidity();
547 :
548 26 : if (validity == KML_VALIDITY_VALID)
549 25 : CPLDebug("KML", "Valid: 1 Version: %s", sVersion_.c_str());
550 :
551 26 : return validity == KML_VALIDITY_VALID;
552 : }
553 :
554 0 : std::string KML::getError() const
555 : {
556 0 : return sError_;
557 : }
558 :
559 24 : int KML::classifyNodes()
560 : {
561 24 : if (poTrunk_ == nullptr)
562 0 : return false;
563 24 : return poTrunk_->classify(this);
564 : }
565 :
566 20 : void KML::eliminateEmpty()
567 : {
568 20 : if (poTrunk_ != nullptr)
569 20 : poTrunk_->eliminateEmpty(this);
570 20 : }
571 :
572 0 : void KML::print(unsigned short nNum)
573 : {
574 0 : if (poTrunk_ != nullptr)
575 0 : poTrunk_->print(nNum);
576 0 : }
577 :
578 5675 : bool KML::isHandled(std::string const &elem) const
579 : {
580 10206 : return isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) ||
581 7554 : isContainer(elem) || isRest(elem) || elem == "Schema" ||
582 10676 : elem == "SimpleField" || elem == "SchemaData" ||
583 13763 : elem == "SimpleData" || elem == "ExtendedData";
584 : }
585 :
586 24 : bool KML::hasOnlyEmpty() const
587 : {
588 24 : return poTrunk_->hasOnlyEmpty();
589 : }
590 :
591 24 : int KML::getNumLayers() const
592 : {
593 24 : return nNumLayers_;
594 : }
595 :
596 920 : bool KML::selectLayer(int nNum)
597 : {
598 920 : if (nNumLayers_ < 1 || nNum >= nNumLayers_)
599 0 : return false;
600 920 : poCurrent_ = papoLayers_[nNum];
601 920 : return true;
602 : }
603 :
604 83 : std::string KML::getCurrentName() const
605 : {
606 83 : std::string tmp;
607 83 : if (poCurrent_ != nullptr)
608 : {
609 83 : tmp = poCurrent_->getNameElement();
610 : }
611 83 : return tmp;
612 : }
613 :
614 203 : Nodetype KML::getCurrentType() const
615 : {
616 203 : if (poCurrent_ != nullptr)
617 203 : return poCurrent_->getType();
618 :
619 0 : return Unknown;
620 : }
621 :
622 75 : int KML::is25D() const
623 : {
624 75 : if (poCurrent_ != nullptr)
625 75 : return poCurrent_->is25D();
626 :
627 0 : return Unknown;
628 : }
629 :
630 61 : int KML::getNumFeatures()
631 : {
632 61 : if (poCurrent_ == nullptr)
633 0 : return -1;
634 :
635 61 : return static_cast<int>(poCurrent_->getNumFeatures());
636 : }
637 :
638 960 : Feature *KML::getFeature(std::size_t nNum, int &nLastAsked, int &nLastCount)
639 : {
640 960 : if (poCurrent_ == nullptr)
641 0 : return nullptr;
642 :
643 960 : return poCurrent_->getFeature(nNum, nLastAsked, nLastCount);
644 : }
645 :
646 116 : void KML::unregisterLayerIfMatchingThisNode(KMLNode *poNode)
647 : {
648 116 : for (int i = 0; i < nNumLayers_;)
649 : {
650 2 : if (papoLayers_[i] == poNode)
651 : {
652 2 : if (i < nNumLayers_ - 1)
653 : {
654 0 : memmove(papoLayers_ + i, papoLayers_ + i + 1,
655 0 : (nNumLayers_ - 1 - i) * sizeof(KMLNode *));
656 : }
657 2 : nNumLayers_--;
658 2 : break;
659 : }
660 0 : i++;
661 : }
662 116 : }
|