libdap++  Updated for version 3.8.2
mime_util.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #undef FILE_METHODS
40 
41 static char rcsid[] not_used =
42  {"$Id: mime_util.cc 24370 2011-03-28 16:21:32Z jimg $"
43  };
44 
45 #include <cstring>
46 #include <cstdio>
47 #include <ctype.h>
48 
49 #ifndef TM_IN_SYS_TIME
50 #include <time.h>
51 #else
52 #include <sys/time.h>
53 #endif
54 
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 
58 #ifndef WIN32
59 #include <unistd.h> // for access
60 #include <sys/wait.h>
61 #else
62 #include <io.h>
63 #include <fcntl.h>
64 #include <process.h>
65 // Win32 does not define this. 08/21/02 jhrg
66 #define F_OK 0
67 #endif
68 
69 #include <iostream>
70 #include <sstream>
71 #include <fstream>
72 #include <string>
73 
74 #include "mime_util.h"
75 #include "Ancillary.h"
76 #include "util.h" // This supplies flush_stream for WIN32.
77 #include "debug.h"
78 
79 #ifdef WIN32
80 #define FILE_DELIMITER '\\'
81 #else // default to unix
82 #define FILE_DELIMITER '/'
83 #endif
84 
85 // ...not using a const string here to avoid global objects. jhrg 12/23/05
86 #define CRLF "\r\n" // Change here, expr-test.cc, in DODSFilter and ResponseBuilder
87 
88 using namespace std;
89 
90 namespace libdap {
91 
97 time_t
98 last_modified_time(const string &name)
99 {
100  struct stat m;
101 
102  if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
103  return m.st_mtime;
104  else
105  return time(0);
106 }
107 // Return a MIME rfc-822 date. The grammar for this is:
108 // date-time = [ day "," ] date time ; dd mm yy
109 // ; hh:mm:ss zzz
110 //
111 // day = "Mon" / "Tue" / "Wed" / "Thu"
112 // / "Fri" / "Sat" / "Sun"
113 //
114 // date = 1*2DIGIT month 2DIGIT ; day month year
115 // ; e.g. 20 Jun 82
116 // NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
117 //
118 // month = "Jan" / "Feb" / "Mar" / "Apr"
119 // / "May" / "Jun" / "Jul" / "Aug"
120 // / "Sep" / "Oct" / "Nov" / "Dec"
121 //
122 // time = hour zone ; ANSI and Military
123 //
124 // hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
125 // ; 00:00:00 - 23:59:59
126 //
127 // zone = "UT" / "GMT" ; Universal Time
128 // ; North American : UT
129 // / "EST" / "EDT" ; Eastern: - 5/ - 4
130 // / "CST" / "CDT" ; Central: - 6/ - 5
131 // / "MST" / "MDT" ; Mountain: - 7/ - 6
132 // / "PST" / "PDT" ; Pacific: - 8/ - 7
133 // / 1ALPHA ; Military: Z = UT;
134 // ; A:-1; (J not used)
135 // ; M:-12; N:+1; Y:+12
136 // / ( ("+" / "-") 4DIGIT ) ; Local differential
137 // ; hours+min. (HHMM)
138 
139 static const char *days[] =
140  {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
141  };
142 static const char *months[] =
143  {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
144  "Aug", "Sep", "Oct", "Nov", "Dec"
145  };
146 
147 #ifdef _MSC_VER
148 #define snprintf sprintf_s
149 #endif
150 
158 string
159 rfc822_date(const time_t t)
160 {
161  struct tm *stm = gmtime(&t);
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 
172 static const int TimLen = 26; // length of string from asctime()
173 static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
174 
188 bool
189 do_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 
221 void
222 ErrMsgT(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  strncpy(TimStr, ctime(&TimBin), TimLen-1);
231  TimStr[TimLen - 2] = '\0'; // overwrite the \n
232  }
233 
234  cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
235 }
236 
237 // Given a pathname, return just the filename component with any extension
238 // removed. The new string resides in newly allocated memory; the caller must
239 // delete it when done using the filename.
240 // Originally from the netcdf distribution (ver 2.3.2).
241 //
242 // *** Change to string class argument and return type. jhrg
243 // *** Changed so it also removes the#path#of#the#file# from decompressed
244 // files. rph.
245 // Returns: A filename, with path and extension information removed. If
246 // memory for the new name cannot be allocated, does not return!
247 
258 string
259 name_path(const string &path)
260 {
261  if (path == "")
262  return string("");
263 
264  string::size_type delim = path.find_last_of(FILE_DELIMITER);
265  string::size_type pound = path.find_last_of("#");
266  string new_path;
267 
268  if (pound != string::npos)
269  new_path = path.substr(pound + 1);
270  else
271  new_path = path.substr(delim + 1);
272 
273  return new_path;
274 }
275 
276 // Send string to set the transfer (mime) type and server version
277 // Note that the content description filed is used to indicate whether valid
278 // information of an error message is contained in the document and the
279 // content-encoding field is used to indicate whether the data is compressed.
280 // If the data stream is to be compressed, arrange for a compression output
281 // filter so that all information sent after the header will be compressed.
282 //
283 // Returns: false if the compression output filter was to be used but could
284 // not be started, true otherwise.
285 
286 static const char *descrip[] =
287  {"unknown", "dods_das", "dods_dds", "dods_data",
288  "dods_error", "web_error", "dap4-ddx", "dap4-data", "dap4-error",
289  "dap4-data-ddx", "dods_ddx"
290  };
291 static const char *encoding[] =
292  {"unknown", "deflate", "x-plain", "gzip", "binary"
293  };
294 
301 get_type(const string &value)
302 {
303  if ((value == "dods_das") | (value == "dods-das"))
304  return dods_das;
305  else if ((value == "dods_dds") | (value == "dods-dds"))
306  return dods_dds;
307  else if ((value == "dods_data") | (value == "dods-data"))
308  return dods_data;
309  else if ((value == "dods_error") | (value == "dods-error"))
310  return dods_error;
311  else if ((value == "web_error") | (value == "web-error"))
312  return web_error;
313  else if ((value == "dap4_ddx") | (value == "dap4-ddx"))
314  return dap4_ddx;
315  else if ((value == "dap4_data") | (value == "dap4-data"))
316  return dap4_data;
317  else if ((value == "dap4_error") | (value == "dap4-error"))
318  return dap4_error;
319  else if ((value == "dap4_data_ddx") | (value == "dap4-data-ddx"))
320  return dap4_data_ddx;
321  else if ((value == "dods_ddx") | (value == "dods-ddx"))
322  return dods_ddx;
323  else
324  return unknown_type;
325 }
326 
333 get_description_type(const string &value)
334 {
335  if ((value == "dods_das") | (value == "dods-das"))
336  return dods_das;
337  else if ((value == "dods_dds") | (value == "dods-dds"))
338  return dods_dds;
339  else if ((value == "dods_data") | (value == "dods-data"))
340  return dods_data;
341  else if ((value == "dods_error") | (value == "dods-error"))
342  return dods_error;
343  else if ((value == "web_error") | (value == "web-error"))
344  return web_error;
345  else if ((value == "dods_ddx") | (value == "dods-ddx"))
346  return dods_ddx;
347  else if ((value == "dap4_ddx") | (value == "dap4-ddx"))
348  return dap4_ddx;
349  else if ((value == "dap4_data") | (value == "dap4-data"))
350  return dap4_data;
351  else if ((value == "dap4_error") | (value == "dap4-error"))
352  return dap4_error;
353  else if ((value == "dap4_data_ddx") | (value == "dap4-data-ddx"))
354  return dap4_data_ddx;
355  else if ((value == "dods_ddx") | (value == "dods-ddx"))
356  return dods_ddx;
357  else
358  return unknown_type;
359 }
360 
361 #if FILE_METHODS
362 
375 void
376 set_mime_text(FILE *out, ObjectType type, const string &ver,
377  EncodingType enc, const time_t last_modified)
378 {
379  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
380  if (ver == "") {
381  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
382  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
383  }
384  else {
385  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
386  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
387  }
388  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
389 
390  const time_t t = time(0);
391  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
392 
393  fprintf(out, "Last-Modified: ") ;
394  if (last_modified > 0)
395  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
396  else
397  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
398 
399  if (type == dap4_ddx)
400  fprintf(out, "Content-Type: text/xml%s", CRLF) ;
401  else
402  fprintf(out, "Content-Type: text/plain%s", CRLF) ;
403 
404  // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
405  // jhrg 12/23/05
406  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
407  if (type == dods_error) // don't cache our error responses.
408  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
409  // Don't write a Content-Encoding header for x-plain since that breaks
410  // Netscape on NT. jhrg 3/23/97
411  if (enc != x_plain)
412  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
413  fprintf(out, CRLF) ;
414 }
415 #endif
416 
430 void
431 set_mime_text(ostream &strm, ObjectType type, const string &ver,
432  EncodingType enc, const time_t last_modified)
433 {
434  strm << "HTTP/1.0 200 OK" << CRLF ;
435  if (ver == "") {
436  strm << "XDODS-Server: " << DVR << CRLF ;
437  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
438  }
439  else {
440  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
441  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
442  }
443  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
444 
445  const time_t t = time(0);
446  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
447 
448  strm << "Last-Modified: " ;
449  if (last_modified > 0)
450  strm << rfc822_date(last_modified).c_str() << CRLF ;
451  else
452  strm << rfc822_date(t).c_str() << CRLF ;
453 
454  if (type == dap4_ddx)
455  strm << "Content-Type: text/xml" << CRLF ;
456  else
457  strm << "Content-Type: text/plain" << CRLF ;
458 
459  // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
460  // jhrg 12/23/05
461  strm << "Content-Description: " << descrip[type] << CRLF ;
462  if (type == dods_error) // don't cache our error responses.
463  strm << "Cache-Control: no-cache" << CRLF ;
464  // Don't write a Content-Encoding header for x-plain since that breaks
465  // Netscape on NT. jhrg 3/23/97
466  if (enc != x_plain)
467  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
468  strm << CRLF ;
469 }
470 
471 #if FILE_METHODS
472 
483 void
484 set_mime_html(FILE *out, ObjectType type, const string &ver,
485  EncodingType enc, const time_t last_modified)
486 {
487  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
488  if (ver == "") {
489  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
490  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
491  }
492  else {
493  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
494  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
495  }
496  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
497 
498  const time_t t = time(0);
499  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
500 
501  fprintf(out, "Last-Modified: ") ;
502  if (last_modified > 0)
503  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
504  else
505  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
506 
507  fprintf(out, "Content-type: text/html%s", CRLF) ;
508  // See note above about Content-Description header. jhrg 12/23/05
509  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
510  if (type == dods_error) // don't cache our error responses.
511  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
512  // Don't write a Content-Encoding header for x-plain since that breaks
513  // Netscape on NT. jhrg 3/23/97
514  if (enc != x_plain)
515  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
516  fprintf(out, CRLF) ;
517 }
518 #endif
519 
531 void
532 set_mime_html(ostream &strm, ObjectType type, const string &ver,
533  EncodingType enc, const time_t last_modified)
534 {
535  strm << "HTTP/1.0 200 OK" << CRLF ;
536  if (ver == "") {
537  strm << "XDODS-Server: " << DVR << CRLF ;
538  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
539  }
540  else {
541  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
542  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
543  }
544  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
545 
546  const time_t t = time(0);
547  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
548 
549  strm << "Last-Modified: " ;
550  if (last_modified > 0)
551  strm << rfc822_date(last_modified).c_str() << CRLF ;
552  else
553  strm << rfc822_date(t).c_str() << CRLF ;
554 
555  strm << "Content-type: text/html" << CRLF ;
556  // See note above about Content-Description header. jhrg 12/23/05
557  strm << "Content-Description: " << descrip[type] << CRLF ;
558  if (type == dods_error) // don't cache our error responses.
559  strm << "Cache-Control: no-cache" << CRLF ;
560  // Don't write a Content-Encoding header for x-plain since that breaks
561  // Netscape on NT. jhrg 3/23/97
562  if (enc != x_plain)
563  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
564  strm << CRLF ;
565 }
566 
567 #if FILE_METHODS
568 
582 void
583 set_mime_binary(FILE *out, ObjectType type, const string &ver,
584  EncodingType enc, const time_t last_modified)
585 {
586  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
587  if (ver == "") {
588  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
589  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
590  }
591  else {
592  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
593  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
594  }
595  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
596 
597  const time_t t = time(0);
598  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
599 
600  fprintf(out, "Last-Modified: ") ;
601  if (last_modified > 0)
602  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
603  else
604  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
605 
606  fprintf(out, "Content-Type: application/octet-stream%s", CRLF) ;
607  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
608  if (enc != x_plain)
609  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
610 
611  fprintf(out, CRLF) ;
612 }
613 #endif
614 
629 void
630 set_mime_binary(ostream &strm, ObjectType type, const string &ver,
631  EncodingType enc, const time_t last_modified)
632 {
633  strm << "HTTP/1.0 200 OK" << CRLF ;
634  if (ver == "") {
635  strm << "XDODS-Server: " << DVR << CRLF ;
636  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
637  }
638  else {
639  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
640  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
641  }
642  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
643 
644  const time_t t = time(0);
645  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
646 
647  strm << "Last-Modified: " ;
648  if (last_modified > 0)
649  strm << rfc822_date(last_modified).c_str() << CRLF ;
650  else
651  strm << rfc822_date(t).c_str() << CRLF ;
652 
653  strm << "Content-Type: application/octet-stream" << CRLF ;
654  strm << "Content-Description: " << descrip[type] << CRLF ;
655  if (enc != x_plain)
656  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
657 
658  strm << CRLF ;
659 }
660 
661 void set_mime_multipart(ostream &strm, const string &boundary,
662  const string &start, ObjectType type,
663  const string &version, EncodingType enc,
664  const time_t last_modified)
665 {
666  strm << "HTTP/1.0 200 OK" << CRLF ;
667  if (version == "") {
668  strm << "XDODS-Server: " << DVR << CRLF ;
669  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
670  }
671  else {
672  strm << "XDODS-Server: " << version.c_str() << CRLF ;
673  strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
674  }
675  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
676 
677  const time_t t = time(0);
678  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
679 
680  strm << "Last-Modified: " ;
681  if (last_modified > 0)
682  strm << rfc822_date(last_modified).c_str() << CRLF ;
683  else
684  strm << rfc822_date(t).c_str() << CRLF ;
685 
686  strm << "Content-Type: Multipart/Related; boundary=" << boundary
687  << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
688  strm << "Content-Description: " << descrip[type] << CRLF ;
689  if (enc != x_plain)
690  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
691 
692  strm << CRLF ;
693 }
694 
695 void set_mime_ddx_boundary(ostream &strm, const string &boundary,
696  const string &cid, ObjectType type, EncodingType enc)
697 {
698  strm << "--" << boundary << CRLF;
699  strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
700  strm << "Content-Id: <" << cid << ">" << CRLF;
701  strm << "Content-Description: " << descrip[type] << CRLF ;
702  if (enc != x_plain)
703  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
704 
705  strm << CRLF;
706 }
707 
708 void set_mime_data_boundary(ostream &strm, const string &boundary,
709  const string &cid, ObjectType type, EncodingType enc)
710 {
711  strm << "--" << boundary << CRLF;
712  strm << "Content-Type: application/octet-stream" << CRLF;
713  strm << "Content-Id: <" << cid << ">" << CRLF;
714  strm << "Content-Description: " << descrip[type] << CRLF ;
715  if (enc != x_plain)
716  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
717 
718  strm << CRLF;
719 }
720 
721 const size_t line_length = 1024;
722 
737 string get_next_mime_header(FILE *in)
738 {
739  // Get the header line and strip \r\n. Some headers end with just \n.
740  // If a blank line is found, return an empty string.
741  char line[line_length];
742  while (!feof(in)) {
743  if (fgets(line, line_length, in)
744  && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
745  return "";
746  else {
747  size_t slen = min(strlen(line), line_length); // Never > line_length
748  line[slen - 1] = '\0'; // remove the newline
749  if (line[slen - 2] == '\r') // ...and the preceding carriage return
750  line[slen - 2] = '\0';
751  return string(line);
752  }
753  }
754 
755  throw Error("I expected to find a MIME header, but got EOF instead.");
756 }
757 
765 void parse_mime_header(const string &header, string &name, string &value)
766 {
767  istringstream iss(header);
768  // Set downcase
769  char s[line_length];
770  iss.getline(s, 1023, ':');
771  name = s;
772 
773  iss.ignore(1023, ' ');
774  iss.getline(s, 1023);
775  value = s;
776 
777  downcase(name);
778  downcase(value);
779 }
780 
792 bool is_boundary(const char *line, const string &boundary)
793 {
794  if (strlen(line) < 2 || !(line[0] == '-' && line[1] == '-'))
795  return false;
796  else
797  return strncmp(line, boundary.c_str(), boundary.length()) == 0;
798 }
799 
810 string read_multipart_boundary(FILE *in, const string &boundary)
811 {
812  string boundary_line = get_next_mime_header(in);
813  // If the caller passed in a value for the boundary, test for that value,
814  // else just see that this line starts with '--'.
815  // The value of 'boundary_line' is returned by this function.
816  if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
817  || boundary_line.find("--") != 0)
818  throw Error(
819  "The DAP4 data response document is broken - missing or malformed boundary.");
820 
821  return boundary_line;
822 }
823 
844 void read_multipart_headers(FILE *in, const string &content_type,
845  const ObjectType object_type, const string &cid)
846 {
847  bool ct = false, cd = false, ci = false;
848 
849  string header = get_next_mime_header(in);
850  while (!header.empty()) {
851  string name, value;
852  parse_mime_header(header, name, value);
853 
854  if (name =="content-type") {
855  ct = true;
856  if (value.find(content_type) == string::npos)
857  throw Error("Content-Type for this part of a DAP4 data response must be " + content_type + ".");
858  }
859  else if (name == "content-description") {
860  cd = true;
861  if (get_description_type(value) != object_type)
862  throw Error("Content-Description for this part of a DAP4 data response must be dap4-ddx or dap4-data-ddx");
863  }
864  else if (name == "content-id") {
865  ci = true;
866  if (!cid.empty() && value != cid)
867  throw Error("Content-Id mismatch. Expected: " + cid
868  + ", but got: " + value);
869  }
870 
871  header = get_next_mime_header(in);
872  }
873 
874  if (!(ct && cd && ci))
875  throw Error("The DAP4 data response document is broken - missing header.");
876 }
885 string cid_to_header_value(const string &cid)
886 {
887  string::size_type offset = cid.find("cid:");
888  if (offset != 0)
889  throw Error("expected CID to start with 'cid:'");
890 
891  string value = "<";
892  value.append(cid.substr(offset + 4));
893  value.append(">");
894  downcase(value);
895 
896  return value;
897 }
898 
899 #if FILE_METHODS
900 
908 void
909 set_mime_error(FILE *out, int code, const string &reason,
910  const string &version)
911 {
912  fprintf(out, "HTTP/1.0 %d %s%s", code, reason.c_str(), CRLF) ;
913  if (version == "") {
914  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
915  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
916  }
917  else {
918  fprintf(out, "XDODS-Server: %s%s", version.c_str(), CRLF) ;
919  fprintf(out, "XOPeNDAP-Server: %s%s", version.c_str(), CRLF) ;
920  }
921  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
922 
923  const time_t t = time(0);
924  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
925  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
926  fprintf(out, CRLF) ;
927 }
928 #endif
929 
938 void
939 set_mime_error(ostream &strm, int code, const string &reason,
940  const string &version)
941 {
942  strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
943  if (version == "") {
944  strm << "XDODS-Server: " << DVR << CRLF ;
945  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
946  }
947  else {
948  strm << "XDODS-Server: " << version.c_str() << CRLF ;
949  strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
950  }
951  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
952 
953  const time_t t = time(0);
954  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
955  strm << "Cache-Control: no-cache" << CRLF ;
956  strm << CRLF ;
957 }
958 
959 #if FILE_METHODS
960 
967 void
969 {
970  fprintf(out, "HTTP/1.0 304 NOT MODIFIED%s", CRLF) ;
971  const time_t t = time(0);
972  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
973  fprintf(out, CRLF) ;
974 }
975 #endif
976 
984 void
985 set_mime_not_modified(ostream &strm)
986 {
987  strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
988  const time_t t = time(0);
989  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
990  strm << CRLF ;
991 }
992 
1002 bool
1003 found_override(string name, string &doc)
1004 {
1005  ifstream ifs((name + ".ovr").c_str());
1006  if (!ifs)
1007  return false;
1008 
1009  char tmp[256];
1010  doc = "";
1011  while (!ifs.eof()) {
1012  ifs.getline(tmp, 255);
1013  strcat(tmp, "\n");
1014  doc += tmp;
1015  }
1016 
1017  ifs.close();
1018  return true;
1019 }
1020 
1030 bool
1032 {
1033  char tmp[256];
1034  while (!feof(in)) {
1035  char *s = fgets(tmp, 255, in);
1036  if (s && strncmp(s, CRLF, 2) == 0)
1037  return true;
1038  }
1039 
1040  return false;
1041 }
1042 
1043 } // namespace libdap
1044 
const size_t line_length
Definition: mime_util.cc:721
#define CRLF
Definition: mime_util.cc:86
void set_mime_not_modified(ostream &strm)
Send a `Not Modified' response.
Definition: mime_util.cc:985
void ErrMsgT(const string &Msgt)
Logs an error message.
Definition: mime_util.cc:222
void set_mime_data_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:708
time_t last_modified_time(const string &name)
Definition: mime_util.cc:98
void downcase(string &s)
Definition: util.cc:367
#define not_used
Definition: config.h:850
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition: mime_util.cc:844
#define FILE_DELIMITER
Definition: mime_util.cc:82
ObjectType
The type of object in the stream coming from the data server.
Definition: ObjectType.h:57
string cid_to_header_value(const string &cid)
Definition: mime_util.cc:885
bool found_override(string name, string &doc)
Definition: mime_util.cc:1003
void parse_mime_header(const string &header, string &name, string &value)
Definition: mime_util.cc:765
const char * version
Definition: getdap.cc:64
void set_mime_binary(ostream &strm, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:630
ObjectType get_description_type(const string &value)
Definition: mime_util.cc:333
bool remove_mime_header(FILE *in)
Read and discard the MIME header of the stream in.
Definition: mime_util.cc:1031
void set_mime_ddx_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:695
ObjectType get_type(const string &value)
Definition: mime_util.cc:301
void set_mime_text(ostream &strm, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:431
string get_next_mime_header(FILE *in)
Definition: mime_util.cc:737
string read_multipart_boundary(FILE *in, const string &boundary)
Definition: mime_util.cc:810
#define DVR
Definition: config.h:67
string rfc822_date(const time_t t)
Definition: mime_util.cc:159
void set_mime_error(ostream &strm, int code, const string &reason, const string &version)
Definition: mime_util.cc:939
string name_path(const string &path)
Returns the filename portion of a pathname.
Definition: mime_util.cc:259
bool is_boundary(const char *line, const string &boundary)
Definition: mime_util.cc:792
void set_mime_html(ostream &strm, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:532
void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, const string &version, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:661
EncodingType
The type of encoding used on the current stream.
Definition: EncodingType.h:48
A class for error processing.
Definition: Error.h:90
#define DAP_PROTOCOL_VERSION
Definition: config.h:37
bool do_version(const string &script_ver, const string &dataset_ver)
Send a version number.
Definition: mime_util.cc:189