libdap Updated for version 3.20.8
libdap4 is an implementation of OPeNDAP's DAP protocol.
mime_util.cc
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// This file is part of libdap, A C++ implementation of the OPeNDAP Data
5// Access Protocol.
6
7// Copyright (c) 2002,2003 OPeNDAP, Inc.
8// Author: James Gallagher <jgallagher@opendap.org>
9// Reza Nekovei <rnekovei@intcomm.net>
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
26
27// (c) COPYRIGHT URI/MIT 1994-2001
28// Please read the full copyright statement in the file COPYRIGHT_URI.
29//
30// Authors:
31// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
32// reza Reza Nekovei <rnekovei@intcomm.net>
33
34// A few useful routines which are used in CGI programs.
35//
36// ReZa 9/30/94
37
38#include "config.h"
39
40#include <cstring>
41#include <cstdio>
42#include <ctype.h>
43
44#ifndef TM_IN_SYS_TIME
45#include <time.h>
46#else
47#include <sys/time.h>
48#endif
49
50#include <sys/types.h>
51#include <sys/stat.h>
52
53#ifndef WIN32
54#include <unistd.h> // for access
55#include <sys/wait.h>
56#else
57#include <io.h>
58#include <fcntl.h>
59#include <process.h>
60// Win32 does not define this. 08/21/02 jhrg
61#define F_OK 0
62#endif
63
64#include <iostream>
65#include <sstream>
66#include <fstream>
67#include <string>
68
69#include "mime_util.h"
70#include "media_types.h"
71
72#include "Ancillary.h"
73#include "util.h" // This supplies flush_stream for WIN32.
74#include "debug.h"
75
76#ifdef WIN32
77#define FILE_DELIMITER '\\'
78#else // default to unix
79#define FILE_DELIMITER '/'
80#endif
81
82// ...not using a const string here to avoid global objects. jhrg 12/23/05
83#define CRLF "\r\n" // Change here, expr-test.cc, in DODSFilter and ResponseBuilder
84
85using namespace std;
86
87namespace libdap {
88
94time_t
95last_modified_time(const string &name)
96{
97 struct stat m;
98
99 if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
100 return m.st_mtime;
101 else
102 return time(0);
103}
104// Return a MIME rfc-822 date. The grammar for this is:
105// date-time = [ day "," ] date time ; dd mm yy
106// ; hh:mm:ss zzz
107//
108// day = "Mon" / "Tue" / "Wed" / "Thu"
109// / "Fri" / "Sat" / "Sun"
110//
111// date = 1*2DIGIT month 2DIGIT ; day month year
112// ; e.g. 20 Jun 82
113// NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
114//
115// month = "Jan" / "Feb" / "Mar" / "Apr"
116// / "May" / "Jun" / "Jul" / "Aug"
117// / "Sep" / "Oct" / "Nov" / "Dec"
118//
119// time = hour zone ; ANSI and Military
120//
121// hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
122// ; 00:00:00 - 23:59:59
123//
124// zone = "UT" / "GMT" ; Universal Time
125// ; North American : UT
126// / "EST" / "EDT" ; Eastern: - 5/ - 4
127// / "CST" / "CDT" ; Central: - 6/ - 5
128// / "MST" / "MDT" ; Mountain: - 7/ - 6
129// / "PST" / "PDT" ; Pacific: - 8/ - 7
130// / 1ALPHA ; Military: Z = UT;
131// ; A:-1; (J not used)
132// ; M:-12; N:+1; Y:+12
133// / ( ("+" / "-") 4DIGIT ) ; Local differential
134// ; hours+min. (HHMM)
135
136static const char *days[] =
137 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
138 };
139static const char *months[] =
140 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
141 "Aug", "Sep", "Oct", "Nov", "Dec"
142 };
143
144#ifdef _MSC_VER
145#define snprintf sprintf_s
146#endif
155string
156rfc822_date(const time_t t)
157{
158 struct tm *stm = gmtime(&t);
159 if (!stm)
160 return "";
161
162 char d[256];
163
164 snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
165 stm->tm_mday, months[stm->tm_mon],
166 1900 + stm->tm_year,
167 stm->tm_hour, stm->tm_min, stm->tm_sec);
168 d[255] = '\0';
169 return string(d);
170}
171
172static const int TimLen = 26; // length of string from asctime()
173//static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
174
188bool
189do_version(const string &script_ver, const string &dataset_ver)
190{
191 fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
192 fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
193 fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
194 fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
195 fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
196 fprintf(stdout, CRLF) ;
197
198 fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
199
200 if (script_ver != "")
201 fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
202
203 if (dataset_ver != "")
204 fprintf(stdout, "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
205
206 fflush(stdout) ; // Not sure this is needed. jhrg 12/23/05
207
208 return true;
209}
210
221void
222ErrMsgT(const string &Msgt)
223{
224 time_t TimBin;
225 char TimStr[TimLen];
226
227 if (time(&TimBin) == (time_t) - 1)
228 strncpy(TimStr, "time() error ", TimLen-1);
229 else {
230 char *ctime_value = ctime(&TimBin);
231 if (!ctime_value)
232 strncpy(TimStr, "Unknown", TimLen-1);
233 else {
234 strncpy(TimStr, ctime_value, TimLen-1);
235 TimStr[TimLen - 2] = '\0'; // overwrite the \n
236 }
237#if 0
238 strncpy(TimStr, ctime(&TimBin), TimLen-1);
239 TimStr[TimLen - 2] = '\0'; // overwrite the \n
240#endif
241 }
242
243 cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
244}
245
246// Given a pathname, return just the filename component with any extension
247// removed. The new string resides in newly allocated memory; the caller must
248// delete it when done using the filename.
249// Originally from the netcdf distribution (ver 2.3.2).
250//
251// *** Change to string class argument and return type. jhrg
252// *** Changed so it also removes the#path#of#the#file# from decompressed
253// files. rph.
254// Returns: A filename, with path and extension information removed. If
255// memory for the new name cannot be allocated, does not return!
256
267string
268name_path(const string &path)
269{
270 if (path == "")
271 return string("");
272
273 string::size_type delim = path.find_last_of(FILE_DELIMITER);
274 string::size_type pound = path.find_last_of("#");
275 string new_path;
276
277 if (pound != string::npos)
278 new_path = path.substr(pound + 1);
279 else
280 new_path = path.substr(delim + 1);
281
282 return new_path;
283}
284
285// Send string to set the transfer (mime) type and server version
286// Note that the content description filed is used to indicate whether valid
287// information of an error message is contained in the document and the
288// content-encoding field is used to indicate whether the data is compressed.
289// If the data stream is to be compressed, arrange for a compression output
290// filter so that all information sent after the header will be compressed.
291//
292// Returns: false if the compression output filter was to be used but could
293// not be started, true otherwise.
294#if 0
295static const char *descrip[] =
296 {"unknown", "dods_das", "dods_dds", "dods_data", "dods_ddx",
297 "dods_error", "web_error", "dap4-dmr", "dap4-data", "dap4-error"
298 };
299#endif
300
301static const char *descrip[] = {
302"unknown_type",
303"dods_das",
304"dods_dds",
305"dods_data",
306"dods_ddx", // This is the old XML DDS/DAS used prior to dap4
307"dods_data_ddx", // This is used for caching data responses
308"dods_error",
309"web_error",
310
311"dap4_dmr", // DAP4 metadata
312"dap4_data", // The DMR with a data blob
313"dap4_error" // The error response for DAP4
314};
315
316static const char *encoding[] =
317 {"unknown", "deflate", "x-plain", "gzip", "binary"
318 };
319
326get_type(const string &value)
327{
328 return get_description_type(value);
329}
330
331// TODO Recode to use the constants in media_types.h. jhrg 11/12/13
332
339get_description_type(const string &value)
340{
341 if ((value == DAS1) || (value == "dods-das"))
342 return dods_das;
343 else if ((value == "dods_dds") || (value == "dods-dds"))
344 return dods_dds;
345 else if ((value == "dods_data") || (value == "dods-data"))
346 return dods_data;
347 else if ((value == "dods_ddx") || (value == "dods-ddx"))
348 return dods_ddx;
349 else if ((value == "dods_data_ddx" || (value == "dods-data-ddx")))
350 return dods_data_ddx;
351 else if ((value == "dods_error") || (value == "dods-error"))
352 return dods_error;
353 else if ((value == "web_error") || (value == "web-error"))
354 return web_error;
355
356 else if ((value == "dap4_dmr") || (value == "dap4-dmr") || (value == DMR_Content_Type))
357 return dap4_dmr;
358 else if ((value == "dap4_data") || (value == "dap4-data") || (value == DAP4_DATA_Content_Type))
359 return dap4_data;
360 else if ((value == "dap4_error") || (value == "dap4-error"))
361 return dap4_error;
362
363 else
364 return unknown_type;
365}
366
380void
381set_mime_text(FILE *out, ObjectType type, const string &ver,
382 EncodingType enc, const time_t last_modified)
383{
384 ostringstream oss;
385 set_mime_text(oss, type, ver, enc, last_modified);
386 fwrite(oss.str().data(), 1, oss.str().length(), out);
387}
388
402void
403set_mime_text(ostream &strm, ObjectType type, const string &ver,
404 EncodingType enc, const time_t last_modified)
405{
406 strm << "HTTP/1.0 200 OK" << CRLF ;
407 if (ver == "") {
408 strm << "XDODS-Server: " << DVR << CRLF ;
409 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
410 }
411 else {
412 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
413 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
414 }
415 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
416
417 const time_t t = time(0);
418 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
419
420 strm << "Last-Modified: " ;
421 if (last_modified > 0)
422 strm << rfc822_date(last_modified).c_str() << CRLF ;
423 else
424 strm << rfc822_date(t).c_str() << CRLF ;
425
426 if (type == dap4_dmr)
427 strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF ;
428 else
429 strm << "Content-Type: text/plain" << CRLF ;
430
431 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
432 // jhrg 12/23/05
433 strm << "Content-Description: " << descrip[type] << CRLF ;
434 if (type == dods_error) // don't cache our error responses.
435 strm << "Cache-Control: no-cache" << CRLF ;
436 // Don't write a Content-Encoding header for x-plain since that breaks
437 // Netscape on NT. jhrg 3/23/97
438 if (enc != x_plain)
439 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
440 strm << CRLF ;
441}
442
458void set_mime_text(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
459 const string &protocol)
460{
461 strm << "HTTP/1.0 200 OK" << CRLF;
462
463 strm << "XDODS-Server: " << DVR << CRLF;
464 strm << "XOPeNDAP-Server: " << DVR << CRLF;
465
466 if (protocol == "")
467 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
468 else
469 strm << "XDAP: " << protocol << CRLF;
470
471 const time_t t = time(0);
472 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
473
474 strm << "Last-Modified: ";
475 if (last_modified > 0)
476 strm << rfc822_date(last_modified).c_str() << CRLF;
477 else
478 strm << rfc822_date(t).c_str() << CRLF;
479
480 if (type == dap4_dmr)
481 strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
482 else
483 strm << "Content-Type: text/plain" << CRLF;
484
485 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
486 // jhrg 12/23/05
487 strm << "Content-Description: " << descrip[type] << CRLF;
488 if (type == dods_error) // don't cache our error responses.
489 strm << "Cache-Control: no-cache" << CRLF;
490 // Don't write a Content-Encoding header for x-plain since that breaks
491 // Netscape on NT. jhrg 3/23/97
492 if (enc != x_plain)
493 strm << "Content-Encoding: " << encoding[enc] << CRLF;
494 strm << CRLF;
495}
496
508void
509set_mime_html(FILE *out, ObjectType type, const string &ver,
510 EncodingType enc, const time_t last_modified)
511{
512 ostringstream oss;
513 set_mime_html(oss, type, ver, enc, last_modified);
514 fwrite(oss.str().data(), 1, oss.str().length(), out);
515}
516
528void
529set_mime_html(ostream &strm, ObjectType type, const string &ver,
530 EncodingType enc, const time_t last_modified)
531{
532 strm << "HTTP/1.0 200 OK" << CRLF ;
533 if (ver == "") {
534 strm << "XDODS-Server: " << DVR << CRLF ;
535 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
536 }
537 else {
538 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
539 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
540 }
541 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
542
543 const time_t t = time(0);
544 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
545
546 strm << "Last-Modified: " ;
547 if (last_modified > 0)
548 strm << rfc822_date(last_modified).c_str() << CRLF ;
549 else
550 strm << rfc822_date(t).c_str() << CRLF ;
551
552 strm << "Content-type: text/html" << CRLF ;
553 // See note above about Content-Description header. jhrg 12/23/05
554 strm << "Content-Description: " << descrip[type] << CRLF ;
555 if (type == dods_error) // don't cache our error responses.
556 strm << "Cache-Control: no-cache" << CRLF ;
557 // Don't write a Content-Encoding header for x-plain since that breaks
558 // Netscape on NT. jhrg 3/23/97
559 if (enc != x_plain)
560 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
561 strm << CRLF ;
562}
563
574void set_mime_html(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
575 const string &protocol)
576{
577 strm << "HTTP/1.0 200 OK" << CRLF;
578
579 strm << "XDODS-Server: " << DVR<< CRLF;
580 strm << "XOPeNDAP-Server: " << DVR<< CRLF;
581
582 if (protocol == "")
583 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
584 else
585 strm << "XDAP: " << protocol << CRLF;
586
587 const time_t t = time(0);
588 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
589
590 strm << "Last-Modified: ";
591 if (last_modified > 0)
592 strm << rfc822_date(last_modified).c_str() << CRLF;
593 else
594 strm << rfc822_date(t).c_str() << CRLF;
595
596 strm << "Content-type: text/html" << CRLF;
597 // See note above about Content-Description header. jhrg 12/23/05
598 strm << "Content-Description: " << descrip[type] << CRLF;
599 if (type == dods_error) // don't cache our error responses.
600 strm << "Cache-Control: no-cache" << CRLF;
601 // Don't write a Content-Encoding header for x-plain since that breaks
602 // Netscape on NT. jhrg 3/23/97
603 if (enc != x_plain)
604 strm << "Content-Encoding: " << encoding[enc] << CRLF;
605 strm << CRLF;
606}
607
622void
623set_mime_binary(FILE *out, ObjectType type, const string &ver,
624 EncodingType enc, const time_t last_modified)
625{
626 ostringstream oss;
627 set_mime_binary(oss, type, ver, enc, last_modified);
628 fwrite(oss.str().data(), 1, oss.str().length(), out);
629}
630
645void
646set_mime_binary(ostream &strm, ObjectType type, const string &ver,
647 EncodingType enc, const time_t last_modified)
648{
649 strm << "HTTP/1.0 200 OK" << CRLF ;
650 if (ver == "") {
651 strm << "XDODS-Server: " << DVR << CRLF ;
652 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
653 }
654 else {
655 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
656 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
657 }
658 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
659
660 const time_t t = time(0);
661 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
662
663 strm << "Last-Modified: " ;
664 if (last_modified > 0)
665 strm << rfc822_date(last_modified).c_str() << CRLF ;
666 else
667 strm << rfc822_date(t).c_str() << CRLF ;
668
669 strm << "Content-Type: application/octet-stream" << CRLF ;
670 strm << "Content-Description: " << descrip[type] << CRLF ;
671 if (enc != x_plain)
672 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
673
674 strm << CRLF ;
675}
676
690void set_mime_binary(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
691 const string &protocol)
692{
693 strm << "HTTP/1.0 200 OK" << CRLF;
694
695 strm << "XDODS-Server: " << DVR << CRLF;
696 strm << "XOPeNDAP-Server: " << DVR << CRLF;
697
698 if (protocol.empty())
699 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
700 else
701 strm << "XDAP: " << protocol << CRLF;
702
703 const time_t t = time(0);
704 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
705
706 strm << "Last-Modified: ";
707 if (last_modified > 0)
708 strm << rfc822_date(last_modified).c_str() << CRLF;
709 else
710 strm << rfc822_date(t).c_str() << CRLF;
711
712 strm << "Content-Type: application/octet-stream" << CRLF;
713 strm << "Content-Description: " << descrip[type] << CRLF;
714 if (enc != x_plain)
715 strm << "Content-Encoding: " << encoding[enc] << CRLF;
716
717 strm << CRLF;
718}
719
720void set_mime_multipart(ostream &strm, const string &boundary,
721 const string &start, ObjectType type,
722 const string &version, EncodingType enc,
723 const time_t last_modified)
724{
725 strm << "HTTP/1.0 200 OK" << CRLF ;
726 if (version == "") {
727 strm << "XDODS-Server: " << DVR << CRLF ;
728 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
729 }
730 else {
731 strm << "XDODS-Server: " << version.c_str() << CRLF ;
732 strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
733 }
734 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
735
736 const time_t t = time(0);
737 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
738
739 strm << "Last-Modified: " ;
740 if (last_modified > 0)
741 strm << rfc822_date(last_modified).c_str() << CRLF ;
742 else
743 strm << rfc822_date(t).c_str() << CRLF ;
744
745 strm << "Content-Type: Multipart/Related; boundary=" << boundary
746 << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
747 strm << "Content-Description: " << descrip[type] << CRLF ;
748 if (enc != x_plain)
749 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
750
751 strm << CRLF ;
752}
753
756void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, EncodingType enc,
757 const time_t last_modified, const string &protocol, const string &url)
758{
759 strm << "HTTP/1.1 200 OK" << CRLF;
760
761 const time_t t = time(0);
762 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
763
764 strm << "Last-Modified: ";
765 if (last_modified > 0)
766 strm << rfc822_date(last_modified).c_str() << CRLF;
767 else
768 strm << rfc822_date(t).c_str() << CRLF;
769
770 strm << "Content-Type: multipart/related; boundary=" << boundary << "; start=\"<" << start
771 << ">\"; type=\"text/xml\"" << CRLF;
772
773 // data-ddx;"; removed as a result of the merge of the hyrax 1.8 release
774 // branch.
775 strm << "Content-Description: " << descrip[type] << ";";
776 if (!url.empty())
777 strm << " url=\"" << url << "\"" << CRLF;
778 else
779 strm << CRLF;
780
781 if (enc != x_plain)
782 strm << "Content-Encoding: " << encoding[enc] << CRLF;
783
784 if (protocol == "")
785 strm << "X-DAP: " << DAP_PROTOCOL_VERSION << CRLF;
786 else
787 strm << "X-DAP: " << protocol << CRLF;
788
789 strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
790
791 strm << CRLF;
792}
793
794void set_mime_ddx_boundary(ostream &strm, const string &boundary,
795 const string &cid, ObjectType type, EncodingType enc)
796{
797 strm << "--" << boundary << CRLF;
798 // 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.
799 strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
800 strm << "Content-Id: <" << cid << ">" << CRLF;
801 strm << "Content-Description: " << descrip[type] << CRLF ;
802 if (enc != x_plain)
803 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
804
805 strm << CRLF;
806}
807
808void set_mime_data_boundary(ostream &strm, const string &boundary,
809 const string &cid, ObjectType type, EncodingType enc)
810{
811 strm << "--" << boundary << CRLF;
812 strm << "Content-Type: application/octet-stream" << CRLF;
813 strm << "Content-Id: <" << cid << ">" << CRLF;
814 strm << "Content-Description: " << descrip[type] << CRLF ;
815 if (enc != x_plain)
816 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
817
818 strm << CRLF;
819}
820
821const size_t line_length = 1024;
822
838string get_next_mime_header(FILE *in)
839{
840 // Get the header line and strip \r\n. Some headers end with just \n.
841 // If a blank line is found, return an empty string.
842 char line[line_length];
843 while (!feof(in)) {
844 if (fgets(line, line_length, in)
845 && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
846 return "";
847 else {
848 size_t slen = min(strlen(line), line_length); // Never > line_length
849 line[slen - 1] = '\0'; // remove the newline
850 if (line[slen - 2] == '\r') // ...and the preceding carriage return
851 line[slen - 2] = '\0';
852 return string(line);
853 }
854 }
855
856 throw Error("I expected to find a MIME header, but got EOF instead.");
857}
858
871string get_next_mime_header(istream &in)
872{
873#if 0
874 // Get the header line and strip \r\n. Some headers end with just \n.
875 // If a blank line is found, return an empty string.
876 char line[line_length];
877 while (!in.eof()) {
878 in.getline(line, line_length);
879 if (strncmp(line, CRLF, 2) == 0 || line[0] == '\n') {
880 return "";
881 }
882 else {
883 size_t slen = min(strlen(line), line_length); // Never > line_length
884 line[slen - 1] = '\0'; // remove the newline
885 if (line[slen - 2] == '\r') // ...and the preceding carriage return
886 line[slen - 2] = '\0';
887 return string(line);
888 }
889 }
890#endif
891 // Get the header line and strip \r\n. Some headers end with just \n.
892 // If a blank line is found, return an empty string.
893 char raw_line[line_length];
894 while (!in.eof()) {
895 in.getline(raw_line, line_length); // strips the trailing newline; terminates with null
896 string line = raw_line;
897 if (line.find('\r') != string::npos)
898 line = line.substr(0, line.size()-1);
899 return line;
900 }
901
902 throw Error("I expected to find a MIME header, but got EOF instead.");
903}
904
912void parse_mime_header(const string &header, string &name, string &value)
913{
914 istringstream iss(header);
915
916 size_t length = header.length() + 1;
917 vector<char> s(length);
918 //char s[line_length];
919 iss.getline(&s[0], length, ':');
920 name = &s[0];
921
922 iss.ignore(length, ' ');
923 iss.getline(&s[0], length);
924 value = &s[0];
925
926 downcase(name);
927 downcase(value);
928}
929
941bool is_boundary(const char *line, const string &boundary)
942{
943 if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
944 return false;
945 else
946 return strncmp(line, boundary.c_str(), boundary.length()) == 0;
947}
948
959string read_multipart_boundary(FILE *in, const string &boundary)
960{
961 string boundary_line = get_next_mime_header(in);
962 // If the caller passed in a value for the boundary, test for that value,
963 // else just see that this line starts with '--'.
964 // The value of 'boundary_line' is returned by this function.
965 if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
966 || boundary_line.find("--") != 0)
967 throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
968
969 return boundary_line;
970}
971
972string read_multipart_boundary(istream &in, const string &boundary)
973{
974 string boundary_line = get_next_mime_header(in);
975 // If the caller passed in a value for the boundary, test for that value,
976 // else just see that this line starts with '--'.
977 // The value of 'boundary_line' is returned by this function.
978 if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
979 || boundary_line.find("--") != 0)
980 throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
981
982 return boundary_line;
983}
984
1005void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
1006{
1007 bool ct = false, cd = false, ci = false;
1008
1009 // The function get_next_mime_header() returns tainted data. Fix this
1010 // if we are going to use this code. jhrg 4/18/17
1011 string header = get_next_mime_header(in);
1012 while (!header.empty()) {
1013 string name, value;
1014 parse_mime_header(header, name, value);
1015
1016 if (name == "content-type") {
1017 ct = true;
1018 if (value.find(content_type) == string::npos)
1019 throw Error(internal_error, "Content-Type for this part of a DAP2 data ddx response must be " + content_type + ".");
1020 }
1021 else if (name == "content-description") {
1022 cd = true;
1023 if (get_description_type(value) != object_type)
1024 throw Error(internal_error, "Content-Description for this part of a DAP2 data ddx response must be dods-ddx or dods-data-ddx");
1025 }
1026 else if (name == "content-id") {
1027 ci = true;
1028 if (!cid.empty() && value != cid)
1029 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
1030 }
1031
1032 header = get_next_mime_header(in);
1033 }
1034
1035 if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
1036}
1037
1038void read_multipart_headers(istream &in, const string &content_type, const ObjectType object_type, const string &cid)
1039{
1040 bool ct = false, cd = false, ci = false;
1041
1042 string header = get_next_mime_header(in);
1043 while (!header.empty()) {
1044 string name, value;
1045 parse_mime_header(header, name, value);
1046
1047 if (name == "content-type") {
1048 ct = true;
1049 if (value.find(content_type) == string::npos)
1050 throw Error(internal_error, "Content-Type for this part of a DAP4 data response must be " + content_type + ".");
1051 }
1052 else if (name == "content-description") {
1053 cd = true;
1054 if (get_description_type(value) != object_type)
1055 throw Error("Content-Description '" + value + "' not the expected value (expected: " + descrip[object_type] + ").");
1056 }
1057 else if (name == "content-id") {
1058 ci = true;
1059 if (!cid.empty() && value != cid)
1060 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
1061 }
1062
1063 header = get_next_mime_header(in);
1064 }
1065
1066 if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
1067}
1068
1077string cid_to_header_value(const string &cid)
1078{
1079 string::size_type offset = cid.find("cid:");
1080 if (offset != 0)
1081 throw Error(internal_error, "expected CID to start with 'cid:'");
1082
1083 string value = "<";
1084 value.append(cid.substr(offset + 4));
1085 value.append(">");
1086 downcase(value);
1087
1088 return value;
1089}
1090
1099void
1100set_mime_error(FILE *out, int code, const string &reason,
1101 const string &version)
1102{
1103 ostringstream oss;
1104 set_mime_error(oss, code, reason, version);
1105 fwrite(oss.str().data(), 1, oss.str().length(), out);
1106}
1107
1116void
1117set_mime_error(ostream &strm, int code, const string &reason,
1118 const string &version)
1119{
1120 strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
1121 if (version == "") {
1122 strm << "XDODS-Server: " << DVR << CRLF ;
1123 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
1124 }
1125 else {
1126 strm << "XDODS-Server: " << version.c_str() << CRLF ;
1127 strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
1128 }
1129 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
1130
1131 const time_t t = time(0);
1132 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
1133 strm << "Cache-Control: no-cache" << CRLF ;
1134 strm << CRLF ;
1135}
1136
1144void
1146{
1147 ostringstream oss;
1149 fwrite(oss.str().data(), 1, oss.str().length(), out);
1150}
1151
1159void
1161{
1162 strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
1163 const time_t t = time(0);
1164 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
1165 strm << CRLF ;
1166}
1167
1168#if 0
1169
1170// This was removed because it's not being used by our server.
1171
1181bool
1182found_override(string name, string &doc)
1183{
1184 ifstream ifs((name + ".ovr").c_str());
1185 if (!ifs)
1186 return false;
1187
1188 char tmp[256];
1189 doc = "";
1190 while (!ifs.eof()) {
1191 ifs.getline(tmp, 255);
1192 tmp[255] = '\0';
1193 strncat(tmp, "\n", sizeof(tmp) - strlen(tmp) - 1);
1194 doc += tmp;
1195 }
1196
1197 ifs.close();
1198 return true;
1199}
1200#endif
1201
1211bool
1213{
1214 char tmp[256];
1215 while (!feof(in)) {
1216 char *s = fgets(tmp, 255, in);
1217 if (s && strncmp(s, CRLF, 2) == 0)
1218 return true;
1219 }
1220
1221 return false;
1222}
1223
1228void
1230{
1231 while(!get_next_mime_header(in).empty()) ;
1232#if 0
1233 string header;
1234 do {
1235 header = get_next_mime_header(in);
1236 } while (!header.empty());
1237#endif
1238}
1239
1240} // namespace libdap
1241
A class for error processing.
Definition Error.h:94
top level DAP object to house generic methods
string read_multipart_boundary(FILE *in, const string &boundary)
Definition mime_util.cc:959
void set_mime_error(FILE *out, int code, const string &reason, const string &version)
void set_mime_html(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition mime_util.cc:509
ObjectType get_description_type(const string &value)
Definition mime_util.cc:339
string cid_to_header_value(const string &cid)
void parse_mime_header(const string &header, string &name, string &value)
Definition mime_util.cc:912
time_t last_modified_time(const string &name)
Definition mime_util.cc:95
string name_path(const string &path)
Returns the filename portion of a pathname.
Definition mime_util.cc:268
void set_mime_not_modified(FILE *out)
Send a ‘Not Modified’ response.
void set_mime_binary(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition mime_util.cc:623
bool do_version(const string &script_ver, const string &dataset_ver)
Send a version number.
Definition mime_util.cc:189
void downcase(string &s)
Definition util.cc:563
bool remove_mime_header(FILE *in)
Read and discard the MIME header of the stream in.
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
EncodingType
The type of encoding used on the current stream.
void ErrMsgT(const string &Msgt)
Logs an error message.
Definition mime_util.cc:222
ObjectType get_type(const string &value)
Definition mime_util.cc:326
string rfc822_date(const time_t t)
Definition mime_util.cc:156
bool is_boundary(const char *line, const string &boundary)
Definition mime_util.cc:941
ObjectType
The type of object in the stream coming from the data server.
Definition ObjectType.h:58
void set_mime_text(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition mime_util.cc:381
string get_next_mime_header(FILE *in)
Definition mime_util.cc:838