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