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 }
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.
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:58
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