libdap Updated for version 3.20.10
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;
159 const struct tm *ret = gmtime_r(&t, &stm);
160 if (!ret)
161 return "";
162
163 char d[256];
164
165 snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm.tm_wday],
166 stm.tm_mday, months[stm.tm_mon],
167 1900 + stm.tm_year,
168 stm.tm_hour, stm.tm_min, stm.tm_sec);
169 d[255] = '\0';
170 return {d};
171}
172
173static const int TimLen = 26; // length of string from asctime()
174//static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
175
189bool
190do_version(const string &script_ver, const string &dataset_ver)
191{
192 fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
193 fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
194 fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
195 fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
196 fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
197 fprintf(stdout, CRLF) ;
198
199 fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
200
201 if (script_ver != "")
202 fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
203
204 if (dataset_ver != "")
205 fprintf(stdout, "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
206
207 fflush(stdout) ; // Not sure this is needed. jhrg 12/23/05
208
209 return true;
210}
211
222void
223ErrMsgT(const string &Msgt)
224{
225 time_t TimBin;
226 char TimStr[TimLen];
227
228 if (time(&TimBin) == (time_t) - 1)
229 strncpy(TimStr, "time() error ", TimLen-1);
230 else {
231 char ctime_value[TimLen];
232 const char *ret = ctime_r(&TimBin, ctime_value);
233 if (!ret)
234 strncpy(TimStr, "Unknown", TimLen-1);
235 else {
236 strncpy(TimStr, ctime_value, TimLen-1);
237 TimStr[TimLen - 2] = '\0'; // overwrite the \n
238 }
239 }
240
241 cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
242}
243
244// Given a pathname, return just the filename component with any extension
245// removed. The new string resides in newly allocated memory; the caller must
246// delete it when done using the filename.
247// Originally from the netcdf distribution (ver 2.3.2).
248//
249// *** Change to string class argument and return type. jhrg
250// *** Changed so it also removes the#path#of#the#file# from decompressed
251// files. rph.
252// Returns: A filename, with path and extension information removed. If
253// memory for the new name cannot be allocated, does not return!
254
265string
266name_path(const string &path)
267{
268 if (path == "")
269 return string("");
270
271 string::size_type delim = path.find_last_of(FILE_DELIMITER);
272 string::size_type pound = path.find_last_of("#");
273 string new_path;
274
275 if (pound != string::npos)
276 new_path = path.substr(pound + 1);
277 else
278 new_path = path.substr(delim + 1);
279
280 return new_path;
281}
282
283// Send string to set the transfer (mime) type and server version
284// Note that the content description filed is used to indicate whether valid
285// information of an error message is contained in the document and the
286// content-encoding field is used to indicate whether the data is compressed.
287// If the data stream is to be compressed, arrange for a compression output
288// filter so that all information sent after the header will be compressed.
289//
290// Returns: false if the compression output filter was to be used but could
291// not be started, true otherwise.
292#if 0
293static const char *descrip[] =
294 {"unknown", "dods_das", "dods_dds", "dods_data", "dods_ddx",
295 "dods_error", "web_error", "dap4-dmr", "dap4-data", "dap4-error"
296 };
297#endif
298
299static const char *descrip[] = {
300"unknown_type",
301"dods_das",
302"dods_dds",
303"dods_data",
304"dods_ddx", // This is the old XML DDS/DAS used prior to dap4
305"dods_data_ddx", // This is used for caching data responses
306"dods_error",
307"web_error",
308
309"dap4_dmr", // DAP4 metadata
310"dap4_data", // The DMR with a data blob
311"dap4_error" // The error response for DAP4
312};
313
314static const char *encoding[] =
315 {"unknown", "deflate", "x-plain", "gzip", "binary"
316 };
317
323ObjectType
324get_type(const string &value)
325{
326 return get_description_type(value);
327}
328
329// TODO Recode to use the constants in media_types.h. jhrg 11/12/13
330
336ObjectType
337get_description_type(const string &value)
338{
339 if ((value == DAS1) || (value == "dods-das"))
340 return dods_das;
341 else if ((value == "dods_dds") || (value == "dods-dds"))
342 return dods_dds;
343 else if ((value == "dods_data") || (value == "dods-data"))
344 return dods_data;
345 else if ((value == "dods_ddx") || (value == "dods-ddx"))
346 return dods_ddx;
347 else if ((value == "dods_data_ddx" || (value == "dods-data-ddx")))
348 return dods_data_ddx;
349 else if ((value == "dods_error") || (value == "dods-error"))
350 return dods_error;
351 else if ((value == "web_error") || (value == "web-error"))
352 return web_error;
353
354 else if ((value == "dap4_dmr") || (value == "dap4-dmr") || (value == DMR_Content_Type))
355 return dap4_dmr;
356 else if ((value == "dap4_data") || (value == "dap4-data") || (value == DAP4_DATA_Content_Type))
357 return dap4_data;
358 else if ((value == "dap4_error") || (value == "dap4-error"))
359 return dap4_error;
360
361 else
362 return unknown_type;
363}
364
378void
379set_mime_text(FILE *out, ObjectType type, const string &ver,
380 EncodingType enc, const time_t last_modified)
381{
382 ostringstream oss;
383 set_mime_text(oss, type, ver, enc, last_modified);
384 fwrite(oss.str().data(), 1, oss.str().length(), out);
385}
386
400void
401set_mime_text(ostream &strm, ObjectType type, const string &ver,
402 EncodingType enc, const time_t last_modified)
403{
404 strm << "HTTP/1.0 200 OK" << CRLF ;
405 if (ver == "") {
406 strm << "XDODS-Server: " << DVR << CRLF ;
407 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
408 }
409 else {
410 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
411 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
412 }
413 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
414
415 const time_t t = time(0);
416 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
417
418 strm << "Last-Modified: " ;
419 if (last_modified > 0)
420 strm << rfc822_date(last_modified).c_str() << CRLF ;
421 else
422 strm << rfc822_date(t).c_str() << CRLF ;
423
424 if (type == dap4_dmr)
425 strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF ;
426 else
427 strm << "Content-Type: text/plain" << CRLF ;
428
429 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
430 // jhrg 12/23/05
431 strm << "Content-Description: " << descrip[type] << CRLF ;
432 if (type == dods_error) // don't cache our error responses.
433 strm << "Cache-Control: no-cache" << CRLF ;
434 // Don't write a Content-Encoding header for x-plain since that breaks
435 // Netscape on NT. jhrg 3/23/97
436 if (enc != x_plain)
437 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
438 strm << CRLF ;
439}
440
456void set_mime_text(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
457 const string &protocol)
458{
459 strm << "HTTP/1.0 200 OK" << CRLF;
460
461 strm << "XDODS-Server: " << DVR << CRLF;
462 strm << "XOPeNDAP-Server: " << DVR << CRLF;
463
464 if (protocol == "")
465 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
466 else
467 strm << "XDAP: " << protocol << CRLF;
468
469 const time_t t = time(0);
470 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
471
472 strm << "Last-Modified: ";
473 if (last_modified > 0)
474 strm << rfc822_date(last_modified).c_str() << CRLF;
475 else
476 strm << rfc822_date(t).c_str() << CRLF;
477
478 if (type == dap4_dmr)
479 strm << "Content-Type: application/vnd.org.opendap.dap4.dataset-metadata+xml" << CRLF;
480 else
481 strm << "Content-Type: text/plain" << CRLF;
482
483 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
484 // jhrg 12/23/05
485 strm << "Content-Description: " << descrip[type] << CRLF;
486 if (type == dods_error) // don't cache our error responses.
487 strm << "Cache-Control: no-cache" << CRLF;
488 // Don't write a Content-Encoding header for x-plain since that breaks
489 // Netscape on NT. jhrg 3/23/97
490 if (enc != x_plain)
491 strm << "Content-Encoding: " << encoding[enc] << CRLF;
492 strm << CRLF;
493}
494
506void
507set_mime_html(FILE *out, ObjectType type, const string &ver,
508 EncodingType enc, const time_t last_modified)
509{
510 ostringstream oss;
511 set_mime_html(oss, type, ver, enc, last_modified);
512 fwrite(oss.str().data(), 1, oss.str().length(), out);
513}
514
526void
527set_mime_html(ostream &strm, ObjectType type, const string &ver,
528 EncodingType enc, const time_t last_modified)
529{
530 strm << "HTTP/1.0 200 OK" << CRLF ;
531 if (ver == "") {
532 strm << "XDODS-Server: " << DVR << CRLF ;
533 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
534 }
535 else {
536 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
537 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
538 }
539 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
540
541 const time_t t = time(0);
542 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
543
544 strm << "Last-Modified: " ;
545 if (last_modified > 0)
546 strm << rfc822_date(last_modified).c_str() << CRLF ;
547 else
548 strm << rfc822_date(t).c_str() << CRLF ;
549
550 strm << "Content-type: text/html" << CRLF ;
551 // See note above about Content-Description header. jhrg 12/23/05
552 strm << "Content-Description: " << descrip[type] << CRLF ;
553 if (type == dods_error) // don't cache our error responses.
554 strm << "Cache-Control: no-cache" << CRLF ;
555 // Don't write a Content-Encoding header for x-plain since that breaks
556 // Netscape on NT. jhrg 3/23/97
557 if (enc != x_plain)
558 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
559 strm << CRLF ;
560}
561
572void set_mime_html(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
573 const string &protocol)
574{
575 strm << "HTTP/1.0 200 OK" << CRLF;
576
577 strm << "XDODS-Server: " << DVR<< CRLF;
578 strm << "XOPeNDAP-Server: " << DVR<< CRLF;
579
580 if (protocol == "")
581 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
582 else
583 strm << "XDAP: " << protocol << CRLF;
584
585 const time_t t = time(0);
586 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
587
588 strm << "Last-Modified: ";
589 if (last_modified > 0)
590 strm << rfc822_date(last_modified).c_str() << CRLF;
591 else
592 strm << rfc822_date(t).c_str() << CRLF;
593
594 strm << "Content-type: text/html" << CRLF;
595 // See note above about Content-Description header. jhrg 12/23/05
596 strm << "Content-Description: " << descrip[type] << CRLF;
597 if (type == dods_error) // don't cache our error responses.
598 strm << "Cache-Control: no-cache" << CRLF;
599 // Don't write a Content-Encoding header for x-plain since that breaks
600 // Netscape on NT. jhrg 3/23/97
601 if (enc != x_plain)
602 strm << "Content-Encoding: " << encoding[enc] << CRLF;
603 strm << CRLF;
604}
605
620void
621set_mime_binary(FILE *out, ObjectType type, const string &ver,
622 EncodingType enc, const time_t last_modified)
623{
624 ostringstream oss;
625 set_mime_binary(oss, type, ver, enc, last_modified);
626 fwrite(oss.str().data(), 1, oss.str().length(), out);
627}
628
643void
644set_mime_binary(ostream &strm, ObjectType type, const string &ver,
645 EncodingType enc, const time_t last_modified)
646{
647 strm << "HTTP/1.0 200 OK" << CRLF ;
648 if (ver == "") {
649 strm << "XDODS-Server: " << DVR << CRLF ;
650 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
651 }
652 else {
653 strm << "XDODS-Server: " << ver.c_str() << CRLF ;
654 strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
655 }
656 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
657
658 const time_t t = time(0);
659 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
660
661 strm << "Last-Modified: " ;
662 if (last_modified > 0)
663 strm << rfc822_date(last_modified).c_str() << CRLF ;
664 else
665 strm << rfc822_date(t).c_str() << CRLF ;
666
667 strm << "Content-Type: application/octet-stream" << CRLF ;
668 strm << "Content-Description: " << descrip[type] << CRLF ;
669 if (enc != x_plain)
670 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
671
672 strm << CRLF ;
673}
674
688void set_mime_binary(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
689 const string &protocol)
690{
691 strm << "HTTP/1.0 200 OK" << CRLF;
692
693 strm << "XDODS-Server: " << DVR << CRLF;
694 strm << "XOPeNDAP-Server: " << DVR << CRLF;
695
696 if (protocol.empty())
697 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF;
698 else
699 strm << "XDAP: " << protocol << CRLF;
700
701 const time_t t = time(0);
702 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
703
704 strm << "Last-Modified: ";
705 if (last_modified > 0)
706 strm << rfc822_date(last_modified).c_str() << CRLF;
707 else
708 strm << rfc822_date(t).c_str() << CRLF;
709
710 strm << "Content-Type: application/octet-stream" << CRLF;
711 strm << "Content-Description: " << descrip[type] << CRLF;
712 if (enc != x_plain)
713 strm << "Content-Encoding: " << encoding[enc] << CRLF;
714
715 strm << CRLF;
716}
717
718void set_mime_multipart(ostream &strm, const string &boundary,
719 const string &start, ObjectType type,
720 const string &version, EncodingType enc,
721 const time_t last_modified)
722{
723 strm << "HTTP/1.0 200 OK" << CRLF ;
724 if (version == "") {
725 strm << "XDODS-Server: " << DVR << CRLF ;
726 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
727 }
728 else {
729 strm << "XDODS-Server: " << version.c_str() << CRLF ;
730 strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
731 }
732 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
733
734 const time_t t = time(0);
735 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
736
737 strm << "Last-Modified: " ;
738 if (last_modified > 0)
739 strm << rfc822_date(last_modified).c_str() << CRLF ;
740 else
741 strm << rfc822_date(t).c_str() << CRLF ;
742
743 strm << "Content-Type: Multipart/Related; boundary=" << boundary
744 << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
745 strm << "Content-Description: " << descrip[type] << CRLF ;
746 if (enc != x_plain)
747 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
748
749 strm << CRLF ;
750}
751
754void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, EncodingType enc,
755 const time_t last_modified, const string &protocol, const string &url)
756{
757 strm << "HTTP/1.1 200 OK" << CRLF;
758
759 const time_t t = time(0);
760 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
761
762 strm << "Last-Modified: ";
763 if (last_modified > 0)
764 strm << rfc822_date(last_modified).c_str() << CRLF;
765 else
766 strm << rfc822_date(t).c_str() << CRLF;
767
768 strm << "Content-Type: multipart/related; boundary=" << boundary << "; start=\"<" << start
769 << ">\"; type=\"text/xml\"" << CRLF;
770
771 // data-ddx;"; removed as a result of the merge of the hyrax 1.8 release
772 // branch.
773 strm << "Content-Description: " << descrip[type] << ";";
774 if (!url.empty())
775 strm << " url=\"" << url << "\"" << CRLF;
776 else
777 strm << CRLF;
778
779 if (enc != x_plain)
780 strm << "Content-Encoding: " << encoding[enc] << CRLF;
781
782 if (protocol == "")
783 strm << "X-DAP: " << DAP_PROTOCOL_VERSION << CRLF;
784 else
785 strm << "X-DAP: " << protocol << CRLF;
786
787 strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
788
789 strm << CRLF;
790}
791
792void set_mime_ddx_boundary(ostream &strm, const string &boundary,
793 const string &cid, ObjectType type, EncodingType enc)
794{
795 strm << "--" << boundary << CRLF;
796 // 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.
797 strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
798 strm << "Content-Id: <" << cid << ">" << CRLF;
799 strm << "Content-Description: " << descrip[type] << CRLF ;
800 if (enc != x_plain)
801 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
802
803 strm << CRLF;
804}
805
806void set_mime_data_boundary(ostream &strm, const string &boundary,
807 const string &cid, ObjectType type, EncodingType enc)
808{
809 strm << "--" << boundary << CRLF;
810 strm << "Content-Type: application/octet-stream" << CRLF;
811 strm << "Content-Id: <" << cid << ">" << CRLF;
812 strm << "Content-Description: " << descrip[type] << CRLF ;
813 if (enc != x_plain)
814 strm << "Content-Encoding: " << encoding[enc] << CRLF ;
815
816 strm << CRLF;
817}
818
819const size_t line_length = 1024;
820
836string get_next_mime_header(FILE *in)
837{
838 // Get the header line and strip \r\n. Some headers end with just \n.
839 // If a blank line is found, return an empty string.
840 char line[line_length];
841 while (!feof(in)) {
842 if (fgets(line, line_length, in)
843 && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
844 return "";
845 else {
846 size_t slen = min(strlen(line), line_length); // Never > line_length
847 line[slen - 1] = '\0'; // remove the newline
848 if (line[slen - 2] == '\r') // ...and the preceding carriage return
849 line[slen - 2] = '\0';
850 return string(line);
851 }
852 }
853
854 throw Error("I expected to find a MIME header, but got EOF instead.");
855}
856
869string get_next_mime_header(istream &in)
870{
871#if 0
872 // Get the header line and strip \r\n. Some headers end with just \n.
873 // If a blank line is found, return an empty string.
874 char line[line_length];
875 while (!in.eof()) {
876 in.getline(line, line_length);
877 if (strncmp(line, CRLF, 2) == 0 || line[0] == '\n') {
878 return "";
879 }
880 else {
881 size_t slen = min(strlen(line), line_length); // Never > line_length
882 line[slen - 1] = '\0'; // remove the newline
883 if (line[slen - 2] == '\r') // ...and the preceding carriage return
884 line[slen - 2] = '\0';
885 return string(line);
886 }
887 }
888#endif
889 // Get the header line and strip \r\n. Some headers end with just \n.
890 // If a blank line is found, return an empty string.
891 char raw_line[line_length];
892 while (!in.eof()) {
893 in.getline(raw_line, line_length); // strips the trailing newline; terminates with null
894 string line = raw_line;
895 if (line.find('\r') != string::npos)
896 line = line.substr(0, line.size()-1);
897 return line;
898 }
899
900 throw Error("I expected to find a MIME header, but got EOF instead.");
901}
902
910void parse_mime_header(const string &header, string &name, string &value)
911{
912 istringstream iss(header);
913
914 size_t length = header.length() + 1;
915 vector<char> s(length);
916 //char s[line_length];
917 iss.getline(s.data(), length, ':');
918 name = s.data();
919
920 iss.ignore(length, ' ');
921 iss.getline(s.data(), length);
922 value = s.data();
923
924 downcase(name);
925 downcase(value);
926}
927
939bool is_boundary(const char *line, const string &boundary)
940{
941 if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
942 return false;
943 else
944 return strncmp(line, boundary.c_str(), boundary.length()) == 0;
945}
946
957string read_multipart_boundary(FILE *in, const string &boundary)
958{
959 string boundary_line = get_next_mime_header(in);
960 // If the caller passed in a value for the boundary, test for that value,
961 // else just see that this line starts with '--'.
962 // The value of 'boundary_line' is returned by this function.
963 if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
964 || boundary_line.find("--") != 0)
965 throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
966
967 return boundary_line;
968}
969
970string read_multipart_boundary(istream &in, const string &boundary)
971{
972 string boundary_line = get_next_mime_header(in);
973 // If the caller passed in a value for the boundary, test for that value,
974 // else just see that this line starts with '--'.
975 // The value of 'boundary_line' is returned by this function.
976 if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
977 || boundary_line.find("--") != 0)
978 throw Error(internal_error, "The DAP4 data response document is broken - missing or malformed boundary.");
979
980 return boundary_line;
981}
982
1003void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
1004{
1005 bool ct = false, cd = false, ci = false;
1006
1007 // The function get_next_mime_header() returns tainted data. Fix this
1008 // if we are going to use this code. jhrg 4/18/17
1009 string header = get_next_mime_header(in);
1010 while (!header.empty()) {
1011 string name, value;
1012 parse_mime_header(header, name, value);
1013
1014 if (name == "content-type") {
1015 ct = true;
1016 if (value.find(content_type) == string::npos)
1017 throw Error(internal_error, "Content-Type for this part of a DAP2 data ddx response must be " + content_type + ".");
1018 }
1019 else if (name == "content-description") {
1020 cd = true;
1021 if (get_description_type(value) != object_type)
1022 throw Error(internal_error, "Content-Description for this part of a DAP2 data ddx response must be dods-ddx or dods-data-ddx");
1023 }
1024 else if (name == "content-id") {
1025 ci = true;
1026 if (!cid.empty() && value != cid)
1027 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
1028 }
1029
1030 header = get_next_mime_header(in);
1031 }
1032
1033 if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
1034}
1035
1036void read_multipart_headers(istream &in, const string &content_type, const ObjectType object_type, const string &cid)
1037{
1038 bool ct = false, cd = false, ci = false;
1039
1040 string header = get_next_mime_header(in);
1041 while (!header.empty()) {
1042 string name, value;
1043 parse_mime_header(header, name, value);
1044
1045 if (name == "content-type") {
1046 ct = true;
1047 if (value.find(content_type) == string::npos)
1048 throw Error(internal_error, "Content-Type for this part of a DAP4 data response must be " + content_type + ".");
1049 }
1050 else if (name == "content-description") {
1051 cd = true;
1052 if (get_description_type(value) != object_type)
1053 throw Error("Content-Description '" + value + "' not the expected value (expected: " + descrip[object_type] + ").");
1054 }
1055 else if (name == "content-id") {
1056 ci = true;
1057 if (!cid.empty() && value != cid)
1058 throw Error("Content-Id mismatch. Expected: " + cid + ", but got: " + value);
1059 }
1060
1061 header = get_next_mime_header(in);
1062 }
1063
1064 if (!(ct && cd && ci)) throw Error(internal_error, "The DAP4 data response document is broken - missing header.");
1065}
1066
1075string cid_to_header_value(const string &cid)
1076{
1077 string::size_type offset = cid.find("cid:");
1078 if (offset != 0)
1079 throw Error(internal_error, "expected CID to start with 'cid:'");
1080
1081 string value = "<";
1082 value.append(cid.substr(offset + 4));
1083 value.append(">");
1084 downcase(value);
1085
1086 return value;
1087}
1088
1097void
1098set_mime_error(FILE *out, int code, const string &reason,
1099 const string &version)
1100{
1101 ostringstream oss;
1102 set_mime_error(oss, code, reason, version);
1103 fwrite(oss.str().data(), 1, oss.str().length(), out);
1104}
1105
1114void
1115set_mime_error(ostream &strm, int code, const string &reason,
1116 const string &version)
1117{
1118 strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
1119 if (version == "") {
1120 strm << "XDODS-Server: " << DVR << CRLF ;
1121 strm << "XOPeNDAP-Server: " << DVR << CRLF ;
1122 }
1123 else {
1124 strm << "XDODS-Server: " << version.c_str() << CRLF ;
1125 strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
1126 }
1127 strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
1128
1129 const time_t t = time(0);
1130 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
1131 strm << "Cache-Control: no-cache" << CRLF ;
1132 strm << CRLF ;
1133}
1134
1142void
1144{
1145 ostringstream oss;
1147 fwrite(oss.str().data(), 1, oss.str().length(), out);
1148}
1149
1157void
1159{
1160 strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
1161 const time_t t = time(0);
1162 strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
1163 strm << CRLF ;
1164}
1165
1166#if 0
1167
1168// This was removed because it's not being used by our server.
1169
1179bool
1180found_override(string name, string &doc)
1181{
1182 ifstream ifs((name + ".ovr").c_str());
1183 if (!ifs)
1184 return false;
1185
1186 char tmp[256];
1187 doc = "";
1188 while (!ifs.eof()) {
1189 ifs.getline(tmp, 255);
1190 tmp[255] = '\0';
1191 strncat(tmp, "\n", sizeof(tmp) - strlen(tmp) - 1);
1192 doc += tmp;
1193 }
1194
1195 ifs.close();
1196 return true;
1197}
1198#endif
1199
1209bool
1211{
1212 char tmp[256];
1213 while (!feof(in)) {
1214 char *s = fgets(tmp, 255, in);
1215 if (s && strncmp(s, CRLF, 2) == 0)
1216 return true;
1217 }
1218
1219 return false;
1220}
1221
1226void
1228{
1229 while(!get_next_mime_header(in).empty()) ;
1230#if 0
1231 string header;
1232 do {
1233 header = get_next_mime_header(in);
1234 } while (!header.empty());
1235#endif
1236}
1237
1238} // namespace libdap
1239
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:957
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:507
ObjectType get_description_type(const string &value)
Definition mime_util.cc:337
string cid_to_header_value(const string &cid)
void parse_mime_header(const string &header, string &name, string &value)
Definition mime_util.cc:910
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:266
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:621
bool do_version(const string &script_ver, const string &dataset_ver)
Send a version number.
Definition mime_util.cc:190
void downcase(string &s)
Definition util.cc:566
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:223
ObjectType get_type(const string &value)
Definition mime_util.cc:324
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:939
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:379
string get_next_mime_header(FILE *in)
Definition mime_util.cc:836