bes  Updated for version 3.17.4
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 
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 
42 #include <cstdio>
43 #include <cerrno>
44 #include <cstring>
45 #include <cstdlib>
46 #include <cassert>
47 
48 #include <sstream>
49 #include <iostream>
50 
51 using std::istringstream;
52 using std::cout;
53 using std::endl;
54 
55 #include "BESUtil.h"
56 #include "BESDebug.h"
57 #include "BESForbiddenError.h"
58 #include "BESNotFoundError.h"
59 #include "BESInternalError.h"
60 
61 #define CRLF "\r\n"
62 
63 #define debug_key "BesUtil"
64 
69 void BESUtil::set_mime_text(ostream &strm)
70 {
71  strm << "HTTP/1.0 200 OK" << CRLF;
72  strm << "XBES-Server: " << PACKAGE_STRING << CRLF;
73 
74  const time_t t = time(0);
75  strm << "Date: " << rfc822_date(t).c_str() << CRLF;
76  strm << "Last-Modified: " << rfc822_date(t).c_str() << CRLF;
77 
78  strm << "Content-Type: text/plain" << CRLF;
79  // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
80  strm << "Content-Description: unknown" << CRLF;
81  strm << CRLF;
82 }
83 
88 void BESUtil::set_mime_html(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/html" << 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 
103 // Return a MIME rfc-822 date. The grammar for this is:
104 // date-time = [ day "," ] date time ; dd mm yy
105 // ; hh:mm:ss zzz
106 //
107 // day = "Mon" / "Tue" / "Wed" / "Thu"
108 // / "Fri" / "Sat" / "Sun"
109 //
110 // date = 1*2DIGIT month 2DIGIT ; day month year
111 // ; e.g. 20 Jun 82
112 // NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
113 //
114 // month = "Jan" / "Feb" / "Mar" / "Apr"
115 // / "May" / "Jun" / "Jul" / "Aug"
116 // / "Sep" / "Oct" / "Nov" / "Dec"
117 //
118 // time = hour zone ; ANSI and Military
119 //
120 // hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
121 // ; 00:00:00 - 23:59:59
122 //
123 // zone = "UT" / "GMT" ; Universal Time
124 // ; North American : UT
125 // / "EST" / "EDT" ; Eastern: - 5/ - 4
126 // / "CST" / "CDT" ; Central: - 6/ - 5
127 // / "MST" / "MDT" ; Mountain: - 7/ - 6
128 // / "PST" / "PDT" ; Pacific: - 8/ - 7
129 // / 1ALPHA ; Military: Z = UT;
130 // ; A:-1; (J not used)
131 // ; M:-12; N:+1; Y:+12
132 // / ( ("+" / "-") 4DIGIT ) ; Local differential
133 // ; hours+min. (HHMM)
134 
135 static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
136 static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
137 
147 string BESUtil::rfc822_date(const time_t t)
148 {
149  struct tm *stm = gmtime(&t);
150  char d[256];
151 
152  snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday], stm->tm_mday, months[stm->tm_mon],
153  1900 + stm->tm_year, stm->tm_hour, stm->tm_min, stm->tm_sec);
154  d[255] = '\0';
155  return string(d);
156 }
157 
158 string BESUtil::unhexstring(string s)
159 {
160  int val;
161  istringstream ss(s);
162  ss >> std::hex >> val;
163  char tmp_str[2];
164  tmp_str[0] = static_cast<char>(val);
165  tmp_str[1] = '\0';
166  return string(tmp_str);
167 }
168 
169 // I modified this to mirror the version in libdap. The change allows several
170 // escape sequences to by listed in 'except'. jhrg 2/18/09
171 string BESUtil::www2id(const string &in, const string &escape, const string &except)
172 {
173  string::size_type i = 0;
174  string res = in;
175  while ((i = res.find_first_of(escape, i)) != string::npos) {
176  if (except.find(res.substr(i, 3)) != string::npos) {
177  i += 3;
178  continue;
179  }
180  res.replace(i, 3, unhexstring(res.substr(i + 1, 2)));
181  }
182 
183  return res;
184 }
185 
186 string BESUtil::lowercase(const string &s)
187 {
188  string return_string = s;
189  for (int j = 0; j < static_cast<int>(return_string.length()); j++) {
190  return_string[j] = (char) tolower(return_string[j]);
191  }
192 
193  return return_string;
194 }
195 
196 string BESUtil::unescape(const string &s)
197 {
198  bool done = false;
199  string::size_type index = 0;
200  /* string::size_type new_index = 0 ; */
201  string new_str;
202  while (!done) {
203  string::size_type bs = s.find('\\', index);
204  if (bs == string::npos) {
205  new_str += s.substr(index, s.length() - index);
206  done = true;
207  }
208  else {
209  new_str += s.substr(index, bs - index);
210  new_str += s[bs + 1];
211  index = bs + 2;
212  }
213  }
214 
215  return new_str;
216 }
217 
239 void BESUtil::check_path(const string &path, const string &root, bool follow_sym_links)
240 {
241  // if nothing is passed in path, then the path checks out since root is
242  // assumed to be valid.
243  if (path == "") return;
244 
245  // Rather than have two basically identical code paths for the two cases (follow and !follow symlinks)
246  // We evaluate the follow_sym_links switch and use a function pointer to get the correct "stat"
247  // function for the eval operation.
248  int (*ye_old_stat_function)(const char *pathname, struct stat *buf);
249  if (follow_sym_links) {
250  BESDEBUG(debug_key, "eval_w10n_resourceId() - Using 'stat' function (follow_sym_links = true)" << endl);
251  ye_old_stat_function = &stat;
252  }
253  else {
254  BESDEBUG(debug_key, "eval_w10n_resourceId() - Using 'lstat' function (follow_sym_links = false)" << endl);
255  ye_old_stat_function = &lstat;
256  }
257 
258  // make sure there are no ../ in the directory, backing up in any way is
259  // not allowed.
260  string::size_type dotdot = path.find("..");
261  if (dotdot != string::npos) {
262  string s = (string) "You are not allowed to access the node " + path;
263  throw BESForbiddenError(s, __FILE__, __LINE__);
264  }
265 
266  // What I want to do is to take each part of path and check to see if it
267  // is a symbolic link and it is accessible. If everything is ok, add the
268  // next part of the path.
269  bool done = false;
270 
271  // what is remaining to check
272  string rem = path;
273  if (rem[0] == '/') rem = rem.substr(1, rem.length() - 1);
274  if (rem[rem.length() - 1] == '/') rem = rem.substr(0, rem.length() - 1);
275 
276  // full path of the thing to check
277  string fullpath = root;
278  if (fullpath[fullpath.length() - 1] == '/') {
279  fullpath = fullpath.substr(0, fullpath.length() - 1);
280  }
281 
282  // path checked so far
283  string checked;
284  while (!done) {
285  size_t slash = rem.find('/');
286  if (slash == string::npos) {
287  fullpath = fullpath + "/" + rem;
288  checked = checked + "/" + rem;
289  done = true;
290  }
291  else {
292  fullpath = fullpath + "/" + rem.substr(0, slash);
293  checked = checked + "/" + rem.substr(0, slash);
294  rem = rem.substr(slash + 1, rem.length() - slash);
295  }
296 
297  struct stat buf;
298  int statret = ye_old_stat_function(fullpath.c_str(), &buf);
299  if (statret == -1) {
300  int errsv = errno;
301  // stat failed, so not accessible. Get the error string,
302  // store in error, and throw exception
303  char *s_err = strerror(errsv);
304  string error = "Unable to access node " + checked + ": ";
305  if (s_err) {
306  error = error + s_err;
307  }
308  else {
309  error = error + "unknown access error";
310  }
311 
312  BESDEBUG(debug_key, "check_path() - error: "<< error << " errno: " << errno << endl);
313 
314  // ENOENT means that the node wasn't found.
315  // On some systems a file that doesn't exist returns ENOTDIR because: w.f.t?
316  // Otherwise, access is being denied for some other reason
317  if (errsv == ENOENT || errsv == ENOTDIR) {
318  // On some systems a file that doesn't exist returns ENOTDIR because: w.f.t?
319  throw BESNotFoundError(error, __FILE__, __LINE__);
320  }
321  else {
322  throw BESForbiddenError(error, __FILE__, __LINE__);
323  }
324  }
325  else {
326  //The call to (stat | lstat) was successful, now check to see if it's a symlink.
327  // Note that if follow_symlinks is true then this will never evaluate as true
328  // because we'll be using 'stat' and not 'lstat' and stat will follow the link
329  // and return information about the file/dir pointed to by the symlink
330  if (S_ISLNK(buf.st_mode)) {
331  string error = "You do not have permission to access " + checked;
332  throw BESForbiddenError(error, __FILE__, __LINE__);
333  }
334  }
335  }
336 
337 #if 0
338  while (!done) {
339  size_t slash = rem.find('/');
340  if (slash == string::npos) {
341  fullpath = fullpath + "/" + rem;
342  checked = checked + "/" + rem;
343  done = true;
344  }
345  else {
346  fullpath = fullpath + "/" + rem.substr(0, slash);
347  checked = checked + "/" + rem.substr(0, slash);
348  rem = rem.substr(slash + 1, rem.length() - slash);
349  }
350 
351  if (!follow_sym_links) {
352  struct stat buf;
353  int statret = lstat(fullpath.c_str(), &buf);
354  if (statret == -1) {
355  int errsv = errno;
356  // stat failed, so not accessible. Get the error string,
357  // store in error, and throw exception
358  char *s_err = strerror(errsv);
359  string error = "Unable to access node " + checked + ": ";
360  if (s_err) {
361  error = error + s_err;
362  }
363  else {
364  error = error + "unknown access error";
365  }
366  // ENOENT means that the node wasn't found. Otherwise, access
367  // is denied for some reason
368  if (errsv == ENOENT) {
369  throw BESNotFoundError(error, __FILE__, __LINE__);
370  }
371  else {
372  throw BESForbiddenError(error, __FILE__, __LINE__);
373  }
374  }
375  else {
376  // lstat was successful, now check if sym link
377  if (S_ISLNK( buf.st_mode )) {
378  string error = "You do not have permission to access "
379  + checked;
380  throw BESForbiddenError(error, __FILE__, __LINE__);
381  }
382  }
383  }
384  else {
385  // just do a stat and see if we can access the thing. If we
386  // can't, get the error information and throw an exception
387  struct stat buf;
388  int statret = stat(fullpath.c_str(), &buf);
389  if (statret == -1) {
390  int errsv = errno;
391  // stat failed, so not accessible. Get the error string,
392  // store in error, and throw exception
393  char *s_err = strerror(errsv);
394  string error = "Unable to access node " + checked + ": ";
395  if (s_err) {
396  error = error + s_err;
397  }
398  else {
399  error = error + "unknown access error";
400  }
401  // ENOENT means that the node wasn't found. Otherwise, access
402  // is denied for some reason
403  if (errsv == ENOENT) {
404  throw BESNotFoundError(error, __FILE__, __LINE__);
405  }
406  else {
407  throw BESForbiddenError(error, __FILE__, __LINE__);
408  }
409  }
410  }
411  }
412 
413 #endif
414 }
415 
416 char *
417 BESUtil::fastpidconverter(char *buf, int base)
418 {
419  return fastpidconverter(getpid(), buf, base);
420 }
421 
422 char *
423 BESUtil::fastpidconverter(long val, /* value to be converted */
424 char *buf, /* output string */
425 int base) /* conversion base */
426 {
427  ldiv_t r; /* result of val / base */
428 
429  if (base > 36 || base < 2) /* no conversion if wrong base */
430  {
431  *buf = '\0';
432  return buf;
433  }
434  if (val < 0) *buf++ = '-';
435  r = ldiv(labs(val), base);
436 
437  /* output digits of val/base first */
438 
439  if (r.quot > 0) buf = fastpidconverter(r.quot, buf, base);
440  /* output last digit */
441 
442  *buf++ = "0123456789abcdefghijklmnopqrstuvwxyz"[(int) r.rem];
443  *buf = '\0';
444  return buf;
445 }
446 
448 {
449  if (!key.empty()) {
450  string::size_type first = key.find_first_not_of(" \t\n\r");
451  string::size_type last = key.find_last_not_of(" \t\n\r");
452  if (first == string::npos)
453  key = "";
454  else {
455  string::size_type num = last - first + 1;
456  string new_key = key.substr(first, num);
457  key = new_key;
458  }
459  }
460 }
461 
462 string BESUtil::entity(char c)
463 {
464  switch (c) {
465  case '>':
466  return "&gt;";
467  case '<':
468  return "&lt;";
469  case '&':
470  return "&amp;";
471  case '\'':
472  return "&apos;";
473  case '\"':
474  return "&quot;";
475  default:
476  return string(1, c); // is this proper default, just the char?
477  }
478 }
479 
486 string BESUtil::id2xml(string in, const string &not_allowed)
487 {
488  string::size_type i = 0;
489 
490  while ((i = in.find_first_of(not_allowed, i)) != string::npos) {
491  in.replace(i, 1, entity(in[i]));
492  i++;
493  }
494 
495  return in;
496 }
497 
503 string BESUtil::xml2id(string in)
504 {
505  string::size_type i = 0;
506 
507  while ((i = in.find("&gt;", i)) != string::npos)
508  in.replace(i, 4, ">");
509 
510  i = 0;
511  while ((i = in.find("&lt;", i)) != string::npos)
512  in.replace(i, 4, "<");
513 
514  i = 0;
515  while ((i = in.find("&amp;", i)) != string::npos)
516  in.replace(i, 5, "&");
517 
518  i = 0;
519  while ((i = in.find("&apos;", i)) != string::npos)
520  in.replace(i, 6, "'");
521 
522  i = 0;
523  while ((i = in.find("&quot;", i)) != string::npos)
524  in.replace(i, 6, "\"");
525 
526  return in;
527 }
528 
542 void BESUtil::explode(char delim, const string &str, list<string> &values)
543 {
544  std::string::size_type start = 0;
545  std::string::size_type qstart = 0;
546  std::string::size_type adelim = 0;
547  std::string::size_type aquote = 0;
548  bool done = false;
549  while (!done) {
550  string aval;
551  if (str[start] == '"') {
552  bool endquote = false;
553  qstart = start + 1;
554  while (!endquote) {
555  aquote = str.find('"', qstart);
556  if (aquote == string::npos) {
557  string currval = str.substr(start, str.length() - start);
558  string err = "BESUtil::explode - No end quote after value " + currval;
559  throw BESInternalError(err, __FILE__, __LINE__);
560  }
561  // could be an escaped escape character and an escaped
562  // quote, or an escaped escape character and a quote
563  if (str[aquote - 1] == '\\') {
564  if (str[aquote - 2] == '\\') {
565  endquote = true;
566  qstart = aquote + 1;
567  }
568  else {
569  qstart = aquote + 1;
570  }
571  }
572  else {
573  endquote = true;
574  qstart = aquote + 1;
575  }
576  }
577  if (str[qstart] != delim && qstart != str.length()) {
578  string currval = str.substr(start, qstart - start);
579  string err = "BESUtil::explode - No delim after end quote " + currval;
580  throw BESInternalError(err, __FILE__, __LINE__);
581  }
582  if (qstart == str.length()) {
583  adelim = string::npos;
584  }
585  else {
586  adelim = qstart;
587  }
588  }
589  else {
590  adelim = str.find(delim, start);
591  }
592  if (adelim == string::npos) {
593  aval = str.substr(start, str.length() - start);
594  done = true;
595  }
596  else {
597  aval = str.substr(start, adelim - start);
598  }
599 
600  values.push_back(aval);
601  start = adelim + 1;
602  if (start == str.length()) {
603  values.push_back("");
604  done = true;
605  }
606  }
607 }
608 
619 string BESUtil::implode(const list<string> &values, char delim)
620 {
621  string result;
622  list<string>::const_iterator i = values.begin();
623  list<string>::const_iterator e = values.end();
624  bool first = true;
625  string::size_type d; // = string::npos ;
626  for (; i != e; i++) {
627  if (!first) result += delim;
628  d = (*i).find(delim);
629  if (d != string::npos && (*i)[0] != '"') {
630  string err = (string) "BESUtil::implode - delimiter exists in value " + (*i);
631  throw BESInternalError(err, __FILE__, __LINE__);
632  }
633  //d = string::npos ;
634  result += (*i);
635  first = false;
636  }
637  return result;
638 }
639 
659 void BESUtil::url_explode(const string &url_str, BESUtil::url &url_parts)
660 {
661  string rest;
662 
663  string::size_type colon = url_str.find(":");
664  if (colon == string::npos) {
665  string err = "BESUtil::url_explode: missing colon for protocol";
666  throw BESInternalError(err, __FILE__, __LINE__);
667  }
668 
669  url_parts.protocol = url_str.substr(0, colon);
670 
671  if (url_str.substr(colon, 3) != "://") {
672  string err = "BESUtil::url_explode: no :// in the URL";
673  throw BESInternalError(err, __FILE__, __LINE__);
674  }
675 
676  colon += 3;
677  rest = url_str.substr(colon);
678 
679  string::size_type slash = rest.find("/");
680  if (slash == string::npos) slash = rest.length();
681 
682  string::size_type at = rest.find("@");
683  if ((at != string::npos) && (at < slash)) {
684  // everything before the @ is username:password
685  string up = rest.substr(0, at);
686  colon = up.find(":");
687  if (colon != string::npos) {
688  url_parts.uname = up.substr(0, colon);
689  url_parts.psswd = up.substr(colon + 1);
690  }
691  else {
692  url_parts.uname = up;
693  }
694  // everything after the @ is domain/path
695  rest = rest.substr(at + 1);
696  }
697  slash = rest.find("/");
698  if (slash == string::npos) slash = rest.length();
699  colon = rest.find(":");
700  if ((colon != string::npos) && (colon < slash)) {
701  // everything before the colon is the domain
702  url_parts.domain = rest.substr(0, colon);
703  // everything after the folon is port/path
704  rest = rest.substr(colon + 1);
705  slash = rest.find("/");
706  if (slash != string::npos) {
707  url_parts.port = rest.substr(0, slash);
708  url_parts.path = rest.substr(slash + 1);
709  }
710  else {
711  url_parts.port = rest;
712  url_parts.path = "";
713  }
714  }
715  else {
716  slash = rest.find("/");
717  if (slash != string::npos) {
718  url_parts.domain = rest.substr(0, slash);
719  url_parts.path = rest.substr(slash + 1);
720  }
721  else {
722  url_parts.domain = rest;
723  }
724  }
725 }
726 
727 string BESUtil::url_create(BESUtil::url &url_parts)
728 {
729  string url = url_parts.protocol + "://";
730  if (!url_parts.uname.empty()) {
731  url += url_parts.uname;
732  if (!url_parts.psswd.empty()) url += ":" + url_parts.psswd;
733  url += "@";
734  }
735  url += url_parts.domain;
736  if (!url_parts.port.empty()) url += ":" + url_parts.port;
737  if (!url_parts.path.empty()) url += "/" + url_parts.path;
738 
739  return url;
740 }
741 
754 string BESUtil::assemblePath(const string &firstPart, const string &secondPart, bool ensureLeadingSlash)
755 {
756 #if 0
757  assert(!firstPart.empty());
758 
759  // This version works but does not remove duplicate slashes
760  string first = firstPart;
761  string second = secondPart;
762 
763  // add a leading slash if needed
764  if (ensureLeadingSlash && first[0] != '/')
765  first = "/" + first;
766 
767  // if 'second' start with a slash, remove it
768  if (second[0] == '/')
769  second = second.substr(1);
770 
771  // glue the two parts together, adding a slash if needed
772  if (first.back() == '/')
773  return first.append(second);
774  else
775  return first.append("/").append(second);
776 #endif
777 
778 #if 1
779  BESDEBUG("util", "BESUtil::assemblePath() - firstPart: "<< firstPart << endl);
780  BESDEBUG("util", "BESUtil::assemblePath() - secondPart: "<< secondPart << endl);
781 
782  assert(!firstPart.empty());
783 
784  string first = firstPart;
785  string second = secondPart;
786 
787  if (ensureLeadingSlash) {
788  if (first[0] != '/') first = "/" + first;
789  }
790 
791  // make sure there are not multiple slashes at the end of the first part...
792  // Note that this removes all of the slashes. jhrg 9/27/16
793  while (!first.empty() && *first.rbegin() == '/') {
794  // C++-11 first.pop_back();
795  first = first.substr(0, first.length() - 1);
796  }
797 
798  // make sure second part does not BEGIN with a slash
799  while (!second.empty() && second[0] == '/') {
800  // erase is faster? second = second.substr(1);
801  second.erase(0, 1);
802  }
803 
804  string newPath = first.append("/").append(second);
805 
806  BESDEBUG("util", "BESUtil::assemblePath() - newPath: "<< newPath << endl);
807 
808  return newPath;
809 #endif
810 
811 #if 0
812  BESDEBUG("util", "BESUtil::assemblePath() - firstPart: "<< firstPart << endl);
813  BESDEBUG("util", "BESUtil::assemblePath() - secondPart: "<< secondPart << endl);
814 
815  string first = firstPart;
816  string second = secondPart;
817 
818  if (ensureLeadingSlash) {
819  if (*first.begin() != '/') first = "/" + first;
820  }
821 
822  // make sure there are not multiple slashes at the end of the first part...
823  while (*first.rbegin() == '/' && first.length() > 0) {
824  first = first.substr(0, first.length() - 1);
825  }
826 
827  // make sure first part ends with a "/"
828  if (*first.rbegin() != '/') {
829  first += "/";
830  }
831 
832  // make sure second part does not BEGIN with a slash
833  while (*second.begin() == '/' && second.length() > 0) {
834  second = second.substr(1);
835  }
836 
837  string newPath = first + second;
838 
839  BESDEBUG("util", "BESUtil::assemblePath() - newPath: "<< newPath << endl);
840 
841  return newPath;
842 #endif
843 }
844 
849 bool BESUtil::endsWith(std::string const &fullString, std::string const &ending)
850 {
851  if (fullString.length() >= ending.length()) {
852  return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
853  }
854  else {
855  return false;
856  }
857 }
error thrown if the resource requested cannot be found
exception thrown if inernal error encountered
static string lowercase(const string &s)
Definition: BESUtil.cc:186
static string www2id(const string &in, const string &escape="%", const string &except="")
Definition: BESUtil.cc:171
static void removeLeadingAndTrailingBlanks(string &key)
Definition: BESUtil.cc:447
static string assemblePath(const string &firstPart, const string &secondPart, bool addLeadingSlash=false)
Assemble path fragments making sure that they are separated by a single &#39;/&#39; character.
Definition: BESUtil.cc:754
static string implode(const list< string > &values, char delim)
Definition: BESUtil.cc:619
static string xml2id(string in)
Definition: BESUtil.cc:503
static void set_mime_html(ostream &strm)
Generate an HTTP 1.0 response header for a html document.
Definition: BESUtil.cc:88
static void explode(char delim, const string &str, list< string > &values)
Definition: BESUtil.cc:542
static void set_mime_text(ostream &strm)
Generate an HTTP 1.0 response header for a text document.
Definition: BESUtil.cc:69
static void url_explode(const string &url_str, BESUtil::url &url_parts)
Given a url, break the url into its different parts.
Definition: BESUtil.cc:659
static string id2xml(string in, const string &not_allowed="><&'\)
Definition: BESUtil.cc:486
static bool endsWith(std::string const &fullString, std::string const &ending)
Definition: BESUtil.cc:849
error thrown if the BES is not allowed to access the resource requested
static char * fastpidconverter(char *buf, int base)
Definition: BESUtil.cc:417
static string unescape(const string &s)
Definition: BESUtil.cc:196
static void check_path(const string &path, const string &root, bool follow_sym_links)
Definition: BESUtil.cc:239