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