Blame DODSFilter.cc

Packit a4aae4
Packit a4aae4
// -*- mode: c++; c-basic-offset:4 -*-
Packit a4aae4
Packit a4aae4
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
Packit a4aae4
// Access Protocol.
Packit a4aae4
Packit a4aae4
// Copyright (c) 2002,2003 OPeNDAP, Inc.
Packit a4aae4
// Author: James Gallagher <jgallagher@opendap.org>
Packit a4aae4
//
Packit a4aae4
// This library is free software; you can redistribute it and/or
Packit a4aae4
// modify it under the terms of the GNU Lesser General Public
Packit a4aae4
// License as published by the Free Software Foundation; either
Packit a4aae4
// version 2.1 of the License, or (at your option) any later version.
Packit a4aae4
//
Packit a4aae4
// This library is distributed in the hope that it will be useful,
Packit a4aae4
// but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4aae4
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4aae4
// Lesser General Public License for more details.
Packit a4aae4
//
Packit a4aae4
// You should have received a copy of the GNU Lesser General Public
Packit a4aae4
// License along with this library; if not, write to the Free Software
Packit a4aae4
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a4aae4
//
Packit a4aae4
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
Packit a4aae4
Packit a4aae4
// (c) COPYRIGHT URI/MIT 1997-1999
Packit a4aae4
// Please read the full copyright statement in the file COPYRIGHT_URI.
Packit a4aae4
//
Packit a4aae4
// Authors:
Packit a4aae4
//      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
Packit a4aae4
Packit a4aae4
// Implementation of the DODSFilter class. This class is used to build dods
Packit a4aae4
// filter programs which, along with a CGI program, comprise OPeNDAP servers.
Packit a4aae4
// jhrg 8/26/97
Packit a4aae4
Packit a4aae4
Packit a4aae4
#include "config.h"
Packit a4aae4
Packit a4aae4
#include <signal.h>
Packit a4aae4
Packit a4aae4
#ifndef WIN32
Packit a4aae4
#include <unistd.h>   // for getopt
Packit a4aae4
#include <sys/wait.h>
Packit a4aae4
#else
Packit a4aae4
#include <io.h>
Packit a4aae4
#include <fcntl.h>
Packit a4aae4
#include <process.h>
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include <iostream>
Packit a4aae4
#include <sstream>
Packit a4aae4
#include <string>
Packit a4aae4
#include <algorithm>
Packit a4aae4
#include <cstdlib>
Packit a4aae4
#include <cstring>
Packit a4aae4
Packit a4aae4
#ifdef HAVE_UUID_UUID_H
Packit a4aae4
#include <uuid/uuid.h>	// used to build CID header value for data ddx
Packit a4aae4
#elif defined(HAVE_UUID_H)
Packit a4aae4
#include <uuid.h>
Packit a4aae4
#else
Packit a4aae4
#error "Could not find UUID library header"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include <GetOpt.h>
Packit a4aae4
Packit a4aae4
#include "DAS.h"
Packit a4aae4
#include "DDS.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
#include "mime_util.h"
Packit a4aae4
#include "Ancillary.h"
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "escaping.h"
Packit a4aae4
#include "DODSFilter.h"
Packit a4aae4
#include "XDRStreamMarshaller.h"
Packit a4aae4
#include "InternalErr.h"
Packit a4aae4
Packit a4aae4
#ifndef WIN32
Packit a4aae4
#include "SignalHandler.h"
Packit a4aae4
#include "EventHandler.h"
Packit a4aae4
#include "AlarmHandler.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#define CRLF "\r\n"             // Change here, expr-test.cc and DODSFilter.cc
Packit a4aae4
Packit a4aae4
using namespace std;
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
const string usage =
Packit a4aae4
    "Usage: <handler name> -o <response> -u <url> [options ...] [data set]\n\
Packit a4aae4
    \n\
Packit a4aae4
    options: -o <response>: DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\
Packit a4aae4
    -u <url>: The complete URL minus the CE (required for DDX)\n\
Packit a4aae4
    -c: Compress the response using the deflate algorithm.\n\
Packit a4aae4
    -e <expr>: When returning a DataDDS, use <expr> as the constraint.\n\
Packit a4aae4
    -v <version>: Use <version> as the version number\n\
Packit a4aae4
    -d <dir>: Look for ancillary file in <dir> (deprecated).\n\
Packit a4aae4
    -f <file>: Look for ancillary data in <file> (deprecated).\n\
Packit a4aae4
    -r <dir>: Use <dir> as a cache directory\n\
Packit a4aae4
    -l <time>: Conditional request; if data source is unchanged since\n\
Packit a4aae4
    <time>, return an HTTP 304 response.\n\
Packit a4aae4
    -t <seconds>: Timeout the handler after <seconds>.\n\
Packit a4aae4
    -h: This message.";
Packit a4aae4
Packit a4aae4
/** Create an instance of DODSFilter using the command line
Packit a4aae4
arguments passed by the CGI (or other) program.  The default
Packit a4aae4
constructor is private; this and the copy constructor (which is
Packit a4aae4
just the default copy constructor) are the only way to create an
Packit a4aae4
instance of DODSFilter.
Packit a4aae4
Packit a4aae4
These are the valid options:
Packit a4aae4
Packit a4aae4
Packit a4aae4
filename
Packit a4aae4
The name of the file on which the filter is to operate.  Usually
Packit a4aae4
this would be the file whose data has been requested. In fact, this class
Packit a4aae4
can be specialized and any meaning can be associated to this
Packit a4aae4
string. It could be the name of a database, for example.
Packit a4aae4
Packit a4aae4
<tt>-o</tt> response
Packit a4aae4
Packit a4aae4
Specifies the type of response desired. The \e response is a string
Packit a4aae4
and must be one of \c DAS, \c DDS, \c DataDDS or \c Version. Note
Packit a4aae4
that \c Version returns version information in the body of the response
Packit a4aae4
and is useful for debugging, et cetera. Each response returns version
Packit a4aae4
information in an HTTP header for internal use by a client.
Packit a4aae4
Packit a4aae4
<tt>-c</tt>
Packit a4aae4
Send compressed data. Data are compressed using the deflate program.
Packit a4aae4
Packit a4aae4
<tt>-e</tt> expression
Packit a4aae4
This option specifies a non-blank constraint expression used to
Packit a4aae4
subsample a dataset.
Packit a4aae4
Packit a4aae4
<tt>-v</tt> cgi-version
Set the CGI/Server version to
Packit a4aae4
<tt>cgi-version</tt>. This is a way for the caller to set version
Packit a4aae4
information passed back to the client either as the response to a
Packit a4aae4
version request of in the response headers.
Packit a4aae4
Packit a4aae4
<tt>-d</tt> ancdir
Packit a4aae4
Specifies that ancillary data be sought in the ancdir
Packit a4aae4
directory. ancdir must end in '/'.
Packit a4aae4
Packit a4aae4
<tt>-f</tt> ancfile
Packit a4aae4
Specifies that ancillary data may be found in a file called
Packit a4aae4
ancfile.
Packit a4aae4
Packit a4aae4
<tt>-r</tt> cache directory
Packit a4aae4
Specify a directory to use if/when files are to be cached. Not all
Packit a4aae4
handlers support caching and each uses its own rules tailored to a
Packit a4aae4
specific file or data type.
Packit a4aae4
Packit a4aae4
<tt>-t</tt> timeout
Specifies a a timeout value in
Packit a4aae4
seconds. If the server runs longer than \e timeout seconds, an Error is
Packit a4aae4
returned to the client explaining that the request has timed out.
Packit a4aae4
Packit a4aae4
<tt>-l</tt> time
Indicates that the request is a
Packit a4aae4
conditional request; send a complete response if and only if the data has
Packit a4aae4
changed since time. If it has not changed since time, then
Packit a4aae4
send a 304 (Not Modified) response. The time parameter is the
Packit a4aae4
<tt>Last-Modified</tt> time from an If-Modified-Since condition GET
Packit a4aae4
request. It is given in seconds since the start of the Unix epoch
Packit a4aae4
(Midnight, 1 Jan 1970).
Packit a4aae4
Packit a4aae4
Packit a4aae4
Packit a4aae4
@brief DODSFilter constructor. */
Packit a4aae4
Packit a4aae4
DODSFilter::DODSFilter(int argc, char *argv[]) throw(Error)
Packit a4aae4
{
Packit a4aae4
    initialize(argc, argv);
Packit a4aae4
Packit a4aae4
    DBG(cerr << "d_comp: " << d_comp << endl);
Packit a4aae4
    DBG(cerr << "d_dap2ce: " << d_dap2ce << endl);
Packit a4aae4
    DBG(cerr << "d_cgi_ver: " << d_cgi_ver << endl);
Packit a4aae4
    DBG(cerr << "d_response: " << d_response << endl);
Packit a4aae4
    DBG(cerr << "d_anc_dir: " << d_anc_dir << endl);
Packit a4aae4
    DBG(cerr << "d_anc_file: " << d_anc_file << endl);
Packit a4aae4
    DBG(cerr << "d_cache_dir: " << d_cache_dir << endl);
Packit a4aae4
    DBG(cerr << "d_conditional_request: " << d_conditional_request << endl);
Packit a4aae4
    DBG(cerr << "d_if_modified_since: " << d_if_modified_since << endl);
Packit a4aae4
    DBG(cerr << "d_url: " << d_url << endl);
Packit a4aae4
    DBG(cerr << "d_timeout: " << d_timeout << endl);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
DODSFilter::~DODSFilter()
Packit a4aae4
{
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Called when initializing a DODSFilter that's not going to be passed a
Packit a4aae4
command line arguments. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::initialize()
Packit a4aae4
{
Packit a4aae4
    // Set default values. Don't use the C++ constructor initialization so
Packit a4aae4
    // that a subclass can have more control over this process.
Packit a4aae4
    d_comp = false;
Packit a4aae4
    d_bad_options = false;
Packit a4aae4
    d_conditional_request = false;
Packit a4aae4
    d_dataset = "";
Packit a4aae4
    d_dap2ce = "";
Packit a4aae4
    d_cgi_ver = "";
Packit a4aae4
    d_anc_dir = "";
Packit a4aae4
    d_anc_file = "";
Packit a4aae4
    d_cache_dir = "";
Packit a4aae4
    d_response = Unknown_Response;;
Packit a4aae4
    d_anc_das_lmt = 0;
Packit a4aae4
    d_anc_dds_lmt = 0;
Packit a4aae4
    d_if_modified_since = -1;
Packit a4aae4
    d_url = "";
Packit a4aae4
    d_program_name = "Unknown";
Packit a4aae4
    d_timeout = 0;
Packit a4aae4
Packit a4aae4
#ifdef WIN32
Packit a4aae4
    //  We want serving from win32 to behave in a manner
Packit a4aae4
    //  similar to the UNIX way - no CR->NL terminated lines
Packit a4aae4
    //  in files. Hence stdout goes to binary mode.
Packit a4aae4
    _setmode(_fileno(stdout), _O_BINARY);
Packit a4aae4
#endif
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Initialize. Specializations can call this once an empty DODSFilter has
Packit a4aae4
been created using the default constructor. Using a method such as this
Packit a4aae4
provides a way to specialize the process_options() method and then have
Packit a4aae4
that specialization called by the subclass' constructor.
Packit a4aae4
Packit a4aae4
This class and any class that specializes it should call this method in
Packit a4aae4
its constructor. Note that when this method is called, the object is \e
Packit a4aae4
not fully constructed.
Packit a4aae4
Packit a4aae4
@param argc The argument count
Packit a4aae4
@param argv The vector of char * argument strings. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::initialize(int argc, char *argv[])
Packit a4aae4
{
Packit a4aae4
    initialize();
Packit a4aae4
Packit a4aae4
    d_program_name = argv[0];
Packit a4aae4
Packit a4aae4
    // This should be specialized by a subclass. This may throw Error.
Packit a4aae4
    int next_arg = process_options(argc, argv);
Packit a4aae4
Packit a4aae4
    // Look at what's left after processing the command line options. Either
Packit a4aae4
    // there MUST be a dataset name OR the caller is asking for version
Packit a4aae4
    // information. If neither is true, then the options are bad.
Packit a4aae4
    if (next_arg < argc) {
Packit a4aae4
        d_dataset = argv[next_arg];
Packit a4aae4
        d_dataset = www2id(d_dataset, "%", "%20");
Packit a4aae4
    }
Packit a4aae4
    else if (get_response() != Version_Response)
Packit a4aae4
        print_usage();   // Throws Error
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Processing the command line options passed to the filter is handled by
Packit a4aae4
this method so that specializations can change the options easily.
Packit a4aae4
Packit a4aae4
@param argc The argument count
Packit a4aae4
@param argv The vector of char * argument strings.
Packit a4aae4
@return The index of the next, unprocessed, argument. This must be the
Packit a4aae4
identifier passed to the filter program that identifies the data source.
Packit a4aae4
It's often a file name. */
Packit a4aae4
int
Packit a4aae4
DODSFilter::process_options(int argc, char *argv[])
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << "Entering process_options... ");
Packit a4aae4
Packit a4aae4
    int option_char;
Packit a4aae4
    GetOpt getopt (argc, argv, "ce: v: d: f: r: l: o: u: t: ");
Packit a4aae4
Packit a4aae4
    while ((option_char = getopt()) != -1) {
Packit a4aae4
        switch (option_char) {
Packit a4aae4
        case 'c': d_comp = true; break;
Packit a4aae4
        case 'e': set_ce(getopt.optarg); break;
Packit a4aae4
        case 'v': set_cgi_version(getopt.optarg); break;
Packit a4aae4
        case 'd': d_anc_dir = getopt.optarg; break;
Packit a4aae4
        case 'f': d_anc_file = getopt.optarg; break;
Packit a4aae4
        case 'r': d_cache_dir = getopt.optarg; break;
Packit a4aae4
        case 'o': set_response(getopt.optarg); break;
Packit a4aae4
        case 'u': set_URL(getopt.optarg); break;
Packit a4aae4
        case 't': d_timeout = atoi(getopt.optarg); break;
Packit a4aae4
        case 'l':
Packit a4aae4
            d_conditional_request = true;
Packit a4aae4
            d_if_modified_since
Packit a4aae4
            = static_cast<time_t>(strtol(getopt.optarg, NULL, 10));
Packit a4aae4
            break;
Packit a4aae4
        case 'h': print_usage();
Packit a4aae4
            break;
Packit a4aae4
                                 // exit(1);
Packit a4aae4
                                 // Removed 12/29/2011; exit should
Packit a4aae4
                                 // not be called by a library. NB:
Packit a4aae4
                                 // print_usage() throws Error.
Packit a4aae4
        default: print_usage();  // Throws Error
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    DBGN(cerr << "exiting." << endl);
Packit a4aae4
Packit a4aae4
    return getopt.optind; // return the index of the next argument
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Is this request conditional?
Packit a4aae4
Packit a4aae4
@return True if the request is conditional.
Packit a4aae4
@see get_request_if_modified_since(). */
Packit a4aae4
bool
Packit a4aae4
DODSFilter::is_conditional() const
Packit a4aae4
{
Packit a4aae4
    return d_conditional_request;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Set the CGI/Server version number. Servers use this when answering
Packit a4aae4
requests for version information. The version `number' should include
Packit a4aae4
both the name of the server (e.g., <tt>ff_dods</tt>) as well
Packit a4aae4
as the version
Packit a4aae4
number. Since this information is typically divined by configure,
Packit a4aae4
it's up to the executable to poke the correct value in using this
Packit a4aae4
method.
Packit a4aae4
Packit a4aae4
Note that the -v switch that this class understands is deprecated
Packit a4aae4
since it is usually called by Perl code. It makes more sense to have
Packit a4aae4
the actual C++ software set the version string.
Packit a4aae4
Packit a4aae4
@param version A version string for this server. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::set_cgi_version(string version)
Packit a4aae4
{
Packit a4aae4
    d_cgi_ver = version;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Return the version information passed to the instance when it was
Packit a4aae4
created. This string is passed to the DODSFilter ctor using the -v
Packit a4aae4
option.
Packit a4aae4
Packit a4aae4
@return The version string supplied at initialization. */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_cgi_version() const
Packit a4aae4
{
Packit a4aae4
    return d_cgi_ver;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Return the entire constraint expression in a string.  This
Packit a4aae4
includes both the projection and selection clauses, but not the
Packit a4aae4
question mark.
Packit a4aae4
Packit a4aae4
@brief Get the constraint expression.
Packit a4aae4
@return A string object that contains the constraint expression. */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_ce() const
Packit a4aae4
{
Packit a4aae4
    return d_dap2ce;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::set_ce(string _ce)
Packit a4aae4
{
Packit a4aae4
    d_dap2ce = www2id(_ce, "%", "%20");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** The ``dataset name'' is the filename or other string that the
Packit a4aae4
filter program will use to access the data. In some cases this
Packit a4aae4
will indicate a disk file containing the data.  In others, it
Packit a4aae4
may represent a database query or some other exotic data
Packit a4aae4
access method.
Packit a4aae4
Packit a4aae4
@brief Get the dataset name.
Packit a4aae4
@return A string object that contains the name of the dataset. */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_dataset_name() const
Packit a4aae4
{
Packit a4aae4
    return d_dataset;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::set_dataset_name(const string ds)
Packit a4aae4
{
Packit a4aae4
    d_dataset = www2id(ds, "%", "%20");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the URL. This returns the URL, minus the constraint originally sent
Packit a4aae4
to the server.
Packit a4aae4
@return The URL. */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_URL() const
Packit a4aae4
{
Packit a4aae4
    return d_url;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Set the URL. Set the URL sent to the server.
Packit a4aae4
@param url The URL, minus the constraint. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::set_URL(const string &url)
Packit a4aae4
{
Packit a4aae4
    if (url.find('?') != url.npos)
Packit a4aae4
        print_usage();  // Throws Error
Packit a4aae4
Packit a4aae4
    d_url = url;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** To read version information that is specific to a certain
Packit a4aae4
dataset, override this method with an implementation that does
Packit a4aae4
what you want. By default, this returns an empty string.
Packit a4aae4
Packit a4aae4
@brief Get the version information for the dataset.
Packit a4aae4
@return A string object that contains the dataset version
Packit a4aae4
information.  */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_dataset_version() const
Packit a4aae4
{
Packit a4aae4
    return "";
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Set the response to be returned. Valid response names are "DAS", "DDS",
Packit a4aae4
"DataDDS, "Version".
Packit a4aae4
Packit a4aae4
@param r The name of the object.
Packit a4aae4
@exception InternalErr Thrown if the response is not one of the valid
Packit a4aae4
names. */
Packit a4aae4
void DODSFilter::set_response(const string &r)
Packit a4aae4
{
Packit a4aae4
    if (r == "DAS" || r == "das") {
Packit a4aae4
	d_response = DAS_Response;
Packit a4aae4
	d_action = "das" ;
Packit a4aae4
    }
Packit a4aae4
    else if (r == "DDS" || r == "dds") {
Packit a4aae4
	d_response = DDS_Response;
Packit a4aae4
	d_action = "dds" ;
Packit a4aae4
    }
Packit a4aae4
    else if (r == "DataDDS" || r == "dods") {
Packit a4aae4
	d_response = DataDDS_Response;
Packit a4aae4
	d_action = "dods" ;
Packit a4aae4
    }
Packit a4aae4
    else if (r == "DDX" || r == "ddx") {
Packit a4aae4
	d_response = DDX_Response;
Packit a4aae4
	d_action = "ddx" ;
Packit a4aae4
    }
Packit a4aae4
    else if (r == "DataDDX" || r == "dataddx") {
Packit a4aae4
	d_response = DataDDX_Response;
Packit a4aae4
	d_action = "dataddx" ;
Packit a4aae4
    }
Packit a4aae4
    else if (r == "Version") {
Packit a4aae4
	d_response = Version_Response;
Packit a4aae4
	d_action = "version" ;
Packit a4aae4
    }
Packit a4aae4
    else
Packit a4aae4
	print_usage();   // Throws Error
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the enum name of the response to be returned. */
Packit a4aae4
DODSFilter::Response
Packit a4aae4
DODSFilter::get_response() const
Packit a4aae4
{
Packit a4aae4
    return d_response;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the string name of the response to be returned. */
Packit a4aae4
string DODSFilter::get_action() const
Packit a4aae4
{
Packit a4aae4
    return d_action;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the dataset's last modified time. This returns the time at which
Packit a4aae4
    the dataset was last modified as defined by UNIX's notion of
Packit a4aae4
    modification. This does not take into account the modification of an
Packit a4aae4
    ancillary DAS or DDS. Time is given in seconds since the epoch (1 Jan
Packit a4aae4
    1970 00:00:00 GMT).
Packit a4aae4
Packit a4aae4
    This method perform a simple check on the file named by the dataset
Packit a4aae4
    given when the DODSFilter instance was created. If the dataset is not
Packit a4aae4
    a filter, this method returns the current time. Servers which provide
Packit a4aae4
    access to non-file-based data should subclass DODSFilter and supply a
Packit a4aae4
    more suitable version of this method.
Packit a4aae4
Packit a4aae4
    From the stat(2) man page: ``Traditionally, <tt>st_mtime</tt>
Packit a4aae4
    is changed by mknod(2), utime(2), and write(2). The
Packit a4aae4
    <tt>st_mtime</tt> is not changed for
Packit a4aae4
    changes in owner, group, hard link count, or mode.''
Packit a4aae4
Packit a4aae4
    @return Time of the last modification in seconds since the epoch.
Packit a4aae4
    @see get_das_last_modified_time()
Packit a4aae4
    @see get_dds_last_modified_time() */
Packit a4aae4
time_t
Packit a4aae4
DODSFilter::get_dataset_last_modified_time() const
Packit a4aae4
{
Packit a4aae4
    return last_modified_time(d_dataset);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the last modified time for the dataset's DAS. This time, given in
Packit a4aae4
    seconds since the epoch (1 Jan 1970 00:00:00 GMT), is the greater of
Packit a4aae4
    the datasets's and any ancillary DAS' last modified time.
Packit a4aae4
Packit a4aae4
    @param anc_location A directory to search for ancillary files (in
Packit a4aae4
    addition to the CWD).
Packit a4aae4
    @return Time of last modification of the DAS.
Packit a4aae4
    @see get_dataset_last_modified_time()
Packit a4aae4
    @see get_dds_last_modified_time() */
Packit a4aae4
time_t
Packit a4aae4
DODSFilter::get_das_last_modified_time(const string &anc_location) const
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
Packit a4aae4
        << anc_location << "call faf(das) d_dataset=" << d_dataset
Packit a4aae4
        << " d_anc_file=" << d_anc_file << endl);
Packit a4aae4
Packit a4aae4
    string name
Packit a4aae4
    = Ancillary::find_ancillary_file(d_dataset, "das",
Packit a4aae4
                          (anc_location == "") ? d_anc_dir : anc_location,
Packit a4aae4
                          d_anc_file);
Packit a4aae4
Packit a4aae4
    return max((name != "") ? last_modified_time(name) : 0,
Packit a4aae4
               get_dataset_last_modified_time());
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the last modified time for the dataset's DDS. This time, given in
Packit a4aae4
    seconds since the epoch (1 Jan 1970 00:00:00 GMT), is the greater of
Packit a4aae4
    the datasets's and any ancillary DDS' last modified time.
Packit a4aae4
Packit a4aae4
    @return Time of last modification of the DDS.
Packit a4aae4
    @see get_dataset_last_modified_time()
Packit a4aae4
    @see get_dds_last_modified_time() */
Packit a4aae4
time_t
Packit a4aae4
DODSFilter::get_dds_last_modified_time(const string &anc_location) const
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
Packit a4aae4
        << anc_location << "call faf(dds) d_dataset=" << d_dataset
Packit a4aae4
        << " d_anc_file=" << d_anc_file << endl);
Packit a4aae4
Packit a4aae4
    string name
Packit a4aae4
    = Ancillary::find_ancillary_file(d_dataset, "dds",
Packit a4aae4
                          (anc_location == "") ? d_anc_dir : anc_location,
Packit a4aae4
                          d_anc_file);
Packit a4aae4
Packit a4aae4
    return max((name != "") ? last_modified_time(name) : 0,
Packit a4aae4
               get_dataset_last_modified_time());
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the last modified time to be used for a particular data request.
Packit a4aae4
    This method should look at both the constraint expression and any
Packit a4aae4
    ancillary files for this dataset. The implementation provided here
Packit a4aae4
    returns the latest time returned by the <tt>get_dataset</tt>...(),
Packit a4aae4
    <tt>get_das</tt>...() and <tt>get_dds</tt>...() methods and
Packit a4aae4
    does not currently check the CE.
Packit a4aae4
Packit a4aae4
    @param anc_location A directory to search for ancillary files (in
Packit a4aae4
    addition to the CWD).
Packit a4aae4
    @return Time of last modification of the data.
Packit a4aae4
    @see get_dataset_last_modified_time()
Packit a4aae4
    @see get_das_last_modified_time()
Packit a4aae4
    @see get_dds_last_modified_time() */
Packit a4aae4
time_t
Packit a4aae4
DODSFilter::get_data_last_modified_time(const string &anc_location) const
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
Packit a4aae4
        << anc_location << "call faf(both) d_dataset=" << d_dataset
Packit a4aae4
        << " d_anc_file=" << d_anc_file << endl);
Packit a4aae4
Packit a4aae4
    string dds_name
Packit a4aae4
    = Ancillary::find_ancillary_file(d_dataset, "dds",
Packit a4aae4
                          (anc_location == "") ? d_anc_dir : anc_location,
Packit a4aae4
                          d_anc_file);
Packit a4aae4
    string das_name
Packit a4aae4
    = Ancillary::find_ancillary_file(d_dataset, "das",
Packit a4aae4
                          (anc_location == "") ? d_anc_dir : anc_location,
Packit a4aae4
                          d_anc_file);
Packit a4aae4
Packit a4aae4
    time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
Packit a4aae4
                   (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
Packit a4aae4
    // Note that this is a call to get_dataset_... not get_data_...
Packit a4aae4
    time_t n = get_dataset_last_modified_time();
Packit a4aae4
Packit a4aae4
    return max(m, n);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the value of a conditional request's If-Modified-Since header.
Packit a4aae4
    This value is used to determine if the request should get a full
Packit a4aae4
    response or a Not Modified (304) response. The time is given in
Packit a4aae4
    seconds since the Unix epoch (midnight, 1 Jan 1970). If no time was
Packit a4aae4
    given with the request, this methods returns -1.
Packit a4aae4
Packit a4aae4
    @return If-Modified-Since time from a condition GET request. */
Packit a4aae4
time_t
Packit a4aae4
DODSFilter::get_request_if_modified_since() const
Packit a4aae4
{
Packit a4aae4
    return d_if_modified_since;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** The <tt>cache_dir</tt> is used to hold the cached .dds and .das files.
Packit a4aae4
    By default, this returns an empty string (store cache files in
Packit a4aae4
    current directory.
Packit a4aae4
Packit a4aae4
    @brief Get the cache directory.
Packit a4aae4
    @return A string object that contains the cache file directory.  */
Packit a4aae4
string
Packit a4aae4
DODSFilter::get_cache_dir() const
Packit a4aae4
{
Packit a4aae4
    return d_cache_dir;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Set the server's timeout value. A value of zero (the default) means no
Packit a4aae4
    timeout.
Packit a4aae4
Packit a4aae4
    @param t Server timeout in seconds. Default is zero (no timeout). */
Packit a4aae4
void
Packit a4aae4
DODSFilter::set_timeout(int t)
Packit a4aae4
{
Packit a4aae4
    d_timeout = t;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get the server's timeout value. */
Packit a4aae4
int
Packit a4aae4
DODSFilter::get_timeout() const
Packit a4aae4
{
Packit a4aae4
    return d_timeout;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Use values of this instance to establish a timeout alarm for the server.
Packit a4aae4
    If the timeout value is zero, do nothing.
Packit a4aae4
Packit a4aae4
    @todo When the alarm handler is called, two CRLF pairs are dumped to the
Packit a4aae4
    stream and then an Error object is sent. No attempt is made to write the
Packit a4aae4
    'correct' MIME headers for an Error object. Instead, a savvy client will
Packit a4aae4
    know that when an exception is thrown during a deserialize operation, it
Packit a4aae4
    should scan ahead in the input stream for an Error object. Add this, or a
Packit a4aae4
    sensible variant once libdap++ supports reliable error delivery. Dumb
Packit a4aae4
    clients will never get the Error object... */
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::establish_timeout(FILE *stream) const
Packit a4aae4
{
Packit a4aae4
#ifndef WIN32
Packit a4aae4
    if (d_timeout > 0) {
Packit a4aae4
        SignalHandler *sh = SignalHandler::instance();
Packit a4aae4
        EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
Packit a4aae4
        delete old_eh;
Packit a4aae4
        alarm(d_timeout);
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::establish_timeout(ostream &stream) const
Packit a4aae4
{
Packit a4aae4
#ifndef WIN32
Packit a4aae4
    if (d_timeout > 0) {
Packit a4aae4
        SignalHandler *sh = SignalHandler::instance();
Packit a4aae4
        EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
Packit a4aae4
        delete old_eh;
Packit a4aae4
        alarm(d_timeout);
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
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.";
Packit a4aae4
Packit a4aae4
/** This message is printed when the filter program is incorrectly
Packit a4aae4
    invoked by the dispatch CGI.  This is an error in the server
Packit a4aae4
    installation or the CGI implementation, so the error message is
Packit a4aae4
    written to stderr instead of stdout.  A server's stderr messages
Packit a4aae4
    show up in the httpd log file. In addition, an error object is
Packit a4aae4
    sent back to the client program telling them that the server is
Packit a4aae4
    broken.
Packit a4aae4
Packit a4aae4
    @brief Print usage information for a filter program. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::print_usage() const
Packit a4aae4
{
Packit a4aae4
    // Write a message to the WWW server error log file.
Packit a4aae4
    ErrMsgT(usage.c_str());
Packit a4aae4
Packit a4aae4
    throw Error(emessage);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function formats and sends to stdout version
Packit a4aae4
    information from the httpd server, the server dispatch scripts,
Packit a4aae4
    the DODS core software, and (optionally) the dataset.
Packit a4aae4
Packit a4aae4
    @brief Send version information back to the client program. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_version_info() const
Packit a4aae4
{
Packit a4aae4
    do_version(d_cgi_ver, get_dataset_version());
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function formats and prints an ASCII representation of a
Packit a4aae4
    DAS on stdout.  This has the effect of sending the DAS object
Packit a4aae4
    back to the client program.
Packit a4aae4
Packit a4aae4
    @brief Transmit a DAS.
Packit a4aae4
    @param out The output FILE to which the DAS is to be sent.
Packit a4aae4
    @param das The DAS object to be sent.
Packit a4aae4
    @param anc_location The directory in which the external DAS file resides.
Packit a4aae4
    @param with_mime_headers If true (the default) send MIME headers.
Packit a4aae4
    @return void
Packit a4aae4
    @see DAS */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    send_das(oss, das, anc_location, with_mime_headers);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function formats and prints an ASCII representation of a
Packit a4aae4
    DAS on stdout.  This has the effect of sending the DAS object
Packit a4aae4
    back to the client program.
Packit a4aae4
Packit a4aae4
    @brief Transmit a DAS.
Packit a4aae4
    @param out The output stream to which the DAS is to be sent.
Packit a4aae4
    @param das The DAS object to be sent.
Packit a4aae4
    @param anc_location The directory in which the external DAS file resides.
Packit a4aae4
    @param with_mime_headers If true (the default) send MIME headers.
Packit a4aae4
    @return void
Packit a4aae4
    @see DAS */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_das(ostream &out, DAS &das, const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    time_t das_lmt = get_das_last_modified_time(anc_location);
Packit a4aae4
    if (is_conditional()
Packit a4aae4
        && das_lmt <= get_request_if_modified_since()
Packit a4aae4
        && with_mime_headers) {
Packit a4aae4
        set_mime_not_modified(out);
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
Packit a4aae4
        das.print(out);
Packit a4aae4
    }
Packit a4aae4
    out << flush ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_das(DAS &das, const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    send_das(cout, das, anc_location, with_mime_headers);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function formats and prints an ASCII representation of a
Packit a4aae4
    DDS on stdout.  When called by a CGI program, this has the
Packit a4aae4
    effect of sending a DDS object back to the client
Packit a4aae4
    program. Either an entire DDS or a constrained DDS may be sent.
Packit a4aae4
Packit a4aae4
    @brief Transmit a DDS.
Packit a4aae4
    @param out The output FILE to which the DAS is to be sent.
Packit a4aae4
    @param dds The DDS to send back to a client.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param constrained If this argument is true, evaluate the
Packit a4aae4
    current constraint expression and send the `constrained DDS'
Packit a4aae4
    back to the client.
Packit a4aae4
    @param anc_location The directory in which the external DAS file resides.
Packit a4aae4
    @param with_mime_headers If true (the default) send MIME headers.
Packit a4aae4
    @return void
Packit a4aae4
    @see DDS */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
Packit a4aae4
                     bool constrained,
Packit a4aae4
                     const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    send_dds(oss, dds, eval, constrained, anc_location, with_mime_headers);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function formats and prints an ASCII representation of a
Packit a4aae4
    DDS on stdout.  When called by a CGI program, this has the
Packit a4aae4
    effect of sending a DDS object back to the client
Packit a4aae4
    program. Either an entire DDS or a constrained DDS may be sent.
Packit a4aae4
Packit a4aae4
    @brief Transmit a DDS.
Packit a4aae4
    @param out The output stream to which the DAS is to be sent.
Packit a4aae4
    @param dds The DDS to send back to a client.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param constrained If this argument is true, evaluate the
Packit a4aae4
    current constraint expression and send the `constrained DDS'
Packit a4aae4
    back to the client.
Packit a4aae4
    @param anc_location The directory in which the external DAS file resides.
Packit a4aae4
    @param with_mime_headers If true (the default) send MIME headers.
Packit a4aae4
    @return void
Packit a4aae4
    @see DDS */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval,
Packit a4aae4
                     bool constrained,
Packit a4aae4
                     const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    // If constrained, parse the constraint. Throws Error or InternalErr.
Packit a4aae4
    if (constrained)
Packit a4aae4
        eval.parse_constraint(d_dap2ce, dds);
Packit a4aae4
Packit a4aae4
    if (eval.functional_expression())
Packit a4aae4
        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.");
Packit a4aae4
Packit a4aae4
    time_t dds_lmt = get_dds_last_modified_time(anc_location);
Packit a4aae4
    if (is_conditional()
Packit a4aae4
        && dds_lmt <= get_request_if_modified_since()
Packit a4aae4
        && with_mime_headers) {
Packit a4aae4
        set_mime_not_modified(out);
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
Packit a4aae4
        if (constrained)
Packit a4aae4
            dds.print_constrained(out);
Packit a4aae4
        else
Packit a4aae4
            dds.print(out);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    out << flush ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
Packit a4aae4
                     bool constrained, const string &anc_location,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    send_dds(cout, dds, eval, constrained, anc_location, with_mime_headers);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// 'lmt' unused. Should it be used to supply a LMT or removed from the
Packit a4aae4
// method? jhrg 8/9/05
Packit a4aae4
void
Packit a4aae4
DODSFilter::functional_constraint(BaseType &var, DDS &dds,
Packit a4aae4
                                  ConstraintEvaluator &eval, FILE *out) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    functional_constraint(var, dds, eval, oss);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// 'lmt' unused. Should it be used to supply a LMT or removed from the
Packit a4aae4
// method? jhrg 8/9/05
Packit a4aae4
void
Packit a4aae4
DODSFilter::functional_constraint(BaseType &var, DDS &dds,
Packit a4aae4
                                  ConstraintEvaluator &eval, ostream &out) const
Packit a4aae4
{
Packit a4aae4
    out << "Dataset {\n" ;
Packit a4aae4
    var.print_decl(out, "    ", true, false, true);
Packit a4aae4
    out << "} function_value;\n" ;
Packit a4aae4
    out << "Data:\n" ;
Packit a4aae4
Packit a4aae4
    out << flush ;
Packit a4aae4
Packit a4aae4
    // Grab a stream that encodes using XDR.
Packit a4aae4
    XDRStreamMarshaller m( out ) ;
Packit a4aae4
Packit a4aae4
    try {
Packit a4aae4
        // In the following call to serialize, suppress CE evaluation.
Packit a4aae4
        var.serialize(eval, dds, m, false);
Packit a4aae4
    }
Packit a4aae4
    catch (Error &e) {
Packit a4aae4
        throw;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                               FILE * out, bool ce_eval) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    dataset_constraint(dds, eval, oss, ce_eval);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                               ostream &out, bool ce_eval) const
Packit a4aae4
{
Packit a4aae4
    // send constrained DDS
Packit a4aae4
    dds.print_constrained(out);
Packit a4aae4
    out << "Data:\n" ;
Packit a4aae4
    out << flush ;
Packit a4aae4
Packit a4aae4
    // Grab a stream that encodes using XDR.
Packit a4aae4
    XDRStreamMarshaller m( out ) ;
Packit a4aae4
Packit a4aae4
    try {
Packit a4aae4
        // Send all variables in the current projection (send_p())
Packit a4aae4
        for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
Packit a4aae4
            if ((*i)->send_p()) {
Packit a4aae4
                DBG(cerr << "Sending " << (*i)->name() << endl);
Packit a4aae4
                (*i)->serialize(eval, dds, m, ce_eval);
Packit a4aae4
            }
Packit a4aae4
    }
Packit a4aae4
    catch (Error & e) {
Packit a4aae4
        throw;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
DODSFilter::dataset_constraint_ddx(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                               ostream &out, const string &boundary,
Packit a4aae4
                               const string &start, bool ce_eval) const
Packit a4aae4
{
Packit a4aae4
    // Write the MPM headers for the DDX (text/xml) part of the response
Packit a4aae4
    set_mime_ddx_boundary(out, boundary, start, dods_ddx);
Packit a4aae4
Packit a4aae4
    // Make cid
Packit a4aae4
    uuid_t uu;
Packit a4aae4
    uuid_generate(uu);
Packit a4aae4
    char uuid[37];
Packit a4aae4
    uuid_unparse(uu, &uuid[0]);
Packit a4aae4
    char domain[256];
Packit a4aae4
    if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
Packit a4aae4
	strncpy(domain, "opendap.org", 255);
Packit a4aae4
Packit a4aae4
    string cid = string(&uuid[0]) + "@" + string(&domain[0]);
Packit a4aae4
Packit a4aae4
    // Send constrained DDX with a data blob reference
Packit a4aae4
    dds.print_xml_writer(out, true, cid);
Packit a4aae4
Packit a4aae4
    // Write the MPM headers for the data part of the response.
Packit a4aae4
    set_mime_data_boundary(out, boundary, cid, dap4_data, binary);
Packit a4aae4
Packit a4aae4
    // Grab a stream that encodes using XDR.
Packit a4aae4
    XDRStreamMarshaller m( out ) ;
Packit a4aae4
Packit a4aae4
    try {
Packit a4aae4
        // Send all variables in the current projection (send_p())
Packit a4aae4
        for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
Packit a4aae4
            if ((*i)->send_p()) {
Packit a4aae4
                DBG(cerr << "Sending " << (*i)->name() << endl);
Packit a4aae4
                (*i)->serialize(eval, dds, m, ce_eval);
Packit a4aae4
            }
Packit a4aae4
    }
Packit a4aae4
    catch (Error & e) {
Packit a4aae4
        throw;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Send the data in the DDS object back to the client program. The data is
Packit a4aae4
    encoded using a Marshaller, and enclosed in a MIME document which is all sent
Packit a4aae4
    to \c data_stream. If this is being called from a CGI, \c data_stream is
Packit a4aae4
    probably \c stdout and writing to it has the effect of sending the
Packit a4aae4
    response back to the client.
Packit a4aae4
Packit a4aae4
    @brief Transmit data.
Packit a4aae4
    @param dds A DDS object containing the data to be sent.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param data_stream Write the response to this FILE.
Packit a4aae4
    @param anc_location A directory to search for ancillary files (in
Packit a4aae4
    addition to the CWD).  This is used in a call to
Packit a4aae4
    get_data_last_modified_time().
Packit a4aae4
    @param with_mime_headers If true, include the MIME headers in the response.
Packit a4aae4
    Defaults to true.
Packit a4aae4
    @return void */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                      FILE * data_stream, const string & anc_location,
Packit a4aae4
                      bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    send_data(dds, eval, oss, anc_location, with_mime_headers);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), data_stream);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Send the data in the DDS object back to the client program. The data is
Packit a4aae4
    encoded using a Marshaller, and enclosed in a MIME document which is all sent
Packit a4aae4
    to \c data_stream. If this is being called from a CGI, \c data_stream is
Packit a4aae4
    probably \c stdout and writing to it has the effect of sending the
Packit a4aae4
    response back to the client.
Packit a4aae4
Packit a4aae4
    @brief Transmit data.
Packit a4aae4
    @param dds A DDS object containing the data to be sent.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param data_stream Write the response to this stream.
Packit a4aae4
    @param anc_location A directory to search for ancillary files (in
Packit a4aae4
    addition to the CWD).  This is used in a call to
Packit a4aae4
    get_data_last_modified_time().
Packit a4aae4
    @param with_mime_headers If true, include the MIME headers in the response.
Packit a4aae4
    Defaults to true.
Packit a4aae4
    @return void */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                      ostream & data_stream, const string & anc_location,
Packit a4aae4
                      bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    // If this is a conditional request and the server should send a 304
Packit a4aae4
    // response, do that and exit. Otherwise, continue on and send the full
Packit a4aae4
    // response.
Packit a4aae4
    time_t data_lmt = get_data_last_modified_time(anc_location);
Packit a4aae4
    if (is_conditional()
Packit a4aae4
        && data_lmt <= get_request_if_modified_since()
Packit a4aae4
        && with_mime_headers) {
Packit a4aae4
        set_mime_not_modified(data_stream);
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
    // Set up the alarm.
Packit a4aae4
    establish_timeout(data_stream);
Packit a4aae4
    dds.set_timeout(d_timeout);
Packit a4aae4
Packit a4aae4
    eval.parse_constraint(d_dap2ce, dds);   // Throws Error if the ce doesn't
Packit a4aae4
					// parse.
Packit a4aae4
Packit a4aae4
    dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
Packit a4aae4
Packit a4aae4
    // Start sending the response...
Packit a4aae4
Packit a4aae4
    // Handle *functional* constraint expressions specially
Packit a4aae4
#if 0
Packit a4aae4
    if (eval.functional_expression()) {
Packit a4aae4
        // Get the result and then start sending the headers. This provides a
Packit a4aae4
        // way to send errors back to the client w/o colliding with the
Packit a4aae4
        // normal response headers. There's some duplication of code with this
Packit a4aae4
        // and the else-clause.
Packit a4aae4
        BaseType *var = eval.eval_function(dds, d_dataset);
Packit a4aae4
        if (!var)
Packit a4aae4
            throw Error(unknown_error, "Error calling the CE function.");
Packit a4aae4
Packit a4aae4
       if (with_mime_headers)
Packit a4aae4
            set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
Packit a4aae4
	data_stream << flush ;
Packit a4aae4
Packit a4aae4
        functional_constraint(*var, dds, eval, data_stream);
Packit a4aae4
        delete var;
Packit a4aae4
        var = 0;
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
    if (eval.function_clauses()) {
Packit a4aae4
	DDS *fdds = eval.eval_function_clauses(dds);
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
Packit a4aae4
        dataset_constraint(*fdds, eval, data_stream, false);
Packit a4aae4
	delete fdds;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
Packit a4aae4
        dataset_constraint(dds, eval, data_stream);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    data_stream << flush ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Send the DDX response. The DDX never contains data, instead it holds a
Packit a4aae4
    reference to a Blob response which is used to get the data values. The
Packit a4aae4
    DDS and DAS objects are built using code that already exists in the
Packit a4aae4
    servers.
Packit a4aae4
Packit a4aae4
    @param dds The dataset's DDS \e with attributes in the variables.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param out Destination
Packit a4aae4
    @param with_mime_headers If true, include the MIME headers in the response.
Packit a4aae4
    Defaults to true. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    send_ddx(dds, eval, oss, with_mime_headers);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Send the DDX response. The DDX never contains data, instead it holds a
Packit a4aae4
    reference to a Blob response which is used to get the data values. The
Packit a4aae4
    DDS and DAS objects are built using code that already exists in the
Packit a4aae4
    servers.
Packit a4aae4
Packit a4aae4
    @param dds The dataset's DDS \e with attributes in the variables.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param out Destination
Packit a4aae4
    @param with_mime_headers If true, include the MIME headers in the response.
Packit a4aae4
    Defaults to true. */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, ostream &out,
Packit a4aae4
                     bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    // If constrained, parse the constraint. Throws Error or InternalErr.
Packit a4aae4
    if (!d_dap2ce.empty())
Packit a4aae4
        eval.parse_constraint(d_dap2ce, dds);
Packit a4aae4
Packit a4aae4
    if (eval.functional_expression())
Packit a4aae4
        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.");
Packit a4aae4
Packit a4aae4
    time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
Packit a4aae4
Packit a4aae4
    // If this is a conditional request and the server should send a 304
Packit a4aae4
    // response, do that and exit. Otherwise, continue on and send the full
Packit a4aae4
    // response.
Packit a4aae4
    if (is_conditional() && dds_lmt <= get_request_if_modified_since()
Packit a4aae4
        && with_mime_headers) {
Packit a4aae4
        set_mime_not_modified(out);
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_text(out, dods_ddx, d_cgi_ver, x_plain, dds_lmt);
Packit a4aae4
        dds.print_xml_writer(out, !d_dap2ce.empty(), "");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Send the data in the DDS object back to the client program. The data is
Packit a4aae4
    encoded using a Marshaller, and enclosed in a MIME document which is all sent
Packit a4aae4
    to \c data_stream. If this is being called from a CGI, \c data_stream is
Packit a4aae4
    probably \c stdout and writing to it has the effect of sending the
Packit a4aae4
    response back to the client.
Packit a4aae4
Packit a4aae4
    @brief Transmit data.
Packit a4aae4
Packit a4aae4
    @param dds A DDS object containing the data to be sent.
Packit a4aae4
    @param eval A reference to the ConstraintEvaluator to use.
Packit a4aae4
    @param data_stream Write the response to this stream.
Packit a4aae4
    @param start
Packit a4aae4
    @param boundary
Packit a4aae4
    @param anc_location A directory to search for ancillary files (in
Packit a4aae4
    addition to the CWD).  This is used in a call to
Packit a4aae4
    get_data_last_modified_time().
Packit a4aae4
    @param with_mime_headers If true, include the MIME headers in the response.
Packit a4aae4
    Defaults to true.
Packit a4aae4
Packit a4aae4
    @return void */
Packit a4aae4
void
Packit a4aae4
DODSFilter::send_data_ddx(DDS & dds, ConstraintEvaluator & eval,
Packit a4aae4
                      ostream & data_stream, const string &start,
Packit a4aae4
                      const string &boundary, const string & anc_location,
Packit a4aae4
                      bool with_mime_headers) const
Packit a4aae4
{
Packit a4aae4
    // If this is a conditional request and the server should send a 304
Packit a4aae4
    // response, do that and exit. Otherwise, continue on and send the full
Packit a4aae4
    // response.
Packit a4aae4
    time_t data_lmt = get_data_last_modified_time(anc_location);
Packit a4aae4
    if (is_conditional()
Packit a4aae4
        && data_lmt <= get_request_if_modified_since()
Packit a4aae4
        && with_mime_headers) {
Packit a4aae4
        set_mime_not_modified(data_stream);
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
    // Set up the alarm.
Packit a4aae4
    establish_timeout(data_stream);
Packit a4aae4
    dds.set_timeout(d_timeout);
Packit a4aae4
Packit a4aae4
    eval.parse_constraint(d_dap2ce, dds);   // Throws Error if the ce doesn't
Packit a4aae4
					// parse.
Packit a4aae4
Packit a4aae4
    dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
Packit a4aae4
Packit a4aae4
    // Start sending the response...
Packit a4aae4
Packit a4aae4
    // Handle *functional* constraint expressions specially
Packit a4aae4
#if 0
Packit a4aae4
    if (eval.functional_expression()) {
Packit a4aae4
        BaseType *var = eval.eval_function(dds, d_dataset);
Packit a4aae4
        if (!var)
Packit a4aae4
            throw Error(unknown_error, "Error calling the CE function.");
Packit a4aae4
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
Packit a4aae4
		d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
	data_stream << flush ;
Packit a4aae4
	BaseTypeFactory btf;
Packit a4aae4
	DDS var_dds(&btf, var->name());
Packit a4aae4
	var->set_send_p(true);
Packit a4aae4
	var_dds.add_var(var);
Packit a4aae4
        serialize_dap2_data_ddx(var_dds, eval, data_stream, boundary, start);
Packit a4aae4
Packit a4aae4
        // functional_constraint_ddx(*var, dds, eval, data_stream, boundary);
Packit a4aae4
        delete var;
Packit a4aae4
        var = 0;
Packit a4aae4
    }
Packit a4aae4
#endif
Packit a4aae4
    if (eval.function_clauses()) {
Packit a4aae4
    	DDS *fdds = eval.eval_function_clauses(dds);
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
Packit a4aae4
        	    d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
        data_stream << flush ;
Packit a4aae4
        dataset_constraint(*fdds, eval, data_stream, false);
Packit a4aae4
    	delete fdds;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        if (with_mime_headers)
Packit a4aae4
            set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
Packit a4aae4
        	    d_cgi_ver, x_plain, data_lmt);
Packit a4aae4
        data_stream << flush ;
Packit a4aae4
        dataset_constraint_ddx(dds, eval, data_stream, boundary, start);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    data_stream << flush ;
Packit a4aae4
Packit a4aae4
    if (with_mime_headers)
Packit a4aae4
	data_stream << CRLF << "--" << boundary << "--" << CRLF;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap
Packit a4aae4