1 /******************************************************************************
2 * $Id: ogrgeorssdatasource.cpp 28636 2015-03-06 19:18:43Z rouault $
3 *
4 * Project: GeoRSS Translator
5 * Purpose: Implements OGRGeoRSSDataSource class
6 * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 *
8 ******************************************************************************
9 * Copyright (c) 2008-2011, Even Rouault <even dot rouault at mines-paris dot org>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "ogr_georss.h"
31 #include "cpl_conv.h"
32 #include "cpl_string.h"
33 #include "cpl_csv.h"
34
35 CPL_CVSID("$Id: ogrgeorssdatasource.cpp 28636 2015-03-06 19:18:43Z rouault $");
36
37 /************************************************************************/
38 /* OGRGeoRSSDataSource() */
39 /************************************************************************/
40
OGRGeoRSSDataSource()41 OGRGeoRSSDataSource::OGRGeoRSSDataSource()
42
43 {
44 papoLayers = NULL;
45 nLayers = 0;
46
47 fpOutput = NULL;
48
49 pszName = NULL;
50
51 eFormat = GEORSS_RSS;
52 eGeomDialect = GEORSS_SIMPLE;
53 bUseExtensions = FALSE;
54 bWriteHeaderAndFooter = TRUE;
55 }
56
57 /************************************************************************/
58 /* ~OGRGeoRSSDataSource() */
59 /************************************************************************/
60
~OGRGeoRSSDataSource()61 OGRGeoRSSDataSource::~OGRGeoRSSDataSource()
62
63 {
64 if ( fpOutput != NULL )
65 {
66 if (bWriteHeaderAndFooter)
67 {
68 if (eFormat == GEORSS_RSS)
69 {
70 VSIFPrintfL(fpOutput, " </channel>\n");
71 VSIFPrintfL(fpOutput, "</rss>\n");
72 }
73 else
74 {
75 VSIFPrintfL(fpOutput, "</feed>\n");
76 }
77 }
78 VSIFCloseL( fpOutput);
79 }
80
81 for( int i = 0; i < nLayers; i++ )
82 delete papoLayers[i];
83 CPLFree( papoLayers );
84 CPLFree( pszName );
85 }
86
87 /************************************************************************/
88 /* TestCapability() */
89 /************************************************************************/
90
TestCapability(const char * pszCap)91 int OGRGeoRSSDataSource::TestCapability( const char * pszCap )
92
93 {
94 if( EQUAL(pszCap,ODsCCreateLayer) )
95 return TRUE;
96 else if( EQUAL(pszCap,ODsCDeleteLayer) )
97 return FALSE;
98 else
99 return FALSE;
100 }
101
102 /************************************************************************/
103 /* GetLayer() */
104 /************************************************************************/
105
GetLayer(int iLayer)106 OGRLayer *OGRGeoRSSDataSource::GetLayer( int iLayer )
107
108 {
109 if( iLayer < 0 || iLayer >= nLayers )
110 return NULL;
111 else
112 return papoLayers[iLayer];
113 }
114
115 /************************************************************************/
116 /* ICreateLayer() */
117 /************************************************************************/
118
ICreateLayer(const char * pszLayerName,OGRSpatialReference * poSRS,CPL_UNUSED OGRwkbGeometryType eType,CPL_UNUSED char ** papszOptions)119 OGRLayer * OGRGeoRSSDataSource::ICreateLayer( const char * pszLayerName,
120 OGRSpatialReference *poSRS,
121 CPL_UNUSED OGRwkbGeometryType eType,
122 CPL_UNUSED char ** papszOptions )
123 {
124 if (fpOutput == NULL)
125 return NULL;
126
127 if (poSRS != NULL && eGeomDialect != GEORSS_GML)
128 {
129 OGRSpatialReference oSRS;
130 oSRS.SetWellKnownGeogCS("WGS84");
131 if (poSRS->IsSame(&oSRS) == FALSE)
132 {
133 CPLError(CE_Failure, CPLE_NotSupported,
134 "For a non GML dialect, only WGS84 SRS is supported");
135 return NULL;
136 }
137 }
138
139 nLayers++;
140 papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
141 papoLayers[nLayers-1] = new OGRGeoRSSLayer( pszName, pszLayerName, this, poSRS, TRUE );
142
143 return papoLayers[nLayers-1];
144 }
145
146 #ifdef HAVE_EXPAT
147 /************************************************************************/
148 /* startElementValidateCbk() */
149 /************************************************************************/
150
startElementValidateCbk(const char * pszName,const char ** ppszAttr)151 void OGRGeoRSSDataSource::startElementValidateCbk(const char *pszName, const char **ppszAttr)
152 {
153 if (validity == GEORSS_VALIDITY_UNKNOWN)
154 {
155 if (strcmp(pszName, "rss") == 0)
156 {
157 validity = GEORSS_VALIDITY_VALID;
158 eFormat = GEORSS_RSS;
159 }
160 else if (strcmp(pszName, "feed") == 0 ||
161 strcmp(pszName, "atom:feed") == 0)
162 {
163 validity = GEORSS_VALIDITY_VALID;
164 eFormat = GEORSS_ATOM;
165 }
166 else if (strcmp(pszName, "rdf:RDF") == 0)
167 {
168 const char** ppszIter = ppszAttr;
169 while(*ppszIter)
170 {
171 if (strcmp(*ppszIter, "xmlns:georss") == 0)
172 {
173 validity = GEORSS_VALIDITY_VALID;
174 eFormat = GEORSS_RSS_RDF;
175 }
176 ppszIter += 2;
177 }
178 }
179 else
180 {
181 validity = GEORSS_VALIDITY_INVALID;
182 }
183 }
184 }
185
186
187 /************************************************************************/
188 /* dataHandlerValidateCbk() */
189 /************************************************************************/
190
dataHandlerValidateCbk(CPL_UNUSED const char * data,CPL_UNUSED int nLen)191 void OGRGeoRSSDataSource::dataHandlerValidateCbk(CPL_UNUSED const char *data,
192 CPL_UNUSED int nLen)
193 {
194 nDataHandlerCounter ++;
195 if (nDataHandlerCounter >= BUFSIZ)
196 {
197 CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
198 XML_StopParser(oCurrentParser, XML_FALSE);
199 }
200 }
201
202
startElementValidateCbk(void * pUserData,const char * pszName,const char ** ppszAttr)203 static void XMLCALL startElementValidateCbk(void *pUserData, const char *pszName, const char **ppszAttr)
204 {
205 OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
206 poDS->startElementValidateCbk(pszName, ppszAttr);
207 }
208
dataHandlerValidateCbk(void * pUserData,const char * data,int nLen)209 static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen)
210 {
211 OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
212 poDS->dataHandlerValidateCbk(data, nLen);
213 }
214 #endif
215
216 /************************************************************************/
217 /* Open() */
218 /************************************************************************/
219
Open(const char * pszFilename,int bUpdateIn)220 int OGRGeoRSSDataSource::Open( const char * pszFilename, int bUpdateIn)
221
222 {
223 if (bUpdateIn)
224 {
225 CPLError(CE_Failure, CPLE_NotSupported,
226 "OGR/GeoRSS driver does not support opening a file in update mode");
227 return FALSE;
228 }
229 #ifdef HAVE_EXPAT
230 pszName = CPLStrdup( pszFilename );
231
232 /* -------------------------------------------------------------------- */
233 /* Try to open the file. */
234 /* -------------------------------------------------------------------- */
235 VSILFILE* fp = VSIFOpenL(pszFilename, "r");
236 if (fp == NULL)
237 return FALSE;
238
239 validity = GEORSS_VALIDITY_UNKNOWN;
240
241 XML_Parser oParser = OGRCreateExpatXMLParser();
242 XML_SetUserData(oParser, this);
243 XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL);
244 XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk);
245 oCurrentParser = oParser;
246
247 char aBuf[BUFSIZ];
248 int nDone;
249 unsigned int nLen;
250 int nCount = 0;
251
252 /* Begin to parse the file and look for the <rss> or <feed> element */
253 /* It *MUST* be the first element of an XML file */
254 /* So once we have read the first element, we know if we can */
255 /* handle the file or not with that driver */
256 do
257 {
258 nDataHandlerCounter = 0;
259 nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
260 nDone = VSIFEofL(fp);
261 if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
262 {
263 if (nLen <= BUFSIZ-1)
264 aBuf[nLen] = 0;
265 else
266 aBuf[BUFSIZ-1] = 0;
267 if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<feed") || strstr(aBuf, "<atom:feed")))
268 {
269 CPLError(CE_Failure, CPLE_AppDefined,
270 "XML parsing of GeoRSS file failed : %s at line %d, column %d",
271 XML_ErrorString(XML_GetErrorCode(oParser)),
272 (int)XML_GetCurrentLineNumber(oParser),
273 (int)XML_GetCurrentColumnNumber(oParser));
274 }
275 validity = GEORSS_VALIDITY_INVALID;
276 break;
277 }
278 if (validity == GEORSS_VALIDITY_INVALID)
279 {
280 break;
281 }
282 else if (validity == GEORSS_VALIDITY_VALID)
283 {
284 break;
285 }
286 else
287 {
288 /* After reading 50 * BUFSIZ bytes, and not finding whether the file */
289 /* is GeoRSS or not, we give up and fail silently */
290 nCount ++;
291 if (nCount == 50)
292 break;
293 }
294 } while (!nDone && nLen > 0 );
295
296 XML_ParserFree(oParser);
297
298 VSIFCloseL(fp);
299
300 if (validity == GEORSS_VALIDITY_VALID)
301 {
302 CPLDebug("GeoRSS", "%s seems to be a GeoRSS file.", pszFilename);
303
304 nLayers = 1;
305 papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
306 papoLayers[0] = new OGRGeoRSSLayer( pszName, "georss", this, NULL, FALSE );
307 }
308
309 return (validity == GEORSS_VALIDITY_VALID);
310 #else
311 char aBuf[256];
312 VSILFILE* fp = VSIFOpenL(pszFilename, "r");
313 if (fp)
314 {
315 unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp );
316 aBuf[nLen] = 0;
317 if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<atom:feed") || strstr(aBuf, "<feed")))
318 {
319 CPLError(CE_Failure, CPLE_NotSupported,
320 "OGR/GeoRSS driver has not been built with read support. Expat library required");
321 }
322 VSIFCloseL(fp);
323 }
324 return FALSE;
325 #endif
326 }
327
328
329 /************************************************************************/
330 /* Create() */
331 /************************************************************************/
332
Create(const char * pszFilename,char ** papszOptions)333 int OGRGeoRSSDataSource::Create( const char *pszFilename,
334 char **papszOptions )
335 {
336 if( fpOutput != NULL)
337 {
338 CPLAssert( FALSE );
339 return FALSE;
340 }
341
342 if (strcmp(pszFilename, "/dev/stdout") == 0)
343 pszFilename = "/vsistdout/";
344
345 /* -------------------------------------------------------------------- */
346 /* Do not override exiting file. */
347 /* -------------------------------------------------------------------- */
348 VSIStatBufL sStatBuf;
349
350 if( VSIStatL( pszFilename, &sStatBuf ) == 0 )
351 {
352 CPLError(CE_Failure, CPLE_NotSupported,
353 "You have to delete %s before being able to create it with the GeoRSS driver",
354 pszFilename);
355 return FALSE;
356 }
357
358 /* -------------------------------------------------------------------- */
359 /* Create the output file. */
360 /* -------------------------------------------------------------------- */
361 pszName = CPLStrdup( pszFilename );
362
363 fpOutput = VSIFOpenL( pszFilename, "w" );
364 if( fpOutput == NULL )
365 {
366 CPLError( CE_Failure, CPLE_OpenFailed,
367 "Failed to create GeoRSS file %s.",
368 pszFilename );
369 return FALSE;
370 }
371
372 const char* pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
373 if (pszFormat)
374 {
375 if (EQUAL(pszFormat, "RSS"))
376 eFormat = GEORSS_RSS;
377 else if (EQUAL(pszFormat, "ATOM"))
378 eFormat = GEORSS_ATOM;
379 else
380 {
381 CPLError(CE_Warning, CPLE_NotSupported,
382 "Unsupported value for %s : %s", "FORMAT", pszFormat);
383 }
384 }
385
386 const char* pszGeomDialect = CSLFetchNameValue(papszOptions, "GEOM_DIALECT");
387 if (pszGeomDialect)
388 {
389 if (EQUAL(pszGeomDialect, "GML"))
390 eGeomDialect = GEORSS_GML;
391 else if (EQUAL(pszGeomDialect, "SIMPLE"))
392 eGeomDialect = GEORSS_SIMPLE;
393 else if (EQUAL(pszGeomDialect, "W3C_GEO"))
394 eGeomDialect = GEORSS_W3C_GEO;
395 else
396 {
397 CPLError(CE_Warning, CPLE_NotSupported,
398 "Unsupported value for %s : %s", "GEOM_DIALECT", pszGeomDialect);
399 }
400 }
401
402 const char* pszWriteHeaderAndFooter = CSLFetchNameValue(papszOptions, "WRITE_HEADER_AND_FOOTER");
403 if (pszWriteHeaderAndFooter && CSLTestBoolean(pszWriteHeaderAndFooter) == FALSE)
404 {
405 bWriteHeaderAndFooter = FALSE;
406 return TRUE;
407 }
408
409 const char* pszHeader = NULL;
410 const char* pszTitle = NULL;
411 const char* pszDescription = NULL;
412 const char* pszLink = NULL;
413 const char* pszUpdated = NULL;
414 const char* pszAuthorName = NULL;
415 const char* pszId = NULL;
416
417 pszHeader = CSLFetchNameValue(papszOptions, "HEADER");
418
419 if (eFormat == GEORSS_RSS && pszHeader == NULL)
420 {
421 pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
422 if (pszTitle == NULL)
423 pszTitle = "title";
424
425 pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
426 if (pszDescription == NULL)
427 pszDescription = "channel_description";
428
429 pszLink = CSLFetchNameValue(papszOptions, "LINK");
430 if (pszLink == NULL)
431 pszLink = "channel_link";
432
433 }
434 else if (eFormat == GEORSS_ATOM && pszHeader == NULL)
435 {
436 pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
437 if (pszTitle == NULL)
438 pszTitle = "title";
439
440 pszUpdated = CSLFetchNameValue(papszOptions, "UPDATED");
441 if (pszUpdated == NULL)
442 pszUpdated = "2009-01-01T00:00:00Z";
443
444 pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
445 if (pszAuthorName == NULL)
446 pszAuthorName = "author";
447
448 pszId = CSLFetchNameValue(papszOptions, "ID");
449 if (pszId == NULL)
450 pszId = "id";
451 }
452
453 const char* pszUseExtensions = CSLFetchNameValue( papszOptions, "USE_EXTENSIONS");
454 bUseExtensions = (pszUseExtensions && CSLTestBoolean(pszUseExtensions));
455
456 /* -------------------------------------------------------------------- */
457 /* Output header of GeoRSS file. */
458 /* -------------------------------------------------------------------- */
459 VSIFPrintfL(fpOutput, "<?xml version=\"1.0\"?>\n");
460 if (eFormat == GEORSS_RSS)
461 {
462 VSIFPrintfL(fpOutput, "<rss version=\"2.0\" ");
463 if (eGeomDialect == GEORSS_GML)
464 VSIFPrintfL(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\"");
465 else if (eGeomDialect == GEORSS_SIMPLE)
466 VSIFPrintfL(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
467 else
468 VSIFPrintfL(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
469 VSIFPrintfL(fpOutput, ">\n");
470 VSIFPrintfL(fpOutput, " <channel>\n");
471 if (pszHeader)
472 {
473 VSIFPrintfL(fpOutput, "%s", pszHeader);
474 }
475 else
476 {
477 VSIFPrintfL(fpOutput, " <title>%s</title>\n", pszTitle);
478 VSIFPrintfL(fpOutput, " <description>%s</description>\n", pszDescription);
479 VSIFPrintfL(fpOutput, " <link>%s</link>\n", pszLink);
480 }
481 }
482 else
483 {
484 VSIFPrintfL(fpOutput, "<feed xmlns=\"http://www.w3.org/2005/Atom\" ");
485 if (eGeomDialect == GEORSS_GML)
486 VSIFPrintfL(fpOutput, "xmlns:gml=\"http://www.opengis.net/gml\"");
487 else if (eGeomDialect == GEORSS_SIMPLE)
488 VSIFPrintfL(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
489 else
490 VSIFPrintfL(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
491 VSIFPrintfL(fpOutput, ">\n");
492 if (pszHeader)
493 {
494 VSIFPrintfL(fpOutput, "%s", pszHeader);
495 }
496 else
497 {
498 VSIFPrintfL(fpOutput, " <title>%s</title>\n", pszTitle);
499 VSIFPrintfL(fpOutput, " <updated>%s</updated>\n", pszUpdated);
500 VSIFPrintfL(fpOutput, " <author><name>%s</name></author>\n", pszAuthorName);
501 VSIFPrintfL(fpOutput, " <id>%s</id>\n", pszId);
502 }
503 }
504
505 return TRUE;
506 }
507