bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
usage.cc
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// This file is part of libdap, A C++ implementation of the OPeNDAP Data
5// Access Protocol.
6
7// Copyright (c) 2002,2003 OPeNDAP, Inc.
8// Author: James Gallagher <jgallagher@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25
26// (c) COPYRIGHT URI/MIT 1996, 1999
27// Please read the full copyright statement in the file COPYRIGHT_URI.
28//
29// Authors:
30// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
31
32// The Usage server. Arguments: three arguments; filter options, the dataset
33// name and the pathname and `api prefix' of the data server. Returns a HTML
34// document that describes what information this dataset contains, special
35// characteristics of the server users might want to know and any special
36// information that the dataset providers want to make available. jhrg
37// 12/9/96
38
39#include "config.h"
40
41#include <iostream>
42#include <fstream>
43#include <string>
44#include <sstream>
45
46#include <libdap/Array.h>
47#include <libdap/Structure.h>
48#include <libdap/Sequence.h>
49#include <libdap/Grid.h>
50#include <libdap/Ancillary.h>
51#include <libdap/DAS.h>
52#include <libdap/util.h>
53
54#include "BESRegex.h"
55
56
57#include "usage.h"
58
59using namespace std;
60using namespace libdap;
61using namespace dap_usage;
62
63namespace dap_usage {
64
65static const BESRegex dim(".*_dim_[0-9]*", 1); // HDF dim attributes.
66static const BESRegex global("(.*global.*)|(.*dods.*)", 1);
67
68static bool
69name_in_kill_file(const string &name) {
70 return dim.match(name.c_str(), (int)name.size()) != -1;
71}
72
73static bool
74name_is_global(string &name) {
75 downcase(name);
76 return global.match(name.c_str(), (int)name.size()) != -1;
77}
78
79// write_global_attributes and write_attributes are almost the same except
80// that the global attributes use fancier formatting. The formatting could be
81// passed in as params, but that would make the code much harder to
82// understand. So, I'm keeping this as two separate functions even though
83// there's some duplication... 3/27/2002 jhrg
84static void
85write_global_attributes(ostringstream &oss, AttrTable *attr, const string &prefix = "") {
86 if (attr) {
87 AttrTable::Attr_iter a;
88 for (a = attr->attr_begin(); a != attr->attr_end(); a++) {
89 if (attr->is_container(a))
90 write_global_attributes(oss, attr->get_attr_table(a),
91 (prefix == "") ? attr->get_name(a)
92 : prefix + string(".") + attr->get_name(a));
93 else {
94 oss << "\n<tr><td align=right valign=top><b>";
95 if (prefix != "")
96 oss << prefix << "." << attr->get_name(a);
97 else
98 oss << attr->get_name(a);
99 oss << "</b>:</td>\n";
100
101 int num_attr = attr->get_attr_num(a) - 1;
102 oss << "<td align=left>";
103 for (int i = 0; i < num_attr; ++i)
104 oss << attr->get_attr(a, i) << ", ";
105 oss << attr->get_attr(a, num_attr) << "<br></td></tr>\n";
106 }
107 }
108 }
109}
110
111static void
112write_attributes(ostringstream &oss, AttrTable *attr, const string &prefix = "") {
113 if (attr) {
114 for (auto a = attr->attr_begin(); a != attr->attr_end(); a++) {
115 if (attr->is_container(a)) {
116 write_attributes(oss, attr->get_attr_table(a),
117 (prefix == "") ? attr->get_name(a)
118 : prefix + string(".") + attr->get_name(a));
119 }
120 else {
121 if (!prefix.empty())
122 oss << prefix << "." << attr->get_name(a);
123 else
124 oss << attr->get_name(a);
125 oss << ": ";
126
127 unsigned int num_attr = attr->get_attr_num(a) - 1;
128 for (int i = 0; i < num_attr; ++i)
129 oss << attr->get_attr(a, i) << ", ";
130 oss << attr->get_attr(a, num_attr) << "<br>\n";
131 }
132 }
133 }
134}
135
148static string
149build_global_attributes(DAS &das, const DDS &) {
150 bool found = false;
151 ostringstream ga;
152
153 ga << "<h3>Dataset Information</h3>\n<center>\n<table>\n";
154
155 for (auto p = das.var_begin(); p != das.var_end(); p++) {
156 string name = das.get_name(p);
157
158 // I used `name_in_dds' originally, but changed to `name_is_global'
159 // because aliases between groups of attributes can result in
160 // attribute group names which are not in the DDS and are *not*
161 // global attributes. jhrg. 5/22/97
162 if (!name_in_kill_file(name)) {
163 if (name_is_global(name)) {
164 AttrTable *attr = das.get_table(p);
165 found = true;
166 write_global_attributes(ga, attr, "");
167 }
168 }
169 }
170
171 ga << "</table>\n</center><p>\n";
172
173 if (found)
174 return ga.str();
175
176 return "";
177}
178
179static string
180fancy_typename(BaseType *v) {
181 string fancy;
182 switch (v->type()) {
183 case dods_byte_c:
184 return "Byte";
185 case dods_int16_c:
186 return "16 bit Integer";
187 case dods_uint16_c:
188 return "16 bit Unsigned integer";
189 case dods_int32_c:
190 return "32 bit Integer";
191 case dods_uint32_c:
192 return "32 bit Unsigned integer";
193 case dods_float32_c:
194 return "32 bit Real";
195 case dods_float64_c:
196 return "64 bit Real";
197 case dods_str_c:
198 return "String";
199 case dods_url_c:
200 return "URL";
201 case dods_array_c: {
202 ostringstream type;
203 Array *a = (Array *) v;
204 type << "Array of " << fancy_typename(a->var()) << "s ";
205 for (Array::Dim_iter p = a->dim_begin(); p != a->dim_end(); p++) {
206 type << "[" << a->dimension_name(p) << " = 0.."
207 << a->dimension_size(p, false) - 1 << "]";
208 }
209 return type.str();
210 }
211
212 case dods_structure_c:
213 return "Structure";
214 case dods_sequence_c:
215 return "Sequence";
216 case dods_grid_c:
217 return "Grid";
218 default:
219 return "Unknown";
220 }
221}
222
223static void
224write_variable(BaseType *btp, DAS &das, ostringstream &vs) {
225 vs << "<td align=right valign=top><b>" << btp->name()
226 << "</b>:</td>\n"
227 << "<td align=left valign=top>" << fancy_typename(btp)
228 << "<br>";
229
230 AttrTable *attr = das.get_table(btp->name());
231
232 write_attributes(vs, attr, "");
233
234 switch (btp->type()) {
235 case dods_byte_c:
236 case dods_int16_c:
237 case dods_uint16_c:
238 case dods_int32_c:
239 case dods_uint32_c:
240 case dods_float32_c:
241 case dods_float64_c:
242 case dods_str_c:
243 case dods_url_c:
244 case dods_array_c:
245 vs << "</td>\n";
246 break;
247
248 case dods_structure_c: {
249 vs << "<table>\n";
250 Structure *sp = dynamic_cast<Structure *>(btp);
251 for (Constructor::Vars_iter p = sp->var_begin(); p != sp->var_end(); p++) {
252 vs << "<tr>";
253 write_variable((*p), das, vs);
254 vs << "</tr>";
255 }
256 vs << "</table>\n";
257 break;
258 }
259
260 case dods_sequence_c: {
261 vs << "<table>\n";
262 Sequence *sp = dynamic_cast<Sequence *>(btp);
263 for (Constructor::Vars_iter p = sp->var_begin(); p != sp->var_end(); p++) {
264 vs << "<tr>";
265 write_variable((*p), das, vs);
266 vs << "</tr>";
267 }
268 vs << "</table>\n";
269 break;
270 }
271
272 case dods_grid_c: {
273 vs << "<table>\n";
274 Grid *gp = dynamic_cast<Grid *>(btp);
275 write_variable(gp->array_var(), das, vs);
276 Grid::Map_iter p;
277 for (p = gp->map_begin(); p != gp->map_end(); p++) {
278 vs << "<tr>";
279 write_variable((*p), das, vs);
280 vs << "</tr>";
281 }
282 vs << "</table>\n";
283 break;
284 }
285
286 default:
287 throw InternalErr(__FILE__, __LINE__, "Unknown type");
288 }
289}
290
298
299static string
300build_variable_summaries(DAS &das, DDS &dds) {
301 ostringstream vs;
302 vs << "<h3>Variables in this Dataset</h3>\n<center>\n<table>\n";
303 // vs << "<tr><th>Variable</th><th>Information</th></tr>\n";
304
305 for (DDS::Vars_iter p = dds.var_begin(); p != dds.var_end(); p++) {
306 vs << "<tr>";
307 write_variable((*p), das, vs);
308 vs << "</tr>";
309 }
310
311 vs << "</table>\n</center><p>\n";
312
313 return vs.str();
314}
315
316void
317html_header(ostream &strm) {
318 strm << "HTTP/1.0 200 OK\r\n";
319 strm << "XDODS-Server: " << PACKAGE_VERSION << "\r\n";
320 strm << "XDAP: " << DAP_PROTOCOL_VERSION << "\r\n";
321 strm << "Content-type: text/html\r\n";
322 strm << "Content-Description: dods_description\r\n";
323 strm << "\r\n"; // MIME header ends with a blank line
324}
325
342void
343write_usage_response(ostream &strm, DDS &dds, DAS &das, const string &dataset_name, const string &server_name,
344 bool httpheader) {
345 // This will require some hacking in libdap; maybe that code should
346 // move here? jhrg
347 string user_html = get_user_supplied_docs(dataset_name, server_name);
348
349 string global_attrs = build_global_attributes(das, dds);
350
351 string variable_sum = build_variable_summaries(das, dds);
352
353 // Write out the HTML document.
354
355 if (httpheader)
356 html_header(strm);
357
358 strm << "<html><head><title>Dataset Information</title></head>"
359 << "\n" << "<body>" << "\n";
360
361 if (global_attrs.size()) {
362 strm << global_attrs.c_str() << "\n" << "<hr>" << "\n";
363 }
364
365 strm << variable_sum.c_str() << "\n";
366
367 strm << "<hr>\n";
368
369 strm << user_html.c_str() << "\n";
370
371 strm << "</body>\n</html>\n";
372}
373
395
396string
397get_user_supplied_docs(const string &name, const string &cgi) {
398 char tmp[256];
399 ostringstream oss;
400 ifstream ifs((cgi + ".html").c_str());
401
402 if (ifs) {
403 while (!ifs.eof()) {
404 ifs.getline(tmp, 255);
405 oss << tmp << "\n";
406 }
407 ifs.close();
408
409 oss << "<hr>";
410 }
411
412 // Problem: This code is run with the CWD as the CGI-BIN directory but
413 // the data are in DocumentRoot (and we don't have the pathname of the
414 // data relative to DocumentRoot). So the only time this will work is
415 // when the server is in the same directory as the data. See bug 815.
416 // 10/08/04 jhrg
417 ifs.open((name + ".html").c_str());
418
419 // If name.html cannot be opened, look for basename.html
420 if (!ifs) {
421 string new_name = Ancillary::find_group_ancillary_file(name, ".html");
422 if (new_name != "")
423 ifs.open(new_name.c_str());
424 }
425
426 if (ifs) {
427 while (!ifs.eof()) {
428 ifs.getline(tmp, 255);
429 oss << tmp << "\n";
430 }
431 ifs.close();
432 }
433
434 return oss.str();
435}
436
437} // namespace dap_usage