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