BESUtil.cc

Go to the documentation of this file.
00001 // BESUtil.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <sys/types.h>
00034 #include <sys/stat.h>
00035 
00036 #include <cerrno>
00037 #include <cstring>
00038 #include <sstream>
00039 #include <iostream>
00040 
00041 using std::istringstream ;
00042 using std::cout ;
00043 using std::endl ;
00044 
00045 #include "BESUtil.h"
00046 #include "BESForbiddenError.h"
00047 #include "BESNotFoundError.h"
00048 #include "config.h"
00049 
00050 #define CRLF "\r\n"
00051 
00056 void
00057 BESUtil::set_mime_text( ostream &strm )
00058 {
00059     strm << "HTTP/1.0 200 OK" << CRLF ;
00060     strm << "XBES-Server: " << PACKAGE_STRING << CRLF ;
00061 
00062     const time_t t = time(0);
00063     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00064     strm << "Last-Modified: " << rfc822_date(t).c_str() << CRLF ;
00065 
00066     strm << "Content-Type: text/plain" << CRLF ;
00067     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00068     strm << "Content-Description: unknown" << CRLF ;
00069     strm << CRLF ;
00070 }
00071 
00076 void
00077 BESUtil::set_mime_html( ostream &strm )
00078 {
00079     strm << "HTTP/1.0 200 OK" << CRLF ;
00080     strm << "XBES-Server: " << PACKAGE_STRING << CRLF ;
00081 
00082     const time_t t = time(0);
00083     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00084     strm << "Last-Modified: " << rfc822_date(t).c_str() << CRLF ;
00085 
00086     strm << "Content-type: text/html" << CRLF ;
00087     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00088     strm << "Content-Description: unknown" << CRLF ;
00089     strm << CRLF ;
00090 }
00091 
00092 // Return a MIME rfc-822 date. The grammar for this is:
00093 //       date-time   =  [ day "," ] date time        ; dd mm yy
00094 //                                                   ;  hh:mm:ss zzz
00095 //
00096 //       day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
00097 //                   /  "Fri"  / "Sat" /  "Sun"
00098 //
00099 //       date        =  1*2DIGIT month 2DIGIT        ; day month year
00100 //                                                   ;  e.g. 20 Jun 82
00101 //                   NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
00102 //
00103 //       month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
00104 //                   /  "May"  /  "Jun" /  "Jul"  /  "Aug"
00105 //                   /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
00106 //
00107 //       time        =  hour zone                    ; ANSI and Military
00108 //
00109 //       hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
00110 //                                                   ; 00:00:00 - 23:59:59
00111 //
00112 //       zone        =  "UT"  / "GMT"                ; Universal Time
00113 //                                                   ; North American : UT
00114 //                   /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
00115 //                   /  "CST" / "CDT"                ;  Central:  - 6/ - 5
00116 //                   /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
00117 //                   /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
00118 //                   /  1ALPHA                       ; Military: Z = UT;
00119 //                                                   ;  A:-1; (J not used)
00120 //                                                   ;  M:-12; N:+1; Y:+12
00121 //                   / ( ("+" / "-") 4DIGIT )        ; Local differential
00122 //                                                   ;  hours+min. (HHMM)
00123 
00124 static const char *days[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
00125 static const char *months[]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 
00126                         "Aug", "Sep", "Oct", "Nov", "Dec"};
00127 
00137 string
00138 BESUtil::rfc822_date(const time_t t)
00139 {
00140     struct tm *stm = gmtime(&t);
00141     char d[256];
00142 
00143     snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
00144             stm->tm_mday, months[stm->tm_mon],
00145             1900 + stm->tm_year,
00146             stm->tm_hour, stm->tm_min, stm->tm_sec);
00147     d[255] = '\0';
00148     return string(d);
00149 }
00150 
00151 string 
00152 BESUtil::unhexstring( string s ) 
00153 {
00154     int val;
00155     istringstream ss( s ) ;
00156     ss >> std::hex >> val;
00157     char tmp_str[2];
00158     tmp_str[0] = static_cast<char>(val);
00159     tmp_str[1] = '\0';
00160     return string(tmp_str);
00161 }
00162 
00163 string 
00164 BESUtil::www2id(const string &in, const string &escape, const string &except)
00165 {
00166     string::size_type i = 0;
00167     string res = in;
00168     while ((i = res.find_first_of(escape, i)) != string::npos) {
00169         if (res.substr(i, 3) == except) {
00170             i += 3;
00171             continue;
00172         }
00173         res.replace(i, 3, unhexstring(res.substr(i + 1, 2)));
00174     }
00175 
00176     return res;
00177 }
00178 
00179 string
00180 BESUtil::lowercase( const string &s )
00181 {
00182     string return_string = s ;
00183     for( int j = 0; j < return_string.length(); j++ )
00184     {
00185         return_string[j] = (char)tolower( return_string[j] ) ;
00186     }
00187 
00188     return return_string ;
00189 }
00190 
00191 string
00192 BESUtil::unescape( const string &s )
00193 {
00194     bool done = false ;
00195     string::size_type index = 0 ;
00196     string::size_type new_index = 0 ;
00197     string new_str ;
00198     while( !done )
00199     {
00200         string::size_type bs = s.find( '\\', index ) ;
00201         if( bs == string::npos )
00202         {
00203             new_str += s.substr( index, s.length() - index ) ;
00204             done = true ;
00205         }
00206         else
00207         {
00208             new_str += s.substr( index, bs - index ) ;
00209             new_str += s[bs+1] ;
00210             index = bs+2 ;
00211         }
00212     }
00213 
00214     return new_str ;
00215 }
00216 
00238 void
00239 BESUtil::check_path( const string &path,
00240                      const string &root,
00241                      bool follow_sym_links )
00242 {
00243     // if nothing is passed in path, then the path checks out since root is
00244     // assumed to be valid.
00245     if( path == "" )
00246         return ;
00247 
00248     // make sure there are no ../ in the directory, backing up in any way is
00249     // not allowed.
00250     string::size_type dotdot = path.find( ".." ) ;
00251     if( dotdot != string::npos )
00252     {
00253         string s = (string)"You are not allowed to access the node " + path;
00254         throw BESForbiddenError( s, __FILE__, __LINE__ ) ;
00255     }
00256 
00257     // What I want to do is to take each part of path and check to see if it
00258     // is a symbolic link and it is accessible. If everything is ok, add the
00259     // next part of the path.
00260     bool done = false ;
00261 
00262     // what is remaining to check
00263     string rem = path ;
00264     if( rem[0] == '/' )
00265         rem = rem.substr( 1, rem.length() - 1 ) ;
00266     if( rem[rem.length()-1] == '/' )
00267         rem = rem.substr( 0, rem.length() - 1 ) ;
00268 
00269     // full path of the thing to check
00270     string fullpath = root ;
00271     if( fullpath[fullpath.length()-1] == '/' )
00272     {
00273         fullpath = fullpath.substr( 0, fullpath.length() - 1 ) ;
00274     }
00275 
00276     // path checked so far
00277     string checked ;
00278 
00279     while( !done )
00280     {
00281         size_t slash = rem.find( '/' ) ;
00282         if( slash == string::npos )
00283         {
00284             fullpath = fullpath + "/" + rem ;
00285             checked = checked + "/" + rem ;
00286             done = true ;
00287         }
00288         else
00289         {
00290             fullpath = fullpath + "/" + rem.substr( 0, slash ) ;
00291             checked = checked + "/" + rem.substr( 0, slash ) ;
00292             rem = rem.substr( slash + 1, rem.length() - slash ) ;
00293         }
00294 
00295         if( !follow_sym_links )
00296         {
00297             struct stat buf;
00298             int statret = lstat( fullpath.c_str(), &buf ) ;
00299             if( statret == -1 )
00300             {
00301                 int errsv = errno ;
00302                 // stat failed, so not accessible. Get the error string,
00303                 // store in error, and throw exception
00304                 char *s_err = strerror( errsv ) ;
00305                 string error = "Unable to access node " + checked + ": " ;
00306                 if( s_err )
00307                 {
00308                     error = error + s_err ;
00309                 }
00310                 else
00311                 {
00312                     error = error + "unknow access error" ;
00313                 }
00314                 // ENOENT means that the node wasn't found. Otherise, access
00315                 // is denied for some reason
00316                 if( errsv == ENOENT )
00317                 {
00318                     throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00319                 }
00320                 else
00321                 {
00322                     throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00323                 }
00324             }
00325             else
00326             {
00327                 // lstat was successful, now check if sym link
00328                 if( S_ISLNK( buf.st_mode ) )
00329                 {
00330                     string error = "You do not have permission to access "
00331                                    + checked ;
00332                     throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00333                 }
00334             }
00335         }
00336         else
00337         {
00338             // just do a stat and see if we can access the thing. If we
00339             // can't, get the error information and throw an exception
00340             struct stat buf ;
00341             int statret = stat( fullpath.c_str(), &buf ) ;
00342             if( statret == -1 )
00343             {
00344                 int errsv = errno ;
00345                 // stat failed, so not accessible. Get the error string,
00346                 // store in error, and throw exception
00347                 char *s_err = strerror( errsv ) ;
00348                 string error = "Unable to access node " + checked + ": " ;
00349                 if( s_err )
00350                 {
00351                     error = error + s_err ;
00352                 }
00353                 else
00354                 {
00355                     error = error + "unknow access error" ;
00356                 }
00357                 // ENOENT means that the node wasn't found. Otherise, access
00358                 // is denied for some reason
00359                 if( errsv == ENOENT )
00360                 {
00361                     throw BESNotFoundError( error, __FILE__, __LINE__ ) ;
00362                 }
00363                 else
00364                 {
00365                     throw BESForbiddenError( error, __FILE__, __LINE__ ) ;
00366                 }
00367             }
00368         }
00369     }
00370 }
00371 

Generated on Tue Mar 4 23:13:36 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.1