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