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 16088 2007-03-28 21:42:19Z 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 ";
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 default: print_usage();
00285 }
00286 }
00287
00288 DBGN(cerr << "exiting." << endl);
00289
00290 return getopt.optind;
00291 }
00292
00297 bool
00298 DODSFilter::is_conditional() const
00299 {
00300 return d_conditional_request;
00301 }
00302
00316 void
00317 DODSFilter::set_cgi_version(string version)
00318 {
00319 d_cgi_ver = version;
00320 }
00321
00327 string
00328 DODSFilter::get_cgi_version() const
00329 {
00330 return d_cgi_ver;
00331 }
00332
00339 string
00340 DODSFilter::get_ce() const
00341 {
00342 return d_ce;
00343 }
00344
00345 void
00346 DODSFilter::set_ce(string _ce)
00347 {
00348 d_ce = www2id(_ce, "%", "%20");
00349 }
00350
00359 string
00360 DODSFilter::get_dataset_name() const
00361 {
00362 return d_dataset;
00363 }
00364
00365 void
00366 DODSFilter::set_dataset_name(const string ds)
00367 {
00368 d_dataset = www2id(ds, "%", "%20");
00369 }
00370
00374 string
00375 DODSFilter::get_URL() const
00376 {
00377 return d_url;
00378 }
00379
00382 void
00383 DODSFilter::set_URL(const string &url)
00384 {
00385 if (url.find('?') != url.npos)
00386 print_usage();
00387
00388 d_url = url;
00389 }
00390
00398 string
00399 DODSFilter::get_dataset_version() const
00400 {
00401 return "";
00402 }
00403
00410 void DODSFilter::set_response(const string &r)
00411 {
00412 if (r == "DAS" || r == "das") {
00413 d_response = DAS_Response;
00414 d_action = "das" ;
00415 }
00416 else if (r == "DDS" || r == "dds") {
00417 d_response = DDS_Response;
00418 d_action = "dds" ;
00419 }
00420 else if (r == "DataDDS" || r == "dods") {
00421 d_response = DataDDS_Response;
00422 d_action = "dods" ;
00423 }
00424 else if (r == "DDX" || r == "ddx") {
00425 d_response = DDX_Response;
00426 d_action = "ddx" ;
00427 }
00428 else if (r == "Version") {
00429 d_response = Version_Response;
00430 d_action = "version" ;
00431 }
00432 else
00433 print_usage();
00434 }
00435
00437 DODSFilter::Response
00438 DODSFilter::get_response() const
00439 {
00440 return d_response;
00441 }
00442
00444 string DODSFilter::get_action() const
00445 {
00446 return d_action;
00447 }
00448
00469 time_t
00470 DODSFilter::get_dataset_last_modified_time() const
00471 {
00472 return last_modified_time(d_dataset);
00473 }
00474
00484 time_t
00485 DODSFilter::get_das_last_modified_time(const string &anc_location) const
00486 {
00487 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00488 << anc_location << "call faf(das) d_dataset=" << d_dataset
00489 << " d_anc_file=" << d_anc_file << endl);
00490
00491 string name
00492 = find_ancillary_file(d_dataset, "das",
00493 (anc_location == "") ? d_anc_dir : anc_location,
00494 d_anc_file);
00495
00496 return max((name != "") ? last_modified_time(name) : 0,
00497 get_dataset_last_modified_time());
00498 }
00499
00507 time_t
00508 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
00509 {
00510 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00511 << anc_location << "call faf(dds) d_dataset=" << d_dataset
00512 << " d_anc_file=" << d_anc_file << endl);
00513
00514 string name
00515 = find_ancillary_file(d_dataset, "dds",
00516 (anc_location == "") ? d_anc_dir : anc_location,
00517 d_anc_file);
00518
00519 return max((name != "") ? last_modified_time(name) : 0,
00520 get_dataset_last_modified_time());
00521 }
00522
00536 time_t
00537 DODSFilter::get_data_last_modified_time(const string &anc_location) const
00538 {
00539 DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00540 << anc_location << "call faf(both) d_dataset=" << d_dataset
00541 << " d_anc_file=" << d_anc_file << endl);
00542
00543 string dds_name
00544 = find_ancillary_file(d_dataset, "dds",
00545 (anc_location == "") ? d_anc_dir : anc_location,
00546 d_anc_file);
00547 string das_name
00548 = find_ancillary_file(d_dataset, "das",
00549 (anc_location == "") ? d_anc_dir : anc_location,
00550 d_anc_file);
00551
00552 time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
00553 (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
00554
00555 time_t n = get_dataset_last_modified_time();
00556
00557 return max(m, n);
00558 }
00559
00567 time_t
00568 DODSFilter::get_request_if_modified_since() const
00569 {
00570 return d_if_modified_since;
00571 }
00572
00579 string
00580 DODSFilter::get_cache_dir() const
00581 {
00582 return d_cache_dir;
00583 }
00584
00589 void
00590 DODSFilter::set_timeout(int t)
00591 {
00592 d_timeout = t;
00593 }
00594
00596 int
00597 DODSFilter::get_timeout() const
00598 {
00599 return d_timeout;
00600 }
00601
00613 void
00614 DODSFilter::establish_timeout(FILE *stream) const
00615 {
00616 #ifndef WIN32
00617 if (d_timeout > 0) {
00618 SignalHandler *sh = SignalHandler::instance();
00619 sh->register_handler(SIGALRM, new AlarmHandler(stream));
00620 alarm(d_timeout);
00621 }
00622 #endif
00623 }
00624
00625
00635 void
00636 DODSFilter::read_ancillary_das(DAS &das, const string &anc_location) const
00637 {
00638 string name = find_ancillary_file(d_dataset, "das",
00639 (anc_location == "") ? d_anc_dir : anc_location,
00640 d_anc_file);
00641
00642 FILE *in = fopen(name.c_str(), "r");
00643 if (in) {
00644 das.parse(in);
00645 int res = fclose(in) ;
00646 if (res) {
00647 DBG(cerr << "DODSFilter::read_ancillary_das - Failed to close file " << (void *)in << endl ;) ;
00648 }
00649 }
00650 }
00651
00661 void
00662 DODSFilter::read_ancillary_dds(DDS &dds, const string &anc_location) const
00663 {
00664 string name = find_ancillary_file(d_dataset, "dds",
00665 (anc_location == "") ? d_anc_dir : anc_location,
00666 d_anc_file);
00667 FILE *in = fopen(name.c_str(), "r");
00668 if (in) {
00669 dds.parse(in);
00670 int res = fclose(in) ;
00671 if (res) {
00672 DBG(cerr << "DODSFilter::read_ancillary_dds - Failed to close " << (void *)in << endl ;) ;
00673 }
00674 }
00675 }
00676
00677 static const char *emessage = "DODS internal server error; usage error. Please report this to the dataset maintainer, or to support@unidata.ucar.edu.";
00678
00688 void
00689 DODSFilter::print_usage() const
00690 {
00691
00692 ErrMsgT(usage.c_str());
00693
00694 throw Error(unknown_error, emessage);
00695 }
00696
00702 void
00703 DODSFilter::send_version_info() const
00704 {
00705 do_version(d_cgi_ver, get_dataset_version());
00706 }
00707
00719 void
00720 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
00721 bool with_mime_headers) const
00722 {
00723 time_t das_lmt = get_das_last_modified_time(anc_location);
00724 if (is_conditional()
00725 && das_lmt <= get_request_if_modified_since()
00726 && with_mime_headers) {
00727 set_mime_not_modified(out);
00728 }
00729 else {
00730 if (with_mime_headers)
00731 set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
00732 das.print(out);
00733 }
00734 fflush(out) ;
00735 }
00736
00737 void
00738 DODSFilter::send_das(DAS &das, const string &anc_location,
00739 bool with_mime_headers) const
00740 {
00741 send_das(stdout, das, anc_location, with_mime_headers);
00742 }
00743
00760 void
00761 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
00762 bool constrained,
00763 const string &anc_location,
00764 bool with_mime_headers) const
00765 {
00766
00767 if (constrained)
00768 eval.parse_constraint(d_ce, dds);
00769
00770 if (eval.functional_expression())
00771 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.");
00772
00773 time_t dds_lmt = get_dds_last_modified_time(anc_location);
00774 if (is_conditional()
00775 && dds_lmt <= get_request_if_modified_since()
00776 && with_mime_headers) {
00777 set_mime_not_modified(out);
00778 }
00779 else {
00780 if (with_mime_headers)
00781 set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
00782 if (constrained)
00783 dds.print_constrained(out);
00784 else
00785 dds.print(out);
00786 }
00787
00788 fflush(out) ;
00789 }
00790
00791 void
00792 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
00793 bool constrained, const string &anc_location,
00794 bool with_mime_headers) const
00795 {
00796 send_dds(stdout, dds, eval, constrained, anc_location, with_mime_headers);
00797 }
00798
00799
00800
00801 void
00802 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
00803 ConstraintEvaluator &eval, FILE *out)
00804 const
00805 {
00806 fprintf(out, "Dataset {\n");
00807 var.print_decl(out, " ", true, false, true);
00808 fprintf(out, "} function_value;\n");
00809 fprintf(out, "Data:\n");
00810
00811 fflush(out);
00812
00813
00814 XDR *xdr_sink = new_xdrstdio(out, XDR_ENCODE);
00815
00816 try {
00817
00818 var.serialize(d_dataset, eval, dds, xdr_sink, false);
00819 delete_xdrstdio(xdr_sink);
00820 }
00821 catch (Error &e) {
00822 delete_xdrstdio(xdr_sink);
00823 throw;
00824 }
00825 }
00826
00827 void
00828 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
00829 FILE * out) const
00830 {
00831
00832 dds.print_constrained(out);
00833 fprintf(out, "Data:\n");
00834 fflush(out);
00835
00836
00837 XDR *xdr_sink = new_xdrstdio(out, XDR_ENCODE);
00838
00839 try {
00840
00841 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
00842 if ((*i)->send_p()) {
00843 DBG(cerr << "Sending " << (*i)->name() << endl);
00844 (*i)->serialize(d_dataset, eval, dds, xdr_sink, true);
00845 }
00846
00847 delete_xdrstdio(xdr_sink);
00848 }
00849 catch (Error & e) {
00850 delete_xdrstdio(xdr_sink);
00851 throw;
00852 }
00853 }
00854
00871 void
00872 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
00873 FILE * data_stream, const string & anc_location,
00874 bool with_mime_headers) const
00875 {
00876
00877
00878
00879 time_t data_lmt = get_data_last_modified_time(anc_location);
00880 if (is_conditional()
00881 && data_lmt <= get_request_if_modified_since()
00882 && with_mime_headers) {
00883 set_mime_not_modified(data_stream);
00884 return;
00885 }
00886
00887 establish_timeout(data_stream);
00888 dds.set_timeout(d_timeout);
00889
00890 eval.parse_constraint(d_ce, dds);
00891
00892
00893 dds.tag_nested_sequences();
00894
00895
00896 #if COMPRESSION_FOR_SERVER3
00897 bool compress = d_comp && deflate_exists();
00898 #endif
00899
00900
00901 if (eval.functional_expression()) {
00902
00903
00904
00905
00906 BaseType *var = eval.eval_function(dds, d_dataset);
00907 if (!var)
00908 throw Error(unknown_error, "Error calling the CE function.");
00909
00910 #if COMPRESSION_FOR_SERVER3
00911 if (with_mime_headers)
00912 set_mime_binary(data_stream, dods_data, d_cgi_ver,
00913 (compress) ? deflate : x_plain, data_lmt);
00914 fflush(data_stream);
00915
00916 int childpid;
00917 if (compress)
00918 data_stream = compressor(data_stream, childpid);
00919 #endif
00920 if (with_mime_headers)
00921 set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
00922
00923 fflush(data_stream);
00924
00925 functional_constraint(*var, dds, eval, data_stream);
00926 delete var;
00927 var = 0;
00928 }
00929 else {
00930 #if COMPRESSION_FOR_SERVER3
00931 if (with_mime_headers)
00932 set_mime_binary(data_stream, dods_data, d_cgi_ver,
00933 (compress) ? deflate : x_plain, data_lmt);
00934 fflush(data_stream);
00935
00936 int childpid;
00937 if (compress)
00938 data_stream = compressor(data_stream, childpid);
00939 #endif
00940 if (with_mime_headers)
00941 set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
00942
00943 dataset_constraint(dds, eval, data_stream);
00944 }
00945
00946 fflush(data_stream);
00947 }
00948
00959 void
00960 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
00961 bool with_mime_headers) const
00962 {
00963
00964 if (!d_ce.empty())
00965 eval.parse_constraint(d_ce, dds);
00966
00967 time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
00968
00969
00970
00971
00972 if (is_conditional() && dds_lmt <= get_request_if_modified_since()
00973 && with_mime_headers) {
00974 set_mime_not_modified(out);
00975 return;
00976 }
00977 else {
00978 if (with_mime_headers)
00979 set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
00980 dds.print_xml(out, !d_ce.empty(), d_url + ".blob?" + d_ce);
00981 }
00982 }
00983
00988 void
00989 DODSFilter::send_blob(DDS &, FILE *, bool)
00990 {
00991 #if 0
00992
00993 bool compress = d_comp && deflate_exists();
00994 time_t data_lmt = get_data_last_modified_time(d_anc_dir);
00995
00996
00997
00998
00999 if (is_conditional() && data_lmt <= get_request_if_modified_since()
01000 && with_mime_headers) {
01001 set_mime_not_modified(out);
01002 return;
01003 }
01004
01005 dds.parse_constraint(d_ce);
01006
01007
01008 if (dds.functional_expression()) {
01009 BaseType *var = dds.eval_function(d_dataset);
01010 if (!var)
01011 throw Error("Error calling the CE function.");
01012
01013 if (with_mime_headers)
01014 set_mime_binary(out, dods_data, d_cgi_ver,
01015 (compress) ? deflate : x_plain, data_lmt);
01016
01017 FILE *comp_sink;
01018 XDR *xdr_sink;
01019 int childpid = get_sinks(out, compress, &comp_sink, &xdr_sink);
01020
01021
01022 if (!var->serialize(d_dataset, dds, xdr_sink, false))
01023 throw Error("Could not send the function result.");
01024
01025 clean_sinks(childpid, compress, xdr_sink, comp_sink);
01026 }
01027 else {
01028 if (with_mime_headers)
01029 set_mime_binary(out, dods_data, d_cgi_ver,
01030 (compress) ? deflate : x_plain, data_lmt);
01031
01032 FILE *comp_sink;
01033 XDR *xdr_sink;
01034 int childpid = get_sinks(out, compress, &comp_sink, &xdr_sink);
01035
01036 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
01037 if ((*i)->send_p())
01038 if (!(*i)->serialize(d_dataset, dds, xdr_sink, true))
01039 throw Error(string("Could not serialize variable '")
01040 + (*i)->name() + string("'."));
01041
01042 clean_sinks(childpid, compress, xdr_sink, comp_sink);
01043 }
01044 #endif
01045 }
01046