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 22703 2010-05-11 18:10:01Z 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 and DODSFilter.cc
00087 
00088 using namespace std;
00089 
00090 namespace libdap {
00091 
00092 static const int TimLen = 26; // length of string from asctime()
00093 static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
00094 
00108 bool
00109 do_version(const string &script_ver, const string &dataset_ver)
00110 {
00111     fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
00112     fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
00113     fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00114     fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00115     fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
00116     fprintf(stdout, CRLF) ;
00117 
00118     fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
00119 
00120     if (script_ver != "")
00121         fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
00122 
00123     if (dataset_ver != "")
00124         fprintf(stdout,  "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
00125 
00126     fflush(stdout) ;            // Not sure this is needed. jhrg 12/23/05
00127 
00128     return true;
00129 }
00130 
00140 void
00141 ErrMsgT(const string &Msgt)
00142 {
00143     time_t TimBin;
00144     char TimStr[TimLen];
00145 
00146     if (time(&TimBin) == (time_t) - 1)
00147         strncpy(TimStr, "time() error           ", TimLen-1);
00148     else {
00149         strncpy(TimStr, ctime(&TimBin), TimLen-1);
00150         TimStr[TimLen - 2] = '\0'; // overwrite the \n
00151     }
00152 
00153     cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
00154 }
00155 
00156 // Given a pathname, return just the filename component with any extension
00157 // removed. The new string resides in newly allocated memory; the caller must
00158 // delete it when done using the filename.
00159 // Originally from the netcdf distribution (ver 2.3.2).
00160 //
00161 // *** Change to string class argument and return type. jhrg
00162 // *** Changed so it also removes the#path#of#the#file# from decompressed
00163 //     files.  rph.
00164 // Returns: A filename, with path and extension information removed. If
00165 // memory for the new name cannot be allocated, does not return!
00166 
00177 string
00178 name_path(const string &path)
00179 {
00180     if (path == "")
00181         return string("");
00182 
00183     string::size_type delim = path.find_last_of(FILE_DELIMITER);
00184     string::size_type pound = path.find_last_of("#");
00185     string new_path;
00186 
00187     if (pound != string::npos)
00188         new_path = path.substr(pound + 1);
00189     else
00190         new_path = path.substr(delim + 1);
00191 
00192     return new_path;
00193 }
00194 
00195 // Return a MIME rfc-822 date. The grammar for this is:
00196 //       date-time   =  [ day "," ] date time        ; dd mm yy
00197 //                                                   ;  hh:mm:ss zzz
00198 //
00199 //       day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
00200 //                   /  "Fri"  / "Sat" /  "Sun"
00201 //
00202 //       date        =  1*2DIGIT month 2DIGIT        ; day month year
00203 //                                                   ;  e.g. 20 Jun 82
00204 //                   NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
00205 //
00206 //       month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
00207 //                   /  "May"  /  "Jun" /  "Jul"  /  "Aug"
00208 //                   /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
00209 //
00210 //       time        =  hour zone                    ; ANSI and Military
00211 //
00212 //       hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
00213 //                                                   ; 00:00:00 - 23:59:59
00214 //
00215 //       zone        =  "UT"  / "GMT"                ; Universal Time
00216 //                                                   ; North American : UT
00217 //                   /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
00218 //                   /  "CST" / "CDT"                ;  Central:  - 6/ - 5
00219 //                   /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
00220 //                   /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
00221 //                   /  1ALPHA                       ; Military: Z = UT;
00222 //                                                   ;  A:-1; (J not used)
00223 //                                                   ;  M:-12; N:+1; Y:+12
00224 //                   / ( ("+" / "-") 4DIGIT )        ; Local differential
00225 //                                                   ;  hours+min. (HHMM)
00226 
00227 static const char *days[] =
00228     {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00229     };
00230 static const char *months[] =
00231     {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
00232      "Aug", "Sep", "Oct", "Nov", "Dec"
00233     };
00234 
00235 #ifdef _MSC_VER
00236 #define snprintf sprintf_s
00237 #endif
00238 
00246 string
00247 rfc822_date(const time_t t)
00248 {
00249     struct tm *stm = gmtime(&t);
00250     char d[256];
00251 
00252     snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
00253             stm->tm_mday, months[stm->tm_mon],
00254             1900 + stm->tm_year,
00255             stm->tm_hour, stm->tm_min, stm->tm_sec);
00256     d[255] = '\0';
00257     return string(d);
00258 }
00259 
00265 time_t
00266 last_modified_time(const string &name)
00267 {
00268     struct stat m;
00269 
00270     if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
00271         return m.st_mtime;
00272     else
00273         return time(0);
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 
00374 void
00375 set_mime_text(FILE *out, ObjectType type, const string &ver,
00376               EncodingType enc, const time_t last_modified)
00377 {
00378     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00379     if (ver == "") {
00380         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00381         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00382     }
00383     else {
00384         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00385         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00386     }
00387     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00388 
00389     const time_t t = time(0);
00390     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00391 
00392     fprintf(out, "Last-Modified: ") ;
00393     if (last_modified > 0)
00394         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00395     else
00396         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00397 
00398     if (type == dap4_ddx)
00399         fprintf(out, "Content-Type: text/xml%s", CRLF) ;
00400     else
00401         fprintf(out, "Content-Type: text/plain%s", CRLF) ;
00402 
00403     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00404     // jhrg 12/23/05
00405     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00406     if (type == dods_error) // don't cache our error responses.
00407         fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00408     // Don't write a Content-Encoding header for x-plain since that breaks
00409     // Netscape on NT. jhrg 3/23/97
00410     if (enc != x_plain)
00411         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00412     fprintf(out, CRLF) ;
00413 }
00414 #endif
00415 
00428 void
00429 set_mime_text(ostream &strm, ObjectType type, const string &ver,
00430               EncodingType enc, const time_t last_modified)
00431 {
00432     strm << "HTTP/1.0 200 OK" << CRLF ;
00433     if (ver == "") {
00434         strm << "XDODS-Server: " << DVR << CRLF ;
00435         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00436     }
00437     else {
00438         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00439         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00440     }
00441     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00442 
00443     const time_t t = time(0);
00444     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00445 
00446     strm << "Last-Modified: " ;
00447     if (last_modified > 0)
00448         strm << rfc822_date(last_modified).c_str() << CRLF ;
00449     else
00450         strm << rfc822_date(t).c_str() << CRLF ;
00451 
00452     if (type == dap4_ddx)
00453         strm << "Content-Type: text/xml" << CRLF ;
00454     else
00455         strm << "Content-Type: text/plain" << CRLF ;
00456 
00457     // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
00458     // jhrg 12/23/05
00459     strm << "Content-Description: " << descrip[type] << CRLF ;
00460     if (type == dods_error) // don't cache our error responses.
00461         strm << "Cache-Control: no-cache" << CRLF ;
00462     // Don't write a Content-Encoding header for x-plain since that breaks
00463     // Netscape on NT. jhrg 3/23/97
00464     if (enc != x_plain)
00465         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00466     strm << CRLF ;
00467 }
00468 
00469 #if FILE_METHODS
00470 
00480 void
00481 set_mime_html(FILE *out, ObjectType type, const string &ver,
00482               EncodingType enc, const time_t last_modified)
00483 {
00484     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00485     if (ver == "") {
00486         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00487         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00488     }
00489     else {
00490         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00491         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00492     }
00493     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00494 
00495     const time_t t = time(0);
00496     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00497 
00498     fprintf(out, "Last-Modified: ") ;
00499     if (last_modified > 0)
00500         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00501     else
00502         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00503 
00504     fprintf(out, "Content-type: text/html%s", CRLF) ;
00505     // See note above about Content-Description header. jhrg 12/23/05
00506     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00507     if (type == dods_error) // don't cache our error responses.
00508         fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00509     // Don't write a Content-Encoding header for x-plain since that breaks
00510     // Netscape on NT. jhrg 3/23/97
00511     if (enc != x_plain)
00512         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00513     fprintf(out, CRLF) ;
00514 }
00515 #endif
00516 
00527 void
00528 set_mime_html(ostream &strm, ObjectType type, const string &ver,
00529               EncodingType enc, const time_t last_modified)
00530 {
00531     strm << "HTTP/1.0 200 OK" << CRLF ;
00532     if (ver == "") {
00533         strm << "XDODS-Server: " << DVR << CRLF ;
00534         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00535     }
00536     else {
00537         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00538         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00539     }
00540     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00541 
00542     const time_t t = time(0);
00543     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00544 
00545     strm << "Last-Modified: " ;
00546     if (last_modified > 0)
00547         strm << rfc822_date(last_modified).c_str() << CRLF ;
00548     else
00549         strm << rfc822_date(t).c_str() << CRLF ;
00550 
00551     strm << "Content-type: text/html" << CRLF ;
00552     // See note above about Content-Description header. jhrg 12/23/05
00553     strm << "Content-Description: " << descrip[type] << CRLF ;
00554     if (type == dods_error) // don't cache our error responses.
00555         strm << "Cache-Control: no-cache" << CRLF ;
00556     // Don't write a Content-Encoding header for x-plain since that breaks
00557     // Netscape on NT. jhrg 3/23/97
00558     if (enc != x_plain)
00559         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00560     strm << CRLF ;
00561 }
00562 
00563 #if FILE_METHODS
00564 
00577 void
00578 set_mime_binary(FILE *out, ObjectType type, const string &ver,
00579                 EncodingType enc, const time_t last_modified)
00580 {
00581     fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
00582     if (ver == "") {
00583         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00584         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00585     }
00586     else {
00587         fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
00588         fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
00589     }
00590     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00591 
00592     const time_t t = time(0);
00593     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00594 
00595     fprintf(out, "Last-Modified: ") ;
00596     if (last_modified > 0)
00597         fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
00598     else
00599         fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
00600 
00601     fprintf(out, "Content-Type: application/octet-stream%s", CRLF) ;
00602     fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
00603     if (enc != x_plain)
00604         fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
00605 
00606     fprintf(out, CRLF) ;
00607 }
00608 #endif
00609 
00623 void
00624 set_mime_binary(ostream &strm, ObjectType type, const string &ver,
00625                 EncodingType enc, const time_t last_modified)
00626 {
00627     strm << "HTTP/1.0 200 OK" << CRLF ;
00628     if (ver == "") {
00629         strm << "XDODS-Server: " << DVR << CRLF ;
00630         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00631     }
00632     else {
00633         strm << "XDODS-Server: " << ver.c_str() << CRLF ;
00634         strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
00635     }
00636     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00637 
00638     const time_t t = time(0);
00639     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00640 
00641     strm << "Last-Modified: " ;
00642     if (last_modified > 0)
00643         strm << rfc822_date(last_modified).c_str() << CRLF ;
00644     else
00645         strm << rfc822_date(t).c_str() << CRLF ;
00646 
00647     strm << "Content-Type: application/octet-stream" << CRLF ;
00648     strm << "Content-Description: " << descrip[type] << CRLF ;
00649     if (enc != x_plain)
00650         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00651 
00652     strm << CRLF ;
00653 }
00654 
00655 void set_mime_multipart(ostream &strm, const string &boundary,
00656         const string &start, ObjectType type,
00657         const string &version, EncodingType enc,
00658         const time_t last_modified)
00659 {
00660     strm << "HTTP/1.0 200 OK" << CRLF ;
00661     if (version == "") {
00662         strm << "XDODS-Server: " << DVR << CRLF ;
00663         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00664     }
00665     else {
00666         strm << "XDODS-Server: " << version.c_str() << CRLF ;
00667         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
00668     }
00669     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00670 
00671     const time_t t = time(0);
00672     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00673 
00674     strm << "Last-Modified: " ;
00675     if (last_modified > 0)
00676         strm << rfc822_date(last_modified).c_str() << CRLF ;
00677     else
00678         strm << rfc822_date(t).c_str() << CRLF ;
00679 
00680     strm << "Content-Type: Multipart/Related; boundary=" << boundary
00681         << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
00682     strm << "Content-Description: " << descrip[type] << CRLF ;
00683     if (enc != x_plain)
00684         strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00685 
00686     strm << CRLF ;
00687 }
00688 
00689 void set_mime_ddx_boundary(ostream &strm, const string &boundary,
00690         const string &cid, ObjectType type, EncodingType enc)
00691 {
00692     strm << "--" << boundary << CRLF;
00693     strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
00694     strm << "Content-Id: <" << cid << ">" << CRLF;
00695     strm << "Content-Description: " << descrip[type] << CRLF ;
00696     if (enc != x_plain)
00697          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00698 
00699     strm << CRLF;
00700 }
00701 
00702 void set_mime_data_boundary(ostream &strm, const string &boundary,
00703         const string &cid, ObjectType type, EncodingType enc)
00704 {
00705     strm << "--" << boundary << CRLF;
00706     strm << "Content-Type: application/octet-stream" << CRLF;
00707     strm << "Content-Id: <" << cid << ">" << CRLF;
00708     strm << "Content-Description: " << descrip[type] << CRLF ;
00709     if (enc != x_plain)
00710          strm << "Content-Encoding: " << encoding[enc] << CRLF ;
00711 
00712     strm << CRLF;
00713 }
00714 
00715 const size_t line_length = 1024;
00716 
00730 string get_next_mime_header(FILE *in)
00731 {
00732     // Get the header line and strip \r\n. Some headers end with just \n.
00733     // If a blank line is found, return an empty string.
00734     char line[line_length];
00735     while (!feof(in)) {
00736         if (fgets(line, line_length, in)
00737                 && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
00738             return "";
00739         else {
00740             size_t slen = min(strlen(line), line_length); // Never > line_length
00741             line[slen - 1] = '\0'; // remove the newline
00742             if (line[slen - 2] == '\r') // ...and the preceding carriage return
00743                 line[slen - 2] = '\0';
00744             return string(line);
00745         }
00746     }
00747 
00748     throw Error("I expected to find a MIME header, but got EOF instead.");
00749 }
00750 
00758 void parse_mime_header(const string &header, string &name, string &value)
00759 {
00760     istringstream iss(header);
00761     // Set downcase
00762     char s[line_length];
00763     iss.getline(s, 1023, ':');
00764     name = s;
00765 
00766     iss.ignore(1023, ' ');
00767     iss.getline(s, 1023);
00768     value = s;
00769 
00770     downcase(name);
00771     downcase(value);
00772 }
00773 
00781 bool is_boundary(const char *line, const string &boundary)
00782 {
00783     if (!(line[0] == '-' && line[1] == '-'))
00784         return false;
00785     else {
00786         return strncmp(line, boundary.c_str(), boundary.length()) == 0;
00787     }
00788 }
00789 
00798 string read_multipart_boundary(FILE *in, const string &boundary)
00799 {
00800     string boundary_line = get_next_mime_header(in);
00801     // If the caller passed in a value for the boundary, test for that value,
00802     // else just see that this line starts with '--'.
00803     // The value of 'boundary_line' is returned by this function.
00804     if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
00805             || boundary_line.find("--") != 0)
00806         throw Error(
00807                 "The DAP4 data response document is broken - missing or malformed boundary.");
00808 
00809     return boundary_line;
00810 }
00811 
00832 void read_multipart_headers(FILE *in, const string &content_type,
00833         const ObjectType object_type, const string &cid)
00834 {
00835     bool ct = false, cd = false, ci = false;
00836 
00837     string header = get_next_mime_header(in);
00838     while (!header.empty()) {
00839         string name, value;
00840         parse_mime_header(header, name, value);
00841 
00842         if (name =="content-type") {
00843             ct = true;
00844             if (value.find(content_type) == string::npos)
00845                 throw Error("Content-Type for this part of a DAP4 data response must be " + content_type + ".");
00846         }
00847         else if (name == "content-description") {
00848             cd = true;
00849             if (get_description_type(value) != object_type)
00850                 throw Error("Content-Description for this part of a DAP4 data response must be dap4-ddx or dap4-data-ddx");
00851         }
00852         else if (name == "content-id") {
00853             ci = true;
00854             if (!cid.empty() && value != cid)
00855                 throw Error("Content-Id mismatch. Expected: " + cid
00856                         + ", but got: " + value);
00857         }
00858 
00859         header = get_next_mime_header(in);
00860     }
00861 
00862     if (!(ct && cd && ci))
00863         throw Error("The DAP4 data response document is broken - missing header.");
00864 }
00872 string cid_to_header_value(const string &cid)
00873 {
00874     string::size_type offset = cid.find("cid:");
00875     if (offset != 0)
00876         throw Error("expected CID to start with 'cid:'");
00877 
00878     string value = "<";
00879     value.append(cid.substr(offset + 4));
00880     value.append(">");
00881     downcase(value);
00882 
00883     return value;
00884 }
00885 
00886 #if FILE_METHODS
00887 
00893 void
00894 set_mime_error(FILE *out, int code, const string &reason,
00895                const string &version)
00896 {
00897     fprintf(out, "HTTP/1.0 %d %s%s", code, reason.c_str(), CRLF) ;
00898     if (version == "") {
00899         fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
00900         fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
00901     }
00902     else {
00903         fprintf(out, "XDODS-Server: %s%s", version.c_str(), CRLF) ;
00904         fprintf(out, "XOPeNDAP-Server: %s%s", version.c_str(), CRLF) ;
00905     }
00906     fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
00907 
00908     const time_t t = time(0);
00909     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00910     fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
00911     fprintf(out, CRLF) ;
00912 }
00913 #endif
00914 
00921 void
00922 set_mime_error(ostream &strm, int code, const string &reason,
00923                const string &version)
00924 {
00925     strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
00926     if (version == "") {
00927         strm << "XDODS-Server: " << DVR << CRLF ;
00928         strm << "XOPeNDAP-Server: " << DVR << CRLF ;
00929     }
00930     else {
00931         strm << "XDODS-Server: " << version.c_str() << CRLF ;
00932         strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
00933     }
00934     strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
00935 
00936     const time_t t = time(0);
00937     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00938     strm << "Cache-Control: no-cache" << CRLF ;
00939     strm << CRLF ;
00940 }
00941 
00942 #if FILE_METHODS
00943 
00949 void
00950 set_mime_not_modified(FILE *out)
00951 {
00952     fprintf(out, "HTTP/1.0 304 NOT MODIFIED%s", CRLF) ;
00953     const time_t t = time(0);
00954     fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
00955     fprintf(out, CRLF) ;
00956 }
00957 #endif
00958 
00965 void
00966 set_mime_not_modified(ostream &strm)
00967 {
00968     strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
00969     const time_t t = time(0);
00970     strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
00971     strm << CRLF ;
00972 }
00973 
00982 bool
00983 found_override(string name, string &doc)
00984 {
00985     ifstream ifs((name + ".ovr").c_str());
00986     if (!ifs)
00987         return false;
00988 
00989     char tmp[256];
00990     doc = "";
00991     while (!ifs.eof()) {
00992         ifs.getline(tmp, 255);
00993         strcat(tmp, "\n");
00994         doc += tmp;
00995     }
00996 
00997         ifs.close();
00998     return true;
00999 }
01000 
01009 bool
01010 remove_mime_header(FILE *in)
01011 {
01012     char tmp[256];
01013     while (!feof(in)) {
01014         char *s = fgets(tmp, 255, in);
01015         if (s && strncmp(s, CRLF, 2) == 0)
01016             return true;
01017     }
01018 
01019     return false;
01020 }
01021 
01022 } // namespace libdap
01023