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