bes  Updated for version 3.20.6
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 <BaseType.h>
46 #include <DDS.h>
47 #include <Constructor.h>
48 #include <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[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 
205 std::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 
219 long 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 
259 void 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 
289 void 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 
326 bool 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 
351 bool 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 
BESSyntaxUserError
error thrown if there is a user syntax error in the request or any other user error
Definition: BESSyntaxUserError.h:41
BESForbiddenError
error thrown if the BES is not allowed to access the resource requested
Definition: BESForbiddenError.h:40