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 }
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 dump(std::ostream &strm) const
dumps information about this object
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...