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 
59 using std::stringstream;
60 using std::istringstream;
61 using std::cout;
62 using std::endl;
63 using std::vector;
64 using std::string;
65 using std::list;
66 using 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 
82 const string BES_KEY_TIMEOUT_CANCEL = "BES.CancelTimeoutOnSend";
83 
88 void 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 
107 void 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 
154 static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
155 static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
156 
166 string 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 
178 string 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
191 string 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 
206 string 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 
216 string 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 
260 void 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 
454 char *
455 BESUtil::fastpidconverter(char *buf, int base)
456 {
457  return fastpidconverter(getpid(), buf, base);
458 }
459 
460 char *
461 BESUtil::fastpidconverter(long val, /* value to be converted */
462 char *buf, /* output string */
463 int 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 
500 string 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 
524 string 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 
541 string 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 
580 void 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 
657 string 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 
697 void 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 
765 string 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 
791 string 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 }
840 string 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 
961 bool 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 
1015 unsigned 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 
1040 string 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 
1086 void 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 
1108 string BESUtil::get_time(bool use_local_time)
1109 {
1110  return get_time(time(0), use_local_time);
1111 }
1112 
1120 string 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 
1159 vector<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 
1176 BESCatalog *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 
1208 void 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 
1226 void 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 
1304 uint64_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 
1399 uint64_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 
1486 void 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 
1509 void 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