00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "config.h"
00038
00039 static char rcsid[] not_used =
00040 {"$Id: DODSFilter.cc 16760 2007-06-26 21:42:26Z jimg $"
00041 };
00042
00043 #include <signal.h>
00044
00045 #ifndef WIN32
00046 #include <unistd.h>
00047 #include <sys/wait.h>
00048 #else
00049 #include <io.h>
00050 #include <fcntl.h>
00051 #include <process.h>
00052 #endif
00053
00054 #include <iostream>
00055 #include <string>
00056 #include <algorithm>
00057
00058 #include <GetOpt.h>
00059
00060 #include "DAS.h"
00061 #include "DDS.h"
00062 #include "debug.h"
00063 #include "cgi_util.h"
00064 #include "util.h"
00065 #include "escaping.h"
00066 #include "DODSFilter.h"
00067 #include "InternalErr.h"
00068 #ifndef WIN32
00069 #include "SignalHandler.h"
00070 #include "EventHandler.h"
00071 #include "AlarmHandler.h"
00072 #endif
00073
00074 using namespace std;
00075
00076 const string usage =
00077 "Usage: <handler name> -o <response> -u <url> [options ...] [data set]\n\
00078 \n\
00079 options: -o <response>: DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\
00080 -u <url>: The complete URL minus the CE (required for DDX)\n\
00081 -c: Compress the response using the deflate algorithm.\n\
00082 -e <expr>: When returning a DataDDS, use <expr> as the constraint.\n\
00083 -v <version>: Use <version> as the version number\n\
00084 -d <dir>: Look for ancillary file in <dir> (deprecated).\n\
00085 -f <file>: Look for ancillary data in <file> (deprecated).\n\
00086 -r <dir>: Use <dir> as a cache directory\n\
00087 -l <time>: Conditional request; if data source is unchanged since\n\
00088 <time>, return an HTTP 304 response.\n\
00089 -t <seconds>: Timeout the handler after <seconds>.\n\
00090 -h: This message.";
00091
00092 #if 0
00093
00094
00095
00096 #ifdef WIN32
00097 #define WAITPID(pid) while(_cwait(NULL, pid, NULL) > 0)
00098 #else
00099 #define WAITPID(pid) while(waitpid(pid, 0, 0) > 0)
00100 #endif
00101 #endif
00102
00167 DODSFilter::DODSFilter(int argc, char *argv[]) throw(Error)
00168 {
00169 initialize(argc, argv);
00170
00171 DBG(cerr << "d_comp: " << d_comp << endl);
00172 DBG(cerr << "d_ce: " << d_ce << endl);
00173 DBG(cerr << "d_cgi_ver: " << d_cgi_ver << endl);
00174 DBG(cerr << "d_response: " << d_response << endl);
00175 DBG(cerr << "d_anc_dir: " << d_anc_dir << endl);
00176 DBG(cerr << "d_anc_file: " << d_anc_file << endl);
00177 DBG(cerr << "d_cache_dir: " << d_cache_dir << endl);
00178 DBG(cerr << "d_conditional_request: " << d_conditional_request << endl);
00179 DBG(cerr << "d_if_modified_since: " << d_if_modified_since << endl);
00180 DBG(cerr << "d_url: " << d_url << endl);
00181 DBG(cerr << "d_timeout: " << d_timeout << endl);
00182 }
00183
00184 DODSFilter::~DODSFilter()
00185 {
00186 }
00187
00190 void
00191 DODSFilter::initialize()
00192 {
00193
00194
00195 d_comp = false;
00196 d_bad_options = false;
00197 d_conditional_request = false;
00198 d_dataset = "";
00199 d_ce = "";
00200 d_cgi_ver = "";
00201 d_anc_dir = "";
00202 d_anc_file = "";
00203 d_cache_dir = "";
00204 d_response = Unknown_Response;;
00205 d_anc_das_lmt = 0;
00206 d_anc_dds_lmt = 0;
00207 d_if_modified_since = -1;
00208 d_url = "";
00209 d_program_name = "Unknown";
00210 d_timeout = 0;
00211
00212 #ifdef WIN32
00213
00214
00215
00216 _setmode(_fileno(stdout), _O_BINARY);
00217 #endif
00218 }
00219
00231 void
00232 DODSFilter::initialize(int argc, char *argv[])
00233 {
00234 initialize();
00235
00236 d_program_name = argv[0];
00237
00238
00239 int next_arg = process_options(argc, argv);
00240
00241
00242
00243
00244 if (next_arg < argc) {
00245 d_dataset = argv[next_arg];
00246 d_dataset = www2id(d_dataset, "%", "%20");
00247 }
00248 else if (get_response() != Version_Response)
00249 print_usage();
00250 }
00251
00260 int
00261 DODSFilter::process_options(int argc, char *argv[])
00262 {
00263 DBG(cerr << "Entering process_options... ");
00264
00265 int option_char;
00266 GetOpt getopt (argc, argv, "ce: v: d: f: r: l: o: u: t: ");
00267
00268 while ((option_char = getopt()) != EOF) {
00269 switch (option_char) {
00270 case 'c': d_comp = true; break;
00271 case 'e': set_ce(getopt.optarg); break;
00272 case 'v': set_cgi_version(getopt.optarg); break;
00273 case 'd': d_anc_dir = getopt.optarg; break;
00274 case 'f': d_anc_file = getopt.optarg; break;
00275 case 'r': d_cache_dir = getopt.optarg; break;
00276 case 'o': set_response(getopt.optarg); break;
00277 case 'u': set_URL(getopt.optarg); break;
00278 case 't': d_timeout = atoi(getopt.optarg); break;
00279 case 'l':
00280 d_conditional_request = true;
00281 d_if_modified_since
00282 = static_cast<time_t>(strtol(getopt.optarg, NULL, 10));
00283 break;
00284 case 'h': print_usage(); exit(1);
00285 default: print_usage();
00286 }
00287 }
00288
00289 DBGN(cerr << "exiting." << endl);
00290
00291 return getopt.optind;
00292 }
00293
00298 bool
00299 DODSFilter::is_conditional() const
00300 {
00301 return d_conditional_request;
00302 }
00303
00317 void
00318 DODSFilter::set_cgi_version(string version)
00319 {
00320 d_cgi_ver = version;
00321 }
00322
00328 string
00329 DODSFilter::get_cgi_version() const
00330 {
00331 return d_cgi_ver;
00332 }
00333
00340 string
00341 DODSFilter::get_ce() const
00342 {
00343 return d_ce;
00344 }
00345
00346 void
00347 DODSFilter::set_ce(string _ce)
00348 {
00349 d_ce = www2id(_ce, "%", "%20");
00350 }
00351
00360 string
00361 DODSFilter::get_dataset_name() const
00362 {
00363 return d_dataset;
00364 }
00365
00366 void
00367 DODSFilter::set_dataset_name(const string ds)
00368 {
00369 d_dataset = www2id(ds, "%", "%20");
00370 }
00371
00375 string
00376 DODSFilter::get_URL() const
00377 {
00378 return d_url;
00379 }
00380
00383 void
00384 DODSFilter::set_URL(const string &url)
00385 {
00386 if (url.find('?') != url.npos)
00387 print_usage();
00388
00389 d_url = url;
00390 }
00391
00399 string
00400 DODSFilter::get_dataset_version() const
00401 {
00402 return "";
00403 }
00404
00411 void DODSFilter::set_response(const string &r)
00412 {
00413 if (r == "DAS" || r == "das") {
00414 d_response = DAS_Response;
00415 d_action = "das" ;
00416 }
00417 else if (r == "DDS" || r == "dds") {
00418 d_response = DDS_Response;
00419 d_action = "dds" ;
00420 }
00421 else if (r == "DataDDS" || r == "dods") {
00422 d_response = DataDDS_Response;
00423 d_action = "dods" ;
00424 }
00425 else if (r == "DDX" || r == "ddx") {
00426 d_response = DDX_Response;
00427 d_action = "ddx" ;
00428 }
00429 else if (r == "Version") {
00430 d_response = Version_Response;
00431 d_action = "version" ;
00432 }
00433 else
00434 print_usage();
00435 }
00436
00438 DODSFilter::Response
00439 DODSFilter::get_response() const
00440 {
00441 return d_response;
00442 }
00443
00445 string DODSFilter::get_action() const
00446 {
00447 return d_action;
00448 }
00449
00470 time_t
00471 DODSFilter::get_dataset_last_modified_time() const
00472 {
00473 return last_modified_time(d_dataset);
00474 }
00475
00485 time_t
00486 DODSFilter::get_das_last_modified_time(const string &anc_location) const
00487 {
00488 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00489 << anc_location << "call faf(das) d_dataset=" << d_dataset
00490 << " d_anc_file=" << d_anc_file << endl);
00491
00492 string name
00493 = find_ancillary_file(d_dataset, "das",
00494 (anc_location == "") ? d_anc_dir : anc_location,
00495 d_anc_file);
00496
00497 return max((name != "") ? last_modified_time(name) : 0,
00498 get_dataset_last_modified_time());
00499 }
00500
00508 time_t
00509 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
00510 {
00511 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00512 << anc_location << "call faf(dds) d_dataset=" << d_dataset
00513 << " d_anc_file=" << d_anc_file << endl);
00514
00515 string name
00516 = find_ancillary_file(d_dataset, "dds",
00517 (anc_location == "") ? d_anc_dir : anc_location,
00518 d_anc_file);
00519
00520 return max((name != "") ? last_modified_time(name) : 0,
00521 get_dataset_last_modified_time());
00522 }
00523
00537 time_t
00538 DODSFilter::get_data_last_modified_time(const string &anc_location) const
00539 {
00540 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00541 << anc_location << "call faf(both) d_dataset=" << d_dataset
00542 << " d_anc_file=" << d_anc_file << endl);
00543
00544 string dds_name
00545 = find_ancillary_file(d_dataset, "dds",
00546 (anc_location == "") ? d_anc_dir : anc_location,
00547 d_anc_file);
00548 string das_name
00549 = find_ancillary_file(d_dataset, "das",
00550 (anc_location == "") ? d_anc_dir : anc_location,
00551 d_anc_file);
00552
00553 time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
00554 (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
00555
00556 time_t n = get_dataset_last_modified_time();
00557
00558 return max(m, n);
00559 }
00560
00568 time_t
00569 DODSFilter::get_request_if_modified_since() const
00570 {
00571 return d_if_modified_since;
00572 }
00573
00580 string
00581 DODSFilter::get_cache_dir() const
00582 {
00583 return d_cache_dir;
00584 }
00585
00590 void
00591 DODSFilter::set_timeout(int t)
00592 {
00593 d_timeout = t;
00594 }
00595
00597 int
00598 DODSFilter::get_timeout() const
00599 {
00600 return d_timeout;
00601 }
00602
00614 void
00615 DODSFilter::establish_timeout(FILE *stream) const
00616 {
00617 #ifndef WIN32
00618 if (d_timeout > 0) {
00619 SignalHandler *sh = SignalHandler::instance();
00620 sh->register_handler(SIGALRM, new AlarmHandler(stream));
00621 alarm(d_timeout);
00622 }
00623 #endif
00624 }
00625
00626
00636 void
00637 DODSFilter::read_ancillary_das(DAS &das, const string &anc_location) const
00638 {
00639 string name = find_ancillary_file(d_dataset, "das",
00640 (anc_location == "") ? d_anc_dir : anc_location,
00641 d_anc_file);
00642
00643 FILE *in = fopen(name.c_str(), "r");
00644 if (in) {
00645 das.parse(in);
00646 int res = fclose(in) ;
00647 if (res) {
00648 DBG(cerr << "DODSFilter::read_ancillary_das - Failed to close file " << (void *)in << endl ;) ;
00649 }
00650 }
00651 }
00652
00662 void
00663 DODSFilter::read_ancillary_dds(DDS &dds, const string &anc_location) const
00664 {
00665 string name = find_ancillary_file(d_dataset, "dds",
00666 (anc_location == "") ? d_anc_dir : anc_location,
00667 d_anc_file);
00668 FILE *in = fopen(name.c_str(), "r");
00669 if (in) {
00670 dds.parse(in);
00671 int res = fclose(in) ;
00672 if (res) {
00673 DBG(cerr << "DODSFilter::read_ancillary_dds - Failed to close " << (void *)in << endl ;) ;
00674 }
00675 }
00676 }
00677
00678 static const char *emessage = "DODS internal server error; usage error. Please report this to the dataset maintainer, or to the opendap-tech@opendap.org mailing list.";
00679
00689 void
00690 DODSFilter::print_usage() const
00691 {
00692
00693 ErrMsgT(usage.c_str());
00694
00695 throw Error(unknown_error, emessage);
00696 }
00697
00703 void
00704 DODSFilter::send_version_info() const
00705 {
00706 do_version(d_cgi_ver, get_dataset_version());
00707 }
00708
00720 void
00721 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
00722 bool with_mime_headers) const
00723 {
00724 time_t das_lmt = get_das_last_modified_time(anc_location);
00725 if (is_conditional()
00726 && das_lmt <= get_request_if_modified_since()
00727 && with_mime_headers) {
00728 set_mime_not_modified(out);
00729 }
00730 else {
00731 if (with_mime_headers)
00732 set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
00733 das.print(out);
00734 }
00735 fflush(out) ;
00736 }
00737
00738 void
00739 DODSFilter::send_das(DAS &das, const string &anc_location,
00740 bool with_mime_headers) const
00741 {
00742 send_das(stdout, das, anc_location, with_mime_headers);
00743 }
00744
00761 void
00762 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
00763 bool constrained,
00764 const string &anc_location,
00765 bool with_mime_headers) const
00766 {
00767
00768 if (constrained)
00769 eval.parse_constraint(d_ce, dds);
00770
00771 if (eval.functional_expression())
00772 throw Error("Function calls can only be used with data requests. To see the structure\nof the underlying data source, reissue the URL without the function.");
00773
00774 time_t dds_lmt = get_dds_last_modified_time(anc_location);
00775 if (is_conditional()
00776 && dds_lmt <= get_request_if_modified_since()
00777 && with_mime_headers) {
00778 set_mime_not_modified(out);
00779 }
00780 else {
00781 if (with_mime_headers)
00782 set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
00783 if (constrained)
00784 dds.print_constrained(out);
00785 else
00786 dds.print(out);
00787 }
00788
00789 fflush(out) ;
00790 }
00791
00792 void
00793 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
00794 bool constrained, const string &anc_location,
00795 bool with_mime_headers) const
00796 {
00797 send_dds(stdout, dds, eval, constrained, anc_location, with_mime_headers);
00798 }
00799
00800
00801
00802 void
00803 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
00804 ConstraintEvaluator &eval, FILE *out)
00805 const
00806 {
00807 fprintf(out, "Dataset {\n");
00808 var.print_decl(out, " ", true, false, true);
00809 fprintf(out, "} function_value;\n");
00810 fprintf(out, "Data:\n");
00811
00812 fflush(out);
00813
00814
00815 XDR *xdr_sink = new_xdrstdio(out, XDR_ENCODE);
00816
00817 try {
00818
00819 var.serialize(d_dataset, eval, dds, xdr_sink, false);
00820 delete_xdrstdio(xdr_sink);
00821 }
00822 catch (Error &e) {
00823 delete_xdrstdio(xdr_sink);
00824 throw;
00825 }
00826 }
00827
00828 void
00829 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
00830 FILE * out) const
00831 {
00832
00833 dds.print_constrained(out);
00834 fprintf(out, "Data:\n");
00835 fflush(out);
00836
00837
00838 XDR *xdr_sink = new_xdrstdio(out, XDR_ENCODE);
00839
00840 try {
00841
00842 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
00843 if ((*i)->send_p()) {
00844 DBG(cerr << "Sending " << (*i)->name() << endl);
00845 (*i)->serialize(d_dataset, eval, dds, xdr_sink, true);
00846 }
00847
00848 delete_xdrstdio(xdr_sink);
00849 }
00850 catch (Error & e) {
00851 delete_xdrstdio(xdr_sink);
00852 throw;
00853 }
00854 }
00855
00872 void
00873 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
00874 FILE * data_stream, const string & anc_location,
00875 bool with_mime_headers) const
00876 {
00877
00878
00879
00880 time_t data_lmt = get_data_last_modified_time(anc_location);
00881 if (is_conditional()
00882 && data_lmt <= get_request_if_modified_since()
00883 && with_mime_headers) {
00884 set_mime_not_modified(data_stream);
00885 return;
00886 }
00887
00888 establish_timeout(data_stream);
00889 dds.set_timeout(d_timeout);
00890
00891 eval.parse_constraint(d_ce, dds);
00892
00893
00894 dds.tag_nested_sequences();
00895
00896
00897 #if COMPRESSION_FOR_SERVER3
00898 bool compress = d_comp && deflate_exists();
00899 #endif
00900
00901
00902 if (eval.functional_expression()) {
00903
00904
00905
00906
00907 BaseType *var = eval.eval_function(dds, d_dataset);
00908 if (!var)
00909 throw Error(unknown_error, "Error calling the CE function.");
00910
00911 #if COMPRESSION_FOR_SERVER3
00912 if (with_mime_headers)
00913 set_mime_binary(data_stream, dods_data, d_cgi_ver,
00914 (compress) ? deflate : x_plain, data_lmt);
00915 fflush(data_stream);
00916
00917 int childpid;
00918 if (compress)
00919 data_stream = compressor(data_stream, childpid);
00920 #endif
00921 if (with_mime_headers)
00922 set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
00923
00924 fflush(data_stream);
00925
00926 functional_constraint(*var, dds, eval, data_stream);
00927 delete var;
00928 var = 0;
00929 }
00930 else {
00931 #if COMPRESSION_FOR_SERVER3
00932 if (with_mime_headers)
00933 set_mime_binary(data_stream, dods_data, d_cgi_ver,
00934 (compress) ? deflate : x_plain, data_lmt);
00935 fflush(data_stream);
00936
00937 int childpid;
00938 if (compress)
00939 data_stream = compressor(data_stream, childpid);
00940 #endif
00941 if (with_mime_headers)
00942 set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
00943
00944 dataset_constraint(dds, eval, data_stream);
00945 }
00946
00947 fflush(data_stream);
00948 }
00949
00960 void
00961 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
00962 bool with_mime_headers) const
00963 {
00964
00965 if (!d_ce.empty())
00966 eval.parse_constraint(d_ce, dds);
00967
00968 time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
00969
00970
00971
00972
00973 if (is_conditional() && dds_lmt <= get_request_if_modified_since()
00974 && with_mime_headers) {
00975 set_mime_not_modified(out);
00976 return;
00977 }
00978 else {
00979 if (with_mime_headers)
00980 set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
00981 dds.print_xml(out, !d_ce.empty(), d_url + ".blob?" + d_ce);
00982 }
00983 }
00984
00989 void
00990 DODSFilter::send_blob(DDS &, FILE *, bool)
00991 {
00992 #if 0
00993
00994 bool compress = d_comp && deflate_exists();
00995 time_t data_lmt = get_data_last_modified_time(d_anc_dir);
00996
00997
00998
00999
01000 if (is_conditional() && data_lmt <= get_request_if_modified_since()
01001 && with_mime_headers) {
01002 set_mime_not_modified(out);
01003 return;
01004 }
01005
01006 dds.parse_constraint(d_ce);
01007
01008
01009 if (dds.functional_expression()) {
01010 BaseType *var = dds.eval_function(d_dataset);
01011 if (!var)
01012 throw Error("Error calling the CE function.");
01013
01014 if (with_mime_headers)
01015 set_mime_binary(out, dods_data, d_cgi_ver,
01016 (compress) ? deflate : x_plain, data_lmt);
01017
01018 FILE *comp_sink;
01019 XDR *xdr_sink;
01020 int childpid = get_sinks(out, compress, &comp_sink, &xdr_sink);
01021
01022
01023 if (!var->serialize(d_dataset, dds, xdr_sink, false))
01024 throw Error("Could not send the function result.");
01025
01026 clean_sinks(childpid, compress, xdr_sink, comp_sink);
01027 }
01028 else {
01029 if (with_mime_headers)
01030 set_mime_binary(out, dods_data, d_cgi_ver,
01031 (compress) ? deflate : x_plain, data_lmt);
01032
01033 FILE *comp_sink;
01034 XDR *xdr_sink;
01035 int childpid = get_sinks(out, compress, &comp_sink, &xdr_sink);
01036
01037 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
01038 if ((*i)->send_p())
01039 if (!(*i)->serialize(d_dataset, dds, xdr_sink, true))
01040 throw Error(string("Could not serialize variable '")
01041 + (*i)->name() + string("'."));
01042
01043 clean_sinks(childpid, compress, xdr_sink, comp_sink);
01044 }
01045 #endif
01046 }
01047