1 /******************************************************************************
2  *
3  * Project:  OGR/DODS Interface
4  * Purpose:  Implements OGRDODSDataSource class.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2004, Frank Warmerdam
9  * Copyright (c) 2010, Even Rouault <even dot rouault at spatialys.com>
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_dods.h"
31 #include "cpl_conv.h"
32 #include "cpl_string.h"
33 
34 CPL_CVSID("$Id: ogrdodsdatasource.cpp b1c9c12ad373e40b955162b45d704070d4ebf7b0 2019-06-19 16:50:15 +0200 Even Rouault $")
35 /************************************************************************/
36 /*                         OGRDODSDataSource()                          */
37 /************************************************************************/
38 
OGRDODSDataSource()39 OGRDODSDataSource::OGRDODSDataSource() :
40     papoLayers(nullptr),
41     nLayers(0),
42     pszName(nullptr),
43     poConnection(nullptr),
44     poBTF(new BaseTypeFactory())
45 {
46     // TODO: This implies that the order in the class declaration is wrong.
47     poDDS = new DDS( poBTF );
48 }
49 
50 /************************************************************************/
51 /*                         ~OGRDODSDataSource()                         */
52 /************************************************************************/
53 
~OGRDODSDataSource()54 OGRDODSDataSource::~OGRDODSDataSource()
55 
56 {
57     CPLFree( pszName );
58 
59     for( int i = 0; i < nLayers; i++ )
60         delete papoLayers[i];
61 
62     CPLFree( papoLayers );
63 
64     if( poConnection != nullptr )
65         delete poConnection;
66 
67     delete poDDS;
68     delete poBTF;
69 }
70 
71 /************************************************************************/
72 /*                                Open()                                */
73 /************************************************************************/
74 
Open(const char * pszNewName)75 int OGRDODSDataSource::Open( const char * pszNewName )
76 
77 {
78     CPLAssert( nLayers == 0 );
79 
80     pszName = CPLStrdup( pszNewName );
81 
82 /* -------------------------------------------------------------------- */
83 /*      Parse the URL into a base url, projection and constraint        */
84 /*      expression.                                                     */
85 /* -------------------------------------------------------------------- */
86     char *pszWrkURL = CPLStrdup( pszNewName + 5 );
87     char *pszFound = strstr(pszWrkURL, "&");
88     if( pszFound )
89     {
90         oConstraints = pszFound;
91         *pszFound = '\0';
92     }
93 
94     pszFound = strstr(pszWrkURL,"?");
95     if( pszFound )
96     {
97         oProjection = pszFound+1;
98         *pszFound = '\0';
99     }
100 
101     // Trim common requests.
102     int nLen = static_cast<int>(strlen(pszWrkURL));
103     if( strcmp(pszWrkURL+nLen-4,".das") == 0 )
104         pszWrkURL[nLen-4] = '\0';
105     else if( strcmp(pszWrkURL+nLen-4,".dds") == 0 )
106         pszWrkURL[nLen-4] = '\0';
107     else if( strcmp(pszWrkURL+nLen-4,".asc") == 0 )
108         pszWrkURL[nLen-4] = '\0';
109     else if( strcmp(pszWrkURL+nLen-5,".dods") == 0 )
110         pszWrkURL[nLen-5] = '\0';
111     else if( strcmp(pszWrkURL+nLen-5,".html") == 0 )
112         pszWrkURL[nLen-5] = '\0';
113 
114     oBaseURL = pszWrkURL;
115     CPLFree( pszWrkURL );
116 
117 /* -------------------------------------------------------------------- */
118 /*      Do we want to override the .dodsrc file setting?  Only do       */
119 /*      the putenv() if there isn't already a DODS_CONF in the          */
120 /*      environment.                                                    */
121 /* -------------------------------------------------------------------- */
122     if( CPLGetConfigOption( "DODS_CONF", nullptr ) != nullptr
123         && getenv("DODS_CONF") == nullptr )
124     {
125         const int knBufSize = 1000;
126         static char szDODS_CONF[knBufSize];
127 
128         snprintf( szDODS_CONF, knBufSize, "DODS_CONF=%.980s",
129                   CPLGetConfigOption( "DODS_CONF", "" ) );
130         // coverity[tainted_string]
131         putenv( szDODS_CONF );
132     }
133 
134 /* -------------------------------------------------------------------- */
135 /*      If we have a overriding AIS file location, apply it now.       */
136 /* -------------------------------------------------------------------- */
137     if( CPLGetConfigOption( "DODS_AIS_FILE", nullptr ) != nullptr )
138     {
139         string oAISFile = CPLGetConfigOption( "DODS_AIS_FILE", "" );
140         RCReader::instance()->set_ais_database( oAISFile );
141     }
142 
143 /* -------------------------------------------------------------------- */
144 /*      Connect to the server.                                          */
145 /* -------------------------------------------------------------------- */
146     string version;
147 
148     try
149     {
150         poConnection = new AISConnect( oBaseURL );
151         version = poConnection->request_version();
152     }
153     catch (Error &e)
154     {
155         CPLError(CE_Failure, CPLE_OpenFailed,
156                  "%s", e.get_error_message().c_str() );
157         return FALSE;
158     }
159 
160 /* -------------------------------------------------------------------- */
161 /*      We presume we only work with version 3 servers.                 */
162 /* -------------------------------------------------------------------- */
163 
164     if (version.empty() || version.find("/3.") == string::npos)
165     {
166         CPLError( CE_Warning, CPLE_AppDefined,
167                   "I connected to the URL but could not get a DAP 3.x version string\n"
168                   "from the server.  I will continue to connect but access may fail.");
169     }
170 
171 /* -------------------------------------------------------------------- */
172 /*      Fetch the DAS and DDS info about the server.                    */
173 /* -------------------------------------------------------------------- */
174     try
175     {
176         poConnection->request_das( oDAS );
177         poConnection->request_dds( *poDDS, oProjection + oConstraints );
178     }
179     catch (Error &e)
180     {
181         CPLError(CE_Failure, CPLE_AppDefined,
182                  "Error fetching DAS or DDS:\n%s",
183                  e.get_error_message().c_str() );
184         return FALSE;
185     }
186 
187 /* -------------------------------------------------------------------- */
188 /*      Do we have any ogr_layer_info attributes in the DAS?  If so,    */
189 /*      use them to define the layers.                                  */
190 /* -------------------------------------------------------------------- */
191     AttrTable::Attr_iter dv_i;
192 
193 #ifdef LIBDAP_39
194     AttrTable* poTable = oDAS.container();
195     if (poTable == nullptr)
196     {
197         CPLError(CE_Failure, CPLE_AppDefined, "Cannot get container");
198         return FALSE;
199     }
200 #else
201     AttrTable* poTable = &oDAS;
202 #endif
203 
204     for( dv_i = poTable->attr_begin(); dv_i != poTable->attr_end(); dv_i++ )
205     {
206         if( STARTS_WITH_CI(poTable->get_name(dv_i).c_str(), "ogr_layer_info")
207             && poTable->is_container( dv_i ) )
208         {
209             AttrTable *poAttr = poTable->get_attr_table( dv_i );
210             string target_container = poAttr->get_attr( "target_container" );
211             BaseType *poVar = poDDS->var( target_container.c_str() );
212 
213             if( poVar == nullptr )
214             {
215                 CPLError( CE_Warning, CPLE_AppDefined,
216                           "Unable to find variable '%s' named in\n"
217                           "ogr_layer_info.target_container, skipping.",
218                           target_container.c_str() );
219                 continue;
220             }
221 
222             if( poVar->type() == dods_sequence_c )
223                 AddLayer(
224                     new OGRDODSSequenceLayer(this,
225                                              target_container.c_str(),
226                                              poAttr) );
227             else if( poVar->type() == dods_grid_c
228                      || poVar->type() == dods_array_c )
229                 AddLayer( new OGRDODSGridLayer(this,target_container.c_str(),
230                                                poAttr) );
231         }
232     }
233 
234 /* -------------------------------------------------------------------- */
235 /*      Walk through the DODS variables looking for easily targeted     */
236 /*      ones.  Eventually this will need to be driven by the AIS info.  */
237 /* -------------------------------------------------------------------- */
238     if( nLayers == 0 )
239     {
240         DDS::Vars_iter v_i;
241 
242         for( v_i = poDDS->var_begin(); v_i != poDDS->var_end(); v_i++ )
243         {
244             BaseType *poVar = *v_i;
245 
246             if( poVar->type() == dods_sequence_c )
247                 AddLayer( new OGRDODSSequenceLayer(this,poVar->name().c_str(),
248                                                    nullptr) );
249             else if( poVar->type() == dods_grid_c
250                      || poVar->type() == dods_array_c )
251                 AddLayer( new OGRDODSGridLayer(this,poVar->name().c_str(),
252                                                nullptr) );
253         }
254     }
255 
256     return TRUE;
257 }
258 
259 /************************************************************************/
260 /*                              AddLayer()                              */
261 /************************************************************************/
262 
AddLayer(OGRDODSLayer * poLayer)263 void OGRDODSDataSource::AddLayer( OGRDODSLayer *poLayer )
264 
265 {
266     papoLayers = (OGRDODSLayer **)
267         CPLRealloc( papoLayers,  sizeof(OGRDODSLayer *) * (nLayers+1) );
268     papoLayers[nLayers++] = poLayer;
269 }
270 
271 /************************************************************************/
272 /*                           TestCapability()                           */
273 /************************************************************************/
274 
TestCapability(const char * pszCap)275 int OGRDODSDataSource::TestCapability( const char * pszCap )
276 
277 {
278     return EQUAL(pszCap,ODsCCreateLayer);
279 }
280 
281 /************************************************************************/
282 /*                              GetLayer()                              */
283 /************************************************************************/
284 
GetLayer(int iLayer)285 OGRLayer *OGRDODSDataSource::GetLayer( int iLayer )
286 
287 {
288     if( iLayer < 0 || iLayer >= nLayers )
289         return nullptr;
290     else
291         return papoLayers[iLayer];
292 }
293