bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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.empty() && rem[0] == '/') rem.erase(0, 1);
117
119
120 // full path of the thing to check
121 string fullpath = catalogRoot;
122
124
125 // path checked so far
126 string checking;
127 // string validPath;
128
129 isFile = false;
130 isDir = false;
131
132 while (!done) {
133 size_t slash = rem.find('/');
134 if (slash == string::npos) {
135 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - Checking final path component: " << rem << endl);
136 fullpath = fullpath + "/" + rem;
137 checking = validPath + "/" + rem;
138 rem = "";
139 done = true;
140 }
141 else {
142 fullpath = fullpath + "/" + rem.substr(0, slash);
143 checking = validPath + "/" + rem.substr(0, slash);
144 rem = rem.substr(slash + 1, rem.size() - slash);
145 }
146
147 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - fullpath: "<< fullpath << endl);
148 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - checking: "<< checking << endl);
149
150 struct stat sb;
151 int statret = ye_old_stat_function(fullpath.c_str(), &sb);
152
153 if (statret == -1) {
154 int errsv = errno;
155 // stat failed, so not accessible. Get the error string,
156 // store in error, and throw exception
157 char *s_err = strerror(errsv);
158 string error = "Unable to access node " + checking + ": ";
159 if (s_err) {
160 error = error + s_err;
161 }
162 else {
163 error = error + "unknown access error";
164 }
165 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - error: "<< error << " errno: " << errno << endl);
166
167 // ENOENT means that the node wasn't found. Otherwise, access
168 // is denied for some reason
169 if (errsv == ENOENT || errsv == ENOTDIR) {
170 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - validPath: "<< validPath << endl);
171 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - remainder: "<< remainder << endl);
172 return;
173 }
174 else {
175 throw BESForbiddenError(error, __FILE__, __LINE__);
176 }
177 }
178 else {
179 validPath = checking;
180 remainder = rem;
181
182 if (S_ISREG(sb.st_mode)) {
183 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is regular file." << endl);
184 isFile = true;
185 isDir = false;
186 }
187 else if (S_ISDIR(sb.st_mode)) {
188 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is directory." << endl);
189 isFile = false;
190 isDir = true;
191 }
192 else if (S_ISLNK(sb.st_mode)) {
193 BESDEBUG(W10N_DEBUG_KEY, "eval_resource_path() - '"<< checking << "' Is symbolic Link." << endl);
194 string error = "Service not configured to traverse symbolic links as embodied by the node '" + checking
195 + "' ACCESS IS FORBIDDEN";
196 throw BESForbiddenError(error, __FILE__, __LINE__);
197 }
198 }
199 }
200}
201
202std::string escape_for_json(const std::string &input)
203{
204 std::stringstream ss;
205 for (size_t i = 0; i < input.size(); ++i) {
206 if (unsigned(input[i]) < '\x20' || input[i] == '\\' || input[i] == '"') {
207 ss << "\\u" << std::setfill('0') << std::setw(4) << std::hex << unsigned(input[i]);
208 }
209 else {
210 ss << input[i];
211 }
212 }
213 return ss.str();
214}
215
216long computeConstrainedShape(libdap::Array *a, std::vector<unsigned int> *shape)
217{
218 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - BEGIN. Array name: "<< a->name() << endl);
219
220 libdap::Array::Dim_iter dIt;
221 unsigned int start;
222 unsigned int stride;
223 unsigned int stop;
224
225 unsigned int dimSize = 1;
226 int dimNum = 0;
227 long totalSize = 1;
228
229 BESDEBUG(W10N_DEBUG_KEY,
230 "w10n::computeConstrainedShape() - Array has " << a->dimensions(true) << " dimensions."<< endl);
231
232 stringstream msg;
233
234 for (dIt = a->dim_begin(); dIt != a->dim_end(); dIt++) {
235 BESDEBUG(W10N_DEBUG_KEY,
236 "w10n::computeConstrainedShape() - Processing dimension '" << a->dimension_name(dIt)<< "'. (dim# "<< dimNum << ")"<< endl);
237 start = a->dimension_start(dIt, true);
238 stride = a->dimension_stride(dIt, true);
239 stop = a->dimension_stop(dIt, true);
240 BESDEBUG(W10N_DEBUG_KEY,
241 "w10n::computeConstrainedShape() - start: " << start << " stride: " << stride << " stop: "<<stop<< endl);
242
243 dimSize = 1 + ((stop - start) / stride);
244 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - dimSize: " << dimSize << endl);
245
246 (*shape)[dimNum++] = dimSize;
247 totalSize *= dimSize;
248
249 }
250 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - totalSize: " << totalSize << endl);
251 BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - END." << endl);
252
253 return totalSize;
254}
255
256void checkConstrainedDDSForW10nDataCompatibility(libdap::DDS *dds)
257{
258 int markedCount = 0;
259
260 for (libdap::DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
261 libdap::BaseType *bt = (*i);
262 if (bt->send_p()) {
263 if (bt->is_constructor_type()) {
264 checkConstructorForW10nDataCompatibility((libdap::Constructor *) bt);
265 }
266 else if (bt->is_vector_type()) {
267 if (bt->var()->is_constructor_type()) {
268 string msg = "Arrays of ";
269 msg += bt->type_name() + " are not supported by the w10n service.";
270 BESDEBUG(W10N_DEBUG_KEY,
271 "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
272 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
273 }
274 }
275 markedCount++;
276 }
277 }
278
279 if (markedCount > 1) {
280 string msg = "More than one variable in the dataset is projected and that's a no-no for w10n data responses.";
281 BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
282 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
283 }
284}
285
286void checkConstructorForW10nDataCompatibility(libdap::Constructor *constructor)
287{
288 int markedCount = 0;
289 for (libdap::Constructor::Vars_iter i = constructor->var_begin(); i != constructor->var_end(); i++) {
290 libdap::BaseType *bt = (*i);
291
292 if (bt->send_p()) {
293 if (bt->is_constructor_type()) {
294 checkConstructorForW10nDataCompatibility((libdap::Constructor *) bt);
295 }
296 else if (bt->is_vector_type()) {
297 if (bt->var()->is_constructor_type()) {
298 string msg = "Arrays of ";
299 msg += bt->type_name() + " are not supported by the w10n service.";
300 BESDEBUG(W10N_DEBUG_KEY,
301 "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
302 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
303 }
304
305 }
306 markedCount++;
307 }
308 }
309
310 if (markedCount > 1) {
311 string msg;
312 if (markedCount == constructor->element_count())
313 msg = "The w10n protocol does not support data responses from nodes. The variable " + constructor->name()
314 + " is a node variable.";
315 else
316 msg = "More than one child variable of the node variable " + constructor->name()
317 + " is projected and that's a no-no for w10n data responses.";
318 BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
319 throw BESSyntaxUserError(msg, __FILE__, __LINE__);
320 }
321}
322
323bool allVariablesMarkedToSend(libdap::DDS *dds)
324{
325 bool allMarked = true;
326
327 for (libdap::DDS::Vars_iter vi = dds->var_begin(), ve = dds->var_end(); vi != ve; vi++) {
328 libdap::BaseType *v = *vi;
329 if (v->send_p()) {
330 if (v->is_constructor_type()) {
331 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v);
332 }
333 else if (v->is_vector_type() && v->var()->is_constructor_type()) {
334 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v->var());
335 }
336 else {
337 allMarked = allMarked && true;
338 }
339 }
340 else {
341 allMarked = allMarked && false;
342 }
343 }
344
345 return allMarked;
346}
347
348bool allVariablesMarkedToSend(libdap::Constructor *ctor)
349{
350 bool allMarked = true;
351
352 libdap::Constructor::Vars_iter vi = ctor->var_begin();
353 libdap::Constructor::Vars_iter ve = ctor->var_end();
354 for (; vi != ve; vi++) {
355 libdap::BaseType *v = *vi;
356 if (v->send_p()) {
357 if (v->is_constructor_type()) {
358 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v);
359 }
360 else if (v->is_vector_type() && v->var()->is_constructor_type()) {
361 allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *) v->var());
362 }
363 else {
364 allMarked = allMarked && true;
365 }
366 }
367 else {
368 allMarked = allMarked && false;
369 }
370 }
371 return allMarked;
372}
373
374} // namespace w10n
375
static void trim_if_trailing_slash(std::string &value)
If the string ends in a slash, remove it This function works for empty strings (doing nothing)....
Definition BESUtil.cc:113