bes  Updated for version 3.20.13
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 
50 using std::istringstream;
51 using std::cout;
52 using 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 
64 namespace w10n {
65 
66 void 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.length() - 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 
202 std::string escape_for_json(const std::string &input)
203 {
204  std::stringstream ss;
205  for (size_t i = 0; i < input.length(); ++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 
216 long 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 
256 void 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 
286 void 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 
323 bool 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 
348 bool 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 
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
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:110