bes  Updated for version 3.20.5
GatewayPathInfoResponseHandler.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 //
3 // GatewayPathInfoResponseHandler.cc
4 //
5 // This file is part of BES dap package
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 <sstream>
29 #include <fstream>
30 #include <time.h>
31 
32 #include <cerrno>
33 #include <cstring>
34 
35 #include "GatewayPathInfoResponseHandler.h"
36 
37 #include "BESDebug.h"
38 
39 #include "BESInfoList.h"
40 #include "BESInfo.h"
41 #include "BESUtil.h"
42 #include "BESRequestHandlerList.h"
43 #include "BESRequestHandler.h"
44 #include "BESNames.h"
45 #include "BESDapNames.h"
46 #include "BESDataNames.h"
47 #include "BESCatalogList.h"
48 #include "BESCatalog.h"
49 #include "BESCatalogEntry.h"
50 #include "BESCatalogUtils.h"
51 #include "BESSyntaxUserError.h"
52 #include "BESForbiddenError.h"
53 #include "BESNotFoundError.h"
54 #include "BESStopWatch.h"
55 
56 #define PATH_INFO_RESPONSE "PathInfo"
57 #define PATH "path"
58 #define VALID_PATH "validPath"
59 #define REMAINDER "remainder"
60 #define IS_DATA "isData"
61 #define IS_FILE "isFile"
62 #define IS_DIR "isDir"
63 #define IS_ACCESSIBLE "access"
64 #define SIZE "size"
65 #define LMT "lastModified"
66 
67 GatewayPathInfoResponseHandler::GatewayPathInfoResponseHandler(const string &name) :
68  BESResponseHandler(name), _response(0)
69 {
70 }
71 
72 GatewayPathInfoResponseHandler::~GatewayPathInfoResponseHandler()
73 {
74 }
75 
87 {
88  BESStopWatch sw;
89  if (BESISDEBUG(TIMING_LOG)) sw.start("GatewayPathInfoResponseHandler::execute", dhi.data[REQUEST_ID]);
90 
91  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::execute() - BEGIN" << endl );
92 
93  BESInfo *info = BESInfoList::TheList()->build_info();
94  _response = info;
95 
96  string container = dhi.data[CONTAINER];
97 #if 0
98  string catname;
100 #endif
101 
103  if (!defcat)
104  throw BESInternalError("Not able to find the default catalog.", __FILE__, __LINE__);
105 
106  // remove all of the leading slashes from the container name
107  string::size_type notslash = container.find_first_not_of("/", 0);
108  if (notslash != string::npos) {
109  container = container.substr(notslash);
110  }
111 
112  // see if there is a catalog name here. It's only a possible catalog name
113  string catname;
114  string::size_type slash = container.find_first_of("/", 0);
115  if (slash != string::npos) {
116  catname = container.substr(0, slash);
117  }
118  else {
119  catname = container;
120  }
121 
122  // see if this catalog exists. If it does, then remove the catalog
123  // name from the container (node)
124  BESCatalog *catobj = BESCatalogList::TheCatalogList()->find_catalog(catname);
125  if (catobj) {
126  if (slash != string::npos) {
127  container = container.substr(slash + 1);
128 
129  // remove repeated slashes
130  notslash = container.find_first_not_of("/", 0);
131  if (notslash != string::npos) {
132  container = container.substr(notslash);
133  }
134  }
135  else {
136  container = "";
137  }
138  }
139 
140  if (container.empty()) container = "/";
141 
142  if (container[0] != '/') container = "/" + container;
143 
144  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::execute() - container: " << container << endl );
145 
146  info->begin_response(SHOW_GATEWAY_PATH_INFO_RESPONSE_STR, dhi);
147 
148  map<string, string> pathInfoAttrs;
149  pathInfoAttrs[PATH] = container;
150 
151  info->begin_tag(PATH_INFO_RESPONSE, &pathInfoAttrs);
152 
153  string validPath, remainder;
154  bool isFile, isDir, canRead;
155  long long size, time;
156 
157 #if 0
158  BESCatalogUtils *utils = BESCatalogUtils::Utils(defcatname);
159 #endif
160 
162  eval_resource_path(container, utils->get_root_dir(), utils->follow_sym_links(), validPath, isFile, isDir, size,
163  time, canRead, remainder);
164 
165  // Now that we know what part of the path is actually something
166  // we can access, find out if the BES sees it as a dataset
167  bool isData = false;
168 
169  // If the valid path is an empty string then we KNOW it's not a dataset
170  if (validPath.length() != 0) {
171 
172  // Get the catalog entry.
173  BESCatalogEntry *entry = 0;
174  // string coi = dhi.data[CATALOG];
175  entry = defcat->show_catalog(validPath, /*coi,*/entry);
176  if (!entry) {
177  string err = (string) "Failed to find the validPath node " + validPath
178  + " this should not be possible. Some thing BAD is happening.";
179  throw BESInternalError(err, __FILE__, __LINE__);
180  }
181 
182  // Retrieve the valid services list
183  list<string> services = entry->get_service_list();
184 
185  // See if there's an OPENDAP_SERVICE available for the node.
186  if (services.size()) {
187  list<string>::const_iterator si = services.begin();
188  list<string>::const_iterator se = services.end();
189  for (; si != se; si++) {
190  if ((*si) == OPENDAP_SERVICE) isData = true;
191  }
192  }
193  }
194 
195  map<string, string> validPathAttrs;
196  validPathAttrs[IS_DATA] = isData ? "true" : "false";
197  validPathAttrs[IS_FILE] = isFile ? "true" : "false";
198  validPathAttrs[IS_DIR] = isDir ? "true" : "false";
199  validPathAttrs[IS_ACCESSIBLE] = canRead ? "true" : "false";
200 
201  // Convert size to string and add as attribute
202  std::ostringstream os_size;
203  os_size << size;
204  validPathAttrs[SIZE] = os_size.str();
205 
206  // Convert lmt to string and add as attribute
207  std::ostringstream os_time;
208  os_time << time;
209  validPathAttrs[LMT] = os_time.str();
210 
211  info->add_tag(VALID_PATH, validPath, &validPathAttrs);
212  info->add_tag(REMAINDER, remainder);
213 
214  info->end_tag(PATH_INFO_RESPONSE);
215 
216  // end the response object
217  info->end_response();
218 
219  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::execute() - END" << endl );
220 
221  }
222 
235 {
236  if (_response) {
237  BESInfo *info = dynamic_cast<BESInfo *>(_response);
238  if (!info) throw BESInternalError("cast error", __FILE__, __LINE__);
239  info->transmit(transmitter, dhi);
240  }
241 }
242 
249 void GatewayPathInfoResponseHandler::dump(ostream &strm) const
250 {
251  strm << BESIndent::LMarg << "GatewayPathInfoResponseHandler::dump - (" << (void *) this << ")" << std::endl;
252  BESIndent::Indent();
254  BESIndent::UnIndent();
255 }
256 
258 GatewayPathInfoResponseHandler::GatewayPathInfoResponseBuilder(const string &name)
259 {
260  return new GatewayPathInfoResponseHandler(name);
261 }
262 
266 void GatewayPathInfoResponseHandler::eval_resource_path(const string &resource_path, const string &catalog_root,
267  const bool follow_sym_links, string &validPath, bool &isFile, bool &isDir, long long &size,
268  long long &lastModifiedTime, bool &canRead, string &remainder)
269 {
270 
271  BESDEBUG(SPI_DEBUG_KEY,
272  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "CatalogRoot: "<< catalog_root << endl);
273 
274  BESDEBUG(SPI_DEBUG_KEY,
275  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "resourceID: "<< resource_path << endl);
276 
277  // nothing valid yet...
278  validPath = "";
279  size = -1;
280  lastModifiedTime = -1;
281 
282  // It's all remainder at this point...
283  string rem = resource_path;
284  remainder = rem;
285 
286  // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
287  // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
288  // function for the eval operation.
289  int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
290  if (follow_sym_links) {
291  BESDEBUG(SPI_DEBUG_KEY,
292  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "Using 'stat' function (follow_sym_links = true)" << endl);
293  ye_old_stat_function = &stat;
294  }
295  else {
296  BESDEBUG(SPI_DEBUG_KEY,
297  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "Using 'lstat' function (follow_sym_links = false)" << endl);
298  ye_old_stat_function = &lstat;
299  }
300 
301  // if nothing is passed in path, then the path checks out since root is
302  // assumed to be valid.
303  if (resource_path == "") {
304  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::"<<__func__ << "() - The resourceID is empty" << endl);
305  return;
306  }
307 
308  // make sure there are no ../ in the path, backing up in any way is
309  // not allowed.
310  string::size_type dotdot = resource_path.find("..");
311  if (dotdot != string::npos) {
312  BESDEBUG(SPI_DEBUG_KEY,
313  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << " ERROR: The resourceID '" << resource_path <<"' contains the substring '..' This is Forbidden." << endl);
314  string s = (string) "Invalid node name '" + resource_path + "' ACCESS IS FORBIDDEN";
315  throw BESForbiddenError(s, __FILE__, __LINE__);
316  }
317 
318  // What I want to do is to take each part of path and check to see if it
319  // is a symbolic link and it is accessible. If everything is ok, add the
320  // next part of the path.
321  bool done = false;
322 
323  // Full file system path to check
324  string fullpath = catalog_root;
325 
326  // localId that we are checking
327  string checking;
328 
329  isFile = false;
330  isDir = false;
331 
332  while (!done) {
333  size_t slash = rem.find('/');
334  if (slash == string::npos) {
335  BESDEBUG(SPI_DEBUG_KEY,
336  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "Checking final path component: " << rem << endl);
337  fullpath = BESUtil::assemblePath(fullpath, rem, true);
338  checking = BESUtil::assemblePath(validPath, rem, true);
339  rem = "";
340  done = true;
341  }
342  else {
343  fullpath = BESUtil::assemblePath(fullpath, rem.substr(0, slash), true);
344  checking = BESUtil::assemblePath(validPath, rem.substr(0, slash), true);
345  rem = rem.substr(slash + 1, rem.length() - slash);
346  }
347 
348  BESDEBUG(SPI_DEBUG_KEY,
349  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "validPath: "<< validPath << endl);
350  BESDEBUG(SPI_DEBUG_KEY,
351  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "checking: "<< checking << endl);
352  BESDEBUG(SPI_DEBUG_KEY,
353  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "fullpath: "<< fullpath << endl);
354 
355  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "rem: "<< rem << endl);
356 
357  BESDEBUG(SPI_DEBUG_KEY,
358  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "remainder: "<< remainder << endl);
359 
360  struct stat sb;
361  int statret = ye_old_stat_function(fullpath.c_str(), &sb);
362 
363  if (statret != -1) {
364  // No Error then keep chugging along.
365  validPath = checking;
366  remainder = rem;
367  }
368  else {
369  int errsv = errno;
370  // stat failed, so not accessible. Get the error string,
371  // store in error, and throw exception
372  char *s_err = strerror(errsv);
373  string error = "Unable to access node " + checking + ": ";
374  if (s_err) {
375  error = error + s_err;
376  }
377  else {
378  error = error + "unknown access error";
379  }
380  BESDEBUG(SPI_DEBUG_KEY,
381  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "error: "<< error << " errno: " << errno << endl);
382 
383  BESDEBUG(SPI_DEBUG_KEY,
384  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "remainder: '" << remainder << "'" << endl);
385 
386  // ENOENT means that the node wasn't found. Otherwise, access
387  // is denied for some reason
388  if (errsv != ENOENT && errsv != ENOTDIR) {
389  throw BESForbiddenError(error, __FILE__, __LINE__);
390  }
391 
392  // Are there slashes in the remainder?
393  size_t s_loc = remainder.find('/');
394  if (s_loc == string::npos) {
395  // if there are no more slashes, we check to see if this final path component contains "."
396  string basename = remainder;
397  bool moreDots = true;
398  while (moreDots) {
399  // working back from end of string, drop each dot (".") suffix until file system match or string gone
400  size_t d_loc = basename.find_last_of(".");
401  if (d_loc != string::npos) {
402  basename = basename.substr(0, d_loc);
403  BESDEBUG(SPI_DEBUG_KEY,
404  "GatewayPathInfoResponseHandler::" << __func__ << "() - basename: "<< basename << endl);
405 
406  string candidate_remainder = remainder.substr(basename.length());
407  BESDEBUG(SPI_DEBUG_KEY,
408  "GatewayPathInfoResponseHandler::" << __func__ << "() - candidate_remainder: "<< candidate_remainder << endl);
409 
410  string candidate_path = BESUtil::assemblePath(validPath, basename, true);
411  BESDEBUG(SPI_DEBUG_KEY,
412  "GatewayPathInfoResponseHandler::" << __func__ << "() - candidate_path: "<< candidate_path << endl);
413 
414  string full_candidate_path = BESUtil::assemblePath(catalog_root, candidate_path, true);
415  BESDEBUG(SPI_DEBUG_KEY,
416  "GatewayPathInfoResponseHandler::" << __func__ << "() - full_candidate_path: "<< full_candidate_path << endl);
417 
418  struct stat sb1;
419  int statret1 = ye_old_stat_function(full_candidate_path.c_str(), &sb1);
420  if (statret1 != -1) {
421  validPath = candidate_path;
422  remainder = candidate_remainder;
423  moreDots = false;
424  }
425  }
426  else {
427  BESDEBUG(SPI_DEBUG_KEY,
428  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "No dots in remainder: "<< remainder << endl);
429  moreDots = false;
430  }
431  }
432  }
433  else {
434  BESDEBUG(SPI_DEBUG_KEY,
435  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "Remainder has slash pollution: "<< remainder << endl);
436  done = true;
437  }
438  }
439  fullpath = BESUtil::assemblePath(catalog_root, validPath, true);
440 
441  statret = ye_old_stat_function(fullpath.c_str(), &sb);
442  if (S_ISREG(sb.st_mode)) {
443  BESDEBUG(SPI_DEBUG_KEY,
444  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is regular file." << endl);
445  isFile = true;
446  isDir = false;
447  }
448  else if (S_ISDIR(sb.st_mode)) {
449  BESDEBUG(SPI_DEBUG_KEY,
450  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is directory." << endl);
451  isFile = false;
452  isDir = true;
453  }
454  else if (S_ISLNK(sb.st_mode)) {
455  BESDEBUG(SPI_DEBUG_KEY,
456  "GatewayPathInfoResponseHandler::"<<__func__ << "() - " << "'"<< fullpath << "' Is symbolic Link." << endl);
457  string error = "Service not configured to traverse symbolic links as embodied by the node '" + checking
458  + "' ACCESS IS FORBIDDEN";
459  throw BESForbiddenError(error, __FILE__, __LINE__);
460  }
461  // sb.st_uid;
462  // sb.st_uid;
463 
464  // Can we read le file?
465  std::ifstream ifile(fullpath.c_str());
466  canRead = ifile.good();
467 
468  size = sb.st_size;
469  // I'm pretty sure that assigning st_mtime to a long long (when it is a time_t) is not a
470  // good plan - time_t is either a 32- or 64-bit signed integer.
471  //
472  // But, see ESCatalogUtils::bes_get_stat_info(BESCatalogEntry *entry, struct stat &buf)
473  // for code that probably does a more universal version. of this (and other things relative
474  // to stat, like symbolic link following).
475  //
476  // I added this #if ... #endif because Linux does not have st_mtimespec in struct stat.
477  // jhrg 2.24.18
478 #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
479  // Compute LMT by converting the time to milliseconds since epoch - because OLFS is picky
480  lastModifiedTime = (sb.st_mtimespec.tv_sec * 1000) + (sb.st_mtimespec.tv_nsec / 1000000);
481 #else
482  lastModifiedTime = sb.st_mtime;
483 #endif
484  }
485  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::" << __func__ << "() - fullpath: " << fullpath << endl);
486  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::" << __func__ << "() - validPath: " << validPath << endl);
487  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::" << __func__ << "() - remainder: " << remainder << endl);
488  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::" << __func__ << "() - rem: " << rem << endl);
489  BESDEBUG(SPI_DEBUG_KEY,
490  "GatewayPathInfoResponseHandler::" << __func__ << "() - isFile: " << (isFile?"true":"false") << endl);
491  BESDEBUG(SPI_DEBUG_KEY,
492  "GatewayPathInfoResponseHandler::" << __func__ << "() - isDir: " << (isDir?"true":"false") << endl);
493  BESDEBUG(SPI_DEBUG_KEY,
494  "GatewayPathInfoResponseHandler::" << __func__ << "() - access: " << (canRead?"true":"false") << endl);
495  BESDEBUG(SPI_DEBUG_KEY, "GatewayPathInfoResponseHandler::" << __func__ << "() - size: " << size << endl);
496  BESDEBUG(SPI_DEBUG_KEY,
497  "GatewayPathInfoResponseHandler::" << __func__ << "() - LMT: " << lastModifiedTime << endl);
498 
499 }
void eval_resource_path(const string &resource_path, const string &catalog_root, const bool follow_sym_links, string &validPath, bool &isFile, bool &isDir, long long &size, long long &lastModifiedTime, bool &canRead, string &remainder)
response handler that returns nodes or leaves within the catalog either at the root or at a specified...
exception thrown if inernal error encountered
virtual void dump(ostream &strm) const
dumps information about this object
virtual void transmit(BESTransmitter *transmitter, BESDataHandlerInterface &dhi)=0
transmit the informational object
virtual BESCatalogEntry * show_catalog(const std::string &container, BESCatalogEntry *entry)=0
virtual void transmit(BESTransmitter *transmitter, BESDataHandlerInterface &dhi)
transmit the response object built by the execute command using the specified transmitter object
virtual bool start(string name)
Definition: BESStopWatch.cc:57
handler object that knows how to create a specific response object
informational response object
Definition: BESInfo.h:68
virtual std::string default_catalog_name() const
The name of the default catalog.
const std::string & get_root_dir() const
Get the root directory of the catalog.
Catalogs provide a hierarchical organization for data.
Definition: BESCatalog.h:51
error thrown if the BES is not allowed to access the resource requested
Structure storing information used by the BES to handle the request.
map< string, string > data
the map of string data that will be required for the current request.
virtual void execute(BESDataHandlerInterface &dhi)
executes the command 'show catalog|leaves [for <node>];' by returning nodes or leaves at the top leve...
virtual void begin_response(const string &response_name, BESDataHandlerInterface &dhi)
begin the informational response
Definition: BESInfo.cc:124
virtual void dump(std::ostream &strm) const
dumps information about this object
virtual BESCatalog * default_catalog() const
The the default catalog.
virtual BESCatalogUtils * get_catalog_utils() const
Get a pointer to the utilities, customized for this catalog.
Definition: BESCatalog.h:113
static BESCatalogList * TheCatalogList()
Get the singleton BESCatalogList instance.
static string assemblePath(const string &firstPart, const string &secondPart, bool leadingSlash=false, bool trailingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
Definition: BESUtil.cc:818