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);
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;
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")))
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;
379 const time_t t = time(0);
382 strm <<
"Last-Modified: ";
383 if (last_modified > 0)
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;
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;
430 strm <<
"XDAP: " << protocol <<
CRLF;
432 const time_t t = time(0);
435 strm <<
"Last-Modified: ";
436 if (last_modified > 0)
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;
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;
497 const time_t t = time(0);
500 strm <<
"Last-Modified: ";
501 if (last_modified > 0)
506 strm <<
"Content-type: text/html" <<
CRLF;
508 strm <<
"Content-Description: " << descrip[type] <<
CRLF;
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;
538 strm <<
"XDAP: " << protocol <<
CRLF;
540 const time_t t = time(0);
543 strm <<
"Last-Modified: ";
544 if (last_modified > 0)
549 strm <<
"Content-type: text/html" <<
CRLF;
551 strm <<
"Content-Description: " << descrip[type] <<
CRLF;
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;
606 const time_t t = time(0);
609 strm <<
"Last-Modified: ";
610 if (last_modified > 0)
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())
646 strm <<
"XDAP: " << protocol <<
CRLF;
648 const time_t t = time(0);
651 strm <<
"Last-Modified: ";
652 if (last_modified > 0)
657 strm <<
"Content-Type: application/octet-stream" <<
CRLF;
658 strm <<
"Content-Description: " << descrip[type] <<
CRLF;
660 strm <<
"Content-Encoding: " << encoding[enc] <<
CRLF;
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;
677 const time_t t = time(0);
680 strm <<
"Last-Modified: ";
681 if (last_modified > 0)
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);
704 strm <<
"Last-Modified: ";
705 if (last_modified > 0)
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;
727 strm <<
"X-DAP: " << protocol <<
CRLF;
729 strm <<
"X-OPeNDAP-Server: " <<
DVR <<
CRLF;
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;
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;
782 if (fgets(line,
line_length, in) && (strncmp(line,
CRLF, 2) == 0 || line[0] ==
'\n'))
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.");
815 if (strncmp(line,
CRLF, 2) == 0 || line[0] ==
'\n') {
820 line[slen - 1] =
'\0';
821 if (line[slen - 2] ==
'\r')
822 line[slen - 2] =
'\0';
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)
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.");
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)
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:");
1014 value.append(cid.substr(offset + 4));
1032 fwrite(oss.str().data(), 1, oss.str().length(), out);
1044 strm <<
"HTTP/1.0 " << code <<
" " << reason.c_str() <<
CRLF;
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;
1054 const time_t t = time(0);
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);
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());
#define internal_error
Internal server error (500)
A class for error processing.
#define DAP_PROTOCOL_VERSION
top level DAP object to house generic methods
void set_mime_data_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
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)
void set_mime_ddx_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
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 set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, const string &version, EncodingType enc, const time_t last_modified)
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)