OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
w10n_utils.cc
Go to the documentation of this file.
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 
29 #include "config.h"
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 
38 #include <cstdio>
39 #include <cerrno>
40 #include <cstring>
41 #include <cstdlib>
42 #include <sstream>
43 #include <iostream>
44 #include <iomanip>
45 
46 #include <BaseType.h>
47 #include <DDS.h>
48 #include <Constructor.h>
49 #include <Array.h>
50 
51 using std::istringstream;
52 using std::cout;
53 using std::endl;
54 
55 #include "BESUtil.h"
56 #include "BESDebug.h"
57 #include "BESForbiddenError.h"
58 #include "BESNotFoundError.h"
59 #include "BESInternalError.h"
60 #include "BESSyntaxUserError.h"
61 
62 #include "w10n_utils.h"
63 #include "W10NNames.h"
64 
65 
66 
67 namespace w10n {
68 
70  const string &w10nResourceId,
71  const string &catalogRoot,
72  const bool follow_sym_links,
73  string &validPath,
74  bool &isFile,
75  bool &isDir,
76  string &remainder) {
77 
78  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - CatalogRoot: "<< catalogRoot << endl);
79  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - w10n resourceID: "<< w10nResourceId << endl);
80 
81 
82  // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
83  // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
84  // function for the eval operation.
85  int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
86  if(follow_sym_links){
87  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - Using 'stat' function (follow_sym_links = true)" << endl);
88  ye_old_stat_function = &stat;
89  }
90  else {
91  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - Using 'lstat' function (follow_sym_links = false)" << endl);
92  ye_old_stat_function = &lstat;
93  }
94 
95 
96  // if nothing is passed in path, then the path checks out since root is
97  // assumed to be valid.
98  if (w10nResourceId == ""){
99  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - w10n resourceID is empty" << endl);
100 
101  validPath = "";
102  remainder = "";
103  return;
104  }
105 
106  // make sure there are no ../ in the path, backing up in any way is
107  // not allowed.
108  string::size_type dotdot = w10nResourceId.find("..");
109  if (dotdot != string::npos) {
110  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - ERROR: w10n resourceID '" << w10nResourceId <<"' contains the substring '..' This is Forbidden." << endl);
111  string s = (string) "Invalid node name '" + w10nResourceId +"' ACCESS IS FORBIDDEN";
112  throw BESForbiddenError(s, __FILE__, __LINE__);
113  }
114 
115  // What I want to do is to take each part of path and check to see if it
116  // is a symbolic link and it is accessible. If everything is ok, add the
117  // next part of the path.
118  bool done = false;
119 
120  // what is remaining to check
121  string rem = w10nResourceId;
122  remainder = rem;
123 
124  // Remove leading slash
125  if (rem[0] == '/')
126  rem = rem.substr(1, rem.length() - 1);
127 
128  // Remove trailing slash
129  if (rem[rem.length() - 1] == '/')
130  rem = rem.substr(0, rem.length() - 1);
131 
132  // full path of the thing to check
133  string fullpath = catalogRoot;
134  // Remove leading slash
135  if (fullpath[fullpath.length() - 1] == '/') {
136  fullpath = fullpath.substr(0, fullpath.length() - 1);
137  }
138 
139 
140  // path checked so far
141  string checking;
142  // string validPath;
143 
144 
145  isFile = false;
146  isDir = false;
147 
148  while (!done) {
149  size_t slash = rem.find('/');
150  if (slash == string::npos) {
151  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - Checking final path component: " << rem << endl);
152  fullpath = fullpath + "/" + rem;
153  checking = validPath + "/" + rem;
154  rem ="";
155  done = true;
156  } else {
157  fullpath = fullpath + "/" + rem.substr(0, slash);
158  checking = validPath + "/" + rem.substr(0, slash);
159  rem = rem.substr(slash + 1, rem.length() - slash);
160  }
161 
162  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - fullpath: "<< fullpath << endl);
163  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - checking: "<< checking << endl);
164 
165 
166  struct stat sb;
167  int statret = ye_old_stat_function(fullpath.c_str(), &sb);
168 
169 
170  if (statret == -1) {
171  int errsv = errno;
172  // stat failed, so not accessible. Get the error string,
173  // store in error, and throw exception
174  char *s_err = strerror(errsv);
175  string error = "Unable to access node " + checking + ": ";
176  if (s_err) {
177  error = error + s_err;
178  } else {
179  error = error + "unknown access error";
180  }
181  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - error: "<< error << " errno: " << errno << endl);
182 
183  // ENOENT means that the node wasn't found. Otherwise, access
184  // is denied for some reason
185  if (errsv == ENOENT || errsv == ENOTDIR) {
186  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - validPath: "<< validPath << endl);
187  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - remainder: "<< remainder << endl);
188  return;
189  } else {
190  throw BESForbiddenError(error, __FILE__, __LINE__);
191  }
192  } else {
193  validPath = checking;
194  remainder = rem;
195 
196  if (S_ISREG(sb.st_mode)) {
197  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - '"<< checking << "' Is regular file." << endl);
198  isFile = true;
199  isDir = false;
200  }
201  else if (S_ISDIR(sb.st_mode)) {
202  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - '"<< checking << "' Is directory." << endl);
203  isFile = false;
204  isDir = true;
205  }
206  else if (S_ISLNK(sb.st_mode)) {
207  BESDEBUG(W10N_DEBUG_KEY,"eval_resource_path() - '"<< checking << "' Is symbolic Link." << endl);
208  string error = "Service not configured to traverse symbolic links as embodied by the node '"
209  + checking + "' ACCESS IS FORBIDDEN";
210  throw BESForbiddenError(error, __FILE__, __LINE__);
211  }
212  }
213 
214 
215  }
216 
217 }
218 
219 
220 
221 
222 std::string escape_for_json(const std::string &input) {
223  std::stringstream ss;
224  for (size_t i = 0; i < input.length(); ++i) {
225  if (unsigned(input[i]) < '\x20' || input[i] == '\\' || input[i] == '"') {
226  ss << "\\u" << std::setfill('0') << std::setw(4) << std::hex << unsigned(input[i]);
227  } else {
228  ss << input[i];
229  }
230  }
231  return ss.str();
232 }
233 
234 
235 
236 
237 
238 
239 
240 
241 #if 0
242 
243 // Crappy version of JSON string escaping function
247 std::string escape_for_json(const std::string &input) {
248  std::ostringstream ss;
249  //for (auto iter = input.cbegin(); iter != input.cend(); iter++) {
250  //C++98/03:
251  for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) {
252  switch (*iter) {
253  case '\\': ss << "\\\\"; break;
254  case '"': ss << "\\\""; break;
255  case '/': ss << "\\/"; break;
256  case '\b': ss << "\\b"; break;
257  case '\f': ss << "\\f"; break;
258  case '\n': ss << "\\n"; break;
259  case '\r': ss << "\\r"; break;
260  case '\t': ss << "\\t"; break;
261  default: ss << *iter; break;
262  }
263  }
264  return ss.str();
265 }
266 #endif
267 
268 
269 #if 0
270 
281 std::string backslash_escape(std::string source, char char_to_escape){
282  std::string escaped_result = source;
283  if(source.find(char_to_escape) != string::npos ){
284  size_t found = 0;
285  for(size_t i=0; i< source.length() ; i++){
286  if(source[i] == char_to_escape){
287  escaped_result.insert( i + found++, "\\");
288  }
289  }
290  }
291  return escaped_result;
292 }
293 #endif
294 
295 
296 long computeConstrainedShape(libdap::Array *a, std::vector<unsigned int> *shape ){
297  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - BEGIN. Array name: "<< a->name() << endl);
298 
299  libdap::Array::Dim_iter dIt;
300  unsigned int start;
301  unsigned int stride;
302  unsigned int stop;
303 
304  unsigned int dimSize = 1;
305  int dimNum = 0;
306  long totalSize = 1;
307 
308  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - Array has " << a->dimensions(true) << " dimensions."<< endl);
309 
310  stringstream msg;
311 
312  for(dIt = a->dim_begin() ; dIt!=a->dim_end() ;dIt++){
313  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - Processing dimension '" << a->dimension_name(dIt)<< "'. (dim# "<< dimNum << ")"<< endl);
314  start = a->dimension_start(dIt, true);
315  stride = a->dimension_stride(dIt, true);
316  stop = a->dimension_stop(dIt, true);
317  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - start: " << start << " stride: " << stride << " stop: "<<stop<< endl);
318 
319  dimSize = 1 + ( (stop - start) / stride);
320  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - dimSize: " << dimSize << endl);
321 
322  (*shape)[dimNum++] = dimSize;
323  totalSize *= dimSize;
324 
325  }
326  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - totalSize: " << totalSize << endl);
327  BESDEBUG(W10N_DEBUG_KEY, "w10n::computeConstrainedShape() - END." << endl);
328 
329  return totalSize;
330 }
331 
332 
333 
335 
336  int markedCount = 0;
337 
338  for (libdap::DDS::Vars_iter i = dds->var_begin(); i != dds->var_end(); i++) {
339  libdap::BaseType *bt = (*i);
340  if (bt->send_p()) {
341  if(bt->is_constructor_type()){
342  checkConstructorForW10nDataCompatibility((libdap::Constructor *)bt);
343  }
344  else if (bt->is_vector_type()){
345  if(bt->var()->is_constructor_type()){
346  string msg = "Arrays of ";
347  msg += bt->type_name() + " are not supported by the w10n service.";
348  BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
349  throw BESSyntaxUserError(msg , __FILE__, __LINE__);
350  }
351  }
352  markedCount++;
353  }
354  }
355  if(markedCount > 1) {
356  string msg = "More than one variable in the dataset is projected and that's a no-no for w10n data responses.";
357  BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstrainedDDSForW10nDataCompatibility() - ERROR! " << msg << endl);
358  throw BESSyntaxUserError(msg , __FILE__, __LINE__);
359  }
360 
361 
362 
363 }
364 
365 void checkConstructorForW10nDataCompatibility(libdap::Constructor *constructor){
366 
367  int markedCount = 0;
368  for (libdap::Constructor::Vars_iter i = constructor->var_begin(); i != constructor->var_end(); i++) {
369  libdap::BaseType *bt = (*i);
370 
371  if (bt->send_p()) {
372  if(bt->is_constructor_type()){
373  checkConstructorForW10nDataCompatibility((libdap::Constructor *)bt);
374  }
375  else if (bt->is_vector_type()){
376  if(bt->var()->is_constructor_type()){
377  string msg = "Arrays of ";
378  msg += bt->type_name() + " are not supported by the w10n service.";
379  BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
380  throw BESSyntaxUserError(msg , __FILE__, __LINE__);
381  }
382 
383  }
384  markedCount++;
385  }
386  }
387 
388 
389  if(markedCount > 1) {
390  string msg;
391  if(markedCount == constructor->element_count())
392  msg = "The w10n protocol does not support data responses from nodes. The variable " + constructor->name()+ " is a node variable.";
393  else
394  msg = "More than one child variable of the node variable "+ constructor->name() + " is projected and that's a no-no for w10n data responses.";
395  BESDEBUG(W10N_DEBUG_KEY, "w10n::checkConstructorForW10nDataCompatibility() - ERROR! " << msg << endl);
396  throw BESSyntaxUserError(msg , __FILE__, __LINE__);
397  }
398 
399 
400 
401 }
402 
403 
404 
405 bool allVariablesMarkedToSend(libdap::DDS *dds){
406 
407  bool allMarked = true;
408 
409  libdap::DDS::Vars_iter vi = dds->var_begin();
410  libdap::DDS::Vars_iter ve = dds->var_end();
411  for (; vi != ve; vi++) {
412  libdap::BaseType *v = *vi;
413  if (v->send_p()) {
414  if(v->is_constructor_type()){
415  allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *)v);
416  }
417  else if(v->is_vector_type() && v->var()->is_constructor_type()){
418  allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *)v->var());
419  }
420  else {
421  allMarked = allMarked && true;
422  }
423  }
424  else {
425  allMarked = allMarked && false;
426  }
427  }
428  return allMarked;
429 
430 }
431 
432 bool allVariablesMarkedToSend(libdap::Constructor *ctor){
433 
434  bool allMarked = true;
435 
436  libdap::Constructor::Vars_iter vi = ctor->var_begin();
437  libdap::Constructor::Vars_iter ve = ctor->var_end();
438  for (; vi != ve; vi++) {
439  libdap::BaseType *v = *vi;
440  if (v->send_p()) {
441  if(v->is_constructor_type()){
442  allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *)v);
443  }
444  else if(v->is_vector_type() && v->var()->is_constructor_type()){
445  allMarked = allMarked && allVariablesMarkedToSend((libdap::Constructor *)v->var());
446  }
447  else {
448  allMarked = allMarked && true;
449  }
450  }
451  else {
452  allMarked = allMarked && false;
453  }
454  }
455  return allMarked;
456 }
457 
458 
459 
460 
461 
462 
463 
464 
465 } // namespace w10n
466 
void checkConstrainedDDSForW10nDataCompatibility(libdap::DDS *dds)
Definition: w10n_utils.cc:334
std::string escape_for_json(const std::string &input)
Definition: w10n_utils.cc:222
void checkConstructorForW10nDataCompatibility(libdap::Constructor *constructor)
Definition: w10n_utils.cc:365
error thrown if there is a user syntax error in the request or any other user error ...
bool allVariablesMarkedToSend(libdap::DDS *dds)
Definition: w10n_utils.cc:405
void eval_resource_path(const string &w10nResourceId, const string &catalogRoot, const bool follow_sym_links, string &validPath, bool &isFile, bool &isDir, string &remainder)
Check if the specified path is valid.
Definition: w10n_utils.cc:69
#define W10N_DEBUG_KEY
Definition: W10NNames.h:4
error thrown if the BES is not allowed to access the resource requested
long computeConstrainedShape(libdap::Array *a, std::vector< unsigned int > *shape)
Definition: w10n_utils.cc:296
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64