bes Updated for version 3.20.13
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
62using std::endl;
63using std::map;
64using std::string;
65using std::list;
66using 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
82ShowPathInfoResponseHandler::ShowPathInfoResponseHandler(const string &name) :
84{
85}
86
87ShowPathInfoResponseHandler::~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;
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
263void ShowPathInfoResponseHandler::dump(ostream &strm) const
264{
265 strm << BESIndent::LMarg << "ShowPathInfoResponseHandler::dump - (" << (void *) this << ")" << std::endl;
266 BESIndent::Indent();
268 BESIndent::UnIndent();
269}
270
272ShowPathInfoResponseHandler::ShowPathInfoResponseBuilder(const string &name)
273{
274 return new ShowPathInfoResponseHandler(name);
275}
276
280void 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 BESCatalog * default_catalog() const
The the default catalog.
virtual std::string default_catalog_name() const
The name of the default catalog.
static BESCatalogList * TheCatalogList()
Get the singleton BESCatalogList instance.
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:112
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:168
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:801
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