1 : /******************************************************************************
2 : * $Id: ogrgpsbabeldatasource.cpp 25598 2013-02-05 22:24:35Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRGPSBabelDataSource class.
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, 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_gpsbabel.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_error.h"
34 : #include "cpl_spawn.h"
35 :
36 : #include <string.h>
37 :
38 : CPL_CVSID("$Id: ogrgpsbabeldatasource.cpp 25598 2013-02-05 22:24:35Z rouault $");
39 :
40 : /************************************************************************/
41 : /* OGRGPSBabelDataSource() */
42 : /************************************************************************/
43 :
44 132 : OGRGPSBabelDataSource::OGRGPSBabelDataSource()
45 :
46 : {
47 132 : nLayers = 0;
48 132 : pszName = NULL;
49 132 : pszGPSBabelDriverName = NULL;
50 132 : pszFilename = NULL;
51 132 : poGPXDS = NULL;
52 132 : }
53 :
54 : /************************************************************************/
55 : /* ~OGRGPSBabelDataSource() */
56 : /************************************************************************/
57 :
58 132 : OGRGPSBabelDataSource::~OGRGPSBabelDataSource()
59 :
60 : {
61 132 : CPLFree(pszName);
62 132 : CPLFree(pszGPSBabelDriverName);
63 132 : CPLFree(pszFilename);
64 :
65 132 : if (poGPXDS)
66 2 : OGRDataSource::DestroyDataSource(poGPXDS);
67 :
68 132 : if (osTmpFileName.size() > 0)
69 2 : VSIUnlink(osTmpFileName.c_str());
70 132 : }
71 :
72 : /************************************************************************/
73 : /* GetArgv() */
74 : /************************************************************************/
75 :
76 2 : static char** GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes,
77 : int bTracks, const char* pszGPSBabelDriverName,
78 : const char* pszFilename)
79 : {
80 2 : char** argv = NULL;
81 2 : argv = CSLAddString(argv, "gpsbabel");
82 2 : if (bExplicitFeatures)
83 : {
84 0 : if (bWaypoints) argv = CSLAddString(argv, "-w");
85 0 : if (bRoutes) argv = CSLAddString(argv, "-r");
86 0 : if (bTracks) argv = CSLAddString(argv, "-t");
87 : }
88 2 : argv = CSLAddString(argv, "-i");
89 2 : argv = CSLAddString(argv, pszGPSBabelDriverName);
90 2 : argv = CSLAddString(argv, "-f");
91 2 : argv = CSLAddString(argv, pszFilename);
92 2 : argv = CSLAddString(argv, "-o");
93 2 : argv = CSLAddString(argv, "gpx,gpxver=1.1");
94 2 : argv = CSLAddString(argv, "-F");
95 2 : argv = CSLAddString(argv, "-");
96 :
97 2 : return argv;
98 : }
99 :
100 : /************************************************************************/
101 : /* IsSpecialFile() */
102 : /************************************************************************/
103 :
104 3 : int OGRGPSBabelDataSource::IsSpecialFile(const char* pszFilename)
105 : {
106 : return (strncmp(pszFilename, "/dev/", 5) == 0 ||
107 : strncmp(pszFilename, "usb:", 4) == 0 ||
108 3 : (strncmp(pszFilename, "COM", 3) == 0 && atoi(pszFilename + 3) > 0));
109 : }
110 :
111 : /************************************************************************/
112 : /* IsValidDriverName() */
113 : /************************************************************************/
114 :
115 2 : int OGRGPSBabelDataSource::IsValidDriverName(const char* pszGPSBabelDriverName)
116 : {
117 : int i;
118 10 : for(i=0;pszGPSBabelDriverName[i] != '\0';i++)
119 : {
120 8 : char ch = pszGPSBabelDriverName[i];
121 8 : if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
122 : (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' || ch == ','))
123 : {
124 : CPLError(CE_Failure, CPLE_AppDefined,
125 0 : "Invalid GPSBabel driver name");
126 0 : return FALSE;
127 : }
128 : }
129 2 : return TRUE;
130 : }
131 :
132 : /************************************************************************/
133 : /* Open() */
134 : /************************************************************************/
135 :
136 132 : int OGRGPSBabelDataSource::Open( const char * pszDatasourceName, int bUpdateIn)
137 :
138 : {
139 132 : int bExplicitFeatures = FALSE;
140 132 : int bWaypoints = TRUE, bTracks = TRUE, bRoutes = TRUE;
141 132 : if (bUpdateIn)
142 : {
143 : CPLError(CE_Failure, CPLE_NotSupported,
144 0 : "OGR/GPSBabel driver does not support opening a file in update mode");
145 0 : return FALSE;
146 : }
147 :
148 132 : if (!EQUALN(pszDatasourceName, "GPSBABEL:", 9))
149 : {
150 131 : VSILFILE* fp = VSIFOpenL(pszDatasourceName, "rb");
151 131 : if (fp == NULL)
152 56 : return FALSE;
153 :
154 : char szHeader[1024 + 1];
155 75 : memset(szHeader, 0, 1024+1);
156 75 : VSIFReadL(szHeader, 1, 1024, fp);
157 75 : if (memcmp(szHeader, "MsRcd", 5) == 0)
158 0 : pszGPSBabelDriverName = CPLStrdup("mapsource");
159 75 : else if (memcmp(szHeader, "MsRcf", 5) == 0)
160 0 : pszGPSBabelDriverName = CPLStrdup("gdb");
161 75 : else if (strstr(szHeader, "<osm") != NULL)
162 0 : pszGPSBabelDriverName = CPLStrdup("osm");
163 76 : else if (strstr(szHeader, "$GPGSA") != NULL ||
164 : strstr(szHeader, "$GPGGA") != NULL)
165 1 : pszGPSBabelDriverName = CPLStrdup("nmea");
166 74 : else if (EQUALN(szHeader, "OziExplorer",11))
167 0 : pszGPSBabelDriverName = CPLStrdup("ozi");
168 74 : else if (strstr(szHeader, "Grid") && strstr(szHeader, "Datum") && strstr(szHeader, "Header"))
169 0 : pszGPSBabelDriverName = CPLStrdup("garmin_txt");
170 74 : else if (szHeader[0] == 13 && szHeader[10] == 'M' && szHeader[11] == 'S' &&
171 0 : (szHeader[12] >= '0' && szHeader[12] <= '9') &&
172 0 : (szHeader[13] >= '0' && szHeader[13] <= '9') &&
173 0 : szHeader[12] * 10 + szHeader[13] >= 30 &&
174 0 : (szHeader[14] == 1 || szHeader[14] == 2) && szHeader[15] == 0 &&
175 0 : szHeader[16] == 0 && szHeader[17] == 0)
176 0 : pszGPSBabelDriverName = CPLStrdup("mapsend");
177 74 : else if (strstr(szHeader, "$PMGNWPL") != NULL ||
178 : strstr(szHeader, "$PMGNRTE") != NULL)
179 0 : pszGPSBabelDriverName = CPLStrdup("magellan");
180 :
181 75 : VSIFCloseL(fp);
182 :
183 75 : if (pszGPSBabelDriverName == NULL)
184 : {
185 74 : return FALSE;
186 : }
187 :
188 1 : pszFilename = CPLStrdup(pszDatasourceName);
189 : }
190 :
191 2 : pszName = CPLStrdup( pszDatasourceName );
192 :
193 2 : if (pszGPSBabelDriverName == NULL)
194 : {
195 1 : const char* pszSep = strchr(pszDatasourceName + 9, ':');
196 1 : if (pszSep == NULL)
197 : {
198 : CPLError(CE_Failure, CPLE_AppDefined,
199 0 : "Wrong syntax. Expected GPSBabel:driver_name:file_name");
200 0 : return FALSE;
201 : }
202 :
203 1 : pszGPSBabelDriverName = CPLStrdup(pszDatasourceName + 9);
204 1 : *(strchr(pszGPSBabelDriverName, ':')) = '\0';
205 :
206 : /* A bit of validation to avoid command line injection */
207 1 : if (!IsValidDriverName(pszGPSBabelDriverName))
208 0 : return FALSE;
209 :
210 : /* Parse optionnal features= option */
211 1 : if (EQUALN(pszSep+1, "features=", 9))
212 : {
213 0 : const char* pszNextSep = strchr(pszSep+1, ':');
214 0 : if (pszNextSep == NULL)
215 : {
216 : CPLError(CE_Failure, CPLE_AppDefined,
217 0 : "Wrong syntax. Expected GPSBabel:driver_name[,options]*:[features=waypoints,tracks,routes:]file_name");
218 0 : return FALSE;
219 : }
220 :
221 0 : char* pszFeatures = CPLStrdup(pszSep+1+9);
222 0 : *strchr(pszFeatures, ':') = 0;
223 0 : char** papszTokens = CSLTokenizeString(pszFeatures);
224 0 : char** papszIter = papszTokens;
225 0 : int bErr = FALSE;
226 0 : bExplicitFeatures = TRUE;
227 0 : bWaypoints = bTracks = bRoutes = FALSE;
228 0 : while(papszIter && *papszIter)
229 : {
230 0 : if (EQUAL(*papszIter, "waypoints"))
231 0 : bWaypoints = TRUE;
232 0 : else if (EQUAL(*papszIter, "tracks"))
233 0 : bTracks = TRUE;
234 0 : else if (EQUAL(*papszIter, "routes"))
235 0 : bRoutes = TRUE;
236 : else
237 : {
238 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for 'features' options");
239 0 : bErr = TRUE;
240 : }
241 0 : papszIter ++;
242 : }
243 0 : CSLDestroy(papszTokens);
244 0 : CPLFree(pszFeatures);
245 :
246 0 : if (bErr)
247 0 : return FALSE;
248 :
249 0 : pszSep = pszNextSep;
250 : }
251 :
252 1 : pszFilename = CPLStrdup(pszSep+1);
253 : }
254 :
255 2 : const char* pszOptionUseTempFile = CPLGetConfigOption("USE_TEMPFILE", NULL);
256 2 : if (pszOptionUseTempFile && CSLTestBoolean(pszOptionUseTempFile))
257 0 : osTmpFileName = CPLGenerateTempFilename(NULL);
258 : else
259 2 : osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this);
260 :
261 2 : int bRet = FALSE;
262 2 : if (IsSpecialFile(pszFilename))
263 : {
264 : /* Special file : don't try to open it */
265 : char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
266 0 : bTracks, pszGPSBabelDriverName, pszFilename);
267 0 : VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
268 0 : bRet = (CPLSpawn(argv, NULL, tmpfp, TRUE) == 0);
269 0 : VSIFCloseL(tmpfp);
270 0 : tmpfp = NULL;
271 0 : CSLDestroy(argv);
272 0 : argv = NULL;
273 : }
274 : else
275 : {
276 2 : VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
277 2 : if (fp == NULL)
278 : {
279 : CPLError(CE_Failure, CPLE_AppDefined,
280 0 : "Cannot open file %s", pszFilename);
281 0 : return FALSE;
282 : }
283 :
284 : char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
285 2 : bTracks, pszGPSBabelDriverName, "-");
286 :
287 2 : VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
288 :
289 2 : CPLPushErrorHandler(CPLQuietErrorHandler);
290 2 : bRet = (CPLSpawn(argv, fp, tmpfp, TRUE) == 0);
291 2 : CPLPopErrorHandler();
292 :
293 2 : CSLDestroy(argv);
294 2 : argv = NULL;
295 :
296 2 : CPLErr nLastErrorType = CPLGetLastErrorType();
297 2 : int nLastErrorNo = CPLGetLastErrorNo();
298 2 : CPLString osLastErrorMsg = CPLGetLastErrorMsg();
299 :
300 2 : VSIFCloseL(tmpfp);
301 2 : tmpfp = NULL;
302 :
303 2 : VSIFCloseL(fp);
304 2 : fp = NULL;
305 :
306 2 : if (!bRet)
307 : {
308 0 : if (strstr(osLastErrorMsg.c_str(), "This format cannot be used in piped commands") == NULL)
309 : {
310 0 : CPLError(nLastErrorType, nLastErrorNo, "%s", osLastErrorMsg.c_str());
311 : }
312 : else
313 : {
314 : VSIStatBuf sStatBuf;
315 0 : if (VSIStat(pszFilename, &sStatBuf) != 0)
316 : {
317 : CPLError(CE_Failure, CPLE_NotSupported,
318 0 : "Driver %s only supports real (non virtual) files", pszGPSBabelDriverName);
319 0 : return FALSE;
320 : }
321 :
322 : /* Try without piping in */
323 : argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
324 0 : bTracks, pszGPSBabelDriverName, pszFilename);
325 0 : tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
326 0 : bRet = (CPLSpawn(argv, NULL, tmpfp, TRUE) == 0);
327 0 : VSIFCloseL(tmpfp);
328 0 : tmpfp = NULL;
329 :
330 0 : CSLDestroy(argv);
331 0 : argv = NULL;
332 : }
333 0 : }
334 : }
335 :
336 :
337 2 : if (bRet)
338 : {
339 2 : poGPXDS = OGRSFDriverRegistrar::Open(osTmpFileName.c_str());
340 2 : if (poGPXDS)
341 : {
342 : OGRLayer* poLayer;
343 :
344 2 : if (bWaypoints)
345 : {
346 2 : poLayer = poGPXDS->GetLayerByName("waypoints");
347 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
348 2 : apoLayers[nLayers++] = poLayer;
349 : }
350 :
351 2 : if (bRoutes)
352 : {
353 2 : poLayer = poGPXDS->GetLayerByName("routes");
354 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
355 0 : apoLayers[nLayers++] = poLayer;
356 2 : poLayer = poGPXDS->GetLayerByName("route_points");
357 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
358 0 : apoLayers[nLayers++] = poLayer;
359 : }
360 :
361 2 : if (bTracks)
362 : {
363 2 : poLayer = poGPXDS->GetLayerByName("tracks");
364 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
365 2 : apoLayers[nLayers++] = poLayer;
366 2 : poLayer = poGPXDS->GetLayerByName("track_points");
367 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
368 0 : apoLayers[nLayers++] = poLayer;
369 : }
370 : }
371 : }
372 :
373 2 : return nLayers > 0;
374 : }
375 :
376 : /************************************************************************/
377 : /* TestCapability() */
378 : /************************************************************************/
379 :
380 0 : int OGRGPSBabelDataSource::TestCapability( const char * pszCap )
381 :
382 : {
383 0 : return FALSE;
384 : }
385 :
386 : /************************************************************************/
387 : /* GetLayer() */
388 : /************************************************************************/
389 :
390 0 : OGRLayer *OGRGPSBabelDataSource::GetLayer( int iLayer )
391 :
392 : {
393 0 : if( iLayer < 0 || iLayer >= nLayers )
394 0 : return NULL;
395 : else
396 0 : return apoLayers[iLayer];
397 : }
|