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