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 22703 2010-05-11 18:10:01Z 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 and DODSFilter.cc
87 
88 using namespace std;
89 
90 namespace libdap {
91 
92 static const int TimLen = 26; // length of string from asctime()
93 static const int CLUMP_SIZE = 1024; // size of clumps to new in fmakeword()
94 
108 bool
109 do_version(const string &script_ver, const string &dataset_ver)
110 {
111  fprintf(stdout, "HTTP/1.0 200 OK%s", CRLF) ;
112  fprintf(stdout, "XDODS-Server: %s%s", DVR, CRLF) ;
113  fprintf(stdout, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
114  fprintf(stdout, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
115  fprintf(stdout, "Content-Type: text/plain%s", CRLF) ;
116  fprintf(stdout, CRLF) ;
117 
118  fprintf(stdout, "Core software version: %s%s", DVR, CRLF) ;
119 
120  if (script_ver != "")
121  fprintf(stdout, "Server Script Revision: %s%s", script_ver.c_str(), CRLF) ;
122 
123  if (dataset_ver != "")
124  fprintf(stdout, "Dataset version: %s%s", dataset_ver.c_str(), CRLF) ;
125 
126  fflush(stdout) ; // Not sure this is needed. jhrg 12/23/05
127 
128  return true;
129 }
130 
140 void
141 ErrMsgT(const string &Msgt)
142 {
143  time_t TimBin;
144  char TimStr[TimLen];
145 
146  if (time(&TimBin) == (time_t) - 1)
147  strncpy(TimStr, "time() error ", TimLen-1);
148  else {
149  strncpy(TimStr, ctime(&TimBin), TimLen-1);
150  TimStr[TimLen - 2] = '\0'; // overwrite the \n
151  }
152 
153  cerr << "[" << TimStr << "] DAP server error: " << Msgt << endl;
154 }
155 
156 // Given a pathname, return just the filename component with any extension
157 // removed. The new string resides in newly allocated memory; the caller must
158 // delete it when done using the filename.
159 // Originally from the netcdf distribution (ver 2.3.2).
160 //
161 // *** Change to string class argument and return type. jhrg
162 // *** Changed so it also removes the#path#of#the#file# from decompressed
163 // files. rph.
164 // Returns: A filename, with path and extension information removed. If
165 // memory for the new name cannot be allocated, does not return!
166 
177 string
178 name_path(const string &path)
179 {
180  if (path == "")
181  return string("");
182 
183  string::size_type delim = path.find_last_of(FILE_DELIMITER);
184  string::size_type pound = path.find_last_of("#");
185  string new_path;
186 
187  if (pound != string::npos)
188  new_path = path.substr(pound + 1);
189  else
190  new_path = path.substr(delim + 1);
191 
192  return new_path;
193 }
194 
195 // Return a MIME rfc-822 date. The grammar for this is:
196 // date-time = [ day "," ] date time ; dd mm yy
197 // ; hh:mm:ss zzz
198 //
199 // day = "Mon" / "Tue" / "Wed" / "Thu"
200 // / "Fri" / "Sat" / "Sun"
201 //
202 // date = 1*2DIGIT month 2DIGIT ; day month year
203 // ; e.g. 20 Jun 82
204 // NB: year is 4 digit; see RFC 1123. 11/30/99 jhrg
205 //
206 // month = "Jan" / "Feb" / "Mar" / "Apr"
207 // / "May" / "Jun" / "Jul" / "Aug"
208 // / "Sep" / "Oct" / "Nov" / "Dec"
209 //
210 // time = hour zone ; ANSI and Military
211 //
212 // hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
213 // ; 00:00:00 - 23:59:59
214 //
215 // zone = "UT" / "GMT" ; Universal Time
216 // ; North American : UT
217 // / "EST" / "EDT" ; Eastern: - 5/ - 4
218 // / "CST" / "CDT" ; Central: - 6/ - 5
219 // / "MST" / "MDT" ; Mountain: - 7/ - 6
220 // / "PST" / "PDT" ; Pacific: - 8/ - 7
221 // / 1ALPHA ; Military: Z = UT;
222 // ; A:-1; (J not used)
223 // ; M:-12; N:+1; Y:+12
224 // / ( ("+" / "-") 4DIGIT ) ; Local differential
225 // ; hours+min. (HHMM)
226 
227 static const char *days[] =
228  {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
229  };
230 static const char *months[] =
231  {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
232  "Aug", "Sep", "Oct", "Nov", "Dec"
233  };
234 
235 #ifdef _MSC_VER
236 #define snprintf sprintf_s
237 #endif
238 
246 string
247 rfc822_date(const time_t t)
248 {
249  struct tm *stm = gmtime(&t);
250  char d[256];
251 
252  snprintf(d, 255, "%s, %02d %s %4d %02d:%02d:%02d GMT", days[stm->tm_wday],
253  stm->tm_mday, months[stm->tm_mon],
254  1900 + stm->tm_year,
255  stm->tm_hour, stm->tm_min, stm->tm_sec);
256  d[255] = '\0';
257  return string(d);
258 }
259 
265 time_t
266 last_modified_time(const string &name)
267 {
268  struct stat m;
269 
270  if (stat(name.c_str(), &m) == 0 && (S_IFREG & m.st_mode))
271  return m.st_mtime;
272  else
273  return time(0);
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 
374 void
375 set_mime_text(FILE *out, ObjectType type, const string &ver,
376  EncodingType enc, const time_t last_modified)
377 {
378  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
379  if (ver == "") {
380  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
381  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
382  }
383  else {
384  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
385  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
386  }
387  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
388 
389  const time_t t = time(0);
390  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
391 
392  fprintf(out, "Last-Modified: ") ;
393  if (last_modified > 0)
394  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
395  else
396  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
397 
398  if (type == dap4_ddx)
399  fprintf(out, "Content-Type: text/xml%s", CRLF) ;
400  else
401  fprintf(out, "Content-Type: text/plain%s", CRLF) ;
402 
403  // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
404  // jhrg 12/23/05
405  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
406  if (type == dods_error) // don't cache our error responses.
407  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
408  // Don't write a Content-Encoding header for x-plain since that breaks
409  // Netscape on NT. jhrg 3/23/97
410  if (enc != x_plain)
411  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
412  fprintf(out, CRLF) ;
413 }
414 #endif
415 
428 void
429 set_mime_text(ostream &strm, ObjectType type, const string &ver,
430  EncodingType enc, const time_t last_modified)
431 {
432  strm << "HTTP/1.0 200 OK" << CRLF ;
433  if (ver == "") {
434  strm << "XDODS-Server: " << DVR << CRLF ;
435  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
436  }
437  else {
438  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
439  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
440  }
441  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
442 
443  const time_t t = time(0);
444  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
445 
446  strm << "Last-Modified: " ;
447  if (last_modified > 0)
448  strm << rfc822_date(last_modified).c_str() << CRLF ;
449  else
450  strm << rfc822_date(t).c_str() << CRLF ;
451 
452  if (type == dap4_ddx)
453  strm << "Content-Type: text/xml" << CRLF ;
454  else
455  strm << "Content-Type: text/plain" << CRLF ;
456 
457  // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
458  // jhrg 12/23/05
459  strm << "Content-Description: " << descrip[type] << CRLF ;
460  if (type == dods_error) // don't cache our error responses.
461  strm << "Cache-Control: no-cache" << CRLF ;
462  // Don't write a Content-Encoding header for x-plain since that breaks
463  // Netscape on NT. jhrg 3/23/97
464  if (enc != x_plain)
465  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
466  strm << CRLF ;
467 }
468 
469 #if FILE_METHODS
470 
480 void
481 set_mime_html(FILE *out, ObjectType type, const string &ver,
482  EncodingType enc, const time_t last_modified)
483 {
484  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
485  if (ver == "") {
486  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
487  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
488  }
489  else {
490  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
491  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
492  }
493  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
494 
495  const time_t t = time(0);
496  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
497 
498  fprintf(out, "Last-Modified: ") ;
499  if (last_modified > 0)
500  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
501  else
502  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
503 
504  fprintf(out, "Content-type: text/html%s", CRLF) ;
505  // See note above about Content-Description header. jhrg 12/23/05
506  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
507  if (type == dods_error) // don't cache our error responses.
508  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
509  // Don't write a Content-Encoding header for x-plain since that breaks
510  // Netscape on NT. jhrg 3/23/97
511  if (enc != x_plain)
512  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
513  fprintf(out, CRLF) ;
514 }
515 #endif
516 
527 void
528 set_mime_html(ostream &strm, ObjectType type, const string &ver,
529  EncodingType enc, const time_t last_modified)
530 {
531  strm << "HTTP/1.0 200 OK" << CRLF ;
532  if (ver == "") {
533  strm << "XDODS-Server: " << DVR << CRLF ;
534  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
535  }
536  else {
537  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
538  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
539  }
540  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
541 
542  const time_t t = time(0);
543  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
544 
545  strm << "Last-Modified: " ;
546  if (last_modified > 0)
547  strm << rfc822_date(last_modified).c_str() << CRLF ;
548  else
549  strm << rfc822_date(t).c_str() << CRLF ;
550 
551  strm << "Content-type: text/html" << CRLF ;
552  // See note above about Content-Description header. jhrg 12/23/05
553  strm << "Content-Description: " << descrip[type] << CRLF ;
554  if (type == dods_error) // don't cache our error responses.
555  strm << "Cache-Control: no-cache" << CRLF ;
556  // Don't write a Content-Encoding header for x-plain since that breaks
557  // Netscape on NT. jhrg 3/23/97
558  if (enc != x_plain)
559  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
560  strm << CRLF ;
561 }
562 
563 #if FILE_METHODS
564 
577 void
578 set_mime_binary(FILE *out, ObjectType type, const string &ver,
579  EncodingType enc, const time_t last_modified)
580 {
581  fprintf(out, "HTTP/1.0 200 OK%s", CRLF) ;
582  if (ver == "") {
583  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
584  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
585  }
586  else {
587  fprintf(out, "XDODS-Server: %s%s", ver.c_str(), CRLF) ;
588  fprintf(out, "XOPeNDAP-Server: %s%s", ver.c_str(), CRLF) ;
589  }
590  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
591 
592  const time_t t = time(0);
593  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
594 
595  fprintf(out, "Last-Modified: ") ;
596  if (last_modified > 0)
597  fprintf(out, "%s%s", rfc822_date(last_modified).c_str(), CRLF) ;
598  else
599  fprintf(out, "%s%s", rfc822_date(t).c_str(), CRLF) ;
600 
601  fprintf(out, "Content-Type: application/octet-stream%s", CRLF) ;
602  fprintf(out, "Content-Description: %s%s", descrip[type], CRLF) ;
603  if (enc != x_plain)
604  fprintf(out, "Content-Encoding: %s%s", encoding[enc], CRLF) ;
605 
606  fprintf(out, CRLF) ;
607 }
608 #endif
609 
623 void
624 set_mime_binary(ostream &strm, ObjectType type, const string &ver,
625  EncodingType enc, const time_t last_modified)
626 {
627  strm << "HTTP/1.0 200 OK" << CRLF ;
628  if (ver == "") {
629  strm << "XDODS-Server: " << DVR << CRLF ;
630  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
631  }
632  else {
633  strm << "XDODS-Server: " << ver.c_str() << CRLF ;
634  strm << "XOPeNDAP-Server: " << ver.c_str() << CRLF ;
635  }
636  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
637 
638  const time_t t = time(0);
639  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
640 
641  strm << "Last-Modified: " ;
642  if (last_modified > 0)
643  strm << rfc822_date(last_modified).c_str() << CRLF ;
644  else
645  strm << rfc822_date(t).c_str() << CRLF ;
646 
647  strm << "Content-Type: application/octet-stream" << CRLF ;
648  strm << "Content-Description: " << descrip[type] << CRLF ;
649  if (enc != x_plain)
650  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
651 
652  strm << CRLF ;
653 }
654 
655 void set_mime_multipart(ostream &strm, const string &boundary,
656  const string &start, ObjectType type,
657  const string &version, EncodingType enc,
658  const time_t last_modified)
659 {
660  strm << "HTTP/1.0 200 OK" << CRLF ;
661  if (version == "") {
662  strm << "XDODS-Server: " << DVR << CRLF ;
663  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
664  }
665  else {
666  strm << "XDODS-Server: " << version.c_str() << CRLF ;
667  strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
668  }
669  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
670 
671  const time_t t = time(0);
672  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
673 
674  strm << "Last-Modified: " ;
675  if (last_modified > 0)
676  strm << rfc822_date(last_modified).c_str() << CRLF ;
677  else
678  strm << rfc822_date(t).c_str() << CRLF ;
679 
680  strm << "Content-Type: Multipart/Related; boundary=" << boundary
681  << "; start=\"<" << start << ">\"; type=\"Text/xml\"" << CRLF ;
682  strm << "Content-Description: " << descrip[type] << CRLF ;
683  if (enc != x_plain)
684  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
685 
686  strm << CRLF ;
687 }
688 
689 void set_mime_ddx_boundary(ostream &strm, const string &boundary,
690  const string &cid, ObjectType type, EncodingType enc)
691 {
692  strm << "--" << boundary << CRLF;
693  strm << "Content-Type: Text/xml; charset=iso-8859-1" << CRLF;
694  strm << "Content-Id: <" << cid << ">" << CRLF;
695  strm << "Content-Description: " << descrip[type] << CRLF ;
696  if (enc != x_plain)
697  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
698 
699  strm << CRLF;
700 }
701 
702 void set_mime_data_boundary(ostream &strm, const string &boundary,
703  const string &cid, ObjectType type, EncodingType enc)
704 {
705  strm << "--" << boundary << CRLF;
706  strm << "Content-Type: application/octet-stream" << CRLF;
707  strm << "Content-Id: <" << cid << ">" << CRLF;
708  strm << "Content-Description: " << descrip[type] << CRLF ;
709  if (enc != x_plain)
710  strm << "Content-Encoding: " << encoding[enc] << CRLF ;
711 
712  strm << CRLF;
713 }
714 
715 const size_t line_length = 1024;
716 
730 string get_next_mime_header(FILE *in)
731 {
732  // Get the header line and strip \r\n. Some headers end with just \n.
733  // If a blank line is found, return an empty string.
734  char line[line_length];
735  while (!feof(in)) {
736  if (fgets(line, line_length, in)
737  && (strncmp(line, CRLF, 2) == 0 || line[0] == '\n'))
738  return "";
739  else {
740  size_t slen = min(strlen(line), line_length); // Never > line_length
741  line[slen - 1] = '\0'; // remove the newline
742  if (line[slen - 2] == '\r') // ...and the preceding carriage return
743  line[slen - 2] = '\0';
744  return string(line);
745  }
746  }
747 
748  throw Error("I expected to find a MIME header, but got EOF instead.");
749 }
750 
758 void parse_mime_header(const string &header, string &name, string &value)
759 {
760  istringstream iss(header);
761  // Set downcase
762  char s[line_length];
763  iss.getline(s, 1023, ':');
764  name = s;
765 
766  iss.ignore(1023, ' ');
767  iss.getline(s, 1023);
768  value = s;
769 
770  downcase(name);
771  downcase(value);
772 }
773 
781 bool is_boundary(const char *line, const string &boundary)
782 {
783  if (!(line[0] == '-' && line[1] == '-'))
784  return false;
785  else {
786  return strncmp(line, boundary.c_str(), boundary.length()) == 0;
787  }
788 }
789 
798 string read_multipart_boundary(FILE *in, const string &boundary)
799 {
800  string boundary_line = get_next_mime_header(in);
801  // If the caller passed in a value for the boundary, test for that value,
802  // else just see that this line starts with '--'.
803  // The value of 'boundary_line' is returned by this function.
804  if ((!boundary.empty() && is_boundary(boundary_line.c_str(), boundary))
805  || boundary_line.find("--") != 0)
806  throw Error(
807  "The DAP4 data response document is broken - missing or malformed boundary.");
808 
809  return boundary_line;
810 }
811 
832 void read_multipart_headers(FILE *in, const string &content_type,
833  const ObjectType object_type, const string &cid)
834 {
835  bool ct = false, cd = false, ci = false;
836 
837  string header = get_next_mime_header(in);
838  while (!header.empty()) {
839  string name, value;
840  parse_mime_header(header, name, value);
841 
842  if (name =="content-type") {
843  ct = true;
844  if (value.find(content_type) == string::npos)
845  throw Error("Content-Type for this part of a DAP4 data response must be " + content_type + ".");
846  }
847  else if (name == "content-description") {
848  cd = true;
849  if (get_description_type(value) != object_type)
850  throw Error("Content-Description for this part of a DAP4 data response must be dap4-ddx or dap4-data-ddx");
851  }
852  else if (name == "content-id") {
853  ci = true;
854  if (!cid.empty() && value != cid)
855  throw Error("Content-Id mismatch. Expected: " + cid
856  + ", but got: " + value);
857  }
858 
859  header = get_next_mime_header(in);
860  }
861 
862  if (!(ct && cd && ci))
863  throw Error("The DAP4 data response document is broken - missing header.");
864 }
872 string cid_to_header_value(const string &cid)
873 {
874  string::size_type offset = cid.find("cid:");
875  if (offset != 0)
876  throw Error("expected CID to start with 'cid:'");
877 
878  string value = "<";
879  value.append(cid.substr(offset + 4));
880  value.append(">");
881  downcase(value);
882 
883  return value;
884 }
885 
886 #if FILE_METHODS
887 
893 void
894 set_mime_error(FILE *out, int code, const string &reason,
895  const string &version)
896 {
897  fprintf(out, "HTTP/1.0 %d %s%s", code, reason.c_str(), CRLF) ;
898  if (version == "") {
899  fprintf(out, "XDODS-Server: %s%s", DVR, CRLF) ;
900  fprintf(out, "XOPeNDAP-Server: %s%s", DVR, CRLF) ;
901  }
902  else {
903  fprintf(out, "XDODS-Server: %s%s", version.c_str(), CRLF) ;
904  fprintf(out, "XOPeNDAP-Server: %s%s", version.c_str(), CRLF) ;
905  }
906  fprintf(out, "XDAP: %s%s", DAP_PROTOCOL_VERSION, CRLF) ;
907 
908  const time_t t = time(0);
909  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
910  fprintf(out, "Cache-Control: no-cache%s", CRLF) ;
911  fprintf(out, CRLF) ;
912 }
913 #endif
914 
921 void
922 set_mime_error(ostream &strm, int code, const string &reason,
923  const string &version)
924 {
925  strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF ;
926  if (version == "") {
927  strm << "XDODS-Server: " << DVR << CRLF ;
928  strm << "XOPeNDAP-Server: " << DVR << CRLF ;
929  }
930  else {
931  strm << "XDODS-Server: " << version.c_str() << CRLF ;
932  strm << "XOPeNDAP-Server: " << version.c_str() << CRLF ;
933  }
934  strm << "XDAP: " << DAP_PROTOCOL_VERSION << CRLF ;
935 
936  const time_t t = time(0);
937  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
938  strm << "Cache-Control: no-cache" << CRLF ;
939  strm << CRLF ;
940 }
941 
942 #if FILE_METHODS
943 
949 void
951 {
952  fprintf(out, "HTTP/1.0 304 NOT MODIFIED%s", CRLF) ;
953  const time_t t = time(0);
954  fprintf(out, "Date: %s%s", rfc822_date(t).c_str(), CRLF) ;
955  fprintf(out, CRLF) ;
956 }
957 #endif
958 
965 void
966 set_mime_not_modified(ostream &strm)
967 {
968  strm << "HTTP/1.0 304 NOT MODIFIED" << CRLF ;
969  const time_t t = time(0);
970  strm << "Date: " << rfc822_date(t).c_str() << CRLF ;
971  strm << CRLF ;
972 }
973 
982 bool
983 found_override(string name, string &doc)
984 {
985  ifstream ifs((name + ".ovr").c_str());
986  if (!ifs)
987  return false;
988 
989  char tmp[256];
990  doc = "";
991  while (!ifs.eof()) {
992  ifs.getline(tmp, 255);
993  strcat(tmp, "\n");
994  doc += tmp;
995  }
996 
997  ifs.close();
998  return true;
999 }
1000 
1009 bool
1011 {
1012  char tmp[256];
1013  while (!feof(in)) {
1014  char *s = fgets(tmp, 255, in);
1015  if (s && strncmp(s, CRLF, 2) == 0)
1016  return true;
1017  }
1018 
1019  return false;
1020 }
1021 
1022 } // namespace libdap
1023 
const size_t line_length
Definition: mime_util.cc:715
#define CRLF
Definition: mime_util.cc:86
void set_mime_not_modified(ostream &strm)
Send a `Not Modified&#39; response.
Definition: mime_util.cc:966
void ErrMsgT(const string &Msgt)
Logs an error message.
Definition: mime_util.cc:141
void set_mime_data_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:702
time_t last_modified_time(const string &name)
Definition: mime_util.cc:266
void downcase(string &s)
Definition: util.cc:367
#define not_used
Definition: config.h:521
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition: mime_util.cc:832
#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:872
bool found_override(string name, string &doc)
Definition: mime_util.cc:983
void parse_mime_header(const string &header, string &name, string &value)
Definition: mime_util.cc:758
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:624
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:1010
void set_mime_ddx_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:689
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:429
string get_next_mime_header(FILE *in)
Definition: mime_util.cc:730
string read_multipart_boundary(FILE *in, const string &boundary)
Definition: mime_util.cc:798
#define DVR
Definition: config.h:67
string rfc822_date(const time_t t)
Definition: mime_util.cc:247
void set_mime_error(ostream &strm, int code, const string &reason, const string &version)
Definition: mime_util.cc:922
string name_path(const string &path)
Returns the filename portion of a pathname.
Definition: mime_util.cc:178
bool is_boundary(const char *line, const string &boundary)
Definition: mime_util.cc:781
void set_mime_html(ostream &strm, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:528
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:655
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:109