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