|
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 |
// Reza Nekovei <rnekovei@intcomm.net>
|
|
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 1994-2001
|
|
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 |
// reza Reza Nekovei <rnekovei@intcomm.net>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// A few useful routines which are used in CGI programs.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// ReZa 9/30/94
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "config.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <cstring>
|
|
Packit |
a4aae4 |
#include <cstdio>
|
|
Packit |
a4aae4 |
#include <ctype.h>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifndef TM_IN_SYS_TIME
|
|
Packit |
a4aae4 |
#include <time.h>
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
#include <sys/time.h>
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <sys/types.h>
|
|
Packit |
a4aae4 |
#include <sys/stat.h>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifndef WIN32
|
|
Packit |
a4aae4 |
#include <unistd.h> // for access
|
|
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 |
// Win32 does not define this. 08/21/02 jhrg
|
|
Packit |
a4aae4 |
#define F_OK 0
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <iostream>
|
|
Packit |
a4aae4 |
#include <sstream>
|
|
Packit |
a4aae4 |
#include <fstream>
|
|
Packit |
a4aae4 |
#include <string>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "mime_util.h"
|
|
Packit |
a4aae4 |
#include "media_types.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "Ancillary.h"
|
|
Packit |
a4aae4 |
#include "util.h" // This supplies flush_stream for WIN32.
|
|
Packit |
a4aae4 |
#include "debug.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifdef WIN32
|
|
Packit |
a4aae4 |
#define FILE_DELIMITER '\\'
|
|
Packit |
a4aae4 |
#else // default to unix
|
|
Packit |
a4aae4 |
#define FILE_DELIMITER '/'
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// ...not using a const string here to avoid global objects. jhrg 12/23/05
|
|
Packit |
a4aae4 |
#define CRLF "\r\n" // Change here, expr-test.cc, in DODSFilter and ResponseBuilder
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
using namespace std;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
namespace libdap {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Get the last modified time. Assume <tt>name</tt> is a file and
|
|
Packit |
a4aae4 |
find its last modified time. If <tt>name</tt> is not a file, then
|
|
Packit |
a4aae4 |
return now as the last modified time.
|
|
Packit |
a4aae4 |
@param name The name of a file.
|
|
Packit |
a4aae4 |
@return The last modified time or the current time. */
|
|
Packit |
a4aae4 |
time_t
|
|
Packit |
a4aae4 |
last_modified_time(const string &name)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
struct stat m;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
|
|
Packit |
a4aae4 |
return m.st_mtime;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
return time(0);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
// Return a MIME rfc-822 date. The grammar for this is:
|
|
Packit |
a4aae4 |
// date-time = [ day "," ] date time ; dd mm yy
|
|
Packit |
a4aae4 |
// ; hh:mm:ss zzz
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// day = "Mon" / "Tue" / "Wed" / "Thu"
|
|
Packit |
a4aae4 |
// / "Fri" / "Sat" / "Sun"
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// date = 1*2DIGIT month 2DIGIT ; day month year
|
|
Packit |
a4aae4 |
// ; e.g. 20 Jun 82
|
|
Packit |
a4aae4 |
// NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// month = "Jan" / "Feb" / "Mar" / "Apr"
|
|
Packit |
a4aae4 |
// / "May" / "Jun" / "Jul" / "Aug"
|
|
Packit |
a4aae4 |
// / "Sep" / "Oct" / "Nov" / "Dec"
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// time = hour zone ; ANSI and Military
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
|
|
Packit |
a4aae4 |
// ; 00:00:00 - 23:59:59
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// zone = "UT" / "GMT" ; Universal Time
|
|
Packit |
a4aae4 |
// ; North American : UT
|
|
Packit |
a4aae4 |
// / "EST" / "EDT" ; Eastern: - 5/ - 4
|
|
Packit |
a4aae4 |
// / "CST" / "CDT" ; Central: - 6/ - 5
|
|
Packit |
a4aae4 |
// / "MST" / "MDT" ; Mountain: - 7/ - 6
|
|
Packit |
a4aae4 |
// / "PST" / "PDT" ; Pacific: - 8/ - 7
|
|
Packit |
a4aae4 |
// / 1ALPHA ; Military: Z = UT;
|
|
Packit |
a4aae4 |
// ; A:-1; (J not used)
|
|
Packit |
a4aae4 |
// ; M:-12; N:+1; Y:+12
|
|
Packit |
a4aae4 |
// / ( ("+" / "-") 4DIGIT ) ; Local differential
|
|
Packit |
a4aae4 |
// ; hours+min. (HHMM)
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static const char *days[] =
|
|
Packit |
a4aae4 |
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
static const char *months[] =
|
|
Packit |
a4aae4 |
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
|
Packit |
a4aae4 |
"Aug", "Sep", "Oct", "Nov", "Dec"
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifdef _MSC_VER
|
|
Packit |
a4aae4 |
#define snprintf sprintf_s
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
/** Given a constant pointer to a <tt>time_t</tt>, return a RFC
|
|
Packit |
a4aae4 |
822/1123 style date.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
This function returns the RFC 822 date with the exception that the RFC
|
|
Packit |
a4aae4 |
1123 modification for four-digit years is implemented.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@return The RFC 822/1123 style date in a C++ string.
|
|
Packit |
a4aae4 |
@param t A const <tt>time_t</tt> pointer. */
|
|
Packit |
a4aae4 |
string
|
|
Packit |
a4aae4 |
rfc822_date(const time_t t)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
struct tm *stm = gmtime(&t);
|
|
Packit |
a4aae4 |
if (!stm)
|
|
Packit |
a4aae4 |
return "";
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
char d[256];
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
|
|
Packit |
a4aae4 |
stm->tm_mday, months[stm->tm_mon],
|
|
Packit |
a4aae4 |
1900 + stm->tm_year,
|
|
Packit |
a4aae4 |
stm->tm_hour, stm->tm_min, stm->tm_sec);
|
|
Packit |
a4aae4 |
d[255] = '\0';
|
|
Packit |
a4aae4 |
return string(d);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static const int TimLen = 26; // length of string from asctime()
|
|
Packit |
a4aae4 |
//static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This sends a formatted block of text to the client, containing
|
|
Packit |
a4aae4 |
version information about various aspects of the server. The
|
|
Packit |
a4aae4 |
arguments allow you to enclose version information about the
|
|
Packit |
a4aae4 |
filter program and the dataset in the message. Either argument
|
|
Packit |
a4aae4 |
(or both) may be omitted, in which case no script or dataset
|
|
Packit |
a4aae4 |
version information will be printed.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Send a version number.
|
|
Packit |
a4aae4 |
@param script_ver The version of the filter script executing this
|
|
Packit |
a4aae4 |
function.
|
|
Packit |
a4aae4 |
@param dataset_ver The version of the dataset.
|
|
Packit |
a4aae4 |
@return TRUE for success. Always returns true.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
bool
|
|
Packit |
a4aae4 |
do_version(const string &script_ver, const string &dataset_ver)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
|
|
Packit |
a4aae4 |
fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
|
|
Packit |
a4aae4 |
fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
|
|
Packit |
a4aae4 |
fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
|
|
Packit |
a4aae4 |
fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
|
|
Packit |
a4aae4 |
fprintf(stdout, CRLF) ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (script_ver != "")
|
|
Packit |
a4aae4 |
fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (dataset_ver != "")
|
|
Packit |
a4aae4 |
fprintf(stdout, "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
fflush(stdout) ; // Not sure this is needed. jhrg 12/23/05
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return true;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Prints an error message in the <tt>httpd</tt> system log file, along with
|
|
Packit |
a4aae4 |
a time stamp and the client host name (or address).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Use this instead of the functions in liberrmsg.a in the programs run by
|
|
Packit |
a4aae4 |
the CGIs to report errors so those errors show up in HTTPD's log files.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@brief Logs an error message.
|
|
Packit |
a4aae4 |
@return void
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
ErrMsgT(const string &Msgt)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
time_t TimBin;
|
|
Packit |
a4aae4 |
char TimStr[TimLen];
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (time(&TimBin) == (time_t) - 1)
|
|
Packit |
a4aae4 |
strncpy(TimStr, "time() error ", TimLen-1);
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
char *ctime_value = ctime(&TimBin);
|
|
Packit |
a4aae4 |
if (!ctime_value)
|
|
Packit |
a4aae4 |
strncpy(TimStr, "Unknown", TimLen-1);
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strncpy(TimStr, ctime_value, TimLen-1);
|
|
Packit |
a4aae4 |
TimStr[TimLen - 2] = '\0'; // overwrite the \n
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
strncpy(TimStr, ctime(&TimBin), TimLen-1);
|
|
Packit |
a4aae4 |
TimStr[TimLen - 2] = '\0'; // overwrite the \n
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Given a pathname, return just the filename component with any extension
|
|
Packit |
a4aae4 |
// removed. The new string resides in newly allocated memory; the caller must
|
|
Packit |
a4aae4 |
// delete it when done using the filename.
|
|
Packit |
a4aae4 |
// Originally from the netcdf distribution (ver 2.3.2).
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// *** Change to string class argument and return type. jhrg
|
|
Packit |
a4aae4 |
// *** Changed so it also removes the#path#of#the#file# from decompressed
|
|
Packit |
a4aae4 |
// files. rph.
|
|
Packit |
a4aae4 |
// Returns: A filename, with path and extension information removed. If
|
|
Packit |
a4aae4 |
// memory for the new name cannot be allocated, does not return!
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Given a pathname, this function returns just the file name
|
|
Packit |
a4aae4 |
component of the path. That is, given <tt>/a/b/c/ralph.nc.das</tt>, it
|
|
Packit |
a4aae4 |
returns <tt>ralph.nc</tt>.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Returns the filename portion of a pathname.
|
|
Packit |
a4aae4 |
@param path A C-style simple string containing a pathname to be
|
|
Packit |
a4aae4 |
parsed.
|
|
Packit |
a4aae4 |
@return A C-style simple string containing the filename component
|
|
Packit |
a4aae4 |
of the given pathname.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string
|
|
Packit |
a4aae4 |
name_path(const string &path)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (path == "")
|
|
Packit |
a4aae4 |
return string("");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string::size_type delim = path.find_last_of(FILE_DELIMITER);
|
|
Packit |
a4aae4 |
string::size_type pound = path.find_last_of("#");
|
|
Packit |
a4aae4 |
string new_path;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (pound != string::npos)
|
|
Packit |
a4aae4 |
new_path = path.substr(pound + 1);
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
new_path = path.substr(delim + 1);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return new_path;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Send string to set the transfer (mime) type and server version
|
|
Packit |
a4aae4 |
// Note that the content description filed is used to indicate whether valid
|
|
Packit |
a4aae4 |
// information of an error message is contained in the document and the
|
|
Packit |
a4aae4 |
// content-encoding field is used to indicate whether the data is compressed.
|
|
Packit |
a4aae4 |
// If the data stream is to be compressed, arrange for a compression output
|
|
Packit |
a4aae4 |
// filter so that all information sent after the header will be compressed.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// Returns: false if the compression output filter was to be used but could
|
|
Packit |
a4aae4 |
// not be started, true otherwise.
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
static const char *descrip[] =
|
|
Packit |
a4aae4 |
{"unknown", "dods_das", "dods_dds", "dods_data", "dods_ddx",
|
|
Packit |
a4aae4 |
"dods_error", "web_error", "dap4-dmr", "dap4-data", "dap4-error"
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static const char *descrip[] = {
|
|
Packit |
a4aae4 |
"unknown_type",
|
|
Packit |
a4aae4 |
"dods_das",
|
|
Packit |
a4aae4 |
"dods_dds",
|
|
Packit |
a4aae4 |
"dods_data",
|
|
Packit |
a4aae4 |
"dods_ddx", // This is the old XML DDS/DAS used prior to dap4
|
|
Packit |
a4aae4 |
"dods_data_ddx", // This is used for caching data responses
|
|
Packit |
a4aae4 |
"dods_error",
|
|
Packit |
a4aae4 |
"web_error",
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
"dap4_dmr", // DAP4 metadata
|
|
Packit |
a4aae4 |
"dap4_data", // The DMR with a data blob
|
|
Packit |
a4aae4 |
"dap4_error" // The error response for DAP4
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static const char *encoding[] =
|
|
Packit |
a4aae4 |
{"unknown", "deflate", "x-plain", "gzip", "binary"
|
|
Packit |
a4aae4 |
};
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This function returns the ObjectType value that matches the given string.
|
|
Packit |
a4aae4 |
Modified to include tests for the descriptions that use hyphens in addition
|
|
Packit |
a4aae4 |
to underscores. 8/1/08 jhrg
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated */
|
|
Packit |
a4aae4 |
ObjectType
|
|
Packit |
a4aae4 |
get_type(const string &value)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return get_description_type(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// TODO Recode to use the constants in media_types.h. jhrg 11/12/13
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This function returns the ObjectType value that matches the given string.
|
|
Packit |
a4aae4 |
Modified to include tests for the descriptions that use hyphens in addition
|
|
Packit |
a4aae4 |
to underscores. 8/1/08 jhrg
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param value Value from the HTTP response header */
|
|
Packit |
a4aae4 |
ObjectType
|
|
Packit |
a4aae4 |
get_description_type(const string &value)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if ((value == DAS1) || (value == "dods-das"))
|
|
Packit |
a4aae4 |
return dods_das;
|
|
Packit |
a4aae4 |
else if ((value == "dods_dds") || (value == "dods-dds"))
|
|
Packit |
a4aae4 |
return dods_dds;
|
|
Packit |
a4aae4 |
else if ((value == "dods_data") || (value == "dods-data"))
|
|
Packit |
a4aae4 |
return dods_data;
|
|
Packit |
a4aae4 |
else if ((value == "dods_ddx") || (value == "dods-ddx"))
|
|
Packit |
a4aae4 |
return dods_ddx;
|
|
Packit |
a4aae4 |
else if ((value == "dods_data_ddx" || (value == "dods-data-ddx")))
|
|
Packit |
a4aae4 |
return dods_data_ddx;
|
|
Packit |
a4aae4 |
else if ((value == "dods_error") || (value == "dods-error"))
|
|
Packit |
a4aae4 |
return dods_error;
|
|
Packit |
a4aae4 |
else if ((value == "web_error") || (value == "web-error"))
|
|
Packit |
a4aae4 |
return web_error;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
else if ((value == "dap4_dmr") || (value == "dap4-dmr") || (value == DMR_Content_Type))
|
|
Packit |
a4aae4 |
return dap4_dmr;
|
|
Packit |
a4aae4 |
else if ((value == "dap4_data") || (value == "dap4-data") || (value == DAP4_DATA_Content_Type))
|
|
Packit |
a4aae4 |
return dap4_data;
|
|
Packit |
a4aae4 |
else if ((value == "dap4_error") || (value == "dap4-error"))
|
|
Packit |
a4aae4 |
return dap4_error;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
return unknown_type;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a text document. This is used
|
|
Packit |
a4aae4 |
when returning a serialized DAS or DDS object.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param out Write the MIME header to this FILE pointer.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_text(FILE *out, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
set_mime_text(oss, type, ver, enc, last_modified);
|
|
Packit |
a4aae4 |
fwrite(oss.str().data(), 1, oss.str().length(), out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a text document. This is used
|
|
Packit |
a4aae4 |
when returning a serialized DAS or DDS object.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_text(ostream &strm, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF ;
|
|
Packit |
a4aae4 |
if (ver == "") {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: " ;
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (type == dap4_dmr)
|
|
Packit |
a4aae4 |
strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF ;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "Content-Type: text/plain" << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
|
|
Packit |
a4aae4 |
// jhrg 12/23/05
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (type == dods_error) // don't cache our error responses.
|
|
Packit |
a4aae4 |
strm << "Cache-Control: no-cache" << CRLF ;
|
|
Packit |
a4aae4 |
// Don't write a Content-Encoding header for x-plain since that breaks
|
|
Packit |
a4aae4 |
// Netscape on NT. jhrg 3/23/97
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a text document. This is used
|
|
Packit |
a4aae4 |
when returning a serialized DAS or DDS object.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@note In Hyrax these headers are not used. Instead the front end of the
|
|
Packit |
a4aae4 |
server will build the response headers
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void set_mime_text(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
|
|
Packit |
a4aae4 |
const string &protocol)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (protocol == "")
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "XDAP: " << protocol << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: ";
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (type == dap4_dmr)
|
|
Packit |
a4aae4 |
strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "Content-Type: text/plain" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
|
|
Packit |
a4aae4 |
// jhrg 12/23/05
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF;
|
|
Packit |
a4aae4 |
if (type == dods_error) // don't cache our error responses.
|
|
Packit |
a4aae4 |
strm << "Cache-Control: no-cache" << CRLF;
|
|
Packit |
a4aae4 |
// Don't write a Content-Encoding header for x-plain since that breaks
|
|
Packit |
a4aae4 |
// Netscape on NT. jhrg 3/23/97
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF;
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a html document.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param out Write the MIME header to this FILE pointer.
|
|
Packit |
a4aae4 |
@param type The type of this this response.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_html(FILE *out, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
set_mime_html(oss, type, ver, enc, last_modified);
|
|
Packit |
a4aae4 |
fwrite(oss.str().data(), 1, oss.str().length(), out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a html document.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_html(ostream &strm, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF ;
|
|
Packit |
a4aae4 |
if (ver == "") {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: " ;
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-type: text/html" << CRLF ;
|
|
Packit |
a4aae4 |
// See note above about Content-Description header. jhrg 12/23/05
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (type == dods_error) // don't cache our error responses.
|
|
Packit |
a4aae4 |
strm << "Cache-Control: no-cache" << CRLF ;
|
|
Packit |
a4aae4 |
// Don't write a Content-Encoding header for x-plain since that breaks
|
|
Packit |
a4aae4 |
// Netscape on NT. jhrg 3/23/97
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for a html document.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time. */
|
|
Packit |
a4aae4 |
void set_mime_html(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
|
|
Packit |
a4aae4 |
const string &protocol)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR<< CRLF;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR<< CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (protocol == "")
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "XDAP: " << protocol << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: ";
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-type: text/html" << CRLF;
|
|
Packit |
a4aae4 |
// See note above about Content-Description header. jhrg 12/23/05
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF;
|
|
Packit |
a4aae4 |
if (type == dods_error) // don't cache our error responses.
|
|
Packit |
a4aae4 |
strm << "Cache-Control: no-cache" << CRLF;
|
|
Packit |
a4aae4 |
// Don't write a Content-Encoding header for x-plain since that breaks
|
|
Packit |
a4aae4 |
// Netscape on NT. jhrg 3/23/97
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF;
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Write an HTTP 1.0 response header for our binary response document (i.e.,
|
|
Packit |
a4aae4 |
the DataDDS object).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param out Write the MIME header to this FILE pointer.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_binary(FILE *out, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
set_mime_binary(oss, type, ver, enc, last_modified);
|
|
Packit |
a4aae4 |
fwrite(oss.str().data(), 1, oss.str().length(), out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Write an HTTP 1.0 response header for our binary response document (i.e.,
|
|
Packit |
a4aae4 |
the DataDDS object).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_binary(ostream &strm, ObjectType type, const string &ver,
|
|
Packit |
a4aae4 |
EncodingType enc, const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF ;
|
|
Packit |
a4aae4 |
if (ver == "") {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: " ;
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-Type: application/octet-stream" << CRLF ;
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Write an HTTP 1.0 response header for our binary response document (i.e.,
|
|
Packit |
a4aae4 |
the DataDDS object).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param type The type of this this response. Defaults to
|
|
Packit |
a4aae4 |
application/octet-stream.
|
|
Packit |
a4aae4 |
@param ver The version string; denotes the libdap implementation
|
|
Packit |
a4aae4 |
version.
|
|
Packit |
a4aae4 |
@param enc How is this response encoded? Can be plain or deflate or the
|
|
Packit |
a4aae4 |
x_... versions of those. Default is x_plain.
|
|
Packit |
a4aae4 |
@param last_modified The time to use for the Last-Modified header value.
|
|
Packit |
a4aae4 |
Default is zero which means use the current time.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void set_mime_binary(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
|
|
Packit |
a4aae4 |
const string &protocol)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (protocol.empty())
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "XDAP: " << protocol << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: ";
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-Type: application/octet-stream" << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF;
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void set_mime_multipart(ostream &strm, const string &boundary,
|
|
Packit |
a4aae4 |
const string &start, ObjectType type,
|
|
Packit |
a4aae4 |
const string &version, EncodingType enc,
|
|
Packit |
a4aae4 |
const time_t last_modified)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 200 OK" << CRLF ;
|
|
Packit |
a4aae4 |
if (version == "") {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << version.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: " ;
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-Type: Multipart/Related; boundary=" << boundary
|
|
Packit |
a4aae4 |
<< "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Build the initial headers for the DAP4 data response */
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, EncodingType enc,
|
|
Packit |
a4aae4 |
const time_t last_modified, const string &protocol, const string &url)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.1 200 OK" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Last-Modified: ";
|
|
Packit |
a4aae4 |
if (last_modified > 0)
|
|
Packit |
a4aae4 |
strm << rfc822_date(last_modified).c_str() << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << rfc822_date(t).c_str() << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "Content-Type: multipart/related; boundary=" << boundary << "; start=\"<" << start
|
|
Packit |
a4aae4 |
<< ">\"; type=\"text/xml\"" << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// data-ddx;"; removed as a result of the merge of the hyrax 1.8 release
|
|
Packit |
a4aae4 |
// branch.
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << ";";
|
|
Packit |
a4aae4 |
if (!url.empty())
|
|
Packit |
a4aae4 |
strm << " url=\"" << url << "\"" << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (protocol == "")
|
|
Packit |
a4aae4 |
strm << "X-DAP: " << DAP_PROTOCOL_VERSION << CRLF;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
strm << "X-DAP: " << protocol << CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void set_mime_ddx_boundary(ostream &strm, const string &boundary,
|
|
Packit |
a4aae4 |
const string &cid, ObjectType type, EncodingType enc)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "--" << boundary << CRLF;
|
|
Packit |
a4aae4 |
// TODO - Bite the bullet and make the encoding UTF-8 as required by dap4. This will break a lot of tests but the baselines could be amended using a bash script and sed.
|
|
Packit |
a4aae4 |
strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Id: <" << cid << ">" << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void set_mime_data_boundary(ostream &strm, const string &boundary,
|
|
Packit |
a4aae4 |
const string &cid, ObjectType type, EncodingType enc)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "--" << boundary << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Type: application/octet-stream" << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Id: <" << cid << ">" << CRLF;
|
|
Packit |
a4aae4 |
strm << "Content-Description: " << descrip[type] << CRLF ;
|
|
Packit |
a4aae4 |
if (enc != x_plain)
|
|
Packit |
a4aae4 |
strm << "Content-Encoding: " << encoding[enc] << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
strm << CRLF;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const size_t line_length = 1024;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Read the next MIME header from the input stream and return it in a string
|
|
Packit |
a4aae4 |
object. This function consumes any leading whitespace before the next
|
|
Packit |
a4aae4 |
header. It returns an empty string when the blank line that separates
|
|
Packit |
a4aae4 |
the headers from the body is found. This function works for header and
|
|
Packit |
a4aae4 |
separator lines that use either a CRLF pair (the correct line ending) or
|
|
Packit |
a4aae4 |
just a newline (a common error).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@note FIXME: This function returns tainted data.
|
|
Packit |
a4aae4 |
@param in Read from this stream (FILE *)
|
|
Packit |
a4aae4 |
@return A string that contains the next header line or is empty indicating
|
|
Packit |
a4aae4 |
the separator has been read.
|
|
Packit |
a4aae4 |
@exception Error is thrown if no header or separator is found.
|
|
Packit |
a4aae4 |
@see parse_mime_header()
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string get_next_mime_header(FILE *in)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Get the header line and strip \r\n. Some headers end with just \n.
|
|
Packit |
a4aae4 |
// If a blank line is found, return an empty string.
|
|
Packit |
a4aae4 |
char line[line_length];
|
|
Packit |
a4aae4 |
while (!feof(in)) {
|
|
Packit |
a4aae4 |
if (fgets(line, line_length, in)
|
|
Packit |
a4aae4 |
&& (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
|
|
Packit |
a4aae4 |
return "";
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
size_t slen = min(strlen(line), line_length); // Never > line_length
|
|
Packit |
a4aae4 |
line[slen - 1] = '\0'; // remove the newline
|
|
Packit |
a4aae4 |
if (line[slen - 2] == '\r') // ...and the preceding carriage return
|
|
Packit |
a4aae4 |
line[slen - 2] = '\0';
|
|
Packit |
a4aae4 |
return string(line);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
throw Error("I expected to find a MIME header, but got EOF instead.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* This function and its companion read the next MIME header from an
|
|
Packit |
a4aae4 |
* input stream (or FILE*) and return it as a string. The stream
|
|
Packit |
a4aae4 |
* pointer is updated to the next line of input. It returns an empty
|
|
Packit |
a4aae4 |
* string when the blank line ending the MIME headers is found.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note FIXME: This function returns tainted data.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param in Read the next header from the input stream
|
|
Packit |
a4aae4 |
* @return Return the next header in a string; an empty string indicates
|
|
Packit |
a4aae4 |
* all headers have been returned.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string get_next_mime_header(istream &in)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
// Get the header line and strip \r\n. Some headers end with just \n.
|
|
Packit |
a4aae4 |
// If a blank line is found, return an empty string.
|
|
Packit |
a4aae4 |
char line[line_length];
|
|
Packit |
a4aae4 |
while (!in.eof()) {
|
|
Packit |
a4aae4 |
in.getline(line, line_length);
|
|
Packit |
a4aae4 |
if (strncmp(line, CRLF, 2) == 0 || line[0] == '\n') {
|
|
Packit |
a4aae4 |
return "";
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
size_t slen = min(strlen(line), line_length); // Never > line_length
|
|
Packit |
a4aae4 |
line[slen - 1] = '\0'; // remove the newline
|
|
Packit |
a4aae4 |
if (line[slen - 2] == '\r') // ...and the preceding carriage return
|
|
Packit |
a4aae4 |
line[slen - 2] = '\0';
|
|
Packit |
a4aae4 |
return string(line);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
// Get the header line and strip \r\n. Some headers end with just \n.
|
|
Packit |
a4aae4 |
// If a blank line is found, return an empty string.
|
|
Packit |
a4aae4 |
char raw_line[line_length];
|
|
Packit |
a4aae4 |
while (!in.eof()) {
|
|
Packit |
a4aae4 |
in.getline(raw_line, line_length); // strips the trailing newline; terminates with null
|
|
Packit |
a4aae4 |
string line = raw_line;
|
|
Packit |
a4aae4 |
if (line.find('\r') != string::npos)
|
|
Packit |
a4aae4 |
line = line.substr(0, line.size()-1);
|
|
Packit |
a4aae4 |
return line;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
throw Error("I expected to find a MIME header, but got EOF instead.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Given a string that contains a MIME header line, parse it into the
|
|
Packit |
a4aae4 |
the header (name) and its value. Both are downcased.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param header The input line, striped of the ending crlf pair
|
|
Packit |
a4aae4 |
@param name A value-result parameter that holds the header name
|
|
Packit |
a4aae4 |
@param value A value-result parameter that holds the header's value.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void parse_mime_header(const string &header, string &name, string &value)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
istringstream iss(header);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
size_t length = header.length() + 1;
|
|
Packit |
a4aae4 |
vector<char> s(length);
|
|
Packit |
a4aae4 |
//char s[line_length];
|
|
Packit |
a4aae4 |
iss.getline(&s[0], length, ':');
|
|
Packit |
a4aae4 |
name = &s[0];
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
iss.ignore(length, ' ');
|
|
Packit |
a4aae4 |
iss.getline(&s[0], length);
|
|
Packit |
a4aae4 |
value = &s[0];
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
downcase(name);
|
|
Packit |
a4aae4 |
downcase(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Is this string the same as the MPM boundary value?
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@note Since fgets() is used to read into line, it is guaranteed to be
|
|
Packit |
a4aae4 |
null terminated.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param line The input to test
|
|
Packit |
a4aae4 |
@param boundary The complete boundary line to test for, excluding
|
|
Packit |
a4aae4 |
terminating characters.
|
|
Packit |
a4aae4 |
@return true is line is a MPM boundary
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
bool is_boundary(const char *line, const string &boundary)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
|
|
Packit |
a4aae4 |
return false;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
return strncmp(line, boundary.c_str(), boundary.length()) == 0;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Read the next line of input and test to see if it is a multipart MIME
|
|
Packit |
a4aae4 |
boundary line. If the value of boundary is the default (an empty string)
|
|
Packit |
a4aae4 |
then just test that the line starts with "--". In either case, return the
|
|
Packit |
a4aae4 |
value of boundary just read.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param boundary Value of the boundary to look for - optional
|
|
Packit |
a4aae4 |
@param in Read from this FILE*
|
|
Packit |
a4aae4 |
@return The value of teh boundary header read
|
|
Packit |
a4aae4 |
@exception Error if no boundary was found.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string read_multipart_boundary(FILE *in, const string &boundary)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string boundary_line = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
// If the caller passed in a value for the boundary, test for that value,
|
|
Packit |
a4aae4 |
// else just see that this line starts with '--'.
|
|
Packit |
a4aae4 |
// The value of 'boundary_line' is returned by this function.
|
|
Packit |
a4aae4 |
if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
|
|
Packit |
a4aae4 |
|| boundary_line.find("--") != 0)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return boundary_line;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string read_multipart_boundary(istream &in, const string &boundary)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string boundary_line = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
// If the caller passed in a value for the boundary, test for that value,
|
|
Packit |
a4aae4 |
// else just see that this line starts with '--'.
|
|
Packit |
a4aae4 |
// The value of 'boundary_line' is returned by this function.
|
|
Packit |
a4aae4 |
if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
|
|
Packit |
a4aae4 |
|| boundary_line.find("--") != 0)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return boundary_line;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Consume the Multipart MIME headers that prefix the DDX in a DataDDX
|
|
Packit |
a4aae4 |
response. The stream pointer is advanced to the start of the DDX. It might
|
|
Packit |
a4aae4 |
seem odd that this function both takes the value of the MPM boundary as
|
|
Packit |
a4aae4 |
a parameter _and_ returns that value as a result, but this code can be
|
|
Packit |
a4aae4 |
used in two different situations. In one case, it is called on a partial
|
|
Packit |
a4aae4 |
document read from stdin and needs to return the value of boundary to the
|
|
Packit |
a4aae4 |
downstream DDX parser so that code can sense the end of the DDX. In the
|
|
Packit |
a4aae4 |
other case, this function is told the value of boundary and tests for it
|
|
Packit |
a4aae4 |
to ensure document correctness.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param in Read from this stream
|
|
Packit |
a4aae4 |
@param content_type The expected value of the Content-Type header
|
|
Packit |
a4aae4 |
@param object_type The expected value of the Content-Description header
|
|
Packit |
a4aae4 |
@param cid The expected value of the Content-Id header - optional.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@return The value of the MIME boundary
|
|
Packit |
a4aae4 |
@exception Error if the boundary is not found or if any of the expected
|
|
Packit |
a4aae4 |
header values don't match. The optional values are tested only if they
|
|
Packit |
a4aae4 |
are given (the default values are not tested).
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
bool ct = false, cd = false, ci = false;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// The function get_next_mime_header() returns tainted data. Fix this
|
|
Packit |
a4aae4 |
// if we are going to use this code. jhrg 4/18/17
|
|
Packit |
a4aae4 |
string header = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
while (!header.empty()) {
|
|
Packit |
a4aae4 |
string name, value;
|
|
Packit |
a4aae4 |
parse_mime_header(header, name, value);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (name == "content-type") {
|
|
Packit |
a4aae4 |
ct = true;
|
|
Packit |
a4aae4 |
if (value.find(content_type) == string::npos)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "Content-Type for this part of a DAP2 data ddx response must be " + content_type + ".");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (name == "content-description") {
|
|
Packit |
a4aae4 |
cd = true;
|
|
Packit |
a4aae4 |
if (get_description_type(value) != object_type)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "Content-Description for this part of a DAP2 data ddx response must be dods-ddx or dods-data-ddx");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (name == "content-id") {
|
|
Packit |
a4aae4 |
ci = true;
|
|
Packit |
a4aae4 |
if (!cid.empty() && value != cid)
|
|
Packit |
a4aae4 |
throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
header = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void read_multipart_headers(istream &in, const string &content_type, const ObjectType object_type, const string &cid)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
bool ct = false, cd = false, ci = false;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string header = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
while (!header.empty()) {
|
|
Packit |
a4aae4 |
string name, value;
|
|
Packit |
a4aae4 |
parse_mime_header(header, name, value);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (name == "content-type") {
|
|
Packit |
a4aae4 |
ct = true;
|
|
Packit |
a4aae4 |
if (value.find(content_type) == string::npos)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "Content-Type for this part of a DAP4 data response must be " + content_type + ".");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (name == "content-description") {
|
|
Packit |
a4aae4 |
cd = true;
|
|
Packit |
a4aae4 |
if (get_description_type(value) != object_type)
|
|
Packit |
a4aae4 |
throw Error("Content-Description '" + value + "' not the expected value (expected: " + descrip[object_type] + ").");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (name == "content-id") {
|
|
Packit |
a4aae4 |
ci = true;
|
|
Packit |
a4aae4 |
if (!cid.empty() && value != cid)
|
|
Packit |
a4aae4 |
throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
header = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Given a Content-Id read from the DDX, return the value to look for in a
|
|
Packit |
a4aae4 |
MPM Content-Id header. This function downcases the CID to match the value
|
|
Packit |
a4aae4 |
returned by parse_mime_header.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param cid The Content-Id read from the DDX
|
|
Packit |
a4aae4 |
@return The header value to look for.
|
|
Packit |
a4aae4 |
@exception Error if the CID does not start with the string "cid:"
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string cid_to_header_value(const string &cid)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string::size_type offset = cid.find("cid:");
|
|
Packit |
a4aae4 |
if (offset != 0)
|
|
Packit |
a4aae4 |
throw Error(internal_error, "expected CID to start with 'cid:'");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string value = "<";
|
|
Packit |
a4aae4 |
value.append(cid.substr(offset + 4));
|
|
Packit |
a4aae4 |
value.append(">");
|
|
Packit |
a4aae4 |
downcase(value);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return value;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for an Error object.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param out Write the MIME header to this FILE pointer.
|
|
Packit |
a4aae4 |
@param code HTTP 1.0 response code. Should be 400, ... 500, ...
|
|
Packit |
a4aae4 |
@param reason Reason string of the HTTP 1.0 response header.
|
|
Packit |
a4aae4 |
@param version The version string; denotes the DAP spec and implementation
|
|
Packit |
a4aae4 |
version. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_error(FILE *out, int code, const string &reason,
|
|
Packit |
a4aae4 |
const string &version)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
set_mime_error(oss, code, reason, version);
|
|
Packit |
a4aae4 |
fwrite(oss.str().data(), 1, oss.str().length(), out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Generate an HTTP 1.0 response header for an Error object.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@param strm Write the MIME header to this stream.
|
|
Packit |
a4aae4 |
@param code HTTP 1.0 response code. Should be 400, ... 500, ...
|
|
Packit |
a4aae4 |
@param reason Reason string of the HTTP 1.0 response header.
|
|
Packit |
a4aae4 |
@param version The version string; denotes the DAP spec and implementation
|
|
Packit |
a4aae4 |
version. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_error(ostream &strm, int code, const string &reason,
|
|
Packit |
a4aae4 |
const string &version)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
if (version == "") {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << DVR << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
strm << "XDODS-Server: " << version.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << "Cache-Control: no-cache" << CRLF ;
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Use this function to create a response signaling that the target of a
|
|
Packit |
a4aae4 |
conditional get has not been modified relative to the condition given in
|
|
Packit |
a4aae4 |
the request. This will have to be a date until the servers support ETags.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@brief Send a `Not Modified' response.
|
|
Packit |
a4aae4 |
@param out Write the response to this FILE pointer. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_not_modified(FILE *out)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
set_mime_not_modified(oss);
|
|
Packit |
a4aae4 |
fwrite(oss.str().data(), 1, oss.str().length(), out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Use this function to create a response signaling that the target of a
|
|
Packit |
a4aae4 |
conditional get has not been modified relative to the condition given in
|
|
Packit |
a4aae4 |
the request. This will have to be a date until the servers support ETags.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@brief Send a `Not Modified' response.
|
|
Packit |
a4aae4 |
@param strm Write the response to this stream. */
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
set_mime_not_modified(ostream &strm)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
|
|
Packit |
a4aae4 |
const time_t t = time(0);
|
|
Packit |
a4aae4 |
strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
|
|
Packit |
a4aae4 |
strm << CRLF ;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This was removed because it's not being used by our server.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Look for the override file by taking the dataset name and
|
|
Packit |
a4aae4 |
appending `.ovr' to it. If such a file exists, then read it in and
|
|
Packit |
a4aae4 |
store the contents in <tt>doc</tt>. Note that the file contents
|
|
Packit |
a4aae4 |
are not checked to see if they are valid HTML (which they must
|
|
Packit |
a4aae4 |
be).
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@return True if the `override file' is present, false otherwise. in the
|
|
Packit |
a4aae4 |
later case <tt>doc</tt>'s contents are undefined. */
|
|
Packit |
a4aae4 |
bool
|
|
Packit |
a4aae4 |
found_override(string name, string &doc)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ifstream ifs((name + ".ovr").c_str());
|
|
Packit |
a4aae4 |
if (!ifs)
|
|
Packit |
a4aae4 |
return false;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
char tmp[256];
|
|
Packit |
a4aae4 |
doc = "";
|
|
Packit |
a4aae4 |
while (!ifs.eof()) {
|
|
Packit |
a4aae4 |
ifs.getline(tmp, 255);
|
|
Packit |
a4aae4 |
tmp[255] = '\0';
|
|
Packit |
a4aae4 |
strncat(tmp, "\n", sizeof(tmp) - strlen(tmp) - 1);
|
|
Packit |
a4aae4 |
doc += tmp;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
ifs.close();
|
|
Packit |
a4aae4 |
return true;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Read the input stream <tt>in</tt> and discard the MIME header. The MIME
|
|
Packit |
a4aae4 |
header is separated from the body of the document by a single blank line.
|
|
Packit |
a4aae4 |
If no MIME header is found, then the input stream is `emptied' and will
|
|
Packit |
a4aae4 |
contain nothing.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@deprecated
|
|
Packit |
a4aae4 |
@brief Read and discard the MIME header of the stream <tt>in</tt>.
|
|
Packit |
a4aae4 |
@return True if a MIME header is found, false otherwise.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
bool
|
|
Packit |
a4aae4 |
remove_mime_header(FILE *in)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
char tmp[256];
|
|
Packit |
a4aae4 |
while (!feof(in)) {
|
|
Packit |
a4aae4 |
char *s = fgets(tmp, 255, in);
|
|
Packit |
a4aae4 |
if (s && strncmp(s, CRLF, 2) == 0)
|
|
Packit |
a4aae4 |
return true;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return false;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Used for test code; strip the leading MIME headers from a response.
|
|
Packit |
a4aae4 |
* @param in
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
remove_mime_header(istream &in)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
while(!get_next_mime_header(in).empty()) ;
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
string header;
|
|
Packit |
a4aae4 |
do {
|
|
Packit |
a4aae4 |
header = get_next_mime_header(in);
|
|
Packit |
a4aae4 |
} while (!header.empty());
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
} // namespace libdap
|
|
Packit |
a4aae4 |
|