69#include "media_types.h"
77#define FILE_DELIMITER '\\'
79#define FILE_DELIMITER '/'
97 if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
134static const char *days[] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"};
135static const char *months[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
138#define snprintf sprintf_s
150 const struct tm *ret = gmtime_r(&t, &stm);
156 snprintf(d, 255,
"%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm.tm_wday], stm.tm_mday, months[stm.tm_mon],
157 1900 + stm.tm_year, stm.tm_hour, stm.tm_min, stm.tm_sec);
162static const int TimLen = 26;
178bool do_version(
const string &script_ver,
const string &dataset_ver) {
179 fprintf(stdout,
"HTTP/1.0 200 OK%s", CRLF);
180 fprintf(stdout,
"XDODS-Server: %s%s", DVR, CRLF);
181 fprintf(stdout,
"XOPeNDAP-Server: %s%s", DVR, CRLF);
182 fprintf(stdout,
"XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF);
183 fprintf(stdout,
"Content-Type: text/plain%s", CRLF);
184 fprintf(stdout, CRLF);
186 fprintf(stdout,
"Core software version: %s%s", DVR, CRLF);
188 if (script_ver !=
"")
189 fprintf(stdout,
"Server Script Revision: %s%s", script_ver.c_str(), CRLF);
191 if (dataset_ver !=
"")
192 fprintf(stdout,
"Dataset version: %s%s", dataset_ver.c_str(), CRLF);
213 if (time(&TimBin) == (time_t)-1)
214 strncpy(TimStr,
"time() error ", TimLen - 1);
216 char ctime_value[TimLen];
217 const char *ret = ctime_r(&TimBin, ctime_value);
219 strncpy(TimStr,
"Unknown", TimLen - 1);
221 strncpy(TimStr, ctime_value, TimLen - 1);
222 TimStr[TimLen - 2] =
'\0';
226 cerr <<
"[" << TimStr <<
"] DAP server error: " << Msgt << endl;
254 string::size_type delim = path.find_last_of(FILE_DELIMITER);
255 string::size_type pound = path.find_last_of(
"#");
258 if (pound != string::npos)
259 new_path = path.substr(pound + 1);
261 new_path = path.substr(delim + 1);
276static const char *descrip[] =
277 {
"unknown",
"dods_das",
"dods_dds",
"dods_data",
"dods_ddx",
278 "dods_error",
"web_error",
"dap4-dmr",
"dap4-data",
"dap4-error"
282static const char *descrip[] = {
283 "unknown_type",
"dods_das",
"dods_dds",
"dods_data",
286 "dods_error",
"web_error",
293static const char *encoding[] = {
"unknown",
"deflate",
"x-plain",
"gzip",
"binary"};
310 if ((value == DAS1) || (value ==
"dods-das"))
312 else if ((value ==
"dods_dds") || (value ==
"dods-dds"))
314 else if ((value ==
"dods_data") || (value ==
"dods-data"))
316 else if ((value ==
"dods_ddx") || (value ==
"dods-ddx"))
318 else if ((value ==
"dods_data_ddx" || (value ==
"dods-data-ddx")))
319 return dods_data_ddx;
320 else if ((value ==
"dods_error") || (value ==
"dods-error"))
322 else if ((value ==
"web_error") || (value ==
"web-error"))
325 else if ((value ==
"dap4_dmr") || (value ==
"dap4-dmr") || (value == DMR_Content_Type))
327 else if ((value ==
"dap4_data") || (value ==
"dap4-data") || (value == DAP4_DATA_Content_Type))
329 else if ((value ==
"dap4_error") || (value ==
"dap4-error"))
352 fwrite(oss.str().data(), 1, oss.str().length(), out);
369 strm <<
"HTTP/1.0 200 OK" << CRLF;
371 strm <<
"XDODS-Server: " << DVR << CRLF;
372 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
374 strm <<
"XDODS-Server: " << ver.c_str() << CRLF;
375 strm <<
"XOPeNDAP-Server: " << ver.c_str() << CRLF;
377 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
379 const time_t t = time(0);
380 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
382 strm <<
"Last-Modified: ";
383 if (last_modified > 0)
384 strm <<
rfc822_date(last_modified).c_str() << CRLF;
388 if (type == dap4_dmr)
389 strm <<
"Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
391 strm <<
"Content-Type: text/plain" << CRLF;
395 strm <<
"Content-Description: " << descrip[type] << CRLF;
396 if (type == dods_error)
397 strm <<
"Cache-Control: no-cache" << CRLF;
401 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
421 const string &protocol) {
422 strm <<
"HTTP/1.0 200 OK" << CRLF;
424 strm <<
"XDODS-Server: " << DVR << CRLF;
425 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
428 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
430 strm <<
"XDAP: " << protocol << CRLF;
432 const time_t t = time(0);
433 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
435 strm <<
"Last-Modified: ";
436 if (last_modified > 0)
437 strm <<
rfc822_date(last_modified).c_str() << CRLF;
441 if (type == dap4_dmr)
442 strm <<
"Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
444 strm <<
"Content-Type: text/plain" << CRLF;
448 strm <<
"Content-Description: " << descrip[type] << CRLF;
449 if (type == dods_error)
450 strm <<
"Cache-Control: no-cache" << CRLF;
454 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
472 fwrite(oss.str().data(), 1, oss.str().length(), out);
487 strm <<
"HTTP/1.0 200 OK" << CRLF;
489 strm <<
"XDODS-Server: " << DVR << CRLF;
490 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
492 strm <<
"XDODS-Server: " << ver.c_str() << CRLF;
493 strm <<
"XOPeNDAP-Server: " << ver.c_str() << CRLF;
495 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
497 const time_t t = time(0);
498 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
500 strm <<
"Last-Modified: ";
501 if (last_modified > 0)
502 strm <<
rfc822_date(last_modified).c_str() << CRLF;
506 strm <<
"Content-type: text/html" << CRLF;
508 strm <<
"Content-Description: " << descrip[type] << CRLF;
509 if (type == dods_error)
510 strm <<
"Cache-Control: no-cache" << CRLF;
514 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
529 const string &protocol) {
530 strm <<
"HTTP/1.0 200 OK" << CRLF;
532 strm <<
"XDODS-Server: " << DVR << CRLF;
533 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
536 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
538 strm <<
"XDAP: " << protocol << CRLF;
540 const time_t t = time(0);
541 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
543 strm <<
"Last-Modified: ";
544 if (last_modified > 0)
545 strm <<
rfc822_date(last_modified).c_str() << CRLF;
549 strm <<
"Content-type: text/html" << CRLF;
551 strm <<
"Content-Description: " << descrip[type] << CRLF;
552 if (type == dods_error)
553 strm <<
"Cache-Control: no-cache" << CRLF;
557 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
578 fwrite(oss.str().data(), 1, oss.str().length(), out);
596 strm <<
"HTTP/1.0 200 OK" << CRLF;
598 strm <<
"XDODS-Server: " << DVR << CRLF;
599 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
601 strm <<
"XDODS-Server: " << ver.c_str() << CRLF;
602 strm <<
"XOPeNDAP-Server: " << ver.c_str() << CRLF;
604 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
606 const time_t t = time(0);
607 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
609 strm <<
"Last-Modified: ";
610 if (last_modified > 0)
611 strm <<
rfc822_date(last_modified).c_str() << CRLF;
615 strm <<
"Content-Type: application/octet-stream" << CRLF;
616 strm <<
"Content-Description: " << descrip[type] << CRLF;
618 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
637 const string &protocol) {
638 strm <<
"HTTP/1.0 200 OK" << CRLF;
640 strm <<
"XDODS-Server: " << DVR << CRLF;
641 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
643 if (protocol.empty())
644 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
646 strm <<
"XDAP: " << protocol << CRLF;
648 const time_t t = time(0);
649 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
651 strm <<
"Last-Modified: ";
652 if (last_modified > 0)
653 strm <<
rfc822_date(last_modified).c_str() << CRLF;
657 strm <<
"Content-Type: application/octet-stream" << CRLF;
658 strm <<
"Content-Description: " << descrip[type] << CRLF;
660 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
665void set_mime_multipart(ostream &strm,
const string &boundary,
const string &start, ObjectType type,
666 const string &version,
EncodingType enc,
const time_t last_modified) {
667 strm <<
"HTTP/1.0 200 OK" << CRLF;
669 strm <<
"XDODS-Server: " << DVR << CRLF;
670 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
672 strm <<
"XDODS-Server: " << version.c_str() << CRLF;
673 strm <<
"XOPeNDAP-Server: " << version.c_str() << CRLF;
675 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
677 const time_t t = time(0);
678 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
680 strm <<
"Last-Modified: ";
681 if (last_modified > 0)
682 strm <<
rfc822_date(last_modified).c_str() << CRLF;
686 strm <<
"Content-Type: Multipart/Related; boundary=" << boundary <<
"; start=\"<" << start
687 <<
">\"; type=\"Text/xml\"" << CRLF;
688 strm <<
"Content-Description: " << descrip[type] << CRLF;
690 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
698 const time_t last_modified,
const string &protocol,
const string &url) {
699 strm <<
"HTTP/1.1 200 OK" << CRLF;
701 const time_t t = time(0);
702 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
704 strm <<
"Last-Modified: ";
705 if (last_modified > 0)
706 strm <<
rfc822_date(last_modified).c_str() << CRLF;
710 strm <<
"Content-Type: multipart/related; boundary=" << boundary <<
"; start=\"<" << start
711 <<
">\"; type=\"text/xml\"" << CRLF;
715 strm <<
"Content-Description: " << descrip[type] <<
";";
717 strm <<
" url=\"" << url <<
"\"" << CRLF;
722 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
725 strm <<
"X-DAP: " << DAP_PROTOCOL_VERSION << CRLF;
727 strm <<
"X-DAP: " << protocol << CRLF;
729 strm <<
"X-OPeNDAP-Server: " << DVR << CRLF;
734void set_mime_ddx_boundary(ostream &strm,
const string &boundary,
const string &cid, ObjectType type,
736 strm <<
"--" << boundary << CRLF;
739 strm <<
"Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
740 strm <<
"Content-Id: <" << cid <<
">" << CRLF;
741 strm <<
"Content-Description: " << descrip[type] << CRLF;
743 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
748void set_mime_data_boundary(ostream &strm,
const string &boundary,
const string &cid,
ObjectType type,
750 strm <<
"--" << boundary << CRLF;
751 strm <<
"Content-Type: application/octet-stream" << CRLF;
752 strm <<
"Content-Id: <" << cid <<
">" << CRLF;
753 strm <<
"Content-Description: " << descrip[type] << CRLF;
755 strm <<
"Content-Encoding: " << encoding[enc] << CRLF;
760const size_t line_length = 1024;
780 char line[line_length];
782 if (fgets(line, line_length, in) && (strncmp(line, CRLF, 2) == 0 || line[0] ==
'\n'))
785 size_t slen = min(strlen(line), line_length);
786 line[slen - 1] =
'\0';
787 if (line[slen - 2] ==
'\r')
788 line[slen - 2] =
'\0';
793 throw Error(
"I expected to find a MIME header, but got EOF instead.");
812 char line[line_length];
814 in.getline(line, line_length);
815 if (strncmp(line, CRLF, 2) == 0 || line[0] ==
'\n') {
819 size_t slen = min(strlen(line), line_length);
820 line[slen - 1] =
'\0';
821 if (line[slen - 2] ==
'\r')
822 line[slen - 2] =
'\0';
829 char raw_line[line_length];
831 in.getline(raw_line, line_length);
832 string line = raw_line;
833 if (line.find(
'\r') != string::npos)
834 line = line.substr(0, line.size() - 1);
838 throw Error(
"I expected to find a MIME header, but got EOF instead.");
849 istringstream iss(header);
851 size_t length = header.length() + 1;
852 vector<char> s(length);
854 iss.getline(s.data(), length,
':');
857 iss.ignore(length,
' ');
858 iss.getline(s.data(), length);
877 if (strlen(line) < 2 || !(line[0] ==
'-' && line[1] ==
'-'))
880 return strncmp(line, boundary.c_str(), boundary.length()) == 0;
898 if ((!boundary.empty() &&
is_boundary(boundary_line.c_str(), boundary)) || boundary_line.find(
"--") != 0)
899 throw Error(internal_error,
"The DAP4 data response document is broken - missing or malformed boundary.");
901 return boundary_line;
909 if ((!boundary.empty() &&
is_boundary(boundary_line.c_str(), boundary)) || boundary_line.find(
"--") != 0)
910 throw Error(internal_error,
"The DAP4 data response document is broken - missing or malformed boundary.");
912 return boundary_line;
936 bool ct =
false, cd =
false, ci =
false;
941 while (!header.empty()) {
945 if (name ==
"content-type") {
947 if (value.find(content_type) == string::npos)
948 throw Error(internal_error,
949 "Content-Type for this part of a DAP2 data ddx response must be " + content_type +
".");
950 }
else if (name ==
"content-description") {
955 "Content-Description for this part of a DAP2 data ddx response must be dods-ddx or dods-data-ddx");
956 }
else if (name ==
"content-id") {
958 if (!cid.empty() && value != cid)
959 throw Error(
"Content-Id mismatch. Expected: " + cid +
", but got: " + value);
965 if (!(ct && cd && ci))
966 throw Error(internal_error,
"The DAP4 data response document is broken - missing header.");
969void read_multipart_headers(istream &in,
const string &content_type,
const ObjectType object_type,
const string &cid) {
970 bool ct =
false, cd =
false, ci =
false;
973 while (!header.empty()) {
977 if (name ==
"content-type") {
979 if (value.find(content_type) == string::npos)
980 throw Error(internal_error,
981 "Content-Type for this part of a DAP4 data response must be " + content_type +
".");
982 }
else if (name ==
"content-description") {
985 throw Error(
"Content-Description '" + value +
986 "' not the expected value (expected: " + descrip[object_type] +
").");
987 }
else if (name ==
"content-id") {
989 if (!cid.empty() && value != cid)
990 throw Error(
"Content-Id mismatch. Expected: " + cid +
", but got: " + value);
996 if (!(ct && cd && ci))
997 throw Error(internal_error,
"The DAP4 data response document is broken - missing header.");
1009 string::size_type offset = cid.find(
"cid:");
1011 throw Error(internal_error,
"expected CID to start with 'cid:'");
1014 value.append(cid.substr(offset + 4));
1029void set_mime_error(FILE *out,
int code,
const string &reason,
const string &version) {
1032 fwrite(oss.str().data(), 1, oss.str().length(), out);
1043void set_mime_error(ostream &strm,
int code,
const string &reason,
const string &version) {
1044 strm <<
"HTTP/1.0 " << code <<
" " << reason.c_str() << CRLF;
1045 if (version ==
"") {
1046 strm <<
"XDODS-Server: " << DVR << CRLF;
1047 strm <<
"XOPeNDAP-Server: " << DVR << CRLF;
1049 strm <<
"XDODS-Server: " << version.c_str() << CRLF;
1050 strm <<
"XOPeNDAP-Server: " << version.c_str() << CRLF;
1052 strm <<
"XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
1054 const time_t t = time(0);
1055 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
1056 strm <<
"Cache-Control: no-cache" << CRLF;
1070 fwrite(oss.str().data(), 1, oss.str().length(), out);
1081 strm <<
"HTTP/1.0 304 NOT MODIFIED" << CRLF;
1082 const time_t t = time(0);
1083 strm <<
"Date: " <<
rfc822_date(t).c_str() << CRLF;
1101found_override(
string name,
string &doc)
1103 ifstream ifs((name +
".ovr").c_str());
1109 while (!ifs.eof()) {
1110 ifs.getline(tmp, 255);
1112 strncat(tmp,
"\n",
sizeof(tmp) - strlen(tmp) - 1);
1133 char *s = fgets(tmp, 255, in);
1134 if (s && strncmp(s, CRLF, 2) == 0)
1152 }
while (!header.empty());
A class for error processing.
top level DAP object to house generic methods
string read_multipart_boundary(FILE *in, const string &boundary)
void set_mime_error(FILE *out, int code, const string &reason, const string &version)
void set_mime_html(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
ObjectType get_description_type(const string &value)
string cid_to_header_value(const string &cid)
void parse_mime_header(const string &header, string &name, string &value)
time_t last_modified_time(const string &name)
string name_path(const string &path)
Returns the filename portion of a pathname.
void set_mime_not_modified(FILE *out)
Send a ‘Not Modified’ response.
void set_mime_binary(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
bool do_version(const string &script_ver, const string &dataset_ver)
Send a version number.
bool remove_mime_header(FILE *in)
Read and discard the MIME header of the stream in.
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
EncodingType
The type of encoding used on the current stream.
void ErrMsgT(const string &Msgt)
Logs an error message.
ObjectType get_type(const string &value)
string rfc822_date(const time_t t)
bool is_boundary(const char *line, const string &boundary)
ObjectType
The type of object in the stream coming from the data server.
void set_mime_text(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
string get_next_mime_header(FILE *in)