libdap++  Updated for version 3.8.2
mime_util.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #undef FILE_METHODS
00040 
00041 static char rcsid[] not_used =
00042     {"$Id: mime_util.cc 24370 2011-03-28 16:21:32Z jimg $"
00043     };
00044 
00045 #include <cstring>
00046 #include <cstdio>
00047 #include <ctype.h>
00048 
00049 #ifndef TM_IN_SYS_TIME
00050 #include <time.h>
00051 #else
00052 #include <sys/time.h>
00053 #endif
00054 
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 
00058 #ifndef WIN32
00059 #include <unistd.h>    // for access
00060 #include <sys/wait.h>
00061 #else
00062 #include <io.h>
00063 #include <fcntl.h>
00064 #include <process.h>
00065 // Win32 does not define this. 08/21/02 jhrg
00066 #define F_OK 0
00067 #endif
00068 
00069 #include <iostream>
00070 #include <sstream>
00071 #include <fstream>
00072 #include <string>
00073 
00074 #include "mime_util.h"
00075 #include "Ancillary.h"
00076 #include "util.h"  // This supplies flush_stream for WIN32.
00077 #include "debug.h"
00078 
00079 #ifdef WIN32
00080 #define FILE_DELIMITER '\\'
00081 #else  //  default to unix
00082 #define FILE_DELIMITER '/'
00083 #endif
00084 
00085 // ...not using a const string here to avoid global objects. jhrg 12/23/05
00086 #define CRLF "\r\n"             // Change here, expr-test.cc, in DODSFilter and ResponseBuilder
00087 
00088 using namespace std;
00089 
00090 namespace libdap {
00091 
00097 time_t
00098 last_modified_time(const string &name)
00099 {
00100     struct stat m;
00101 
00102     if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
00103         return m.st_mtime;
00104     else
00105         return time(0);
00106 }
00107 // Return a MIME rfc-822 date. The grammar for this is:
00108 //       date-time   =  [ day "," ] date time        ; dd mm yy
00109 //                                                   ;  hh:mm:ss zzz
00110 //
00111 //       day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
00112 //                   /  "Fri"  / "Sat" /  "Sun"
00113 //
00114 //       date        =  1*2DIGIT month 2DIGIT        ; day month year
00115 //                                                   ;  e.g. 20 Jun 82
00116 //                   NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
00117 //
00118 //       month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
00119 //                   /  "May"  /  "Jun" /  "Jul"  /  "Aug"
00120 //                   /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
00121 //
00122 //       time        =  hour zone                    ; ANSI and Military
00123 //
00124 //       hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
00125 //                                                   ; 00:00:00 - 23:59:59
00126 //
00127 //       zone        =  "UT"  / "GMT"                ; Universal Time
00128 //                                                   ; North American : UT
00129 //                   /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
00130 //                   /  "CST" / "CDT"                ;  Central:  - 6/ - 5
00131 //                   /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
00132 //                   /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
00133 //                   /  1ALPHA                       ; Military: Z = UT;
00134 //                                                   ;  A:-1; (J not used)
00135 //                                                   ;  M:-12; N:+1; Y:+12
00136 //                   / ( ("+" / "-") 4DIGIT )        ; Local differential
00137 //                                                   ;  hours+min. (HHMM)
00138 
00139 static const char *days[] =
00140     {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00141     };
00142 static const char *months[] =
00143     {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
00144      "Aug", "Sep", "Oct", "Nov", "Dec"
00145     };
00146 
00147 #ifdef _MSC_VER
00148 #define snprintf sprintf_s
00149 #endif
00150 
00158 string
00159 rfc822_date(const time_t t)
00160 {
00161     struct tm *stm = gmtime(&t);
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         strncpy(TimStr, ctime(&TimBin), TimLen-1);
00231         TimStr[TimLen - 2] = '\0'; // overwrite the \n
00232     }
00233 
00234     cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
00235 }
00236 
00237 // Given a pathname, return just the filename component with any extension
00238 // removed. The new string resides in newly allocated memory; the caller must
00239 // delete it when done using the filename.
00240 // Originally from the netcdf distribution (ver 2.3.2).
00241 //
00242 // *** Change to string class argument and return type. jhrg
00243 // *** Changed so it also removes the#path#of#the#file# from decompressed
00244 //     files.  rph.
00245 // Returns: A filename, with path and extension information removed. If
00246 // memory for the new name cannot be allocated, does not return!
00247 
00258 string
00259 name_path(const string &path)
00260 {
00261     if (path == "")
00262         return string("");
00263 
00264     string::size_type delim = path.find_last_of(FILE_DELIMITER);
00265     string::size_type pound = path.find_last_of("#");
00266     string new_path;
00267 
00268     if (pound != string::npos)
00269         new_path = path.substr(pound + 1);
00270     else
00271         new_path = path.substr(delim + 1);
00272 
00273     return new_path;
00274 }
00275 
00276 // Send string to set the transfer (mime) type and server version
00277 // Note that the content description filed is used to indicate whether valid
00278 // information of an error message is contained in the document and the
00279 // content-encoding field is used to indicate whether the data is compressed.
00280 // If the data stream is to be compressed, arrange for a compression output
00281 // filter so that all information sent after the header will be compressed.
00282 //
00283 // Returns: false if the compression output filter was to be used but could
00284 // not be started, true otherwise.
00285 
00286 static const char *descrip[] =
00287     {"unknown", "dods_das", "dods_dds", "dods_data",
00288      "dods_error", "web_error", "dap4-ddx", "dap4-data", "dap4-error",
00289      "dap4-data-ddx", "dods_ddx"
00290     };
00291 static const char *encoding[] =
00292     {"unknown", "deflate", "x-plain", "gzip", "binary"
00293     };
00294 
00300 ObjectType
00301 get_type(const string &value)
00302 {
00303     if ((value == "dods_das") | (value == "dods-das"))
00304         return dods_das;
00305     else if ((value == "dods_dds") | (value == "dods-dds"))
00306         return dods_dds;
00307     else if ((value == "dods_data") | (value == "dods-data"))
00308         return dods_data;
00309     else if ((value == "dods_error") | (value == "dods-error"))
00310         return dods_error;
00311     else if ((value == "web_error") | (value == "web-error"))
00312         return web_error;
00313     else if ((value == "dap4_ddx") | (value == "dap4-ddx"))
00314         return dap4_ddx;
00315     else if ((value == "dap4_data") | (value == "dap4-data"))
00316         return dap4_data;
00317     else if ((value == "dap4_error") | (value == "dap4-error"))
00318         return dap4_error;
00319     else if ((value == "dap4_data_ddx") | (value == "dap4-data-ddx"))
00320         return dap4_data_ddx;
00321     else if ((value == "dods_ddx") | (value == "dods-ddx"))
00322         return dods_ddx;
00323     else
00324         return unknown_type;
00325 }
00326 
00332 ObjectType
00333 get_description_type(const string &value)
00334 {
00335     if ((value == "dods_das") | (value == "dods-das"))
00336         return dods_das;
00337     else if ((value == "dods_dds") | (value == "dods-dds"))
00338         return dods_dds;
00339     else if ((value == "dods_data") | (value == "dods-data"))
00340         return dods_data;
00341     else if ((value == "dods_error") | (value == "dods-error"))
00342         return dods_error;
00343     else if ((value == "web_error") | (value == "web-error"))
00344         return web_error;
00345     else if ((value == "dods_ddx") | (value == "dods-ddx"))
00346         return dods_ddx;
00347     else if ((value == "dap4_ddx") | (value == "dap4-ddx"))
00348         return dap4_ddx;
00349     else if ((value == "dap4_data") | (value == "dap4-data"))
00350         return dap4_data;
00351     else if ((value == "dap4_error") | (value == "dap4-error"))
00352         return dap4_error;
00353     else if ((value == "dap4_data_ddx") | (value == "dap4-data-ddx"))
00354         return dap4_data_ddx;
00355     else if ((value == "dods_ddx") | (value == "dods-ddx"))
00356         return dods_ddx;
00357     else
00358         return unknown_type;
00359 }
00360 
00361 #if FILE_METHODS
00362 
00375 void
00376 set_mime_text(FILE *out, ObjectType type, const string &ver,
00377               EncodingType enc, const time_t last_modified)
00378 {
00379     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00380     if (ver == "") {
00381         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00382         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00383     }
00384     else {
00385         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00386         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00387     }
00388     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00389 
00390     const time_t t = time(0);
00391     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00392 
00393     fprintf(out, "Last-Modified: ") ;
00394     if (last_modified > 0)
00395         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00396     else
00397         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00398 
00399     if (type == dap4_ddx)
00400         fprintf(out, "Content-Type: text/xml%s", CRLF) ;
00401     else
00402         fprintf(out, "Content-Type: text/plain%s", CRLF) ;
00403 
00404     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00405     // jhrg 12/23/05
00406     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00407     if (type == dods_error) // don't cache our error responses.
00408         fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00409     // Don't write a Content-Encoding header for x-plain since that breaks
00410     // Netscape on NT. jhrg 3/23/97
00411     if (enc != x_plain)
00412         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00413     fprintf(out, CRLF) ;
00414 }
00415 #endif
00416 
00430 void
00431 set_mime_text(ostream &strm, ObjectType type, const string &ver,
00432               EncodingType enc, const time_t last_modified)
00433 {
00434     strm << "HTTP/1.0 200 OK" << CRLF ;
00435     if (ver == "") {
00436         strm << "XDODS-Server: " << DVR << CRLF ;
00437         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00438     }
00439     else {
00440         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00441         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00442     }
00443     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00444 
00445     const time_t t = time(0);
00446     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00447 
00448     strm << "Last-Modified: " ;
00449     if (last_modified > 0)
00450         strm << rfc822_date(last_modified).c_str() << CRLF ;
00451     else
00452         strm << rfc822_date(t).c_str() << CRLF ;
00453 
00454     if (type == dap4_ddx)
00455         strm << "Content-Type: text/xml" << CRLF ;
00456     else
00457         strm << "Content-Type: text/plain" << CRLF ;
00458 
00459     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00460     // jhrg 12/23/05
00461     strm << "Content-Description: " << descrip[type] << CRLF ;
00462     if (type == dods_error) // don't cache our error responses.
00463         strm << "Cache-Control: no-cache" << CRLF ;
00464     // Don't write a Content-Encoding header for x-plain since that breaks
00465     // Netscape on NT. jhrg 3/23/97
00466     if (enc != x_plain)
00467         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00468     strm << CRLF ;
00469 }
00470 
00471 #if FILE_METHODS
00472 
00483 void
00484 set_mime_html(FILE *out, ObjectType type, const string &ver,
00485               EncodingType enc, const time_t last_modified)
00486 {
00487     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00488     if (ver == "") {
00489         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00490         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00491     }
00492     else {
00493         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00494         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00495     }
00496     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00497 
00498     const time_t t = time(0);
00499     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00500 
00501     fprintf(out, "Last-Modified: ") ;
00502     if (last_modified > 0)
00503         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00504     else
00505         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00506 
00507     fprintf(out, "Content-type: text/html%s", CRLF) ;
00508     // See note above about Content-Description header. jhrg 12/23/05
00509     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00510     if (type == dods_error) // don't cache our error responses.
00511         fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00512     // Don't write a Content-Encoding header for x-plain since that breaks
00513     // Netscape on NT. jhrg 3/23/97
00514     if (enc != x_plain)
00515         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00516     fprintf(out, CRLF) ;
00517 }
00518 #endif
00519 
00531 void
00532 set_mime_html(ostream &strm, ObjectType type, const string &ver,
00533               EncodingType enc, const time_t last_modified)
00534 {
00535     strm << "HTTP/1.0 200 OK" << CRLF ;
00536     if (ver == "") {
00537         strm << "XDODS-Server: " << DVR << CRLF ;
00538         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00539     }
00540     else {
00541         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00542         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00543     }
00544     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00545 
00546     const time_t t = time(0);
00547     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00548 
00549     strm << "Last-Modified: " ;
00550     if (last_modified > 0)
00551         strm << rfc822_date(last_modified).c_str() << CRLF ;
00552     else
00553         strm << rfc822_date(t).c_str() << CRLF ;
00554 
00555     strm << "Content-type: text/html" << CRLF ;
00556     // See note above about Content-Description header. jhrg 12/23/05
00557     strm << "Content-Description: " << descrip[type] << CRLF ;
00558     if (type == dods_error) // don't cache our error responses.
00559         strm << "Cache-Control: no-cache" << CRLF ;
00560     // Don't write a Content-Encoding header for x-plain since that breaks
00561     // Netscape on NT. jhrg 3/23/97
00562     if (enc != x_plain)
00563         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00564     strm << CRLF ;
00565 }
00566 
00567 #if FILE_METHODS
00568 
00582 void
00583 set_mime_binary(FILE *out, ObjectType type, const string &ver,
00584                 EncodingType enc, const time_t last_modified)
00585 {
00586     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00587     if (ver == "") {
00588         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00589         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00590     }
00591     else {
00592         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00593         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00594     }
00595     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00596 
00597     const time_t t = time(0);
00598     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00599 
00600     fprintf(out, "Last-Modified: ") ;
00601     if (last_modified > 0)
00602         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00603     else
00604         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00605 
00606     fprintf(out, "Content-Type: application/octet-stream%s", CRLF) ;
00607     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00608     if (enc != x_plain)
00609         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00610 
00611     fprintf(out, CRLF) ;
00612 }
00613 #endif
00614 
00629 void
00630 set_mime_binary(ostream &strm, ObjectType type, const string &ver,
00631                 EncodingType enc, const time_t last_modified)
00632 {
00633     strm << "HTTP/1.0 200 OK" << CRLF ;
00634     if (ver == "") {
00635         strm << "XDODS-Server: " << DVR << CRLF ;
00636         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00637     }
00638     else {
00639         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00640         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00641     }
00642     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00643 
00644     const time_t t = time(0);
00645     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00646 
00647     strm << "Last-Modified: " ;
00648     if (last_modified > 0)
00649         strm << rfc822_date(last_modified).c_str() << CRLF ;
00650     else
00651         strm << rfc822_date(t).c_str() << CRLF ;
00652 
00653     strm << "Content-Type: application/octet-stream" << CRLF ;
00654     strm << "Content-Description: " << descrip[type] << CRLF ;
00655     if (enc != x_plain)
00656         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00657 
00658     strm << CRLF ;
00659 }
00660 
00661 void set_mime_multipart(ostream &strm, const string &boundary,
00662         const string &start, ObjectType type,
00663         const string &version, EncodingType enc,
00664         const time_t last_modified)
00665 {
00666     strm << "HTTP/1.0 200 OK" << CRLF ;
00667     if (version == "") {
00668         strm << "XDODS-Server: " << DVR << CRLF ;
00669         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00670     }
00671     else {
00672         strm << "XDODS-Server: " << version.c_str() << CRLF ;
00673         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
00674     }
00675     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00676 
00677     const time_t t = time(0);
00678     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00679 
00680     strm << "Last-Modified: " ;
00681     if (last_modified > 0)
00682         strm << rfc822_date(last_modified).c_str() << CRLF ;
00683     else
00684         strm << rfc822_date(t).c_str() << CRLF ;
00685 
00686     strm << "Content-Type: Multipart/Related; boundary=" << boundary
00687         << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
00688     strm << "Content-Description: " << descrip[type] << CRLF ;
00689     if (enc != x_plain)
00690         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00691 
00692     strm << CRLF ;
00693 }
00694 
00695 void set_mime_ddx_boundary(ostream &strm, const string &boundary,
00696         const string &cid, ObjectType type, EncodingType enc)
00697 {
00698     strm << "--" << boundary << CRLF;
00699     strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
00700     strm << "Content-Id: <" << cid << ">" << CRLF;
00701     strm << "Content-Description: " << descrip[type] << CRLF ;
00702     if (enc != x_plain)
00703          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00704 
00705     strm << CRLF;
00706 }
00707 
00708 void set_mime_data_boundary(ostream &strm, const string &boundary,
00709         const string &cid, ObjectType type, EncodingType enc)
00710 {
00711     strm << "--" << boundary << CRLF;
00712     strm << "Content-Type: application/octet-stream" << CRLF;
00713     strm << "Content-Id: <" << cid << ">" << CRLF;
00714     strm << "Content-Description: " << descrip[type] << CRLF ;
00715     if (enc != x_plain)
00716          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00717 
00718     strm << CRLF;
00719 }
00720 
00721 const size_t line_length = 1024;
00722 
00737 string get_next_mime_header(FILE *in)
00738 {
00739     // Get the header line and strip \r\n. Some headers end with just \n.
00740     // If a blank line is found, return an empty string.
00741     char line[line_length];
00742     while (!feof(in)) {
00743         if (fgets(line, line_length, in)
00744                 && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
00745             return "";
00746         else {
00747             size_t slen = min(strlen(line), line_length); // Never > line_length
00748             line[slen - 1] = '\0'; // remove the newline
00749             if (line[slen - 2] == '\r') // ...and the preceding carriage return
00750                 line[slen - 2] = '\0';
00751             return string(line);
00752         }
00753     }
00754 
00755     throw Error("I expected to find a MIME header, but got EOF instead.");
00756 }
00757 
00765 void parse_mime_header(const string &header, string &name, string &value)
00766 {
00767     istringstream iss(header);
00768     // Set downcase
00769     char s[line_length];
00770     iss.getline(s, 1023, ':');
00771     name = s;
00772 
00773     iss.ignore(1023, ' ');
00774     iss.getline(s, 1023);
00775     value = s;
00776 
00777     downcase(name);
00778     downcase(value);
00779 }
00780 
00792 bool is_boundary(const char *line, const string &boundary)
00793 {
00794     if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
00795                 return false;
00796     else
00797                 return strncmp(line, boundary.c_str(), boundary.length()) == 0;
00798 }
00799 
00810 string read_multipart_boundary(FILE *in, const string &boundary)
00811 {
00812     string boundary_line = get_next_mime_header(in);
00813     // If the caller passed in a value for the boundary, test for that value,
00814     // else just see that this line starts with '--'.
00815     // The value of 'boundary_line' is returned by this function.
00816     if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
00817             || boundary_line.find("--") != 0)
00818         throw Error(
00819                 "The DAP4 data response document is broken - missing or malformed boundary.");
00820 
00821     return boundary_line;
00822 }
00823 
00844 void read_multipart_headers(FILE *in, const string &content_type,
00845         const ObjectType object_type, const string &cid)
00846 {
00847     bool ct = false, cd = false, ci = false;
00848 
00849     string header = get_next_mime_header(in);
00850     while (!header.empty()) {
00851         string name, value;
00852         parse_mime_header(header, name, value);
00853 
00854         if (name =="content-type") {
00855             ct = true;
00856             if (value.find(content_type) == string::npos)
00857                 throw Error("Content-Type for this part of a DAP4 data response must be " + content_type + ".");
00858         }
00859         else if (name == "content-description") {
00860             cd = true;
00861             if (get_description_type(value) != object_type)
00862                 throw Error("Content-Description for this part of a DAP4 data response must be dap4-ddx or dap4-data-ddx");
00863         }
00864         else if (name == "content-id") {
00865             ci = true;
00866             if (!cid.empty() && value != cid)
00867                 throw Error("Content-Id mismatch. Expected: " + cid
00868                         + ", but got: " + value);
00869         }
00870 
00871         header = get_next_mime_header(in);
00872     }
00873 
00874     if (!(ct && cd && ci))
00875         throw Error("The DAP4 data response document is broken - missing header.");
00876 }
00885 string cid_to_header_value(const string &cid)
00886 {
00887     string::size_type offset = cid.find("cid:");
00888     if (offset != 0)
00889         throw Error("expected CID to start with 'cid:'");
00890 
00891     string value = "<";
00892     value.append(cid.substr(offset + 4));
00893     value.append(">");
00894     downcase(value);
00895 
00896     return value;
00897 }
00898 
00899 #if FILE_METHODS
00900 
00908 void
00909 set_mime_error(FILE *out, int code, const string &reason,
00910                const string &version)
00911 {
00912     fprintf(out, "HTTP/1.0 %d %s%s", code, reason.c_str(), CRLF) ;
00913     if (version == "") {
00914         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00915         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00916     }
00917     else {
00918         fprintf(out, "XDODS-Server: %s%s", version.c_str(), CRLF) ;
00919         fprintf(out, "XOPeNDAP-Server: %s%s", version.c_str(), CRLF) ;
00920     }
00921     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00922 
00923     const time_t t = time(0);
00924     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00925     fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00926     fprintf(out, CRLF) ;
00927 }
00928 #endif
00929 
00938 void
00939 set_mime_error(ostream &strm, int code, const string &reason,
00940                const string &version)
00941 {
00942     strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
00943     if (version == "") {
00944         strm << "XDODS-Server: " << DVR << CRLF ;
00945         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00946     }
00947     else {
00948         strm << "XDODS-Server: " << version.c_str() << CRLF ;
00949         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
00950     }
00951     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00952 
00953     const time_t t = time(0);
00954     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00955     strm << "Cache-Control: no-cache" << CRLF ;
00956     strm << CRLF ;
00957 }
00958 
00959 #if FILE_METHODS
00960 
00967 void
00968 set_mime_not_modified(FILE *out)
00969 {
00970     fprintf(out, "HTTP/1.0 304 NOT MODIFIED%s", CRLF) ;
00971     const time_t t = time(0);
00972     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00973     fprintf(out, CRLF) ;
00974 }
00975 #endif
00976 
00984 void
00985 set_mime_not_modified(ostream &strm)
00986 {
00987     strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
00988     const time_t t = time(0);
00989     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00990     strm << CRLF ;
00991 }
00992 
01002 bool
01003 found_override(string name, string &doc)
01004 {
01005     ifstream ifs((name + ".ovr").c_str());
01006     if (!ifs)
01007         return false;
01008 
01009     char tmp[256];
01010     doc = "";
01011     while (!ifs.eof()) {
01012         ifs.getline(tmp, 255);
01013         strcat(tmp, "\n");
01014         doc += tmp;
01015     }
01016 
01017         ifs.close();
01018     return true;
01019 }
01020 
01030 bool
01031 remove_mime_header(FILE *in)
01032 {
01033     char tmp[256];
01034     while (!feof(in)) {
01035         char *s = fgets(tmp, 255, in);
01036         if (s && strncmp(s, CRLF, 2) == 0)
01037             return true;
01038     }
01039 
01040     return false;
01041 }
01042 
01043 } // namespace libdap
01044