1 : /******************************************************************************
2 : * $Id: ogrgftdatasource.cpp 25483 2013-01-10 17:06:59Z warmerdam $
3 : *
4 : * Project: Google Fusion Table Translator
5 : * Purpose: Implements OGRGFTDataSource class
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2011, Even Rouault <even dot rouault at mines dash 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_gft.h"
31 :
32 : CPL_CVSID("$Id: ogrgftdatasource.cpp 25483 2013-01-10 17:06:59Z warmerdam $");
33 :
34 : #define GDAL_API_KEY "AIzaSyA_2h1_wXMOLHNSVeo-jf1ACME-M1XMgP0"
35 : #define FUSION_TABLE_SCOPE "https://www.googleapis.com/Fauth/fusiontables"
36 :
37 : /************************************************************************/
38 : /* OGRGFTDataSource() */
39 : /************************************************************************/
40 :
41 142 : OGRGFTDataSource::OGRGFTDataSource()
42 :
43 : {
44 142 : papoLayers = NULL;
45 142 : nLayers = 0;
46 :
47 142 : pszName = NULL;
48 :
49 142 : bReadWrite = FALSE;
50 142 : bUseHTTPS = FALSE;
51 :
52 142 : bMustCleanPersistant = FALSE;
53 142 : }
54 :
55 : /************************************************************************/
56 : /* ~OGRGFTDataSource() */
57 : /************************************************************************/
58 :
59 142 : OGRGFTDataSource::~OGRGFTDataSource()
60 :
61 : {
62 337 : for( int i = 0; i < nLayers; i++ )
63 195 : delete papoLayers[i];
64 142 : CPLFree( papoLayers );
65 :
66 142 : if (bMustCleanPersistant)
67 : {
68 8 : char** papszOptions = CSLAddString(NULL, CPLSPrintf("CLOSE_PERSISTENT=GFT:%p", this));
69 8 : CPLHTTPFetch( GetAPIURL(), papszOptions);
70 8 : CSLDestroy(papszOptions);
71 : }
72 :
73 142 : CPLFree( pszName );
74 142 : }
75 :
76 : /************************************************************************/
77 : /* TestCapability() */
78 : /************************************************************************/
79 :
80 3 : int OGRGFTDataSource::TestCapability( const char * pszCap )
81 :
82 : {
83 3 : if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) )
84 3 : return TRUE;
85 0 : else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) )
86 0 : return TRUE;
87 : else
88 0 : return FALSE;
89 : }
90 :
91 : /************************************************************************/
92 : /* GetLayer() */
93 : /************************************************************************/
94 :
95 175 : OGRLayer *OGRGFTDataSource::GetLayer( int iLayer )
96 :
97 : {
98 175 : if( iLayer < 0 || iLayer >= nLayers )
99 0 : return NULL;
100 : else
101 175 : return papoLayers[iLayer];
102 : }
103 :
104 : /************************************************************************/
105 : /* GetLayerByName() */
106 : /************************************************************************/
107 :
108 8 : OGRLayer *OGRGFTDataSource::GetLayerByName(const char * pszLayerName)
109 : {
110 8 : OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName);
111 8 : if (poLayer)
112 5 : return poLayer;
113 :
114 3 : char* pszGeomColumnName = NULL;
115 3 : char* pszName = CPLStrdup(pszLayerName);
116 3 : char *pszLeftParenthesis = strchr(pszName, '(');
117 3 : if( pszLeftParenthesis != NULL )
118 : {
119 0 : *pszLeftParenthesis = '\0';
120 0 : pszGeomColumnName = CPLStrdup(pszLeftParenthesis+1);
121 0 : int len = strlen(pszGeomColumnName);
122 0 : if (len > 0 && pszGeomColumnName[len - 1] == ')')
123 0 : pszGeomColumnName[len - 1] = '\0';
124 : }
125 :
126 3 : CPLString osTableId(pszName);
127 85 : for(int i=0;i<nLayers;i++)
128 : {
129 82 : if( strcmp(papoLayers[i]->GetName(), pszName) == 0)
130 : {
131 0 : osTableId = ((OGRGFTTableLayer*)papoLayers[i])->GetTableId();
132 0 : break;
133 : }
134 : }
135 :
136 : poLayer = new OGRGFTTableLayer(this, pszLayerName, osTableId,
137 3 : pszGeomColumnName);
138 3 : CPLFree(pszName);
139 3 : CPLFree(pszGeomColumnName);
140 3 : if (poLayer->GetLayerDefn()->GetFieldCount() == 0)
141 : {
142 3 : delete poLayer;
143 3 : return NULL;
144 : }
145 0 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
146 0 : papoLayers[nLayers ++] = poLayer;
147 0 : return poLayer;
148 : }
149 :
150 : /************************************************************************/
151 : /* OGRGFTGetOptionValue() */
152 : /************************************************************************/
153 :
154 32 : CPLString OGRGFTGetOptionValue(const char* pszFilename,
155 : const char* pszOptionName)
156 : {
157 32 : CPLString osOptionName(pszOptionName);
158 32 : osOptionName += "=";
159 32 : const char* pszOptionValue = strstr(pszFilename, osOptionName);
160 32 : if (!pszOptionValue)
161 24 : return "";
162 :
163 8 : CPLString osOptionValue(pszOptionValue + strlen(osOptionName));
164 8 : const char* pszSpace = strchr(osOptionValue.c_str(), ' ');
165 8 : if (pszSpace)
166 0 : osOptionValue.resize(pszSpace - osOptionValue.c_str());
167 8 : return osOptionValue;
168 : }
169 :
170 : /************************************************************************/
171 : /* Open() */
172 : /************************************************************************/
173 :
174 142 : int OGRGFTDataSource::Open( const char * pszFilename, int bUpdateIn)
175 :
176 : {
177 142 : if (!EQUALN(pszFilename, "GFT:", 4))
178 134 : return FALSE;
179 :
180 8 : bReadWrite = bUpdateIn;
181 :
182 8 : pszName = CPLStrdup( pszFilename );
183 :
184 8 : osAuth = OGRGFTGetOptionValue(pszFilename, "auth");
185 8 : if (osAuth.size() == 0)
186 8 : osAuth = CPLGetConfigOption("GFT_AUTH", "");
187 :
188 8 : osRefreshToken = OGRGFTGetOptionValue(pszFilename, "refresh");
189 8 : if (osRefreshToken.size() == 0)
190 1 : osRefreshToken = CPLGetConfigOption("GFT_REFRESH_TOKEN", "");
191 :
192 8 : osAPIKey = CPLGetConfigOption("GFT_APIKEY", GDAL_API_KEY);
193 :
194 8 : CPLString osTables = OGRGFTGetOptionValue(pszFilename, "tables");
195 :
196 8 : bUseHTTPS = TRUE;
197 :
198 16 : osAccessToken = OGRGFTGetOptionValue(pszFilename, "access");
199 8 : if (osAccessToken.size() == 0)
200 8 : osAccessToken = CPLGetConfigOption("GFT_ACCESS_TOKEN","");
201 8 : if (osAccessToken.size() == 0 && osRefreshToken.size() > 0)
202 : {
203 : osAccessToken.Seize(GOA2GetAccessToken(osRefreshToken,
204 7 : FUSION_TABLE_SCOPE));
205 7 : if (osAccessToken.size() == 0)
206 0 : return FALSE;
207 : }
208 :
209 8 : if (osAccessToken.size() == 0 && osAuth.size() > 0)
210 : {
211 0 : osRefreshToken.Seize(GOA2GetRefreshToken(osAuth, FUSION_TABLE_SCOPE));
212 0 : if (osRefreshToken.size() == 0)
213 0 : return FALSE;
214 : }
215 :
216 8 : if (osAccessToken.size() == 0)
217 : {
218 1 : if (osTables.size() == 0)
219 : {
220 : CPLError(CE_Failure, CPLE_AppDefined,
221 0 : "Unauthenticated access requires explicit tables= parameter");
222 0 : return FALSE;
223 : }
224 : }
225 :
226 8 : if (osTables.size() != 0)
227 : {
228 1 : char** papszTables = CSLTokenizeString2(osTables, ",", 0);
229 4 : for(int i=0;papszTables && papszTables[i];i++)
230 : {
231 1 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
232 1 : papoLayers[nLayers ++] = new OGRGFTTableLayer(this, papszTables[i], papszTables[i]);
233 : }
234 1 : CSLDestroy(papszTables);
235 1 : return TRUE;
236 : }
237 :
238 : /* Get list of tables */
239 7 : CPLHTTPResult * psResult = RunSQL("SHOW TABLES");
240 :
241 7 : if (psResult == NULL)
242 0 : return FALSE;
243 :
244 7 : char* pszLine = (char*) psResult->pabyData;
245 7 : if (pszLine == NULL ||
246 : psResult->pszErrBuf != NULL ||
247 : strncmp(pszLine, "table id,name", strlen("table id,name")) != 0)
248 : {
249 0 : CPLHTTPDestroyResult(psResult);
250 0 : return FALSE;
251 : }
252 :
253 7 : pszLine = OGRGFTGotoNextLine(pszLine);
254 236 : while(pszLine != NULL && *pszLine != 0)
255 : {
256 222 : char* pszNextLine = OGRGFTGotoNextLine(pszLine);
257 222 : if (pszNextLine)
258 222 : pszNextLine[-1] = 0;
259 :
260 222 : char** papszTokens = CSLTokenizeString2(pszLine, ",", 0);
261 222 : if (CSLCount(papszTokens) == 2)
262 : {
263 194 : CPLString osTableId(papszTokens[0]);
264 194 : CPLString osLayerName(papszTokens[1]);
265 2787 : for(int i=0;i<nLayers;i++)
266 : {
267 2593 : if (strcmp(papoLayers[i]->GetName(), osLayerName) == 0)
268 : {
269 0 : osLayerName += " (";
270 0 : osLayerName += osTableId;
271 0 : osLayerName += ")";
272 0 : break;
273 : }
274 : }
275 194 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
276 194 : papoLayers[nLayers ++] = new OGRGFTTableLayer(this, osLayerName, osTableId);
277 : }
278 222 : CSLDestroy(papszTokens);
279 :
280 222 : pszLine = pszNextLine;
281 : }
282 :
283 7 : CPLHTTPDestroyResult(psResult);
284 :
285 7 : return TRUE;
286 : }
287 :
288 : /************************************************************************/
289 : /* GetAPIURL() */
290 : /************************************************************************/
291 :
292 48 : const char* OGRGFTDataSource::GetAPIURL() const
293 : {
294 48 : const char* pszAPIURL = CPLGetConfigOption("GFT_API_URL", NULL);
295 48 : if (pszAPIURL)
296 0 : return pszAPIURL;
297 48 : else if (bUseHTTPS)
298 48 : return "https://www.googleapis.com/fusiontables/v1/query";
299 : else
300 0 : return "http://www.googleapis.com/fusiontables/v1/query";
301 : }
302 :
303 : /************************************************************************/
304 : /* CreateLayer() */
305 : /************************************************************************/
306 :
307 4 : OGRLayer *OGRGFTDataSource::CreateLayer( const char *pszName,
308 : OGRSpatialReference *poSpatialRef,
309 : OGRwkbGeometryType eGType,
310 : char ** papszOptions )
311 : {
312 4 : if (!bReadWrite)
313 : {
314 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode");
315 0 : return NULL;
316 : }
317 :
318 4 : if (osAccessToken.size() == 0)
319 : {
320 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode");
321 0 : return NULL;
322 : }
323 :
324 : /* -------------------------------------------------------------------- */
325 : /* Do we already have this layer? If so, should we blow it */
326 : /* away? */
327 : /* -------------------------------------------------------------------- */
328 : int iLayer;
329 :
330 113 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
331 : {
332 109 : if( EQUAL(pszName,papoLayers[iLayer]->GetName()) )
333 : {
334 0 : if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
335 : && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
336 : {
337 0 : DeleteLayer( pszName );
338 0 : break;
339 : }
340 : else
341 : {
342 : CPLError( CE_Failure, CPLE_AppDefined,
343 : "Layer %s already exists, CreateLayer failed.\n"
344 : "Use the layer creation option OVERWRITE=YES to "
345 : "replace it.",
346 0 : pszName );
347 0 : return NULL;
348 : }
349 : }
350 : }
351 :
352 4 : OGRGFTTableLayer* poLayer = new OGRGFTTableLayer(this, pszName);
353 4 : poLayer->SetGeometryType(eGType);
354 4 : papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*));
355 4 : papoLayers[nLayers ++] = poLayer;
356 4 : return poLayer;
357 : }
358 :
359 : /************************************************************************/
360 : /* DeleteLayer() */
361 : /************************************************************************/
362 :
363 4 : void OGRGFTDataSource::DeleteLayer( const char *pszLayerName )
364 :
365 : {
366 : int iLayer;
367 :
368 : /* -------------------------------------------------------------------- */
369 : /* Try to find layer. */
370 : /* -------------------------------------------------------------------- */
371 32 : for( iLayer = 0; iLayer < nLayers; iLayer++ )
372 : {
373 32 : if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) )
374 4 : break;
375 : }
376 :
377 4 : if( iLayer == nLayers )
378 : {
379 : CPLError( CE_Failure, CPLE_AppDefined,
380 : "Attempt to delete layer '%s', but this layer is not known to OGR.",
381 0 : pszLayerName );
382 0 : return;
383 : }
384 :
385 4 : DeleteLayer(iLayer);
386 : }
387 :
388 : /************************************************************************/
389 : /* DeleteLayer() */
390 : /************************************************************************/
391 :
392 4 : OGRErr OGRGFTDataSource::DeleteLayer(int iLayer)
393 : {
394 4 : if (!bReadWrite)
395 : {
396 : CPLError(CE_Failure, CPLE_AppDefined,
397 0 : "Operation not available in read-only mode");
398 0 : return OGRERR_FAILURE;
399 : }
400 :
401 4 : if (osAccessToken.size() == 0)
402 : {
403 : CPLError(CE_Failure, CPLE_AppDefined,
404 0 : "Operation not available in unauthenticated mode");
405 0 : return OGRERR_FAILURE;
406 : }
407 :
408 4 : if( iLayer < 0 || iLayer >= nLayers )
409 : {
410 : CPLError( CE_Failure, CPLE_AppDefined,
411 : "Layer %d not in legal range of 0 to %d.",
412 0 : iLayer, nLayers-1 );
413 0 : return OGRERR_FAILURE;
414 : }
415 :
416 4 : CPLString osTableId = ((OGRGFTTableLayer*)papoLayers[iLayer])->GetTableId();
417 4 : CPLString osLayerName = GetLayer(iLayer)->GetName();
418 :
419 : /* -------------------------------------------------------------------- */
420 : /* Blow away our OGR structures related to the layer. This is */
421 : /* pretty dangerous if anything has a reference to this layer! */
422 : /* -------------------------------------------------------------------- */
423 4 : CPLDebug( "GFT", "DeleteLayer(%s)", osLayerName.c_str() );
424 :
425 4 : delete papoLayers[iLayer];
426 : memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
427 4 : sizeof(void *) * (nLayers - iLayer - 1) );
428 4 : nLayers--;
429 :
430 : /* -------------------------------------------------------------------- */
431 : /* Remove from the database. */
432 : /* -------------------------------------------------------------------- */
433 :
434 4 : CPLString osSQL("DROP TABLE ");
435 4 : osSQL += osTableId;
436 :
437 4 : CPLHTTPResult* psResult = RunSQL( osSQL );
438 :
439 4 : if (psResult == NULL || psResult->nStatus != 0)
440 : {
441 0 : CPLError(CE_Failure, CPLE_AppDefined, "Table deletion failed (1)");
442 0 : CPLHTTPDestroyResult(psResult);
443 0 : return OGRERR_FAILURE;
444 : }
445 :
446 4 : CPLHTTPDestroyResult(psResult);
447 :
448 4 : return OGRERR_NONE;
449 : }
450 :
451 : /************************************************************************/
452 : /* AddHTTPOptions() */
453 : /************************************************************************/
454 :
455 40 : char** OGRGFTDataSource::AddHTTPOptions(char** papszOptions)
456 : {
457 40 : bMustCleanPersistant = TRUE;
458 :
459 40 : if (strlen(osAccessToken) > 0)
460 : papszOptions = CSLAddString(papszOptions,
461 : CPLSPrintf("HEADERS=Authorization: Bearer %s",
462 37 : osAccessToken.c_str()));
463 :
464 40 : return CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=GFT:%p", this));
465 : }
466 :
467 : /************************************************************************/
468 : /* RunSQL() */
469 : /************************************************************************/
470 :
471 40 : CPLHTTPResult * OGRGFTDataSource::RunSQL(const char* pszUnescapedSQL)
472 : {
473 40 : CPLString osSQL("POSTFIELDS=sql=");
474 : /* Do post escaping */
475 4146 : for(int i=0;pszUnescapedSQL[i] != 0;i++)
476 : {
477 4106 : const int ch = ((unsigned char*)pszUnescapedSQL)[i];
478 8212 : if (ch != '&' && ch >= 32 && ch < 128)
479 4106 : osSQL += (char)ch;
480 : else
481 0 : osSQL += CPLSPrintf("%%%02X", ch);
482 : }
483 :
484 : /* -------------------------------------------------------------------- */
485 : /* Provide the API Key - used to rate limit access (see */
486 : /* GFT_APIKEY config) */
487 : /* -------------------------------------------------------------------- */
488 40 : osSQL += "&key=";
489 40 : osSQL += osAPIKey;
490 :
491 : /* -------------------------------------------------------------------- */
492 : /* Force old style CSV output from calls - maybe we want to */
493 : /* migrate to JSON output at some point? */
494 : /* -------------------------------------------------------------------- */
495 40 : osSQL += "&alt=csv";
496 :
497 : /* -------------------------------------------------------------------- */
498 : /* Collection the header options and execute request. */
499 : /* -------------------------------------------------------------------- */
500 40 : char** papszOptions = CSLAddString(AddHTTPOptions(), osSQL);
501 40 : CPLHTTPResult * psResult = CPLHTTPFetch( GetAPIURL(), papszOptions);
502 40 : CSLDestroy(papszOptions);
503 :
504 : /* -------------------------------------------------------------------- */
505 : /* Check for some error conditions and report. HTML Messages */
506 : /* are transformed info failure. */
507 : /* -------------------------------------------------------------------- */
508 40 : if (psResult && psResult->pszContentType &&
509 : strncmp(psResult->pszContentType, "text/html", 9) == 0)
510 : {
511 3 : CPLDebug( "GFT", "RunSQL HTML Response:%s", psResult->pabyData );
512 : CPLError(CE_Failure, CPLE_AppDefined,
513 3 : "HTML error page returned by server");
514 3 : CPLHTTPDestroyResult(psResult);
515 3 : psResult = NULL;
516 : }
517 40 : if (psResult && psResult->pszErrBuf != NULL)
518 : {
519 0 : CPLDebug( "GFT", "RunSQL Error Message:%s", psResult->pszErrBuf );
520 : }
521 40 : else if (psResult && psResult->nStatus != 0)
522 : {
523 0 : CPLDebug( "GFT", "RunSQL Error Status:%d", psResult->nStatus );
524 : }
525 :
526 40 : return psResult;
527 : }
528 :
529 : /************************************************************************/
530 : /* ExecuteSQL() */
531 : /************************************************************************/
532 :
533 5 : OGRLayer * OGRGFTDataSource::ExecuteSQL( const char *pszSQLCommand,
534 : OGRGeometry *poSpatialFilter,
535 : const char *pszDialect )
536 :
537 : {
538 5 : if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") )
539 : return OGRDataSource::ExecuteSQL( pszSQLCommand,
540 : poSpatialFilter,
541 0 : pszDialect );
542 :
543 : /* -------------------------------------------------------------------- */
544 : /* Special case DELLAYER: command. */
545 : /* -------------------------------------------------------------------- */
546 5 : if( EQUALN(pszSQLCommand,"DELLAYER:",9) )
547 : {
548 4 : const char *pszLayerName = pszSQLCommand + 9;
549 :
550 8 : while( *pszLayerName == ' ' )
551 0 : pszLayerName++;
552 :
553 4 : DeleteLayer( pszLayerName );
554 4 : return NULL;
555 : }
556 :
557 : /* -------------------------------------------------------------------- */
558 : /* Create layer. */
559 : /* -------------------------------------------------------------------- */
560 1 : OGRGFTResultLayer *poLayer = NULL;
561 :
562 1 : CPLString osSQL = pszSQLCommand;
563 1 : poLayer = new OGRGFTResultLayer( this, osSQL );
564 2 : if (!poLayer->RunSQL())
565 : {
566 0 : delete poLayer;
567 0 : return NULL;
568 : }
569 :
570 1 : if( poSpatialFilter != NULL )
571 0 : poLayer->SetSpatialFilter( poSpatialFilter );
572 :
573 1 : return poLayer;
574 : }
575 :
576 : /************************************************************************/
577 : /* ReleaseResultSet() */
578 : /************************************************************************/
579 :
580 1 : void OGRGFTDataSource::ReleaseResultSet( OGRLayer * poLayer )
581 :
582 : {
583 1 : delete poLayer;
584 1 : }
585 :
586 : /************************************************************************/
587 : /* OGRGFTGotoNextLine() */
588 : /************************************************************************/
589 :
590 608 : char* OGRGFTGotoNextLine(char* pszData)
591 : {
592 608 : char* pszNextLine = strchr(pszData, '\n');
593 608 : if (pszNextLine)
594 608 : return pszNextLine + 1;
595 0 : return NULL;
596 : }
|