bes Updated for version 3.20.10
BESUtil.cc
1// BESUtil.cc
2
3// This file is part of bes, A C++ back-end server implementation framework
4// for the OPeNDAP Data Access Protocol.
5
6// Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7// Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact University Corporation for Atmospheric Research at
24// 3080 Center Green Drive, Boulder, CO 80301
25
26// (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27// Please read the full copyright statement in the file COPYRIGHT_UCAR.
28//
29// Authors:
30// pwest Patrick West <pwest@ucar.edu>
31// jgarcia Jose Garcia <jgarcia@ucar.edu>
32
33#include "config.h"
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39#include <thread> // std::this_thread::sleep_for
40#include <chrono> // std::chrono::seconds
41#include <string> // std::string, std::stol
42
43#if HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46
47#include <cstdio>
48#include <cerrno>
49#include <cstring>
50#include <cstdlib>
51#include <ctime>
52#include <cassert>
53#include <vector>
54#include <list>
55
56#include <sstream>
57#include <iostream>
58
59using std::stringstream;
60using std::istringstream;
61using std::cout;
62using std::endl;
63using std::vector;
64using std::string;
65using std::list;
66using std::ostream;
67
68#include "TheBESKeys.h"
69#include "BESUtil.h"
70#include "BESDebug.h"
71#include "BESForbiddenError.h"
72#include "BESNotFoundError.h"
73#include "BESInternalError.h"
74#include "BESLog.h"
75#include "BESCatalogList.h"
76
77#define CRLF "\r\n"
78
79#define MODULE "util"
80#define prolog string("BESUtil::").append(__func__).append("() - ")
81
82const string BES_KEY_TIMEOUT_CANCEL = "BES.CancelTimeoutOnSend";
83
88void BESUtil::set_mime_text(ostream &strm)
89{
90 strm << "HTTP/1.0 200 OK" << CRLF;
91 strm << "XBES-Server: " << PACKAGE_STRING << CRLF;
92
93 const time_t t = time(0);
94 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
95 strm << "Last-Modified: " << rfc822_date(t).c_str() << CRLF;
96
97 strm << "Content-Type: text/plain" << CRLF;
98 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
99 strm << "Content-Description: unknown" << CRLF;
100 strm << CRLF;
101}
102
107void BESUtil::set_mime_html(ostream &strm)
108{
109 strm << "HTTP/1.0 200 OK" << CRLF;
110 strm << "XBES-Server: " << PACKAGE_STRING << CRLF;
111
112 const time_t t = time(0);
113 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
114 strm << "Last-Modified: " << rfc822_date(t).c_str() << CRLF;
115
116 strm << "Content-type: text/html" << CRLF;
117 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
118 strm << "Content-Description: unknown" << CRLF;
119 strm << CRLF;
120}
121
122// Return a MIME rfc-822 date. The grammar for this is:
123// date-time = [ day "," ] date time ; dd mm yy
124// ; hh:mm:ss zzz
125//
126// day = "Mon" / "Tue" / "Wed" / "Thu"
127// / "Fri" / "Sat" / "Sun"
128//
129// date = 1*2DIGIT month 2DIGIT ; day month year
130// ; e.g. 20 Jun 82
131// NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
132//
133// month = "Jan" / "Feb" / "Mar" / "Apr"
134// / "May" / "Jun" / "Jul" / "Aug"
135// / "Sep" / "Oct" / "Nov" / "Dec"
136//
137// time = hour zone ; ANSI and Military
138//
139// hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
140// ; 00:00:00 - 23:59:59
141//
142// zone = "UT" / "GMT" ; Universal Time
143// ; North American : UT
144// / "EST" / "EDT" ; Eastern: - 5/ - 4
145// / "CST" / "CDT" ; Central: - 6/ - 5
146// / "MST" / "MDT" ; Mountain: - 7/ - 6
147// / "PST" / "PDT" ; Pacific: - 8/ - 7
148// / 1ALPHA ; Military: Z = UT;
149// ; A:-1; (J not used)
150// ; M:-12; N:+1; Y:+12
151// / ( ("+" / "-") 4DIGIT ) ; Local differential
152// ; hours+min. (HHMM)
153
154static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
155static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
156
166string BESUtil::rfc822_date(const time_t t)
167{
168 struct tm stm{};
169 gmtime_r(&t, &stm);
170 char d[256];
171
172 snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm.tm_wday], stm.tm_mday,
173 months[stm.tm_mon], 1900 + stm.tm_year, stm.tm_hour, stm.tm_min, stm.tm_sec);
174 d[255] = '\0';
175 return {d};
176}
177
178string BESUtil::unhexstring(const string& s)
179{
180 int val;
181 istringstream ss(s);
182 ss >> std::hex >> val;
183 char tmp_str[2];
184 tmp_str[0] = static_cast<char>(val);
185 tmp_str[1] = '\0';
186 return {tmp_str};
187}
188
189// I modified this to mirror the version in libdap. The change allows several
190// escape sequences to by listed in 'except'. jhrg 2/18/09
191string BESUtil::www2id(const string &in, const string &escape, const string &except)
192{
193 string::size_type i = 0;
194 string res = in;
195 while ((i = res.find_first_of(escape, i)) != string::npos) {
196 if (except.find(res.substr(i, 3)) != string::npos) {
197 i += 3;
198 continue;
199 }
200 res.replace(i, 3, unhexstring(res.substr(i + 1, 2)));
201 }
202
203 return res;
204}
205
206string BESUtil::lowercase(const string &s)
207{
208 string return_string = s;
209 for (int j = 0; j < static_cast<int>(return_string.length()); j++) {
210 return_string[j] = (char) tolower(return_string[j]);
211 }
212
213 return return_string;
214}
215
216string BESUtil::unescape(const string &s)
217{
218 bool done = false;
219 string::size_type index = 0;
220 /* string::size_type new_index = 0 ; */
221 string new_str;
222 while (!done) {
223 string::size_type bs = s.find('\\', index);
224 if (bs == string::npos) {
225 new_str += s.substr(index, s.length() - index);
226 done = true;
227 }
228 else {
229 new_str += s.substr(index, bs - index);
230 new_str += s[bs + 1];
231 index = bs + 2;
232 }
233 }
234
235 return new_str;
236}
237
260void BESUtil::check_path(const string &path, const string &root, bool follow_sym_links)
261{
262 // if nothing is passed in path, then the path checks out since root is
263 // assumed to be valid.
264 if (path == "") return;
265
266 // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
267 // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
268 // function for the eval operation.
269 int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
270 if (follow_sym_links) {
271 BESDEBUG(MODULE, prolog << "Using 'stat' function (follow_sym_links = true)" << endl);
272 ye_old_stat_function = &stat;
273 }
274 else {
275 BESDEBUG(MODULE, "check_path() - Using 'lstat' function (follow_sym_links = false)" << endl);
276 ye_old_stat_function = &lstat;
277 }
278
279 // make sure there are no ../ in the directory, backing up in any way is
280 // not allowed.
281 string::size_type dotdot = path.find("..");
282 if (dotdot != string::npos) {
283 string s ("Upward path traversal (i.e. '..') is not supported. path: ");
284 s.append(path);
285 throw BESForbiddenError(s, __FILE__, __LINE__);
286 }
287
288 // What I want to do is to take each part of path and check to see if it
289 // is a symbolic link and it is accessible. If everything is ok, add the
290 // next part of the path. This is a downward traversal, staring with the
291 // the root directory (aka BES.Catalog.catalog.RootDirectory in the config)
292 // add the left most remaining path component to the end of the full_path,
293 // stat that, and proceed to the next until done.
294 //
295 // Initialize and normalize the remaining_path, stripping leading and trailing slashes
296 string remaining_path = path;
297 BESDEBUG(MODULE, prolog << "remaining_path: " << remaining_path << endl);
298 if (remaining_path[0] == '/') {
299 remaining_path = remaining_path.substr(1);
300 }
301 if (remaining_path[remaining_path.length() - 1] == '/') {
302 remaining_path = remaining_path.substr(0, remaining_path.length() - 1);
303 }
304
305 // The fullpath is our "graph" starting with root and becoming the request resource.
306 string fullpath = root;
307 // Normalize the fullpath, stripping only the trailing slash (it's a fully qualifed path)
308 if (fullpath[fullpath.length() - 1] == '/') {
309 fullpath = fullpath.substr(0, fullpath.length() - 1);
310 }
311
312 bool done = false;
313 while (!done) {
314 // Find the end of the leftmost path component on the remaining_path.
315 size_t slash = remaining_path.find('/');
316 if (slash == string::npos) {
317 // no more slashes, we're done,
318 fullpath.append("/").append(remaining_path);
319 remaining_path="";
320 done = true;
321 }
322 else {
323 // otherwise, append & remove
324 fullpath.append("/").append(remaining_path.substr(0, slash));
325 remaining_path = remaining_path.substr(slash + 1, remaining_path.length() - slash);
326 }
327 // Test...
328 BESDEBUG(MODULE, prolog << "Testing: " << fullpath << endl);
329 struct stat buf;
330 int statret = ye_old_stat_function(fullpath.c_str(), &buf);
331 if (statret == -1) {
332 // stat failed, so not accessible. Get the error string,
333 int errsv = errno;
334 // store in error, and throw exception
335 char *s_err = strerror(errsv);
336 //string error = "Unable to access node " + checked + ": ";
337 string error = "Unable to access node " + fullpath + ": ";
338 if (s_err)
339 error.append(s_err);
340 else
341 error.append("unknown error");
342
343 // ENOENT means that the node wasn't found.
344 // On some systems a file that doesn't exist returns ENOTDIR because: w.f.t?
345 // Otherwise, access is being denied for some other reason
346 if (errsv == ENOENT || errsv == ENOTDIR) {
347 // On some systems a file that doesn't exist returns ENOTDIR because: w.f.t?
348 stringstream ss;
349 ss << "Failed to locate resource " << fullpath;
350 BESDEBUG(MODULE, prolog << "ERROR: " << ss.str() << " errno: " << errno << endl);
351 throw BESNotFoundError(ss.str(), __FILE__, __LINE__);
352 }
353 else {
354 stringstream ss;
355 ss << "Unable to access node " << fullpath;
356 BESDEBUG(MODULE, prolog << "ERROR: " << ss.str() << " errno: " << errno << endl);
357 throw BESForbiddenError(ss.str(), __FILE__, __LINE__);
358 }
359 }
360 else {
361 // The call to (stat | lstat) was successful, now check to see if it's a symlink.
362 // Note that if follow_symlinks is true then this will never evaluate as true
363 // because we'll be using 'stat' and not 'lstat' and stat will follow the link
364 // and return information about the file/dir pointed to by the symlink
365 if (S_ISLNK(buf.st_mode)) {
366 //string error = "You do not have permission to access " + checked;
367 stringstream ss;
368 ss << "You do not have permission to access " << fullpath;
369 BESDEBUG(MODULE, prolog << "ERROR: " << ss.str() << " errno: " << errno << endl);
370 throw BESForbiddenError(ss.str(), __FILE__, __LINE__);
371 }
372 }
373 }
374
375#if 0
376 while (!done) {
377 size_t slash = rem.find('/');
378 if (slash == string::npos) {
379 fullpath = fullpath + "/" + rem;
380 checked = checked + "/" + rem;
381 done = true;
382 }
383 else {
384 fullpath = fullpath + "/" + rem.substr(0, slash);
385 checked = checked + "/" + rem.substr(0, slash);
386 rem = rem.substr(slash + 1, rem.length() - slash);
387 }
388
389 if (!follow_sym_links) {
390 struct stat buf;
391 int statret = lstat(fullpath.c_str(), &buf);
392 if (statret == -1) {
393 int errsv = errno;
394 // stat failed, so not accessible. Get the error string,
395 // store in error, and throw exception
396 char *s_err = strerror(errsv);
397 string error = "Unable to access node " + checked + ": ";
398 if (s_err) {
399 error = error + s_err;
400 }
401 else {
402 error = error + "unknown access error";
403 }
404 // ENOENT means that the node wasn't found. Otherwise, access
405 // is denied for some reason
406 if (errsv == ENOENT) {
407 throw BESNotFoundError(error, __FILE__, __LINE__);
408 }
409 else {
410 throw BESForbiddenError(error, __FILE__, __LINE__);
411 }
412 }
413 else {
414 // lstat was successful, now check if sym link
415 if (S_ISLNK( buf.st_mode )) {
416 string error = "You do not have permission to access "
417 + checked;
418 throw BESForbiddenError(error, __FILE__, __LINE__);
419 }
420 }
421 }
422 else {
423 // just do a stat and see if we can access the thing. If we
424 // can't, get the error information and throw an exception
425 struct stat buf;
426 int statret = stat(fullpath.c_str(), &buf);
427 if (statret == -1) {
428 int errsv = errno;
429 // stat failed, so not accessible. Get the error string,
430 // store in error, and throw exception
431 char *s_err = strerror(errsv);
432 string error = "Unable to access node " + checked + ": ";
433 if (s_err) {
434 error = error + s_err;
435 }
436 else {
437 error = error + "unknown access error";
438 }
439 // ENOENT means that the node wasn't found. Otherwise, access
440 // is denied for some reason
441 if (errsv == ENOENT) {
442 throw BESNotFoundError(error, __FILE__, __LINE__);
443 }
444 else {
445 throw BESForbiddenError(error, __FILE__, __LINE__);
446 }
447 }
448 }
449 }
450
451#endif
452}
453
454char *
455BESUtil::fastpidconverter(char *buf, int base)
456{
457 return fastpidconverter(getpid(), buf, base);
458}
459
460char *
461BESUtil::fastpidconverter(long val, /* value to be converted */
462char *buf, /* output string */
463int base) /* conversion base */
464{
465 ldiv_t r; /* result of val / base */
466
467 if (base > 36 || base < 2) /* no conversion if wrong base */
468 {
469 *buf = '\0';
470 return buf;
471 }
472 if (val < 0) *buf++ = '-';
473 r = ldiv(labs(val), base);
474
475 /* output digits of val/base first */
476
477 if (r.quot > 0) buf = fastpidconverter(r.quot, buf, base);
478 /* output last digit */
479
480 *buf++ = "0123456789abcdefghijklmnopqrstuvwxyz"[(int) r.rem];
481 *buf = '\0';
482 return buf;
483}
484
486{
487 if (!key.empty()) {
488 string::size_type first = key.find_first_not_of(" \t\n\r");
489 string::size_type last = key.find_last_not_of(" \t\n\r");
490 if (first == string::npos)
491 key = "";
492 else {
493 string::size_type num = last - first + 1;
494 string new_key = key.substr(first, num);
495 key = new_key;
496 }
497 }
498}
499
500string BESUtil::entity(char c)
501{
502 switch (c) {
503 case '>':
504 return "&gt;";
505 case '<':
506 return "&lt;";
507 case '&':
508 return "&amp;";
509 case '\'':
510 return "&apos;";
511 case '\"':
512 return "&quot;";
513 default:
514 return string(1, c); // is this proper default, just the char?
515 }
516}
517
524string BESUtil::id2xml(string in, const string &not_allowed)
525{
526 string::size_type i = 0;
527
528 while ((i = in.find_first_of(not_allowed, i)) != string::npos) {
529 in.replace(i, 1, entity(in[i]));
530 i++;
531 }
532
533 return in;
534}
535
541string BESUtil::xml2id(string in)
542{
543 string::size_type i = 0;
544
545 while ((i = in.find("&gt;", i)) != string::npos)
546 in.replace(i, 4, ">");
547
548 i = 0;
549 while ((i = in.find("&lt;", i)) != string::npos)
550 in.replace(i, 4, "<");
551
552 i = 0;
553 while ((i = in.find("&amp;", i)) != string::npos)
554 in.replace(i, 5, "&");
555
556 i = 0;
557 while ((i = in.find("&apos;", i)) != string::npos)
558 in.replace(i, 6, "'");
559
560 i = 0;
561 while ((i = in.find("&quot;", i)) != string::npos)
562 in.replace(i, 6, "\"");
563
564 return in;
565}
566
580void BESUtil::explode(char delim, const string &str, list<string> &values)
581{
582 std::string::size_type start = 0;
583 std::string::size_type qstart = 0;
584 std::string::size_type adelim = 0;
585 std::string::size_type aquote = 0;
586 bool done = false;
587 while (!done) {
588 string aval;
589 if (str[start] == '"') {
590 bool endquote = false;
591 qstart = start + 1;
592 while (!endquote) {
593 aquote = str.find('"', qstart);
594 if (aquote == string::npos) {
595 string currval = str.substr(start, str.length() - start);
596 string err = "BESUtil::explode - No end quote after value " + currval;
597 throw BESInternalError(err, __FILE__, __LINE__);
598 }
599 // could be an escaped escape character and an escaped
600 // quote, or an escaped escape character and a quote
601 if (str[aquote - 1] == '\\') {
602 if (str[aquote - 2] == '\\') {
603 endquote = true;
604 qstart = aquote + 1;
605 }
606 else {
607 qstart = aquote + 1;
608 }
609 }
610 else {
611 endquote = true;
612 qstart = aquote + 1;
613 }
614 }
615 if (str[qstart] != delim && qstart != str.length()) {
616 string currval = str.substr(start, qstart - start);
617 string err = "BESUtil::explode - No delim after end quote " + currval;
618 throw BESInternalError(err, __FILE__, __LINE__);
619 }
620 if (qstart == str.length()) {
621 adelim = string::npos;
622 }
623 else {
624 adelim = qstart;
625 }
626 }
627 else {
628 adelim = str.find(delim, start);
629 }
630 if (adelim == string::npos) {
631 aval = str.substr(start, str.length() - start);
632 done = true;
633 }
634 else {
635 aval = str.substr(start, adelim - start);
636 }
637
638 values.push_back(aval);
639 start = adelim + 1;
640 if (start == str.length()) {
641 values.push_back("");
642 done = true;
643 }
644 }
645}
646
657string BESUtil::implode(const list<string> &values, char delim)
658{
659 string result;
660 list<string>::const_iterator i = values.begin();
661 list<string>::const_iterator e = values.end();
662 bool first = true;
663 string::size_type d; // = string::npos ;
664 for (; i != e; i++) {
665 if (!first) result += delim;
666 d = (*i).find(delim);
667 if (d != string::npos && (*i)[0] != '"') {
668 string err = (string) "BESUtil::implode - delimiter exists in value " + (*i);
669 throw BESInternalError(err, __FILE__, __LINE__);
670 }
671 //d = string::npos ;
672 result += (*i);
673 first = false;
674 }
675 return result;
676}
677
697void BESUtil::url_explode(const string &url_str, BESUtil::url &url_parts)
698{
699 string rest;
700
701 string::size_type colon = url_str.find(":");
702 if (colon == string::npos) {
703 string err = "BESUtil::url_explode: missing colon for protocol";
704 throw BESInternalError(err, __FILE__, __LINE__);
705 }
706
707 url_parts.protocol = url_str.substr(0, colon);
708
709 if (url_str.substr(colon, 3) != "://") {
710 string err = "BESUtil::url_explode: no :// in the URL";
711 throw BESInternalError(err, __FILE__, __LINE__);
712 }
713
714 colon += 3;
715 rest = url_str.substr(colon);
716
717 string::size_type slash = rest.find("/");
718 if (slash == string::npos) slash = rest.length();
719
720 string::size_type at = rest.find("@");
721 if ((at != string::npos) && (at < slash)) {
722 // everything before the @ is username:password
723 string up = rest.substr(0, at);
724 colon = up.find(":");
725 if (colon != string::npos) {
726 url_parts.uname = up.substr(0, colon);
727 url_parts.psswd = up.substr(colon + 1);
728 }
729 else {
730 url_parts.uname = up;
731 }
732 // everything after the @ is domain/path
733 rest = rest.substr(at + 1);
734 }
735 slash = rest.find("/");
736 if (slash == string::npos) slash = rest.length();
737 colon = rest.find(":");
738 if ((colon != string::npos) && (colon < slash)) {
739 // everything before the colon is the domain
740 url_parts.domain = rest.substr(0, colon);
741 // everything after the folon is port/path
742 rest = rest.substr(colon + 1);
743 slash = rest.find("/");
744 if (slash != string::npos) {
745 url_parts.port = rest.substr(0, slash);
746 url_parts.path = rest.substr(slash + 1);
747 }
748 else {
749 url_parts.port = rest;
750 url_parts.path = "";
751 }
752 }
753 else {
754 slash = rest.find("/");
755 if (slash != string::npos) {
756 url_parts.domain = rest.substr(0, slash);
757 url_parts.path = rest.substr(slash + 1);
758 }
759 else {
760 url_parts.domain = rest;
761 }
762 }
763}
764
765string BESUtil::url_create(BESUtil::url &url_parts)
766{
767 string url = url_parts.protocol + "://";
768 if (!url_parts.uname.empty()) {
769 url += url_parts.uname;
770 if (!url_parts.psswd.empty()) url += ":" + url_parts.psswd;
771 url += "@";
772 }
773 url += url_parts.domain;
774 if (!url_parts.port.empty()) url += ":" + url_parts.port;
775 if (!url_parts.path.empty()) url += "/" + url_parts.path;
776
777 return url;
778}
779
780
791string BESUtil::pathConcat(const string &firstPart, const string &secondPart, char separator)
792{
793 string first = firstPart;
794 string second = secondPart;
795 string sep(1,separator);
796
797 // make sure there are not multiple slashes at the end of the first part...
798 // Note that this removes all of the slashes. jhrg 9/27/16
799 while (!first.empty() && *first.rbegin() == separator) {
800 // C++-11 first.pop_back();
801 first = first.substr(0, first.length() - 1);
802 }
803 // make sure second part does not BEGIN with a slash
804 while (!second.empty() && second[0] == separator) {
805 // erase is faster? second = second.substr(1);
806 second.erase(0, 1);
807 }
808 string newPath;
809 if (first.empty()) {
810 newPath = second;
811 }
812 else if (second.empty()) {
813 newPath = first;
814 }
815 else {
816 newPath = first.append(sep).append(second);
817 }
818 return newPath;
819}
840string BESUtil::assemblePath(const string &firstPart, const string &secondPart, bool leadingSlash, bool trailingSlash)
841{
842#if 0
843 assert(!firstPart.empty());
844
845 // This version works but does not remove duplicate slashes
846 string first = firstPart;
847 string second = secondPart;
848
849 // add a leading slash if needed
850 if (ensureLeadingSlash && first[0] != '/')
851 first = "/" + first;
852
853 // if 'second' start with a slash, remove it
854 if (second[0] == '/')
855 second = second.substr(1);
856
857 // glue the two parts together, adding a slash if needed
858 if (first.back() == '/')
859 return first.append(second);
860 else
861 return first.append("/").append(second);
862#endif
863
864#if 1
865 BESDEBUG(MODULE, prolog << "firstPart: '" << firstPart << "'" << endl);
866 BESDEBUG(MODULE, prolog << "secondPart: '" << secondPart << "'" << endl);
867
868#if 0
869 // assert(!firstPart.empty()); // I dropped this because I had to ask, why? Why does it matter? ndp 2017
870
871 string first = firstPart;
872 string second = secondPart;
873
874 // make sure there are not multiple slashes at the end of the first part...
875 // Note that this removes all of the slashes. jhrg 9/27/16
876 while (!first.empty() && *first.rbegin() == '/') {
877 // C++-11 first.pop_back();
878 first = first.substr(0, first.length() - 1);
879 }
880
881 // make sure second part does not BEGIN with a slash
882 while (!second.empty() && second[0] == '/') {
883 // erase is faster? second = second.substr(1);
884 second.erase(0, 1);
885 }
886
887 string newPath;
888
889 if (first.empty()) {
890 newPath = second;
891 }
892 else if (second.empty()) {
893 newPath = first;
894 }
895 else {
896 newPath = first.append("/").append(second);
897 }
898#endif
899
900 string newPath = BESUtil::pathConcat(firstPart,secondPart);
901 if (leadingSlash) {
902 if (newPath.empty()) {
903 newPath = "/";
904 }
905 else if (newPath.compare(0, 1, "/")) {
906 newPath = "/" + newPath;
907 }
908 }
909
910 if (trailingSlash) {
911 if (newPath.compare(newPath.length(), 1, "/")) {
912 newPath = newPath.append("/");
913 }
914 }
915 else {
916 while(newPath.length()>1 && *newPath.rbegin() == '/')
917 newPath = newPath.substr(0,newPath.length()-1);
918 }
919 BESDEBUG(MODULE, prolog << "newPath: " << newPath << endl);
920 return newPath;
921#endif
922
923#if 0
924 BESDEBUG("util", "BESUtil::assemblePath() - firstPart: "<< firstPart << endl);
925 BESDEBUG("util", "BESUtil::assemblePath() - secondPart: "<< secondPart << endl);
926
927 string first = firstPart;
928 string second = secondPart;
929
930 if (ensureLeadingSlash) {
931 if (*first.begin() != '/') first = "/" + first;
932 }
933
934 // make sure there are not multiple slashes at the end of the first part...
935 while (*first.rbegin() == '/' && first.length() > 0) {
936 first = first.substr(0, first.length() - 1);
937 }
938
939 // make sure first part ends with a "/"
940 if (*first.rbegin() != '/') {
941 first += "/";
942 }
943
944 // make sure second part does not BEGIN with a slash
945 while (*second.begin() == '/' && second.length() > 0) {
946 second = second.substr(1);
947 }
948
949 string newPath = first + second;
950
951 BESDEBUG("util", "BESUtil::assemblePath() - newPath: "<< newPath << endl);
952
953 return newPath;
954#endif
955}
956
961bool BESUtil::endsWith(string const &fullString, string const &ending)
962{
963 if (fullString.length() >= ending.length()) {
964 return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
965 }
966 else {
967 return false;
968 }
969}
970
993{
994 const string false_str = "false";
995 const string no_str = "no";
996
997 bool cancel_timeout_on_send = true;
998 bool found = false;
999 string value;
1000
1001 TheBESKeys::TheKeys()->get_value(BES_KEY_TIMEOUT_CANCEL, value, found);
1002 if (found) {
1003 value = BESUtil::lowercase(value);
1004 if ( value == false_str || value == no_str) cancel_timeout_on_send = false;
1005 }
1006 BESDEBUG(MODULE, __func__ << "() - cancel_timeout_on_send: " << (cancel_timeout_on_send ? "true" : "false") << endl);
1007 if (cancel_timeout_on_send) alarm(0);
1008}
1009
1015unsigned int BESUtil::replace_all(string &s, string find_this, string replace_with_this)
1016{
1017 unsigned int replace_count = 0;
1018 size_t pos = s.find(find_this);
1019 while (pos != string::npos) {
1020 // Replace current matching substring
1021 s.replace(pos, find_this.size(), replace_with_this);
1022 // Get the next occurrence from current position
1023 pos = s.find(find_this, pos + replace_with_this.size());
1024 replace_count++;
1025 }
1026 return replace_count;
1027}
1028
1040string BESUtil::normalize_path(const string &raw_path, bool leading_separator, bool trailing_separator, const string separator /* = "/" */)
1041{
1042 if (separator.length() != 1)
1043 throw BESInternalError("Path separators must be a single character. The string '" + separator + "' does not qualify.", __FILE__, __LINE__);
1044 char separator_char = separator[0];
1045 string double_separator;
1046 double_separator = double_separator.append(separator).append(separator);
1047
1048 string path(raw_path);
1049
1050 replace_all(path, double_separator, separator);
1051
1052 if (path.empty()) {
1053 path = separator;
1054 }
1055 if (path == separator) {
1056 return path;
1057 }
1058 if (leading_separator) {
1059 if (path[0] != separator_char) {
1060 path = string(separator).append(path);
1061 }
1062 }
1063 else {
1064 if (path[0] == separator_char) {
1065 path = path.substr(1);
1066 }
1067 }
1068 if (trailing_separator) {
1069 if (*path.rbegin() != separator_char) {
1070 path = path.append(separator);
1071 }
1072 }
1073 else {
1074 if (*path.rbegin() == separator_char) {
1075 path = path.substr(0, path.length() - 1);
1076 }
1077 }
1078 return path;
1079}
1080
1086void BESUtil::tokenize(const string& str, vector<string>& tokens, const string& delimiters /* = "/" */)
1087{
1088 // Skip delimiters at beginning.
1089 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
1090 // Find first "non-delimiter".
1091 string::size_type pos = str.find_first_of(delimiters, lastPos);
1092 while (string::npos != pos || string::npos != lastPos) {
1093 // Found a token, add it to the vector.
1094 tokens.push_back(str.substr(lastPos, pos - lastPos));
1095 // Skip delimiters. Note the "not_of"
1096 lastPos = str.find_first_not_of(delimiters, pos);
1097 // Find next "non-delimiter"
1098 pos = str.find_first_of(delimiters, lastPos);
1099 }
1100}
1101
1108string BESUtil::get_time(bool use_local_time)
1109{
1110 return get_time(time(0), use_local_time);
1111}
1112
1120string BESUtil::get_time(time_t the_time, bool use_local_time)
1121{
1122 char buf[sizeof "YYYY-MM-DDTHH:MM:SS zones"];
1123 int status = 0;
1124
1125 // From StackOverflow:
1126 // This will work too, if your compiler doesn't support %F or %T:
1127 // strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S%Z", gmtime(&now));
1128 //
1129 // UTC is the default. Override to local time based on the
1130 // passed parameter 'use_local_time'
1131 struct tm result{};
1132 if (!use_local_time) {
1133 gmtime_r(&the_time, &result);
1134 status = strftime(buf, sizeof buf, "%FT%T%Z", &result);
1135 }
1136 else {
1137 localtime_r(&the_time, &result);
1138 status = strftime(buf, sizeof buf, "%FT%T%Z", &result);
1139 }
1140
1141 if (!status) {
1142 ERROR_LOG(prolog + "Error formatting time value!");
1143 return "date-format-error";
1144 }
1145
1146 return buf;
1147}
1148
1159vector<string> BESUtil::split(const string &s, char delim /* '/' */, bool skip_empty /* true */)
1160{
1161 stringstream ss(s);
1162 string item;
1163 vector<string> tokens;
1164
1165 while (getline(ss, item, delim)) {
1166
1167 if (item.empty() && skip_empty)
1168 continue;
1169
1170 tokens.push_back(item);
1171 }
1172
1173 return tokens;
1174}
1175
1176BESCatalog *BESUtil::separateCatalogFromPath(std::string &ppath)
1177{
1178 BESCatalog *catalog = 0; // pointer to a singleton; do not delete
1179 vector<string> path_tokens;
1180
1181 // BESUtil::normalize_path() removes duplicate separators and adds leading and trailing separators as directed.
1182 string path = BESUtil::normalize_path(ppath, false, false);
1183 BESDEBUG(MODULE, prolog << "Normalized path: " << path << endl);
1184
1185 // Because we may need to alter the container/file/resource name by removing
1186 // a catalog name from the first node in the path we use "use_container" to store
1187 // the altered container path.
1188 string use_container = ppath;
1189
1190 // Breaks path into tokens
1191 BESUtil::tokenize(path, path_tokens);
1192 if (!path_tokens.empty()) {
1193 BESDEBUG(MODULE, "First path token: " << path_tokens[0] << endl);
1194 catalog = BESCatalogList::TheCatalogList()->find_catalog(path_tokens[0]);
1195 if (catalog) {
1196 BESDEBUG(MODULE, prolog << "Located catalog " << catalog->get_catalog_name() << " from path component" << endl);
1197 // Since the catalog name is in the path we
1198 // need to drop it this should leave container
1199 // with a leading
1200 ppath = BESUtil::normalize_path(path.substr(path_tokens[0].length()), true, false);
1201 BESDEBUG(MODULE, prolog << "Modified container/path value to: " << use_container << endl);
1202 }
1203 }
1204
1205 return catalog;
1206}
1207
1208void ios_state_msg(std::ios &ios_ref, std::stringstream &msg) {
1209 msg << " {ios.good()=" << (ios_ref.good() ? "true" : "false") << "}";
1210 msg << " {ios.eof()=" << (ios_ref.eof()?"true":"false") << "}";
1211 msg << " {ios.fail()=" << (ios_ref.fail()?"true":"false") << "}";
1212 msg << " {ios.bad()=" << (ios_ref.bad()?"true":"false") << "}";
1213}
1214
1215// size of the buffer used to read from the temporary file built on disk and
1216// send data to the client over the network connection (socket/stream)
1217#define OUTPUT_FILE_BLOCK_SIZE 4096
1218
1226void BESUtil::file_to_stream(const std::string &file_name, std::ostream &o_strm)
1227{
1228 stringstream msg;
1229 msg << prolog << "Using ostream: " << (void *) &o_strm << " cout: " << (void *) &cout << endl;
1230 BESDEBUG(MODULE, msg.str());
1231 INFO_LOG( msg.str());
1232
1233 char rbuffer[OUTPUT_FILE_BLOCK_SIZE];
1234 std::ifstream i_stream(file_name, std::ios_base::in | std::ios_base::binary); // Use binary mode so we can
1235
1236 // good() returns true if !(eofbit || badbit || failbit)
1237 if(!i_stream.good()){
1238 stringstream msg;
1239 msg << prolog << "Failed to open file " << file_name;
1240 ios_state_msg(i_stream, msg);
1241 BESDEBUG(MODULE, msg.str() << endl);
1242 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1243 }
1244
1245 // good() returns true if !(eofbit || badbit || failbit)
1246 if(!o_strm.good()){
1247 stringstream msg;
1248 msg << prolog << "Problem with ostream. " << file_name;
1249 ios_state_msg(i_stream, msg);
1250 BESDEBUG(MODULE, msg.str() << endl);
1251 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1252 }
1253
1254 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1255 // This is where the file is copied.
1256 uint64_t tcount = 0;
1257 while (i_stream.good() && o_strm.good()){
1258 i_stream.read(&rbuffer[0], OUTPUT_FILE_BLOCK_SIZE); // Read at most n bytes into
1259 o_strm.write(&rbuffer[0], i_stream.gcount()); // buf, then write the buf to
1260 tcount += i_stream.gcount();
1261 }
1262 o_strm.flush();
1263
1264 // fail() is true if failbit || badbit got set, but does not consider eofbit
1265 if(i_stream.fail() && !i_stream.eof()){
1266 stringstream msg;
1267 msg << prolog << "There was an ifstream error when reading from: " << file_name;
1268 ios_state_msg(i_stream, msg);
1269 msg << " last_lap: " << i_stream.gcount() << " bytes";
1270 msg << " total_read: " << tcount << " bytes";
1271 BESDEBUG(MODULE, msg.str() << endl);
1272 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1273 }
1274
1275 // If we're not at the eof of the input stream then we have failed.
1276 if (!i_stream.eof()){
1277 stringstream msg;
1278 msg << prolog << "Failed to reach EOF on source file: " << file_name;
1279 ios_state_msg(i_stream, msg);
1280 msg << " last_lap: " << i_stream.gcount() << " bytes";
1281 msg << " total_read: " << tcount << " bytes";
1282 BESDEBUG(MODULE, msg.str() << endl);
1283 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1284 }
1285
1286 // And if something went wrong on the output stream we have failed.
1287 if(!o_strm.good()){
1288 stringstream msg;
1289 msg << prolog << "There was an ostream error during transmit. Transmitted " << tcount << " bytes.";
1290 ios_state_msg(o_strm, msg);
1291 auto crntpos = o_strm.tellp();
1292 msg << " current_position: " << crntpos << endl;
1293 BESDEBUG(MODULE, msg.str());
1294 ERROR_LOG(msg.str());
1295 // TODO Should we throw an exception here? Maybe BESInternalFatalError ??
1296 }
1297
1298 msg.str("");
1299 msg << prolog << "Sent "<< tcount << " bytes from file '" << file_name<< "'. " << endl;
1300 BESDEBUG(MODULE,msg.str());
1301 INFO_LOG(msg.str());
1302}
1303
1304uint64_t BESUtil::file_to_stream_helper(const std::string &file_name, std::ostream &o_strm, uint64_t byteCount){
1305
1306 stringstream msg;
1307 msg << prolog << "Using ostream: " << (void *) &o_strm << " cout: " << (void *) &cout << endl;
1308 BESDEBUG(MODULE, msg.str());
1309 INFO_LOG( msg.str());
1310
1311 char rbuffer[OUTPUT_FILE_BLOCK_SIZE];
1312 std::ifstream i_stream(file_name, std::ios_base::in | std::ios_base::binary); // Use binary mode so we can
1313
1314 // good() returns true if !(eofbit || badbit || failbit)
1315 if(!i_stream.good()){
1316 stringstream msg;
1317 msg << prolog << "Failed to open file " << file_name;
1318 ios_state_msg(i_stream, msg);
1319 BESDEBUG(MODULE, msg.str() << endl);
1320 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1321 }
1322
1323 // good() returns true if !(eofbit || badbit || failbit)
1324 if(!o_strm.good()){
1325 stringstream msg;
1326 msg << prolog << "Problem with ostream. " << file_name;
1327 ios_state_msg(i_stream, msg);
1328 BESDEBUG(MODULE, msg.str() << endl);
1329 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1330 }
1331
1332 // this is where we advance to the last byte that was read
1333 i_stream.seekg(byteCount);
1334
1335 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1336 // This is where the file is copied.
1337 while (i_stream.good() && o_strm.good()){
1338 i_stream.read(&rbuffer[0], OUTPUT_FILE_BLOCK_SIZE); // Read at most n bytes into
1339 o_strm.write(&rbuffer[0], i_stream.gcount()); // buf, then write the buf to
1340 BESDEBUG(MODULE, "i_stream: " << i_stream.gcount() << endl);
1341 byteCount += i_stream.gcount();
1342 }
1343 o_strm.flush();
1344
1345 // fail() is true if failbit || badbit got set, but does not consider eofbit
1346 if(i_stream.fail() && !i_stream.eof()){
1347 stringstream msg;
1348 msg << prolog << "There was an ifstream error when reading from: " << file_name;
1349 ios_state_msg(i_stream, msg);
1350 msg << " last_lap: " << i_stream.gcount() << " bytes";
1351 msg << " total_read: " << byteCount << " bytes";
1352 BESDEBUG(MODULE, msg.str() << endl);
1353 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1354 }
1355
1356 // If we're not at the eof of the input stream then we have failed.
1357 if (!i_stream.eof()){
1358 stringstream msg;
1359 msg << prolog << "Failed to reach EOF on source file: " << file_name;
1360 ios_state_msg(i_stream, msg);
1361 msg << " last_lap: " << i_stream.gcount() << " bytes";
1362 msg << " total_read: " << byteCount << " bytes";
1363 BESDEBUG(MODULE, msg.str() << endl);
1364 throw BESInternalError(msg.str(),__FILE__,__LINE__);
1365 }
1366
1367 // And if something went wrong on the output stream we have failed.
1368 if(!o_strm.good()){
1369 stringstream msg;
1370 msg << prolog << "There was an ostream error during transmit. Transmitted " << byteCount << " bytes.";
1371 ios_state_msg(o_strm, msg);
1372 auto crntpos = o_strm.tellp();
1373 msg << " current_position: " << crntpos << endl;
1374 BESDEBUG(MODULE, msg.str());
1375 ERROR_LOG(msg.str());
1376 // TODO Should we throw an exception here? Maybe BESInternalFatalError ??
1377 }
1378
1379 msg.str(prolog);
1380 msg << "Sent "<< byteCount << " bytes from file '" << file_name<< "'. " << endl;
1381 BESDEBUG(MODULE,msg.str());
1382 INFO_LOG(msg.str());
1383
1384 i_stream.close();
1385
1386 return byteCount;
1387}
1388
1389
1390// I added this because maybe using the low-level file calls was important. I'm not
1391// sure and the iostreams in C++ are safer. jhrg 6/4/21
1392#define FILE_CALLS 0
1393
1399uint64_t BESUtil::file_to_stream_task(const std::string &file_name, std::atomic<bool> &file_write_done, std::ostream &o_strm) {
1400 stringstream msg;
1401 msg << prolog << "Using ostream: " << (void *) &o_strm << " cout: " << (void *) &cout << endl;
1402 BESDEBUG(MODULE, msg.str());
1403 INFO_LOG(msg.str());
1404
1405 char rbuffer[OUTPUT_FILE_BLOCK_SIZE];
1406
1407 // this hack gets the code past the tests when the task is launched
1408 // using the async policy. It's not needed if the task is run as a
1409 // deferred task. jhrg 6/4/21
1410 //sleep(1);
1411
1412 std::ifstream i_stream(file_name, std::ios_base::in | std::ios_base::binary);
1413#if FILE_CALLS
1414 int fd = open(file_name.c_str(), O_RDONLY | O_NONBLOCK);
1415 int eof = false;
1416#endif
1417
1418 //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1419 // This is where the file is copied.
1420 BESDEBUG(MODULE, "Starting transfer" << endl);
1421 uint64_t tcount = 0;
1422 while (!i_stream.bad() && !i_stream.fail() && o_strm.good()) {
1423 if (file_write_done && i_stream.eof()) {
1424 BESDEBUG(MODULE, "breaking out of loop" << endl);
1425 break;
1426 }
1427 else {
1428 i_stream.read(&rbuffer[0], OUTPUT_FILE_BLOCK_SIZE); // Read at most n bytes into
1429
1430#if FILE_CALLS
1431 int status = read(fd, &rbuffer[0], OUTPUT_FILE_BLOCK_SIZE);
1432 if (status == 0) {
1433 eof = true;
1434 }
1435 else if (status == -1) {
1436 BESDEBUG(MODULE, "read() call error: " << errno << endl);
1437 }
1438
1439 o_strm.write(&rbuffer[0], status); // buf, then write the buf to
1440 tcount += status;
1441#endif
1442
1443 o_strm.write(&rbuffer[0], i_stream.gcount()); // buf, then write the buf to
1444 tcount += i_stream.gcount();
1445 BESDEBUG(MODULE, "transfer bytes " << tcount << endl);
1446 }
1447 }
1448
1449#if FILE_CALLS
1450 close(fd);
1451#endif
1452 o_strm.flush();
1453
1454 // And if something went wrong on the output stream we have failed.
1455 if(!o_strm.good()){
1456 stringstream msg;
1457 msg << prolog << "There was an ostream error during transmit. Transmitted " << tcount << " bytes.";
1458 ios_state_msg(o_strm, msg);
1459 auto crntpos = o_strm.tellp();
1460 msg << " current_position: " << crntpos << endl;
1461 BESDEBUG(MODULE, msg.str());
1462 INFO_LOG(msg.str());
1463 }
1464
1465 msg.str(prolog);
1466 msg << "Sent "<< tcount << " bytes from file '" << file_name<< "'. " << endl;
1467 BESDEBUG(MODULE,msg.str());
1468 INFO_LOG(msg.str());
1469
1470 return tcount;
1471}
1472
1473#if 0
1479
1486void BESUtil::split(const string &s, const string &delimiter, vector<uint64_t> &res)
1487{
1488 const size_t delim_len = delimiter.length();
1489
1490 size_t pos_start = 0, pos_end;
1491
1492 while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
1493 res.push_back (stoull(s.substr(pos_start, pos_end - pos_start)));
1494 pos_start = pos_end + delim_len;
1495 }
1496
1497 res.push_back (stoull(s.substr (pos_start)));
1498}
1499
1509void BESUtil::split(const string &s, const string &delimiter, vector<string> &res)
1510{
1511 const size_t delim_len = delimiter.length();
1512
1513 size_t pos_start = 0, pos_end;
1514
1515 while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
1516 res.push_back(s.substr(pos_start, pos_end - pos_start));
1517 pos_start = pos_end + delim_len;
1518 }
1519
1520 res.push_back(s.substr (pos_start));
1521}
1522#endif
static BESCatalogList * TheCatalogList()
Get the singleton BESCatalogList instance.
Catalogs provide a hierarchical organization for data.
Definition: BESCatalog.h:51
virtual std::string get_catalog_name() const
Get the name for this catalog.
Definition: BESCatalog.h:103
error thrown if the BES is not allowed to access the resource requested
exception thrown if internal error encountered
error thrown if the resource requested cannot be found
static std::vector< std::string > split(const std::string &s, char delim='/', bool skip_empty=true)
Splits the string s into the return vector of tokens using the delimiter delim and skipping empty val...
Definition: BESUtil.cc:1159
static uint64_t file_to_stream_task(const std::string &file_name, std::atomic< bool > &file_write_done, std::ostream &o_strm)
Definition: BESUtil.cc:1399
static void explode(char delim, const std::string &str, std::list< std::string > &values)
Definition: BESUtil.cc:580
static void url_explode(const std::string &url_str, BESUtil::url &url_parts)
Given a url, break the url into its different parts.
Definition: BESUtil.cc:697
static bool endsWith(std::string const &fullString, std::string const &ending)
Definition: BESUtil.cc:961
static void tokenize(const std::string &str, std::vector< std::string > &tokens, const std::string &delimiters="/")
Definition: BESUtil.cc:1086
static void set_mime_text(std::ostream &strm)
Generate an HTTP 1.0 response header for a text document.
Definition: BESUtil.cc:88
static std::string id2xml(std::string in, const std::string &not_allowed="><&'\"")
Definition: BESUtil.cc:524
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition: BESUtil.cc:992
static void check_path(const std::string &path, const std::string &root, bool follow_sym_links)
Check if the specified path is valid.
Definition: BESUtil.cc:260
static unsigned int replace_all(std::string &s, std::string find_this, std::string replace_with_this)
Operates on the string 's' to replaces every occurrence of the value of the string 'find_this' with t...
Definition: BESUtil.cc:1015
static void set_mime_html(std::ostream &strm)
Generate an HTTP 1.0 response header for a html document.
Definition: BESUtil.cc:107
static std::string lowercase(const std::string &s)
Definition: BESUtil.cc:206
static std::string pathConcat(const std::string &firstPart, const std::string &secondPart, char separator='/')
Concatenate path fragments making sure that they are separated by a single '/' character.
Definition: BESUtil.cc:791
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:840
static std::string www2id(const std::string &in, const std::string &escape="%", const std::string &except="")
Definition: BESUtil.cc:191
static std::string implode(const std::list< std::string > &values, char delim)
Definition: BESUtil.cc:657
static void file_to_stream(const std::string &file_name, std::ostream &o_strm)
Copies the contents of the file identified by file_name to the stream o_strm.
Definition: BESUtil.cc:1226
static std::string normalize_path(const std::string &path, bool leading_separator, bool trailing_separator, std::string separator="/")
Removes duplicate separators and provides leading and trailing separators as directed.
Definition: BESUtil.cc:1040
static std::string xml2id(std::string in)
Definition: BESUtil.cc:541
static std::string unescape(const std::string &s)
Definition: BESUtil.cc:216
static char * fastpidconverter(char *buf, int base)
Definition: BESUtil.cc:455
static void removeLeadingAndTrailingBlanks(std::string &key)
Definition: BESUtil.cc:485
static std::string get_time(bool use_local_time=false)
Definition: BESUtil.cc:1108
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71