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