libdap  Updated for version 3.17.0
DODSFilter.cc
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 //
00010 // This library is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU Lesser General Public
00012 // License as published by the Free Software Foundation; either
00013 // version 2.1 of the License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00023 //
00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00025 
00026 // (c) COPYRIGHT URI/MIT 1997-1999
00027 // Please read the full copyright statement in the file COPYRIGHT_URI.
00028 //
00029 // Authors:
00030 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00031 
00032 // Implementation of the DODSFilter class. This class is used to build dods
00033 // filter programs which, along with a CGI program, comprise OPeNDAP servers.
00034 // jhrg 8/26/97
00035 
00036 
00037 #include "config.h"
00038 
00039 #include <signal.h>
00040 
00041 #ifndef WIN32
00042 #include <unistd.h>   // for getopt
00043 #include <sys/wait.h>
00044 #else
00045 #include <io.h>
00046 #include <fcntl.h>
00047 #include <process.h>
00048 #endif
00049 
00050 #include <iostream>
00051 #include <sstream>
00052 #include <string>
00053 #include <algorithm>
00054 #include <cstdlib>
00055 #include <cstring>
00056 
00057 #ifdef HAVE_UUID_UUID_H
00058 #include <uuid/uuid.h>  // used to build CID header value for data ddx
00059 #elif defined(HAVE_UUID_H)
00060 #include <uuid.h>
00061 #else
00062 #error "Could not find UUID library header"
00063 #endif
00064 
00065 #include <GetOpt.h>
00066 
00067 #include "DAS.h"
00068 #include "DDS.h"
00069 #include "debug.h"
00070 #include "mime_util.h"
00071 #include "Ancillary.h"
00072 #include "util.h"
00073 #include "escaping.h"
00074 #include "DODSFilter.h"
00075 #include "XDRStreamMarshaller.h"
00076 #include "InternalErr.h"
00077 
00078 #ifndef WIN32
00079 #include "SignalHandler.h"
00080 #include "EventHandler.h"
00081 #include "AlarmHandler.h"
00082 #endif
00083 
00084 #define CRLF "\r\n"             // Change here, expr-test.cc and DODSFilter.cc
00085 
00086 using namespace std;
00087 
00088 namespace libdap {
00089 
00090 const string usage =
00091     "Usage: <handler name> -o <response> -u <url> [options ...] [data set]\n\
00092     \n\
00093     options: -o <response>: DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\
00094     -u <url>: The complete URL minus the CE (required for DDX)\n\
00095     -c: Compress the response using the deflate algorithm.\n\
00096     -e <expr>: When returning a DataDDS, use <expr> as the constraint.\n\
00097     -v <version>: Use <version> as the version number\n\
00098     -d <dir>: Look for ancillary file in <dir> (deprecated).\n\
00099     -f <file>: Look for ancillary data in <file> (deprecated).\n\
00100     -r <dir>: Use <dir> as a cache directory\n\
00101     -l <time>: Conditional request; if data source is unchanged since\n\
00102     <time>, return an HTTP 304 response.\n\
00103     -t <seconds>: Timeout the handler after <seconds>.\n\
00104     -h: This message.";
00105 
00170 DODSFilter::DODSFilter(int argc, char *argv[]) throw(Error)
00171 {
00172     initialize(argc, argv);
00173 
00174     DBG(cerr << "d_comp: " << d_comp << endl);
00175     DBG(cerr << "d_dap2ce: " << d_dap2ce << endl);
00176     DBG(cerr << "d_cgi_ver: " << d_cgi_ver << endl);
00177     DBG(cerr << "d_response: " << d_response << endl);
00178     DBG(cerr << "d_anc_dir: " << d_anc_dir << endl);
00179     DBG(cerr << "d_anc_file: " << d_anc_file << endl);
00180     DBG(cerr << "d_cache_dir: " << d_cache_dir << endl);
00181     DBG(cerr << "d_conditional_request: " << d_conditional_request << endl);
00182     DBG(cerr << "d_if_modified_since: " << d_if_modified_since << endl);
00183     DBG(cerr << "d_url: " << d_url << endl);
00184     DBG(cerr << "d_timeout: " << d_timeout << endl);
00185 }
00186 
00187 DODSFilter::~DODSFilter()
00188 {
00189 }
00190 
00193 void
00194 DODSFilter::initialize()
00195 {
00196     // Set default values. Don't use the C++ constructor initialization so
00197     // that a subclass can have more control over this process.
00198     d_comp = false;
00199     d_bad_options = false;
00200     d_conditional_request = false;
00201     d_dataset = "";
00202     d_dap2ce = "";
00203     d_cgi_ver = "";
00204     d_anc_dir = "";
00205     d_anc_file = "";
00206     d_cache_dir = "";
00207     d_response = Unknown_Response;;
00208     d_anc_das_lmt = 0;
00209     d_anc_dds_lmt = 0;
00210     d_if_modified_since = -1;
00211     d_url = "";
00212     d_program_name = "Unknown";
00213     d_timeout = 0;
00214 
00215 #ifdef WIN32
00216     //  We want serving from win32 to behave in a manner
00217     //  similar to the UNIX way - no CR->NL terminated lines
00218     //  in files. Hence stdout goes to binary mode.
00219     _setmode(_fileno(stdout), _O_BINARY);
00220 #endif
00221 }
00222 
00234 void
00235 DODSFilter::initialize(int argc, char *argv[])
00236 {
00237     initialize();
00238 
00239     d_program_name = argv[0];
00240 
00241     // This should be specialized by a subclass. This may throw Error.
00242     int next_arg = process_options(argc, argv);
00243 
00244     // Look at what's left after processing the command line options. Either
00245     // there MUST be a dataset name OR the caller is asking for version
00246     // information. If neither is true, then the options are bad.
00247     if (next_arg < argc) {
00248         d_dataset = argv[next_arg];
00249         d_dataset = www2id(d_dataset, "%", "%20");
00250     }
00251     else if (get_response() != Version_Response)
00252         print_usage();   // Throws Error
00253 }
00254 
00263 int
00264 DODSFilter::process_options(int argc, char *argv[])
00265 {
00266     DBG(cerr << "Entering process_options... ");
00267 
00268     int option_char;
00269     GetOpt getopt (argc, argv, "ce: v: d: f: r: l: o: u: t: ");
00270 
00271     while ((option_char = getopt()) != -1) {
00272         switch (option_char) {
00273         case 'c': d_comp = true; break;
00274         case 'e': set_ce(getopt.optarg); break;
00275         case 'v': set_cgi_version(getopt.optarg); break;
00276         case 'd': d_anc_dir = getopt.optarg; break;
00277         case 'f': d_anc_file = getopt.optarg; break;
00278         case 'r': d_cache_dir = getopt.optarg; break;
00279         case 'o': set_response(getopt.optarg); break;
00280         case 'u': set_URL(getopt.optarg); break;
00281         case 't': d_timeout = atoi(getopt.optarg); break;
00282         case 'l':
00283             d_conditional_request = true;
00284             d_if_modified_since
00285             = static_cast<time_t>(strtol(getopt.optarg, NULL, 10));
00286             break;
00287         case 'h': print_usage();
00288             break;
00289                                  // exit(1);
00290                                  // Removed 12/29/2011; exit should
00291                                  // not be called by a library. NB:
00292                                  // print_usage() throws Error.
00293         default: print_usage();  // Throws Error
00294             break;
00295         }
00296     }
00297 
00298     DBGN(cerr << "exiting." << endl);
00299 
00300     return getopt.optind; // return the index of the next argument
00301 }
00302 
00307 bool
00308 DODSFilter::is_conditional() const
00309 {
00310     return d_conditional_request;
00311 }
00312 
00326 void
00327 DODSFilter::set_cgi_version(string version)
00328 {
00329     d_cgi_ver = version;
00330 }
00331 
00337 string
00338 DODSFilter::get_cgi_version() const
00339 {
00340     return d_cgi_ver;
00341 }
00342 
00349 string
00350 DODSFilter::get_ce() const
00351 {
00352     return d_dap2ce;
00353 }
00354 
00355 void
00356 DODSFilter::set_ce(string _ce)
00357 {
00358     d_dap2ce = www2id(_ce, "%", "%20");
00359 }
00360 
00369 string
00370 DODSFilter::get_dataset_name() const
00371 {
00372     return d_dataset;
00373 }
00374 
00375 void
00376 DODSFilter::set_dataset_name(const string ds)
00377 {
00378     d_dataset = www2id(ds, "%", "%20");
00379 }
00380 
00384 string
00385 DODSFilter::get_URL() const
00386 {
00387     return d_url;
00388 }
00389 
00392 void
00393 DODSFilter::set_URL(const string &url)
00394 {
00395     if (url.find('?') != url.npos)
00396         print_usage();  // Throws Error
00397 
00398     d_url = url;
00399 }
00400 
00408 string
00409 DODSFilter::get_dataset_version() const
00410 {
00411     return "";
00412 }
00413 
00420 void DODSFilter::set_response(const string &r)
00421 {
00422     if (r == "DAS" || r == "das") {
00423         d_response = DAS_Response;
00424         d_action = "das" ;
00425     }
00426     else if (r == "DDS" || r == "dds") {
00427         d_response = DDS_Response;
00428         d_action = "dds" ;
00429     }
00430     else if (r == "DataDDS" || r == "dods") {
00431         d_response = DataDDS_Response;
00432         d_action = "dods" ;
00433     }
00434     else if (r == "DDX" || r == "ddx") {
00435         d_response = DDX_Response;
00436         d_action = "ddx" ;
00437     }
00438     else if (r == "DataDDX" || r == "dataddx") {
00439         d_response = DataDDX_Response;
00440         d_action = "dataddx" ;
00441     }
00442     else if (r == "Version") {
00443         d_response = Version_Response;
00444         d_action = "version" ;
00445     }
00446     else
00447         print_usage();   // Throws Error
00448 }
00449 
00451 DODSFilter::Response
00452 DODSFilter::get_response() const
00453 {
00454     return d_response;
00455 }
00456 
00458 string DODSFilter::get_action() const
00459 {
00460     return d_action;
00461 }
00462 
00483 time_t
00484 DODSFilter::get_dataset_last_modified_time() const
00485 {
00486     return last_modified_time(d_dataset);
00487 }
00488 
00498 time_t
00499 DODSFilter::get_das_last_modified_time(const string &anc_location) const
00500 {
00501     DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00502         << anc_location << "call faf(das) d_dataset=" << d_dataset
00503         << " d_anc_file=" << d_anc_file << endl);
00504 
00505     string name
00506     = Ancillary::find_ancillary_file(d_dataset, "das",
00507                           (anc_location == "") ? d_anc_dir : anc_location,
00508                           d_anc_file);
00509 
00510     return max((name != "") ? last_modified_time(name) : 0,
00511                get_dataset_last_modified_time());
00512 }
00513 
00521 time_t
00522 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
00523 {
00524     DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00525         << anc_location << "call faf(dds) d_dataset=" << d_dataset
00526         << " d_anc_file=" << d_anc_file << endl);
00527 
00528     string name
00529     = Ancillary::find_ancillary_file(d_dataset, "dds",
00530                           (anc_location == "") ? d_anc_dir : anc_location,
00531                           d_anc_file);
00532 
00533     return max((name != "") ? last_modified_time(name) : 0,
00534                get_dataset_last_modified_time());
00535 }
00536 
00550 time_t
00551 DODSFilter::get_data_last_modified_time(const string &anc_location) const
00552 {
00553     DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
00554         << anc_location << "call faf(both) d_dataset=" << d_dataset
00555         << " d_anc_file=" << d_anc_file << endl);
00556 
00557     string dds_name
00558     = Ancillary::find_ancillary_file(d_dataset, "dds",
00559                           (anc_location == "") ? d_anc_dir : anc_location,
00560                           d_anc_file);
00561     string das_name
00562     = Ancillary::find_ancillary_file(d_dataset, "das",
00563                           (anc_location == "") ? d_anc_dir : anc_location,
00564                           d_anc_file);
00565 
00566     time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
00567                    (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
00568     // Note that this is a call to get_dataset_... not get_data_...
00569     time_t n = get_dataset_last_modified_time();
00570 
00571     return max(m, n);
00572 }
00573 
00581 time_t
00582 DODSFilter::get_request_if_modified_since() const
00583 {
00584     return d_if_modified_since;
00585 }
00586 
00593 string
00594 DODSFilter::get_cache_dir() const
00595 {
00596     return d_cache_dir;
00597 }
00598 
00603 void
00604 DODSFilter::set_timeout(int t)
00605 {
00606     d_timeout = t;
00607 }
00608 
00610 int
00611 DODSFilter::get_timeout() const
00612 {
00613     return d_timeout;
00614 }
00615 
00627 void
00628 DODSFilter::establish_timeout(FILE *stream) const
00629 {
00630 #ifndef WIN32
00631     if (d_timeout > 0) {
00632         SignalHandler *sh = SignalHandler::instance();
00633         EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
00634         delete old_eh;
00635         alarm(d_timeout);
00636     }
00637 #endif
00638 }
00639 
00640 void
00641 DODSFilter::establish_timeout(ostream &stream) const
00642 {
00643 #ifndef WIN32
00644     if (d_timeout > 0) {
00645         SignalHandler *sh = SignalHandler::instance();
00646         EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
00647         delete old_eh;
00648         alarm(d_timeout);
00649     }
00650 #endif
00651 }
00652 
00653 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.";
00654 
00664 void
00665 DODSFilter::print_usage() const
00666 {
00667     // Write a message to the WWW server error log file.
00668     ErrMsgT(usage.c_str());
00669 
00670     throw Error(emessage);
00671 }
00672 
00678 void
00679 DODSFilter::send_version_info() const
00680 {
00681     do_version(d_cgi_ver, get_dataset_version());
00682 }
00683 
00695 void
00696 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
00697                      bool with_mime_headers) const
00698 {
00699     ostringstream oss;
00700     send_das(oss, das, anc_location, with_mime_headers);
00701     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
00702 }
00703 
00715 void
00716 DODSFilter::send_das(ostream &out, DAS &das, const string &anc_location,
00717                      bool with_mime_headers) const
00718 {
00719     time_t das_lmt = get_das_last_modified_time(anc_location);
00720     if (is_conditional()
00721         && das_lmt <= get_request_if_modified_since()
00722         && with_mime_headers) {
00723         set_mime_not_modified(out);
00724     }
00725     else {
00726         if (with_mime_headers)
00727             set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
00728         das.print(out);
00729     }
00730     out << flush ;
00731 }
00732 
00733 void
00734 DODSFilter::send_das(DAS &das, const string &anc_location,
00735                      bool with_mime_headers) const
00736 {
00737     send_das(cout, das, anc_location, with_mime_headers);
00738 }
00739 
00756 void
00757 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
00758                      bool constrained,
00759                      const string &anc_location,
00760                      bool with_mime_headers) const
00761 {
00762     ostringstream oss;
00763     send_dds(oss, dds, eval, constrained, anc_location, with_mime_headers);
00764     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
00765 }
00766 
00783 void
00784 DODSFilter::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval,
00785                      bool constrained,
00786                      const string &anc_location,
00787                      bool with_mime_headers) const
00788 {
00789     // If constrained, parse the constraint. Throws Error or InternalErr.
00790     if (constrained)
00791         eval.parse_constraint(d_dap2ce, dds);
00792 
00793     if (eval.functional_expression())
00794         throw Error("Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
00795 
00796     time_t dds_lmt = get_dds_last_modified_time(anc_location);
00797     if (is_conditional()
00798         && dds_lmt <= get_request_if_modified_since()
00799         && with_mime_headers) {
00800         set_mime_not_modified(out);
00801     }
00802     else {
00803         if (with_mime_headers)
00804             set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
00805         if (constrained)
00806             dds.print_constrained(out);
00807         else
00808             dds.print(out);
00809     }
00810 
00811     out << flush ;
00812 }
00813 
00814 void
00815 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
00816                      bool constrained, const string &anc_location,
00817                      bool with_mime_headers) const
00818 {
00819     send_dds(cout, dds, eval, constrained, anc_location, with_mime_headers);
00820 }
00821 
00822 // 'lmt' unused. Should it be used to supply a LMT or removed from the
00823 // method? jhrg 8/9/05
00824 void
00825 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
00826                                   ConstraintEvaluator &eval, FILE *out) const
00827 {
00828     ostringstream oss;
00829     functional_constraint(var, dds, eval, oss);
00830     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
00831 }
00832 
00833 // 'lmt' unused. Should it be used to supply a LMT or removed from the
00834 // method? jhrg 8/9/05
00835 void
00836 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
00837                                   ConstraintEvaluator &eval, ostream &out) const
00838 {
00839     out << "Dataset {\n" ;
00840     var.print_decl(out, "    ", true, false, true);
00841     out << "} function_value;\n" ;
00842     out << "Data:\n" ;
00843 
00844     out << flush ;
00845 
00846     // Grab a stream that encodes using XDR.
00847     XDRStreamMarshaller m( out ) ;
00848 
00849     try {
00850         // In the following call to serialize, suppress CE evaluation.
00851         var.serialize(eval, dds, m, false);
00852     }
00853     catch (Error &e) {
00854         throw;
00855     }
00856 }
00857 
00858 void
00859 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
00860                                FILE * out, bool ce_eval) const
00861 {
00862     ostringstream oss;
00863     dataset_constraint(dds, eval, oss, ce_eval);
00864     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
00865 }
00866 
00867 void
00868 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
00869                                ostream &out, bool ce_eval) const
00870 {
00871     // send constrained DDS
00872     dds.print_constrained(out);
00873     out << "Data:\n" ;
00874     out << flush ;
00875 
00876     // Grab a stream that encodes using XDR.
00877     XDRStreamMarshaller m( out ) ;
00878 
00879     try {
00880         // Send all variables in the current projection (send_p())
00881         for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
00882             if ((*i)->send_p()) {
00883                 DBG(cerr << "Sending " << (*i)->name() << endl);
00884                 (*i)->serialize(eval, dds, m, ce_eval);
00885             }
00886     }
00887     catch (Error & e) {
00888         throw;
00889     }
00890 }
00891 
00892 void
00893 DODSFilter::dataset_constraint_ddx(DDS & dds, ConstraintEvaluator & eval,
00894                                ostream &out, const string &boundary,
00895                                const string &start, bool ce_eval) const
00896 {
00897     // Write the MPM headers for the DDX (text/xml) part of the response
00898     set_mime_ddx_boundary(out, boundary, start, dods_ddx);
00899 
00900     // Make cid
00901     uuid_t uu;
00902     uuid_generate(uu);
00903     char uuid[37];
00904     uuid_unparse(uu, &uuid[0]);
00905     char domain[256];
00906     if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
00907         strncpy(domain, "opendap.org", 255);
00908 
00909     string cid = string(&uuid[0]) + "@" + string(&domain[0]);
00910 
00911     // Send constrained DDX with a data blob reference
00912     dds.print_xml_writer(out, true, cid);
00913 
00914     // Write the MPM headers for the data part of the response.
00915     set_mime_data_boundary(out, boundary, cid, dap4_data, binary);
00916 
00917     // Grab a stream that encodes using XDR.
00918     XDRStreamMarshaller m( out ) ;
00919 
00920     try {
00921         // Send all variables in the current projection (send_p())
00922         for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
00923             if ((*i)->send_p()) {
00924                 DBG(cerr << "Sending " << (*i)->name() << endl);
00925                 (*i)->serialize(eval, dds, m, ce_eval);
00926             }
00927     }
00928     catch (Error & e) {
00929         throw;
00930     }
00931 }
00932 
00949 void
00950 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
00951                       FILE * data_stream, const string & anc_location,
00952                       bool with_mime_headers) const
00953 {
00954     ostringstream oss;
00955     send_data(dds, eval, oss, anc_location, with_mime_headers);
00956     fwrite(oss.str().data(), sizeof(char), oss.str().length(), data_stream);
00957 }
00958 
00975 void
00976 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
00977                       ostream & data_stream, const string & anc_location,
00978                       bool with_mime_headers) const
00979 {
00980     // If this is a conditional request and the server should send a 304
00981     // response, do that and exit. Otherwise, continue on and send the full
00982     // response.
00983     time_t data_lmt = get_data_last_modified_time(anc_location);
00984     if (is_conditional()
00985         && data_lmt <= get_request_if_modified_since()
00986         && with_mime_headers) {
00987         set_mime_not_modified(data_stream);
00988         return;
00989     }
00990     // Set up the alarm.
00991     establish_timeout(data_stream);
00992     dds.set_timeout(d_timeout);
00993 
00994     eval.parse_constraint(d_dap2ce, dds);   // Throws Error if the ce doesn't
00995                                         // parse.
00996 
00997     dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
00998 
00999     // Start sending the response...
01000 
01001     // Handle *functional* constraint expressions specially
01002 #if 0
01003     if (eval.functional_expression()) {
01004         // Get the result and then start sending the headers. This provides a
01005         // way to send errors back to the client w/o colliding with the
01006         // normal response headers. There's some duplication of code with this
01007         // and the else-clause.
01008         BaseType *var = eval.eval_function(dds, d_dataset);
01009         if (!var)
01010             throw Error(unknown_error, "Error calling the CE function.");
01011 
01012        if (with_mime_headers)
01013             set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
01014 
01015         data_stream << flush ;
01016 
01017         functional_constraint(*var, dds, eval, data_stream);
01018         delete var;
01019         var = 0;
01020     }
01021 #endif
01022     if (eval.function_clauses()) {
01023         DDS *fdds = eval.eval_function_clauses(dds);
01024         if (with_mime_headers)
01025             set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
01026 
01027         dataset_constraint(*fdds, eval, data_stream, false);
01028         delete fdds;
01029     }
01030     else {
01031         if (with_mime_headers)
01032             set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
01033 
01034         dataset_constraint(dds, eval, data_stream);
01035     }
01036 
01037     data_stream << flush ;
01038 }
01039 
01050 void
01051 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
01052                      bool with_mime_headers) const
01053 {
01054     ostringstream oss;
01055     send_ddx(dds, eval, oss, with_mime_headers);
01056     fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
01057 }
01058 
01069 void
01070 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, ostream &out,
01071                      bool with_mime_headers) const
01072 {
01073     // If constrained, parse the constraint. Throws Error or InternalErr.
01074     if (!d_dap2ce.empty())
01075         eval.parse_constraint(d_dap2ce, dds);
01076 
01077     if (eval.functional_expression())
01078         throw Error("Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
01079 
01080     time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
01081 
01082     // If this is a conditional request and the server should send a 304
01083     // response, do that and exit. Otherwise, continue on and send the full
01084     // response.
01085     if (is_conditional() && dds_lmt <= get_request_if_modified_since()
01086         && with_mime_headers) {
01087         set_mime_not_modified(out);
01088         return;
01089     }
01090     else {
01091         if (with_mime_headers)
01092             set_mime_text(out, dods_ddx, d_cgi_ver, x_plain, dds_lmt);
01093         dds.print_xml_writer(out, !d_dap2ce.empty(), "");
01094     }
01095 }
01096 
01117 void
01118 DODSFilter::send_data_ddx(DDS & dds, ConstraintEvaluator & eval,
01119                       ostream & data_stream, const string &start,
01120                       const string &boundary, const string & anc_location,
01121                       bool with_mime_headers) const
01122 {
01123     // If this is a conditional request and the server should send a 304
01124     // response, do that and exit. Otherwise, continue on and send the full
01125     // response.
01126     time_t data_lmt = get_data_last_modified_time(anc_location);
01127     if (is_conditional()
01128         && data_lmt <= get_request_if_modified_since()
01129         && with_mime_headers) {
01130         set_mime_not_modified(data_stream);
01131         return;
01132     }
01133     // Set up the alarm.
01134     establish_timeout(data_stream);
01135     dds.set_timeout(d_timeout);
01136 
01137     eval.parse_constraint(d_dap2ce, dds);   // Throws Error if the ce doesn't
01138                                         // parse.
01139 
01140     dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
01141 
01142     // Start sending the response...
01143 
01144     // Handle *functional* constraint expressions specially
01145 #if 0
01146     if (eval.functional_expression()) {
01147         BaseType *var = eval.eval_function(dds, d_dataset);
01148         if (!var)
01149             throw Error(unknown_error, "Error calling the CE function.");
01150 
01151         if (with_mime_headers)
01152             set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
01153                 d_cgi_ver, x_plain, data_lmt);
01154         data_stream << flush ;
01155         BaseTypeFactory btf;
01156         DDS var_dds(&btf, var->name());
01157         var->set_send_p(true);
01158         var_dds.add_var(var);
01159         serialize_dap2_data_ddx(var_dds, eval, data_stream, boundary, start);
01160 
01161         // functional_constraint_ddx(*var, dds, eval, data_stream, boundary);
01162         delete var;
01163         var = 0;
01164     }
01165 #endif
01166     if (eval.function_clauses()) {
01167         DDS *fdds = eval.eval_function_clauses(dds);
01168         if (with_mime_headers)
01169             set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
01170                     d_cgi_ver, x_plain, data_lmt);
01171         data_stream << flush ;
01172         dataset_constraint(*fdds, eval, data_stream, false);
01173         delete fdds;
01174     }
01175     else {
01176         if (with_mime_headers)
01177             set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
01178                     d_cgi_ver, x_plain, data_lmt);
01179         data_stream << flush ;
01180         dataset_constraint_ddx(dds, eval, data_stream, boundary, start);
01181     }
01182 
01183     data_stream << flush ;
01184 
01185     if (with_mime_headers)
01186         data_stream << CRLF << "--" << boundary << "--" << CRLF;
01187 }
01188 
01189 } // namespace libdap
01190