Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRGPSBabelDataSource class.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_conv.h"
30 : #include "cpl_string.h"
31 : #include "cpl_error.h"
32 : #include "cpl_spawn.h"
33 : #include "ogr_gpsbabel.h"
34 :
35 : #include <cstring>
36 : #include <algorithm>
37 :
38 : /************************************************************************/
39 : /* OGRGPSBabelDataSource() */
40 : /************************************************************************/
41 :
42 10 : OGRGPSBabelDataSource::OGRGPSBabelDataSource()
43 : {
44 10 : }
45 :
46 : /************************************************************************/
47 : /* ~OGRGPSBabelDataSource() */
48 : /************************************************************************/
49 :
50 20 : OGRGPSBabelDataSource::~OGRGPSBabelDataSource()
51 :
52 : {
53 10 : CPLFree(pszName);
54 10 : CPLFree(pszGPSBabelDriverName);
55 10 : CPLFree(pszFilename);
56 :
57 10 : OGRGPSBabelDataSource::CloseDependentDatasets();
58 :
59 10 : if (!osTmpFileName.empty())
60 6 : VSIUnlink(osTmpFileName.c_str());
61 20 : }
62 :
63 : /************************************************************************/
64 : /* CloseDependentDatasets() */
65 : /************************************************************************/
66 :
67 10 : int OGRGPSBabelDataSource::CloseDependentDatasets()
68 : {
69 10 : if (poGPXDS == nullptr)
70 5 : return FALSE;
71 :
72 5 : GDALClose(poGPXDS);
73 5 : poGPXDS = nullptr;
74 5 : return TRUE;
75 : }
76 :
77 : /************************************************************************/
78 : /* GetArgv() */
79 : /************************************************************************/
80 :
81 5 : static char **GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes,
82 : int bTracks, const char *pszGPSBabelDriverName,
83 : const char *pszFilename)
84 : {
85 5 : char **argv = CSLAddString(nullptr, "gpsbabel");
86 5 : if (bExplicitFeatures)
87 : {
88 3 : if (bWaypoints)
89 1 : argv = CSLAddString(argv, "-w");
90 3 : if (bRoutes)
91 1 : argv = CSLAddString(argv, "-r");
92 3 : if (bTracks)
93 1 : argv = CSLAddString(argv, "-t");
94 : }
95 5 : argv = CSLAddString(argv, "-i");
96 5 : argv = CSLAddString(argv, pszGPSBabelDriverName);
97 5 : argv = CSLAddString(argv, "-f");
98 5 : argv = CSLAddString(argv, pszFilename);
99 5 : argv = CSLAddString(argv, "-o");
100 5 : argv = CSLAddString(argv, "gpx,gpxver=1.1");
101 5 : argv = CSLAddString(argv, "-F");
102 5 : argv = CSLAddString(argv, "-");
103 :
104 5 : return argv;
105 : }
106 :
107 : /************************************************************************/
108 : /* IsSpecialFile() */
109 : /************************************************************************/
110 :
111 7 : bool OGRGPSBabelDataSource::IsSpecialFile(const char *pszFilename)
112 : {
113 14 : return STARTS_WITH(pszFilename, "/dev/") ||
114 14 : STARTS_WITH(pszFilename, "usb:") ||
115 14 : (STARTS_WITH(pszFilename, "COM") && atoi(pszFilename + 3) > 0);
116 : }
117 :
118 : /************************************************************************/
119 : /* IsValidDriverName() */
120 : /************************************************************************/
121 :
122 8 : bool OGRGPSBabelDataSource::IsValidDriverName(const char *pszGPSBabelDriverName)
123 : {
124 34 : for (int i = 0; pszGPSBabelDriverName[i] != '\0'; i++)
125 : {
126 26 : char ch = pszGPSBabelDriverName[i];
127 26 : if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
128 0 : (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' ||
129 : ch == ','))
130 : {
131 0 : CPLError(CE_Failure, CPLE_AppDefined,
132 : "Invalid GPSBabel driver name");
133 0 : return false;
134 : }
135 : }
136 8 : return true;
137 : }
138 :
139 : /************************************************************************/
140 : /* Open() */
141 : /************************************************************************/
142 :
143 10 : int OGRGPSBabelDataSource::Open(const char *pszDatasourceName,
144 : const char *pszGPSBabelDriverNameIn,
145 : char **papszOpenOptionsIn)
146 :
147 : {
148 10 : constexpr const char *GPSBABEL_PREFIX = "GPSBABEL:";
149 10 : if (!STARTS_WITH_CI(pszDatasourceName, GPSBABEL_PREFIX))
150 : {
151 1 : CPLAssert(pszGPSBabelDriverNameIn);
152 1 : pszGPSBabelDriverName = CPLStrdup(pszGPSBabelDriverNameIn);
153 1 : pszFilename = CPLStrdup(pszDatasourceName);
154 : }
155 : else
156 : {
157 9 : if (CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"))
158 0 : pszFilename =
159 0 : CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"));
160 :
161 9 : if (CSLFetchNameValue(papszOpenOptionsIn, "GPSBABEL_DRIVER"))
162 : {
163 0 : if (pszFilename == nullptr)
164 : {
165 0 : CPLError(CE_Failure, CPLE_AppDefined, "Missing FILENAME");
166 0 : return FALSE;
167 : }
168 :
169 0 : pszGPSBabelDriverName =
170 0 : CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "DRIVER"));
171 :
172 : /* A bit of validation to avoid command line injection */
173 0 : if (!IsValidDriverName(pszGPSBabelDriverName))
174 0 : return FALSE;
175 : }
176 : }
177 :
178 10 : pszName = CPLStrdup(pszDatasourceName);
179 :
180 10 : bool bExplicitFeatures = false;
181 10 : bool bWaypoints = true;
182 10 : bool bTracks = true;
183 10 : bool bRoutes = true;
184 :
185 10 : if (pszGPSBabelDriverName == nullptr)
186 : {
187 9 : const char *pszDatasourceNameAfterPrefix =
188 : pszDatasourceName + strlen(GPSBABEL_PREFIX);
189 9 : const char *pszSep = strchr(pszDatasourceNameAfterPrefix, ':');
190 9 : if (pszSep == nullptr)
191 : {
192 2 : CPLError(CE_Failure, CPLE_AppDefined,
193 : "Wrong syntax. Expected GPSBabel:driver_name:file_name");
194 2 : return FALSE;
195 : }
196 :
197 7 : pszGPSBabelDriverName = CPLStrdup(pszDatasourceNameAfterPrefix);
198 7 : pszGPSBabelDriverName[pszSep - pszDatasourceNameAfterPrefix] = '\0';
199 :
200 : /* A bit of validation to avoid command line injection */
201 7 : if (!IsValidDriverName(pszGPSBabelDriverName))
202 0 : return FALSE;
203 :
204 : /* Parse optional features= option */
205 7 : const char *pszAfterSep = pszSep + 1;
206 7 : constexpr const char *FEATURES_EQUAL = "features=";
207 7 : if (STARTS_WITH_CI(pszAfterSep, FEATURES_EQUAL))
208 : {
209 5 : const char *pszAfterFeaturesEqual =
210 : pszAfterSep + strlen(FEATURES_EQUAL);
211 5 : const char *pszNextSep = strchr(pszAfterFeaturesEqual, ':');
212 5 : if (pszNextSep == nullptr)
213 : {
214 1 : CPLError(CE_Failure, CPLE_AppDefined,
215 : "Wrong syntax. Expected "
216 : "GPSBabel:driver_name[,options]*:["
217 : "features=waypoints,tracks,routes:]file_name");
218 1 : return FALSE;
219 : }
220 :
221 4 : char *pszFeatures = CPLStrdup(pszAfterFeaturesEqual);
222 4 : pszFeatures[pszNextSep - pszAfterFeaturesEqual] = 0;
223 4 : char **papszTokens = CSLTokenizeString(pszFeatures);
224 4 : char **papszIter = papszTokens;
225 4 : bool bErr = false;
226 4 : bExplicitFeatures = true;
227 4 : bWaypoints = false;
228 4 : bTracks = false;
229 4 : bRoutes = false;
230 8 : while (papszIter && *papszIter)
231 : {
232 4 : if (EQUAL(*papszIter, "waypoints"))
233 1 : bWaypoints = true;
234 3 : else if (EQUAL(*papszIter, "tracks"))
235 1 : bTracks = true;
236 2 : else if (EQUAL(*papszIter, "routes"))
237 1 : bRoutes = true;
238 : else
239 : {
240 1 : CPLError(CE_Failure, CPLE_AppDefined,
241 : "Wrong value for 'features' options");
242 1 : bErr = true;
243 : }
244 4 : papszIter++;
245 : }
246 4 : CSLDestroy(papszTokens);
247 4 : CPLFree(pszFeatures);
248 :
249 4 : if (bErr)
250 1 : return FALSE;
251 :
252 3 : pszAfterSep = pszNextSep + 1;
253 : }
254 :
255 5 : if (pszFilename == nullptr)
256 5 : pszFilename = CPLStrdup(pszAfterSep);
257 : }
258 :
259 : const char *pszOptionUseTempFile =
260 6 : CPLGetConfigOption("USE_TEMPFILE", nullptr);
261 6 : if (pszOptionUseTempFile && CPLTestBool(pszOptionUseTempFile))
262 0 : osTmpFileName = CPLGenerateTempFilename(nullptr);
263 : else
264 6 : osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this);
265 :
266 6 : bool bRet = false;
267 6 : if (IsSpecialFile(pszFilename))
268 : {
269 : /* Special file : don't try to open it */
270 0 : char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
271 0 : pszGPSBabelDriverName, pszFilename);
272 0 : VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
273 0 : bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
274 0 : VSIFCloseL(tmpfp);
275 0 : tmpfp = nullptr;
276 0 : CSLDestroy(argv);
277 0 : argv = nullptr;
278 : }
279 : else
280 : {
281 6 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
282 6 : if (fp == nullptr)
283 : {
284 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open file %s",
285 : pszFilename);
286 1 : return FALSE;
287 : }
288 :
289 10 : char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
290 5 : pszGPSBabelDriverName, "-");
291 :
292 5 : VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
293 :
294 5 : CPLPushErrorHandler(CPLQuietErrorHandler);
295 5 : bRet = (CPLSpawn(argv, fp, tmpfp, TRUE) == 0);
296 5 : CPLPopErrorHandler();
297 :
298 5 : CSLDestroy(argv);
299 5 : argv = nullptr;
300 :
301 5 : CPLErr nLastErrorType = CPLGetLastErrorType();
302 5 : CPLErrorNum nLastErrorNo = CPLGetLastErrorNo();
303 5 : CPLString osLastErrorMsg = CPLGetLastErrorMsg();
304 :
305 5 : VSIFCloseL(tmpfp);
306 5 : tmpfp = nullptr;
307 :
308 5 : VSIFCloseL(fp);
309 5 : fp = nullptr;
310 :
311 5 : if (!bRet)
312 : {
313 0 : if (strstr(osLastErrorMsg.c_str(),
314 0 : "This format cannot be used in piped commands") ==
315 : nullptr)
316 : {
317 0 : CPLError(nLastErrorType, nLastErrorNo, "%s",
318 : osLastErrorMsg.c_str());
319 : }
320 : else
321 : {
322 : VSIStatBuf sStatBuf;
323 0 : if (VSIStat(pszFilename, &sStatBuf) != 0)
324 : {
325 0 : CPLError(CE_Failure, CPLE_NotSupported,
326 : "Driver %s only supports real (non virtual) "
327 : "files",
328 : pszGPSBabelDriverName);
329 0 : return FALSE;
330 : }
331 :
332 : /* Try without piping in */
333 0 : argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
334 0 : pszGPSBabelDriverName, pszFilename);
335 0 : tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
336 0 : bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
337 0 : VSIFCloseL(tmpfp);
338 0 : tmpfp = nullptr;
339 :
340 0 : CSLDestroy(argv);
341 0 : argv = nullptr;
342 : }
343 : }
344 : }
345 :
346 5 : if (bRet)
347 : {
348 5 : poGPXDS = static_cast<GDALDataset *>(GDALOpenEx(
349 : osTmpFileName.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
350 5 : if (poGPXDS)
351 : {
352 5 : if (bWaypoints)
353 : {
354 3 : OGRLayer *poLayer = poGPXDS->GetLayerByName("waypoints");
355 3 : if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
356 3 : apoLayers[nLayers++] = poLayer;
357 : }
358 :
359 5 : if (bRoutes)
360 : {
361 3 : OGRLayer *poLayer = poGPXDS->GetLayerByName("routes");
362 3 : if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
363 1 : apoLayers[nLayers++] = poLayer;
364 3 : poLayer = poGPXDS->GetLayerByName("route_points");
365 3 : if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
366 1 : apoLayers[nLayers++] = poLayer;
367 : }
368 :
369 5 : if (bTracks)
370 : {
371 3 : OGRLayer *poLayer = poGPXDS->GetLayerByName("tracks");
372 3 : if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
373 3 : apoLayers[nLayers++] = poLayer;
374 3 : poLayer = poGPXDS->GetLayerByName("track_points");
375 3 : if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
376 1 : apoLayers[nLayers++] = poLayer;
377 : }
378 : }
379 : }
380 :
381 5 : return nLayers > 0;
382 : }
383 :
384 : /************************************************************************/
385 : /* TestCapability() */
386 : /************************************************************************/
387 :
388 0 : int OGRGPSBabelDataSource::TestCapability(const char * /* pszCap */)
389 : {
390 0 : return FALSE;
391 : }
392 :
393 : /************************************************************************/
394 : /* GetLayer() */
395 : /************************************************************************/
396 :
397 3 : OGRLayer *OGRGPSBabelDataSource::GetLayer(int iLayer)
398 :
399 : {
400 3 : if (iLayer < 0 || iLayer >= nLayers)
401 0 : return nullptr;
402 :
403 3 : return apoLayers[iLayer];
404 : }
|