libdap
Updated for version 3.17.0
|
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