bes Updated for version 3.20.10
w10n_utils.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2//
3// w10n_utils.cc
4//
5// This file is part of BES w10n handler
6//
7// Copyright (c) 2015v OPeNDAP, Inc.
8// Author: Nathan Potter <ndp@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// Please read the full copyright statement in the file COPYRIGHT_URI.
26//
27
28#include "config.h"
29
30#include <sys/types.h>
31#include <sys/stat.h>
32
33#if HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37#include <cstdio>
38#include <cerrno>
39#include <cstring>
40#include <cstdlib>
41#include <sstream>
42#include <iostream>
43#include <iomanip>
44
45#include <libdap/BaseType.h>
46#include <libdap/DDS.h>
47#include <libdap/Constructor.h>
48#include <libdap/Array.h>
49
50using std::istringstream;
51using std::cout;
52using std::endl;
53
54#include "BESUtil.h"
55#include "BESDebug.h"
56#include "BESForbiddenError.h"
57#include "BESNotFoundError.h"
58#include "BESInternalError.h"
59#include "BESSyntaxUserError.h"
60
61#include "w10n_utils.h"
62#include "W10NNames.h"
63
64namespace w10n {
65
66void eval_resource_path(const string &w10nResourceId, const string &catalogRoot, const bool follow_sym_links,
67 string &validPath, bool &isFile, bool &isDir, string &remainder)
68{
69
70 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - CatalogRoot: "<< catalogRoot << endl);
71 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - w10n resourceID: "<< w10nResourceId << endl);
72
73 // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
74 // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
75 // function for the eval operation.
76 int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
77 if (follow_sym_links) {
78 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - Using 'stat' function (follow_sym_links = true)" << endl);
79 ye_old_stat_function = &stat;
80 }
81 else {
82 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - Using 'lstat' function (follow_sym_links = false)" << endl);
83 ye_old_stat_function = &lstat;
84 }
85
86 // if nothing is passed in path, then the path checks out since root is
87 // assumed to be valid.
88 if (w10nResourceId == "") {
89 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - w10n resourceID is empty" << endl);
90
91 validPath = "";
92 remainder = "";
93 return;
94 }
95
96 // make sure there are no ../ in the path, backing up in any way is
97 // not allowed.
98 string::size_type dotdot = w10nResourceId.find("..");
99 if (dotdot != string::npos) {
100 BESDEBUG(W10N_DEBUG_KEY,
101 "eval_resource_path() - ERROR: w10n resourceID '" << w10nResourceId <<"' contains the substring '..' This is Forbidden." << endl);
102 string s = (string) "Invalid node name '" + w10nResourceId + "' ACCESS IS FORBIDDEN";
103 throw BESForbiddenError(s, __FILE__, __LINE__);
104 }
105
106 // What I want to do is to take each part of path and check to see if it
107 // is a symbolic link and it is accessible. If everything is ok, add the
108 // next part of the path.
109 bool done = false;
110
111 // what is remaining to check
112 string rem = w10nResourceId;
113 remainder = rem;
114
115 // Remove leading slash
116 if (rem[0] == '/') rem = rem.substr(1, rem.length() - 1);
117
118 // Remove trailing slash
119 if (rem[rem.length() - 1] == '/') rem = rem.substr(0, rem.length() - 1);
120
121 // full path of the thing to check
122 string fullpath = catalogRoot;
123 // Remove leading slash
124 if (fullpath[fullpath.length() - 1] == '/') {
125 fullpath = fullpath.substr(0, fullpath.length() - 1);
126 }
127
128 // path checked so far
129 string checking;
130 // string validPath;
131
132 isFile = false;
133 isDir = false;
134
135 while (!done) {
136 size_t slash = rem.find('/');
137 if (slash == string::npos) {
138 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - Checking final path component: " << rem << endl);
139 fullpath = fullpath + "/" + rem;
140 checking = validPath + "/" + rem;
141 rem = "";
142 done = true;
143 }
144 else {
145 fullpath = fullpath + "/" + rem.substr(0, slash);
146 checking = validPath + "/" + rem.substr(0, slash);
147 rem = rem.substr(slash + 1, rem.length() - slash);
148 }
149
150 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - fullpath: "<< fullpath << endl);
151 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - checking: "<< checking << endl);
152
153 struct stat sb;
154 int statret = ye_old_stat_function(fullpath.c_str(), &sb);
155
156 if (statret == -1) {
157 int errsv = errno;
158 // stat failed, so not accessible. Get the error string,
159 // store in error, and throw exception
160 char *s_err = strerror(errsv);
161 string error = "Unable to access node " + checking + ": ";
162 if (s_err) {
163 error = error + s_err;
164 }
165 else {
166 error = error + "unknown access error";
167 }
168 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - error: "<< error << " errno: " << errno << endl);
169
170 // ENOENT means that the node wasn't found. Otherwise, access
171 // is denied for some reason
172 if (errsv == ENOENT || errsv == ENOTDIR) {
173 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - validPath: "<< validPath << endl);
174 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - remainder: "<< remainder << endl);
175 return;
176 }
177 else {
178 throw BESForbiddenError(error, __FILE__, __LINE__);
179 }
180 }
181 else {
182 validPath = checking;
183 remainder = rem;
184
185 if (S_ISREG(sb.st_mode)) {
186 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is regular file." << endl);
187 isFile = true;
188 isDir = false;
189 }
190 else if (S_ISDIR(sb.st_mode)) {
191 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is directory." << endl);
192 isFile = false;
193 isDir = true;
194 }
195 else if (S_ISLNK(sb.st_mode)) {
196 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is symbolic Link." << endl);
197 string error = "Service not configured to traverse symbolic links as embodied by the node '" + checking
198 + "' ACCESS IS FORBIDDEN";
199 throw BESForbiddenError(error, __FILE__, __LINE__);
200 }
201 }
202 }
203}
204
205std::string escape_for_json(const std::string &input)
206{
207 std::stringstream ss;
208 for (size_t i = 0; i < input.length(); ++i) {
209 if (unsigned(input[i]) < '\x20' || input[i] == '\\' || input[i] == '"') {
210 ss << "\\u" << std::setfill('0') << std::setw(4) << std::hex << unsigned(input[i]);
211 }
212 else {
213 ss << input[i];
214 }
215 }
216 return ss.str();
217}
218
219long computeConstrainedShape(libdap::Array *a, std::vector<unsigned int> *shape)
220{
221 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - BEGIN. Array name: "<< a->name() << endl);
222
223 libdap::Array::Dim_iter dIt;
224 unsigned int start;
225 unsigned int stride;
226 unsigned int stop;
227
228 unsigned int dimSize = 1;
229 int dimNum = 0;
230 long totalSize = 1;
231
232 BESDEBUG(W10N_DEBUG_KEY,
233 "w10n::computeConstrainedShape() - Array has " << a->dimensions(true) << " dimensions."<< endl);
234
235 stringstream msg;
236
237 for (dIt = a->dim_begin(); dIt != a->dim_end(); dIt++) {
238 BESDEBUG(W10N_DEBUG_KEY,
239 "w10n::computeConstrainedShape() - Processing dimension '" << a->dimension_name(dIt)<< "'. (dim# "<< dimNum << ")"<< endl);
240 start = a->dimension_start(dIt, true);
241 stride = a->dimension_stride(dIt, true);
242 stop = a->dimension_stop(dIt, true);
243 BESDEBUG(W10N_DEBUG_KEY,
244 "w10n::computeConstrainedShape() - start: " << start << " stride: " << stride << " stop: "<<stop<< endl);
245
246 dimSize = 1 + ((stop - start) / stride);
247 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - dimSize: " << dimSize << endl);
248
249 (*shape)[dimNum++] = dimSize;
250 totalSize *= dimSize;
251
252 }
253 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - totalSize: " << totalSize << endl);
254 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - END." << endl);
255
256 return totalSize;
257}
258
259void checkConstrainedDDSForW10nDataCompatibility(libdap::DDS *dds)
260{
261 int markedCount = 0;
262
263 for (libdap::DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
264 libdap::BaseType *bt = (*i);
265 if (bt->send_p()) {
266 if (bt->is_constructor_type()) {
267 checkConstructorForW10nDataCompatibility((libdap::Constructor *) bt);
268 }
269 else if (bt->is_vector_type()) {
270 if (bt->var()->is_constructor_type()) {
271 string msg = "Arrays of ";
272 msg += bt->type_name() + " are not supported by the w10n service.";
273 BESDEBUG(W10N_DEBUG_KEY,
274 "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
275 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
276 }
277 }
278 markedCount++;
279 }
280 }
281
282 if (markedCount > 1) {
283 string msg = "More than one variable in the dataset is projected and that's a no-no for w10n data responses.";
284 BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
285 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
286 }
287}
288
289void checkConstructorForW10nDataCompatibility(libdap::Constructor *constructor)
290{
291 int markedCount = 0;
292 for (libdap::Constructor::Vars_iter i = constructor->var_begin(); i != constructor->var_end(); i++) {
293 libdap::BaseType *bt = (*i);
294
295 if (bt->send_p()) {
296 if (bt->is_constructor_type()) {
297 checkConstructorForW10nDataCompatibility((libdap::Constructor *) bt);
298 }
299 else if (bt->is_vector_type()) {
300 if (bt->var()->is_constructor_type()) {
301 string msg = "Arrays of ";
302 msg += bt->type_name() + " are not supported by the w10n service.";
303 BESDEBUG(W10N_DEBUG_KEY,
304 "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
305 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
306 }
307
308 }
309 markedCount++;
310 }
311 }
312
313 if (markedCount > 1) {
314 string msg;
315 if (markedCount == constructor->element_count())
316 msg = "The w10n protocol does not support data responses from nodes. The variable " + constructor->name()
317 + " is a node variable.";
318 else
319 msg = "More than one child variable of the node variable " + constructor->name()
320 + " is projected and that's a no-no for w10n data responses.";
321 BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
322 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
323 }
324}
325
326bool allVariablesMarkedToSend(libdap::DDS *dds)
327{
328 bool allMarked = true;
329
330 for (libdap::DDS::Vars_iter vi = dds->var_begin(), ve = dds->var_end(); vi != ve; vi++) {
331 libdap::BaseType *v = *vi;
332 if (v->send_p()) {
333 if (v->is_constructor_type()) {
334 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v);
335 }
336 else if (v->is_vector_type() && v->var()->is_constructor_type()) {
337 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v->var());
338 }
339 else {
340 allMarked = allMarked && true;
341 }
342 }
343 else {
344 allMarked = allMarked && false;
345 }
346 }
347
348 return allMarked;
349}
350
351bool allVariablesMarkedToSend(libdap::Constructor *ctor)
352{
353 bool allMarked = true;
354
355 libdap::Constructor::Vars_iter vi = ctor->var_begin();
356 libdap::Constructor::Vars_iter ve = ctor->var_end();
357 for (; vi != ve; vi++) {
358 libdap::BaseType *v = *vi;
359 if (v->send_p()) {
360 if (v->is_constructor_type()) {
361 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v);
362 }
363 else if (v->is_vector_type() && v->var()->is_constructor_type()) {
364 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v->var());
365 }
366 else {
367 allMarked = allMarked && true;
368 }
369 }
370 else {
371 allMarked = allMarked && false;
372 }
373 }
374 return allMarked;
375}
376
377} // namespace w10n
378
error thrown if the BES is not allowed to access the resource requested
error thrown if there is a user syntax error in the request or any other user error