libdap  Updated for version 3.17.0
mime_util.cc
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //         Reza Nekovei <rnekovei@intcomm.net>
00010 //
00011 // This library is free software; you can redistribute it and/or
00012 // modify it under the terms of the GNU Lesser General Public
00013 // License as published by the Free Software Foundation; either
00014 // version 2.1 of the License, or (at your option) any later version.
00015 //
00016 // This library is distributed in the hope that it will be useful,
00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019 // Lesser General Public License for more details.
00020 //
00021 // You should have received a copy of the GNU Lesser General Public
00022 // License along with this library; if not, write to the Free Software
00023 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00024 //
00025 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00026 
00027 // (c) COPYRIGHT URI/MIT 1994-2001
00028 // Please read the full copyright statement in the file COPYRIGHT_URI.
00029 //
00030 // Authors:
00031 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00032 //      reza            Reza Nekovei <rnekovei@intcomm.net>
00033 
00034 // A few useful routines which are used in CGI programs.
00035 //
00036 // ReZa 9/30/94
00037 
00038 #include "config.h"
00039 
00040 #include <cstring>
00041 #include <cstdio>
00042 #include <ctype.h>
00043 
00044 #ifndef TM_IN_SYS_TIME
00045 #include <time.h>
00046 #else
00047 #include <sys/time.h>
00048 #endif
00049 
00050 #include <sys/types.h>
00051 #include <sys/stat.h>
00052 
00053 #ifndef WIN32
00054 #include <unistd.h>    // for access
00055 #include <sys/wait.h>
00056 #else
00057 #include <io.h>
00058 #include <fcntl.h>
00059 #include <process.h>
00060 // Win32 does not define this. 08/21/02 jhrg
00061 #define F_OK 0
00062 #endif
00063 
00064 #include <iostream>
00065 #include <sstream>
00066 #include <fstream>
00067 #include <string>
00068 
00069 #include "mime_util.h"
00070 #include "media_types.h"
00071 
00072 #include "Ancillary.h"
00073 #include "util.h"  // This supplies flush_stream for WIN32.
00074 #include "debug.h"
00075 
00076 #ifdef WIN32
00077 #define FILE_DELIMITER '\\'
00078 #else  //  default to unix
00079 #define FILE_DELIMITER '/'
00080 #endif
00081 
00082 // ...not using a const string here to avoid global objects. jhrg 12/23/05
00083 #define CRLF "\r\n"             // Change here, expr-test.cc, in DODSFilter and ResponseBuilder
00084 
00085 using namespace std;
00086 
00087 namespace libdap {
00088 
00094 time_t
00095 last_modified_time(const string &name)
00096 {
00097     struct stat m;
00098 
00099     if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
00100         return m.st_mtime;
00101     else
00102         return time(0);
00103 }
00104 // Return a MIME rfc-822 date. The grammar for this is:
00105 //       date-time   =  [ day "," ] date time        ; dd mm yy
00106 //                                                   ;  hh:mm:ss zzz
00107 //
00108 //       day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
00109 //                   /  "Fri"  / "Sat" /  "Sun"
00110 //
00111 //       date        =  1*2DIGIT month 2DIGIT        ; day month year
00112 //                                                   ;  e.g. 20 Jun 82
00113 //                   NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
00114 //
00115 //       month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
00116 //                   /  "May"  /  "Jun" /  "Jul"  /  "Aug"
00117 //                   /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
00118 //
00119 //       time        =  hour zone                    ; ANSI and Military
00120 //
00121 //       hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
00122 //                                                   ; 00:00:00 - 23:59:59
00123 //
00124 //       zone        =  "UT"  / "GMT"                ; Universal Time
00125 //                                                   ; North American : UT
00126 //                   /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
00127 //                   /  "CST" / "CDT"                ;  Central:  - 6/ - 5
00128 //                   /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
00129 //                   /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
00130 //                   /  1ALPHA                       ; Military: Z = UT;
00131 //                                                   ;  A:-1; (J not used)
00132 //                                                   ;  M:-12; N:+1; Y:+12
00133 //                   / ( ("+" / "-") 4DIGIT )        ; Local differential
00134 //                                                   ;  hours+min. (HHMM)
00135 
00136 static const char *days[] =
00137     {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00138     };
00139 static const char *months[] =
00140     {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
00141      "Aug", "Sep", "Oct", "Nov", "Dec"
00142     };
00143 
00144 #ifdef _MSC_VER
00145 #define snprintf sprintf_s
00146 #endif
00147 
00155 string
00156 rfc822_date(const time_t t)
00157 {
00158     struct tm *stm = gmtime(&t);
00159     if (!stm)
00160         return "";
00161 
00162     char d[256];
00163 
00164     snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
00165             stm->tm_mday, months[stm->tm_mon],
00166             1900 + stm->tm_year,
00167             stm->tm_hour, stm->tm_min, stm->tm_sec);
00168     d[255] = '\0';
00169     return string(d);
00170 }
00171 
00172 static const int TimLen = 26; // length of string from asctime()
00173 //static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
00174 
00188 bool
00189 do_version(const string &script_ver, const string &dataset_ver)
00190 {
00191     fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
00192     fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
00193     fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00194     fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00195     fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
00196     fprintf(stdout, CRLF) ;
00197 
00198     fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
00199 
00200     if (script_ver != "")
00201         fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
00202 
00203     if (dataset_ver != "")
00204         fprintf(stdout,  "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
00205 
00206     fflush(stdout) ;            // Not sure this is needed. jhrg 12/23/05
00207 
00208     return true;
00209 }
00210 
00221 void
00222 ErrMsgT(const string &Msgt)
00223 {
00224     time_t TimBin;
00225     char TimStr[TimLen];
00226 
00227     if (time(&TimBin) == (time_t) - 1)
00228         strncpy(TimStr, "time() error           ", TimLen-1);
00229     else {
00230         char *ctime_value = ctime(&TimBin);
00231         if (!ctime_value)
00232                 strncpy(TimStr, "Unknown", TimLen-1);
00233         else {
00234                 strncpy(TimStr, ctime_value, TimLen-1);
00235                 TimStr[TimLen - 2] = '\0'; // overwrite the \n
00236         }
00237 #if 0
00238         strncpy(TimStr, ctime(&TimBin), TimLen-1);
00239         TimStr[TimLen - 2] = '\0'; // overwrite the \n
00240 #endif
00241     }
00242 
00243     cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
00244 }
00245 
00246 // Given a pathname, return just the filename component with any extension
00247 // removed. The new string resides in newly allocated memory; the caller must
00248 // delete it when done using the filename.
00249 // Originally from the netcdf distribution (ver 2.3.2).
00250 //
00251 // *** Change to string class argument and return type. jhrg
00252 // *** Changed so it also removes the#path#of#the#file# from decompressed
00253 //     files.  rph.
00254 // Returns: A filename, with path and extension information removed. If
00255 // memory for the new name cannot be allocated, does not return!
00256 
00267 string
00268 name_path(const string &path)
00269 {
00270     if (path == "")
00271         return string("");
00272 
00273     string::size_type delim = path.find_last_of(FILE_DELIMITER);
00274     string::size_type pound = path.find_last_of("#");
00275     string new_path;
00276 
00277     if (pound != string::npos)
00278         new_path = path.substr(pound + 1);
00279     else
00280         new_path = path.substr(delim + 1);
00281 
00282     return new_path;
00283 }
00284 
00285 // Send string to set the transfer (mime) type and server version
00286 // Note that the content description filed is used to indicate whether valid
00287 // information of an error message is contained in the document and the
00288 // content-encoding field is used to indicate whether the data is compressed.
00289 // If the data stream is to be compressed, arrange for a compression output
00290 // filter so that all information sent after the header will be compressed.
00291 //
00292 // Returns: false if the compression output filter was to be used but could
00293 // not be started, true otherwise.
00294 #if 0
00295 static const char *descrip[] =
00296     {"unknown", "dods_das", "dods_dds", "dods_data", "dods_ddx",
00297      "dods_error", "web_error", "dap4-dmr", "dap4-data", "dap4-error"
00298     };
00299 #endif
00300 
00301 static const char *descrip[] = {
00302 "unknown_type",
00303 "dods_das",
00304 "dods_dds",
00305 "dods_data",
00306 "dods_ddx",       // This is the old XML DDS/DAS used prior to dap4
00307 "dods_data_ddx",  // This is used for caching data responses
00308 "dods_error",
00309 "web_error",
00310 
00311 "dap4_dmr",       // DAP4 metadata
00312 "dap4_data",      // The DMR with a data blob
00313 "dap4_error"      // The error response for DAP4
00314 };
00315 
00316 static const char *encoding[] =
00317     {"unknown", "deflate", "x-plain", "gzip", "binary"
00318     };
00319 
00325 ObjectType
00326 get_type(const string &value)
00327 {
00328         return get_description_type(value);
00329 }
00330 
00331 // TODO Recode to use the constants in media_types.h. jhrg 11/12/13
00332 
00338 ObjectType
00339 get_description_type(const string &value)
00340 {
00341     if ((value == DAS1) || (value == "dods-das"))
00342         return dods_das;
00343     else if ((value == "dods_dds") || (value == "dods-dds"))
00344         return dods_dds;
00345     else if ((value == "dods_data") || (value == "dods-data"))
00346         return dods_data;
00347     else if ((value == "dods_ddx") || (value == "dods-ddx"))
00348         return dods_ddx;
00349     else if ((value == "dods_data_ddx" || (value == "dods-data-ddx")))
00350         return dods_data_ddx;
00351     else if ((value == "dods_error") || (value == "dods-error"))
00352         return dods_error;
00353     else if ((value == "web_error") || (value == "web-error"))
00354         return web_error;
00355 
00356     else if ((value == "dap4_dmr") || (value == "dap4-dmr") || (value == DMR_Content_Type))
00357         return dap4_dmr;
00358     else if ((value == "dap4_data") || (value == "dap4-data") || (value == DAP4_DATA_Content_Type))
00359         return dap4_data;
00360     else if ((value == "dap4_error") || (value == "dap4-error"))
00361         return dap4_error;
00362 
00363     else
00364         return unknown_type;
00365 }
00366 
00380 void
00381 set_mime_text(FILE *out, ObjectType type, const string &ver,
00382               EncodingType enc, const time_t last_modified)
00383 {
00384     ostringstream oss;
00385     set_mime_text(oss, type, ver, enc, last_modified);
00386     fwrite(oss.str().data(), 1, oss.str().length(), out);
00387 }
00388 
00402 void
00403 set_mime_text(ostream &strm, ObjectType type, const string &ver,
00404               EncodingType enc, const time_t last_modified)
00405 {
00406     strm << "HTTP/1.0 200 OK" << CRLF ;
00407     if (ver == "") {
00408         strm << "XDODS-Server: " << DVR << CRLF ;
00409         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00410     }
00411     else {
00412         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00413         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00414     }
00415     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00416 
00417     const time_t t = time(0);
00418     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00419 
00420     strm << "Last-Modified: " ;
00421     if (last_modified > 0)
00422         strm << rfc822_date(last_modified).c_str() << CRLF ;
00423     else
00424         strm << rfc822_date(t).c_str() << CRLF ;
00425 
00426     if (type == dap4_dmr)
00427         strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF ;
00428     else
00429         strm << "Content-Type: text/plain" << CRLF ;
00430 
00431     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00432     // jhrg 12/23/05
00433     strm << "Content-Description: " << descrip[type] << CRLF ;
00434     if (type == dods_error) // don't cache our error responses.
00435         strm << "Cache-Control: no-cache" << CRLF ;
00436     // Don't write a Content-Encoding header for x-plain since that breaks
00437     // Netscape on NT. jhrg 3/23/97
00438     if (enc != x_plain)
00439         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00440     strm << CRLF ;
00441 }
00442 
00458 void set_mime_text(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
00459         const string &protocol)
00460 {
00461     strm << "HTTP/1.0 200 OK" << CRLF;
00462 
00463     strm << "XDODS-Server: " << DVR << CRLF;
00464     strm << "XOPeNDAP-Server: " << DVR << CRLF;
00465 
00466     if (protocol == "")
00467         strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
00468     else
00469         strm << "XDAP: " << protocol << CRLF;
00470 
00471     const time_t t = time(0);
00472     strm << "Date: " << rfc822_date(t).c_str() << CRLF;
00473 
00474     strm << "Last-Modified: ";
00475     if (last_modified > 0)
00476         strm << rfc822_date(last_modified).c_str() << CRLF;
00477     else
00478         strm << rfc822_date(t).c_str() << CRLF;
00479 
00480     if (type == dap4_dmr)
00481         strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
00482     else
00483         strm << "Content-Type: text/plain" << CRLF;
00484 
00485     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00486     // jhrg 12/23/05
00487     strm << "Content-Description: " << descrip[type] << CRLF;
00488     if (type == dods_error) // don't cache our error responses.
00489         strm << "Cache-Control: no-cache" << CRLF;
00490     // Don't write a Content-Encoding header for x-plain since that breaks
00491     // Netscape on NT. jhrg 3/23/97
00492     if (enc != x_plain)
00493         strm << "Content-Encoding: " << encoding[enc] << CRLF;
00494     strm << CRLF;
00495 }
00496 
00508 void
00509 set_mime_html(FILE *out, ObjectType type, const string &ver,
00510               EncodingType enc, const time_t last_modified)
00511 {
00512     ostringstream oss;
00513     set_mime_html(oss, type, ver, enc, last_modified);
00514     fwrite(oss.str().data(), 1, oss.str().length(), out);
00515 }
00516 
00528 void
00529 set_mime_html(ostream &strm, ObjectType type, const string &ver,
00530               EncodingType enc, const time_t last_modified)
00531 {
00532     strm << "HTTP/1.0 200 OK" << CRLF ;
00533     if (ver == "") {
00534         strm << "XDODS-Server: " << DVR << CRLF ;
00535         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00536     }
00537     else {
00538         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00539         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00540     }
00541     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00542 
00543     const time_t t = time(0);
00544     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00545 
00546     strm << "Last-Modified: " ;
00547     if (last_modified > 0)
00548         strm << rfc822_date(last_modified).c_str() << CRLF ;
00549     else
00550         strm << rfc822_date(t).c_str() << CRLF ;
00551 
00552     strm << "Content-type: text/html" << CRLF ;
00553     // See note above about Content-Description header. jhrg 12/23/05
00554     strm << "Content-Description: " << descrip[type] << CRLF ;
00555     if (type == dods_error) // don't cache our error responses.
00556         strm << "Cache-Control: no-cache" << CRLF ;
00557     // Don't write a Content-Encoding header for x-plain since that breaks
00558     // Netscape on NT. jhrg 3/23/97
00559     if (enc != x_plain)
00560         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00561     strm << CRLF ;
00562 }
00563 
00574 void set_mime_html(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
00575         const string &protocol)
00576 {
00577     strm << "HTTP/1.0 200 OK" << CRLF;
00578 
00579     strm << "XDODS-Server: " << DVR<< CRLF;
00580     strm << "XOPeNDAP-Server: " << DVR<< CRLF;
00581 
00582     if (protocol == "")
00583         strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
00584     else
00585         strm << "XDAP: " << protocol << CRLF;
00586 
00587     const time_t t = time(0);
00588     strm << "Date: " << rfc822_date(t).c_str() << CRLF;
00589 
00590     strm << "Last-Modified: ";
00591     if (last_modified > 0)
00592         strm << rfc822_date(last_modified).c_str() << CRLF;
00593     else
00594         strm << rfc822_date(t).c_str() << CRLF;
00595 
00596     strm << "Content-type: text/html" << CRLF;
00597     // See note above about Content-Description header. jhrg 12/23/05
00598     strm << "Content-Description: " << descrip[type] << CRLF;
00599     if (type == dods_error) // don't cache our error responses.
00600         strm << "Cache-Control: no-cache" << CRLF;
00601     // Don't write a Content-Encoding header for x-plain since that breaks
00602     // Netscape on NT. jhrg 3/23/97
00603     if (enc != x_plain)
00604         strm << "Content-Encoding: " << encoding[enc] << CRLF;
00605     strm << CRLF;
00606 }
00607 
00622 void
00623 set_mime_binary(FILE *out, ObjectType type, const string &ver,
00624                 EncodingType enc, const time_t last_modified)
00625 {
00626     ostringstream oss;
00627     set_mime_binary(oss, type, ver, enc, last_modified);
00628     fwrite(oss.str().data(), 1, oss.str().length(), out);
00629 }
00630 
00645 void
00646 set_mime_binary(ostream &strm, ObjectType type, const string &ver,
00647                 EncodingType enc, const time_t last_modified)
00648 {
00649     strm << "HTTP/1.0 200 OK" << CRLF ;
00650     if (ver == "") {
00651         strm << "XDODS-Server: " << DVR << CRLF ;
00652         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00653     }
00654     else {
00655         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00656         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00657     }
00658     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00659 
00660     const time_t t = time(0);
00661     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00662 
00663     strm << "Last-Modified: " ;
00664     if (last_modified > 0)
00665         strm << rfc822_date(last_modified).c_str() << CRLF ;
00666     else
00667         strm << rfc822_date(t).c_str() << CRLF ;
00668 
00669     strm << "Content-Type: application/octet-stream" << CRLF ;
00670     strm << "Content-Description: " << descrip[type] << CRLF ;
00671     if (enc != x_plain)
00672         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00673 
00674     strm << CRLF ;
00675 }
00676 
00690 void set_mime_binary(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
00691         const string &protocol)
00692 {
00693     strm << "HTTP/1.0 200 OK" << CRLF;
00694 
00695     strm << "XDODS-Server: " << DVR << CRLF;
00696     strm << "XOPeNDAP-Server: " << DVR << CRLF;
00697 
00698     if (protocol.empty())
00699         strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
00700     else
00701         strm << "XDAP: " << protocol << CRLF;
00702 
00703     const time_t t = time(0);
00704     strm << "Date: " << rfc822_date(t).c_str() << CRLF;
00705 
00706     strm << "Last-Modified: ";
00707     if (last_modified > 0)
00708         strm << rfc822_date(last_modified).c_str() << CRLF;
00709     else
00710         strm << rfc822_date(t).c_str() << CRLF;
00711 
00712     strm << "Content-Type: application/octet-stream" << CRLF;
00713     strm << "Content-Description: " << descrip[type] << CRLF;
00714     if (enc != x_plain)
00715         strm << "Content-Encoding: " << encoding[enc] << CRLF;
00716 
00717     strm << CRLF;
00718 }
00719 
00720 void set_mime_multipart(ostream &strm, const string &boundary,
00721         const string &start, ObjectType type,
00722         const string &version, EncodingType enc,
00723         const time_t last_modified)
00724 {
00725     strm << "HTTP/1.0 200 OK" << CRLF ;
00726     if (version == "") {
00727         strm << "XDODS-Server: " << DVR << CRLF ;
00728         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00729     }
00730     else {
00731         strm << "XDODS-Server: " << version.c_str() << CRLF ;
00732         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
00733     }
00734     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00735 
00736     const time_t t = time(0);
00737     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00738 
00739     strm << "Last-Modified: " ;
00740     if (last_modified > 0)
00741         strm << rfc822_date(last_modified).c_str() << CRLF ;
00742     else
00743         strm << rfc822_date(t).c_str() << CRLF ;
00744 
00745     strm << "Content-Type: Multipart/Related; boundary=" << boundary
00746         << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
00747     strm << "Content-Description: " << descrip[type] << CRLF ;
00748     if (enc != x_plain)
00749         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00750 
00751     strm << CRLF ;
00752 }
00753 
00756 void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, EncodingType enc,
00757         const time_t last_modified, const string &protocol, const string &url)
00758 {
00759     strm << "HTTP/1.1 200 OK" << CRLF;
00760 
00761     const time_t t = time(0);
00762     strm << "Date: " << rfc822_date(t).c_str() << CRLF;
00763 
00764     strm << "Last-Modified: ";
00765     if (last_modified > 0)
00766         strm << rfc822_date(last_modified).c_str() << CRLF;
00767     else
00768         strm << rfc822_date(t).c_str() << CRLF;
00769 
00770     strm << "Content-Type: multipart/related; boundary=" << boundary << "; start=\"<" << start
00771             << ">\"; type=\"text/xml\"" << CRLF;
00772 
00773     // data-ddx;"; removed as a result of the merge of the hyrax 1.8 release
00774     // branch.
00775     strm << "Content-Description: " << descrip[type] << ";";
00776     if (!url.empty())
00777         strm << " url=\"" << url << "\"" << CRLF;
00778     else
00779         strm << CRLF;
00780 
00781     if (enc != x_plain)
00782         strm << "Content-Encoding: " << encoding[enc] << CRLF;
00783 
00784     if (protocol == "")
00785         strm << "X-DAP: " << DAP_PROTOCOL_VERSION << CRLF;
00786     else
00787         strm << "X-DAP: " << protocol << CRLF;
00788 
00789     strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
00790 
00791     strm << CRLF;
00792 }
00793 
00794 void set_mime_ddx_boundary(ostream &strm, const string &boundary,
00795         const string &cid, ObjectType type, EncodingType enc)
00796 {
00797     strm << "--" << boundary << CRLF;
00798     strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
00799     strm << "Content-Id: <" << cid << ">" << CRLF;
00800     strm << "Content-Description: " << descrip[type] << CRLF ;
00801     if (enc != x_plain)
00802          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00803 
00804     strm << CRLF;
00805 }
00806 
00807 void set_mime_data_boundary(ostream &strm, const string &boundary,
00808         const string &cid, ObjectType type, EncodingType enc)
00809 {
00810     strm << "--" << boundary << CRLF;
00811     strm << "Content-Type: application/octet-stream" << CRLF;
00812     strm << "Content-Id: <" << cid << ">" << CRLF;
00813     strm << "Content-Description: " << descrip[type] << CRLF ;
00814     if (enc != x_plain)
00815          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00816 
00817     strm << CRLF;
00818 }
00819 
00820 const size_t line_length = 1024;
00821 
00836 string get_next_mime_header(FILE *in)
00837 {
00838     // Get the header line and strip \r\n. Some headers end with just \n.
00839     // If a blank line is found, return an empty string.
00840     char line[line_length];
00841     while (!feof(in)) {
00842         if (fgets(line, line_length, in)
00843                 && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
00844             return "";
00845         else {
00846             size_t slen = min(strlen(line), line_length); // Never > line_length
00847             line[slen - 1] = '\0'; // remove the newline
00848             if (line[slen - 2] == '\r') // ...and the preceding carriage return
00849                 line[slen - 2] = '\0';
00850             return string(line);
00851         }
00852     }
00853 
00854     throw Error("I expected to find a MIME header, but got EOF instead.");
00855 }
00856 
00857 string get_next_mime_header(istream &in)
00858 {
00859 #if 0
00860     // Get the header line and strip \r\n. Some headers end with just \n.
00861     // If a blank line is found, return an empty string.
00862         char line[line_length];
00863         while (!in.eof()) {
00864                 in.getline(line, line_length);
00865                 if (strncmp(line, CRLF, 2) == 0 || line[0] == '\n') {
00866                         return "";
00867                 }
00868                 else {
00869                         size_t slen = min(strlen(line), line_length); // Never > line_length
00870                         line[slen - 1] = '\0'; // remove the newline
00871                         if (line[slen - 2] == '\r') // ...and the preceding carriage return
00872                                 line[slen - 2] = '\0';
00873                         return string(line);
00874                 }
00875         }
00876 #endif
00877     // Get the header line and strip \r\n. Some headers end with just \n.
00878     // If a blank line is found, return an empty string.
00879         char raw_line[line_length];
00880         while (!in.eof()) {
00881                 in.getline(raw_line, line_length); // strips the trailing newline; terminates with null
00882                 string line = raw_line;
00883                 if (line.find('\r') != string::npos)
00884                         line = line.substr(0, line.size()-1);
00885                 return line;
00886         }
00887 
00888         throw Error("I expected to find a MIME header, but got EOF instead.");
00889 }
00890 
00898 void parse_mime_header(const string &header, string &name, string &value)
00899 {
00900     istringstream iss(header);
00901 
00902     size_t length = header.length() + 1;
00903     vector<char> s(length);
00904     //char s[line_length];
00905     iss.getline(&s[0], length, ':');
00906     name = &s[0];
00907 
00908     iss.ignore(length, ' ');
00909     iss.getline(&s[0], length);
00910     value = &s[0];
00911 
00912     downcase(name);
00913     downcase(value);
00914 }
00915 
00927 bool is_boundary(const char *line, const string &boundary)
00928 {
00929     if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
00930                 return false;
00931     else
00932                 return strncmp(line, boundary.c_str(), boundary.length()) == 0;
00933 }
00934 
00945 string read_multipart_boundary(FILE *in, const string &boundary)
00946 {
00947     string boundary_line = get_next_mime_header(in);
00948     // If the caller passed in a value for the boundary, test for that value,
00949     // else just see that this line starts with '--'.
00950     // The value of 'boundary_line' is returned by this function.
00951     if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
00952             || boundary_line.find("--") != 0)
00953         throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
00954 
00955     return boundary_line;
00956 }
00957 
00958 string read_multipart_boundary(istream &in, const string &boundary)
00959 {
00960     string boundary_line = get_next_mime_header(in);
00961     // If the caller passed in a value for the boundary, test for that value,
00962     // else just see that this line starts with '--'.
00963     // The value of 'boundary_line' is returned by this function.
00964     if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
00965             || boundary_line.find("--") != 0)
00966         throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
00967 
00968     return boundary_line;
00969 }
00970 
00991 void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
00992 {
00993         bool ct = false, cd = false, ci = false;
00994 
00995         string header = get_next_mime_header(in);
00996         while (!header.empty()) {
00997                 string name, value;
00998                 parse_mime_header(header, name, value);
00999 
01000                 if (name == "content-type") {
01001                         ct = true;
01002                         if (value.find(content_type) == string::npos)
01003                                 throw Error(internal_error, "Content-Type for this part of a DAP2 data ddx response must be " + content_type + ".");
01004                 }
01005                 else if (name == "content-description") {
01006                         cd = true;
01007                         if (get_description_type(value) != object_type)
01008                                 throw Error(internal_error, "Content-Description for this part of a DAP2 data ddx response must be dods-ddx or dods-data-ddx");
01009                 }
01010                 else if (name == "content-id") {
01011                         ci = true;
01012                         if (!cid.empty() && value != cid)
01013                                 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
01014                 }
01015 
01016                 header = get_next_mime_header(in);
01017         }
01018 
01019         if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
01020 }
01021 
01022 void read_multipart_headers(istream &in, const string &content_type, const ObjectType object_type, const string &cid)
01023 {
01024         bool ct = false, cd = false, ci = false;
01025 
01026         string header = get_next_mime_header(in);
01027         while (!header.empty()) {
01028                 string name, value;
01029                 parse_mime_header(header, name, value);
01030 
01031                 if (name == "content-type") {
01032                         ct = true;
01033                         if (value.find(content_type) == string::npos)
01034                                 throw Error(internal_error, "Content-Type for this part of a DAP4 data response must be " + content_type + ".");
01035                 }
01036                 else if (name == "content-description") {
01037                         cd = true;
01038                         if (get_description_type(value) != object_type)
01039                                 throw Error("Content-Description '" + value + "' not the expected value (expected: " + descrip[object_type] + ").");
01040                 }
01041                 else if (name == "content-id") {
01042                         ci = true;
01043                         if (!cid.empty() && value != cid)
01044                                 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
01045                 }
01046 
01047                 header = get_next_mime_header(in);
01048         }
01049 
01050         if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
01051 }
01052 
01061 string cid_to_header_value(const string &cid)
01062 {
01063     string::size_type offset = cid.find("cid:");
01064     if (offset != 0)
01065         throw Error(internal_error, "expected CID to start with 'cid:'");
01066 
01067     string value = "<";
01068     value.append(cid.substr(offset + 4));
01069     value.append(">");
01070     downcase(value);
01071 
01072     return value;
01073 }
01074 
01083 void
01084 set_mime_error(FILE *out, int code, const string &reason,
01085                const string &version)
01086 {
01087     ostringstream oss;
01088     set_mime_error(oss, code, reason, version);
01089     fwrite(oss.str().data(), 1, oss.str().length(), out);
01090 }
01091 
01100 void
01101 set_mime_error(ostream &strm, int code, const string &reason,
01102                const string &version)
01103 {
01104     strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
01105     if (version == "") {
01106         strm << "XDODS-Server: " << DVR << CRLF ;
01107         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
01108     }
01109     else {
01110         strm << "XDODS-Server: " << version.c_str() << CRLF ;
01111         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
01112     }
01113     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
01114 
01115     const time_t t = time(0);
01116     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
01117     strm << "Cache-Control: no-cache" << CRLF ;
01118     strm << CRLF ;
01119 }
01120 
01128 void
01129 set_mime_not_modified(FILE *out)
01130 {
01131     ostringstream oss;
01132     set_mime_not_modified(oss);
01133     fwrite(oss.str().data(), 1, oss.str().length(), out);
01134 }
01135 
01143 void
01144 set_mime_not_modified(ostream &strm)
01145 {
01146     strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
01147     const time_t t = time(0);
01148     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
01149     strm << CRLF ;
01150 }
01151 
01152 #if 0
01153 
01154 // This was removed because it's not being used by our server.
01155 
01165 bool
01166 found_override(string name, string &doc)
01167 {
01168     ifstream ifs((name + ".ovr").c_str());
01169     if (!ifs)
01170         return false;
01171 
01172     char tmp[256];
01173     doc = "";
01174     while (!ifs.eof()) {
01175         ifs.getline(tmp, 255);
01176         tmp[255] = '\0';
01177         strncat(tmp, "\n", sizeof(tmp) - strlen(tmp) - 1);
01178         doc += tmp;
01179     }
01180 
01181         ifs.close();
01182     return true;
01183 }
01184 #endif
01185 
01195 bool
01196 remove_mime_header(FILE *in)
01197 {
01198     char tmp[256];
01199     while (!feof(in)) {
01200         char *s = fgets(tmp, 255, in);
01201         if (s && strncmp(s, CRLF, 2) == 0)
01202             return true;
01203     }
01204 
01205     return false;
01206 }
01207 
01212 void
01213 remove_mime_header(istream &in)
01214 {
01215         while(!get_next_mime_header(in).empty()) ;
01216 #if 0
01217         string header;
01218         do {
01219                 header = get_next_mime_header(in);
01220         } while (!header.empty());
01221 #endif
01222 }
01223 
01224 } // namespace libdap
01225