Panda3D
multifile.cxx
1 // Filename: multifile.cxx
2 // Created by: mike (09Jan97)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "multifile.h"
16 
17 #include "config_express.h"
18 #include "streamWriter.h"
19 #include "streamReader.h"
20 #include "datagram.h"
21 #include "zStream.h"
22 #include "encryptStream.h"
23 #include "virtualFileSystem.h"
24 #include "virtualFile.h"
25 
26 #include <algorithm>
27 #include <iterator>
28 #include <time.h>
29 
30 // This sequence of bytes begins each Multifile to identify it as a
31 // Multifile.
32 const char Multifile::_header[] = "pmf\0\n\r";
33 const size_t Multifile::_header_size = 6;
34 
35 // These numbers identify the version of the Multifile. Generally, a
36 // change in the major version is intolerable; while a Multifile with
37 // an older minor version may still be read.
38 const int Multifile::_current_major_ver = 1;
39 
40 const int Multifile::_current_minor_ver = 1;
41 // Bumped to version 1.1 on 6/8/06 to add timestamps.
42 
43 // To confirm that the supplied password matches, we write the
44 // Mutifile magic header at the beginning of the encrypted stream.
45 // I suppose this does compromise the encryption security a tiny
46 // bit by making it easy for crackers to validate that a
47 // particular password guess matches or doesn't match, but the
48 // encryption algorithm doesn't depend on this being difficult
49 // anyway.
50 const char Multifile::_encrypt_header[] = "crypty";
51 const size_t Multifile::_encrypt_header_size = 6;
52 
53 
54 
55 //
56 // A Multifile consists of the following elements:
57 //
58 // (1) A header. This is always the first n bytes of the Multifile,
59 // and contains a magic number to identify the file, as well as
60 // version numbers and any file-specific parameters.
61 //
62 // char[6] The string Multifile::_header, a magic number.
63 // int16 The file's major version number
64 // int16 The file's minor version number
65 // uint32 Scale factor. This scales all address references within
66 // the file. Normally 1, this may be set larger to
67 // support Multifiles larger than 4GB.
68 // uint32 An overall modification timestamp for the entire multifile.
69 
70 //
71 // (2) Zero or more index entries, one for each subfile within the
72 // Multifile. These entries are of variable length. The first one of
73 // these immediately follows the header, and the first word of each
74 // index entry contains the address of the next index entry. A zero
75 // "next" address marks the end of the chain. These may appear at any
76 // point within the Multifile; they do not necessarily appear in
77 // sequential order at the beginning of the file (although they will
78 // after the file has been "packed").
79 //
80 // uint32 The address of the next entry. 0 to mark the end.
81 // uint32 The address of this subfile's data record.
82 // uint32 The length in bytes of this subfile's data record.
83 // uint16 The Subfile::_flags member.
84 // [uint32] The original, uncompressed and unencrypted length of the
85 // subfile, if it is compressed or encrypted. This field
86 // is only present if one or both of the SF_compressed
87 // or SF_encrypted bits are set in _flags.
88 // uint32 A modification timestamp for the subfile.
89 // uint16 The length in bytes of the subfile's name.
90 // char[n] The subfile's name.
91 //
92 // (3) Zero or more data entries, one for each subfile. These may
93 // appear at any point within the Multifile; they do not necessarily
94 // follow each index entry, nor are they necessarily all grouped
95 // together at the end (although they will be all grouped together at
96 // the end after the file has been "packed"). These are just blocks
97 // of literal data.
98 //
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: Multifile::Constructor
102 // Access: Published
103 // Description:
104 ////////////////////////////////////////////////////////////////////
105 Multifile::
106 Multifile() :
107  _read_filew(_read_file),
108  _read_write_filew(_read_write_file)
109 {
110  ConfigVariableInt multifile_encryption_iteration_count
111  ("multifile-encryption-iteration-count", 0,
112  PRC_DESC("This is a special value of encryption-iteration-count used to encrypt "
113  "subfiles within a multifile. It has a default value of 0 (just one "
114  "application), on the assumption that the files from a multifile must "
115  "be loaded quickly, without paying the cost of an expensive hash on "
116  "each subfile in order to decrypt it."));
117 
118  _read = (IStreamWrapper *)NULL;
119  _write = (ostream *)NULL;
120  _offset = 0;
121  _owns_stream = false;
122  _next_index = 0;
123  _last_index = 0;
124  _last_data_byte = 0;
125  _needs_repack = false;
126  _timestamp = 0;
127  _timestamp_dirty = false;
128  _record_timestamp = true;
129  _scale_factor = 1;
130  _new_scale_factor = 1;
131  _encryption_flag = false;
132  _encryption_iteration_count = multifile_encryption_iteration_count;
133  _file_major_ver = 0;
134  _file_minor_ver = 0;
135 
136 #ifdef HAVE_OPENSSL
137  // Get these values from the config file via an EncryptStreamBuf.
138  EncryptStreamBuf tbuf;
139  _encryption_algorithm = tbuf.get_algorithm();
140  _encryption_key_length = tbuf.get_key_length();
141 #endif
142 }
143 
144 ////////////////////////////////////////////////////////////////////
145 // Function: Multifile::Destructor
146 // Access: Published
147 // Description:
148 ////////////////////////////////////////////////////////////////////
149 Multifile::
150 ~Multifile() {
151  close();
152 }
153 
154 ////////////////////////////////////////////////////////////////////
155 // Function: Multifile::Copy Constructor
156 // Access: Private
157 // Description: Don't try to copy Multifiles.
158 ////////////////////////////////////////////////////////////////////
159 Multifile::
160 Multifile(const Multifile &copy) :
161  _read_filew(_read_file),
162  _read_write_filew(_read_write_file)
163 {
164  nassertv(false);
165 }
166 
167 ////////////////////////////////////////////////////////////////////
168 // Function: Multifile::Copy Assignment Operator
169 // Access: Private
170 // Description: Don't try to copy Multifiles.
171 ////////////////////////////////////////////////////////////////////
172 void Multifile::
173 operator = (const Multifile &copy) {
174  nassertv(false);
175 }
176 
177 ////////////////////////////////////////////////////////////////////
178 // Function: Multifile::open_read
179 // Access: Published
180 // Description: Opens the named Multifile on disk for reading. The
181 // Multifile index is read in, and the list of subfiles
182 // becomes available; individual subfiles may then be
183 // extracted or read, but the list of subfiles may not
184 // be modified.
185 //
186 // Also see the version of open_read() which accepts an
187 // istream. Returns true on success, false on failure.
188 ////////////////////////////////////////////////////////////////////
189 bool Multifile::
190 open_read(const Filename &multifile_name, const streampos &offset) {
191  close();
192  Filename fname = multifile_name;
193  fname.set_binary();
194 
196  PT(VirtualFile) vfile = vfs->get_file(fname);
197  if (vfile == NULL) {
198  return false;
199  }
200  istream *multifile_stream = vfile->open_read_file(false);
201  if (multifile_stream == NULL) {
202  return false;
203  }
204 
205  _timestamp = vfile->get_timestamp();
206  _timestamp_dirty = true;
207  _read = new IStreamWrapper(multifile_stream, true);
208  _owns_stream = true;
209  _multifile_name = multifile_name;
210  _offset = offset;
211  return read_index();
212 }
213 
214 ////////////////////////////////////////////////////////////////////
215 // Function: Multifile::open_read
216 // Access: Public
217 // Description: Opens an anonymous Multifile for reading using an
218 // istream. There must be seek functionality via
219 // seekg() and tellg() on the istream.
220 //
221 // If owns_pointer is true, then the Multifile assumes
222 // ownership of the stream pointer and will delete it
223 // when the multifile is closed, including if this
224 // function returns false.
225 ////////////////////////////////////////////////////////////////////
226 bool Multifile::
227 open_read(IStreamWrapper *multifile_stream, bool owns_pointer,
228  const streampos &offset) {
229  close();
230  _timestamp = time(NULL);
231  _timestamp_dirty = true;
232  _read = multifile_stream;
233  _owns_stream = owns_pointer;
234  _offset = offset;
235  return read_index();
236 }
237 
238 ////////////////////////////////////////////////////////////////////
239 // Function: Multifile::open_write
240 // Access: Published
241 // Description: Opens the named Multifile on disk for writing. If
242 // there already exists a file by that name, it is
243 // truncated. The Multifile is then prepared for
244 // accepting a brand new set of subfiles, which will be
245 // written to the indicated filename. Individual
246 // subfiles may not be extracted or read.
247 //
248 // Also see the version of open_write() which accepts an
249 // ostream. Returns true on success, false on failure.
250 ////////////////////////////////////////////////////////////////////
251 bool Multifile::
252 open_write(const Filename &multifile_name) {
253  close();
254  Filename fname = multifile_name;
255  fname.set_binary();
256  if (!fname.open_write(_write_file, true)) {
257  return false;
258  }
259  _timestamp = time(NULL);
260  _timestamp_dirty = true;
261  _write = &_write_file;
262  _multifile_name = multifile_name;
263  return true;
264 }
265 
266 ////////////////////////////////////////////////////////////////////
267 // Function: Multifile::open_write
268 // Access: Public
269 // Description: Opens an anonymous Multifile for writing using an
270 // ostream. There must be seek functionality via
271 // seekp() and tellp() on the pstream.
272 //
273 // If owns_pointer is true, then the Multifile assumes
274 // ownership of the stream pointer and will delete it
275 // when the multifile is closed, including if this
276 // function returns false.
277 ////////////////////////////////////////////////////////////////////
278 bool Multifile::
279 open_write(ostream *multifile_stream, bool owns_pointer) {
280  close();
281  _timestamp = time(NULL);
282  _timestamp_dirty = true;
283  _write = multifile_stream;
284  _owns_stream = owns_pointer;
285  _write->seekp(0, ios::beg);
286  return true;
287 }
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: Multifile::open_read_write
291 // Access: Published
292 // Description: Opens the named Multifile on disk for reading and
293 // writing. If there already exists a file by that
294 // name, its index is read. Subfiles may be added or
295 // removed, and the resulting changes will be written to
296 // the named file.
297 //
298 // Also see the version of open_read_write() which
299 // accepts an iostream. Returns true on success, false
300 // on failure.
301 ////////////////////////////////////////////////////////////////////
302 bool Multifile::
303 open_read_write(const Filename &multifile_name) {
304  close();
305  Filename fname = multifile_name;
306  fname.set_binary();
307  bool exists = fname.exists();
308  if (!fname.open_read_write(_read_write_file)) {
309  return false;
310  }
311  if (exists) {
312  _timestamp = fname.get_timestamp();
313  } else {
314  _timestamp = time(NULL);
315  }
316  _timestamp_dirty = true;
317  _read = &_read_write_filew;
318  _write = &_read_write_file;
319  _multifile_name = multifile_name;
320 
321  if (exists) {
322  return read_index();
323  } else {
324  return true;
325  }
326 }
327 
328 ////////////////////////////////////////////////////////////////////
329 // Function: Multifile::open_read_write
330 // Access: Public
331 // Description: Opens an anonymous Multifile for reading and writing
332 // using an iostream. There must be seek functionality
333 // via seekg()/seekp() and tellg()/tellp() on the
334 // iostream.
335 //
336 // If owns_pointer is true, then the Multifile assumes
337 // ownership of the stream pointer and will delete it
338 // when the multifile is closed, including if this
339 // function returns false.
340 ////////////////////////////////////////////////////////////////////
341 bool Multifile::
342 open_read_write(iostream *multifile_stream, bool owns_pointer) {
343  close();
344  _timestamp = time(NULL);
345  _timestamp_dirty = true;
346 
347  // We don't support locking when opening a file in read-write mode,
348  // because we don't bother with locking on write. But we need to
349  // have an IStreamWrapper to assign to the _read member, so we
350  // create one on-the-fly here.
351  _read = new StreamWrapper(multifile_stream, owns_pointer);
352  _write = multifile_stream;
353  _owns_stream = true; // Because we own the StreamWrapper, above.
354  _write->seekp(0, ios::beg);
355 
356  // Check whether the read stream is empty.
357  multifile_stream->seekg(0, ios::end);
358  if (multifile_stream->tellg() == (streampos)0) {
359  // The read stream is empty, which is always valid.
360  return true;
361  }
362 
363  // The read stream is not empty, so we'd better have a valid
364  // Multifile.
365  return read_index();
366 }
367 
368 ////////////////////////////////////////////////////////////////////
369 // Function: Multifile::close
370 // Access: Published
371 // Description: Closes the Multifile if it is open. All changes are
372 // flushed to disk, and the file becomes invalid for
373 // further operations until the next call to open().
374 ////////////////////////////////////////////////////////////////////
375 void Multifile::
376 close() {
377  if (_new_scale_factor != _scale_factor) {
378  // If we have changed the scale factor recently, we need to force
379  // a repack.
380  repack();
381  } else {
382  flush();
383  }
384 
385  if (_owns_stream) {
386  // We prefer to delete the IStreamWrapper over the ostream, if
387  // possible.
388  if (_read != (IStreamWrapper *)NULL) {
389  delete _read;
390  } else if (_write != (ostream *)NULL) {
391  delete _write;
392  }
393  }
394 
395  _read = (IStreamWrapper *)NULL;
396  _write = (ostream *)NULL;
397  _offset = 0;
398  _owns_stream = false;
399  _next_index = 0;
400  _last_index = 0;
401  _needs_repack = false;
402  _timestamp = 0;
403  _timestamp_dirty = false;
404  _scale_factor = 1;
405  _new_scale_factor = 1;
406  _encryption_flag = false;
407  _file_major_ver = 0;
408  _file_minor_ver = 0;
409 
410  _read_file.close();
411  _write_file.close();
412  _read_write_file.close();
413  _multifile_name = Filename();
414 
415  clear_subfiles();
416 }
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: Multifile::set_scale_factor
420 // Access: Published
421 // Description: Changes the internal scale factor for this Multifile.
422 //
423 // This is normally 1, but it may be set to any
424 // arbitrary value (greater than zero) to support
425 // Multifile archives that exceed 4GB, if necessary.
426 // (Individual subfiles may still not exceed 4GB.)
427 //
428 // All addresses within the file are rounded up to the
429 // next multiple of _scale_factor, and zeros are written
430 // to the file to fill the resulting gaps. Then the
431 // address is divided by _scale_factor and written out
432 // as a 32-bit integer. Thus, setting a scale factor of
433 // 2 supports up to 8GB files, 3 supports 12GB files,
434 // etc.
435 //
436 // Calling this function on an already-existing
437 // Multifile will have no immediate effect until a
438 // future call to repack() or close() (or until the
439 // Multifile is destructed).
440 ////////////////////////////////////////////////////////////////////
441 void Multifile::
442 set_scale_factor(size_t scale_factor) {
443  nassertv(is_write_valid());
444  nassertv(scale_factor != (size_t)0);
445 
446  if (_next_index == (streampos)0) {
447  // If it's a brand new Multifile, we can go ahead and set it
448  // immediately.
449  _scale_factor = scale_factor;
450  } else {
451  // Otherwise, we'd better have read access so we can repack it
452  // later.
453  nassertv(is_read_valid());
454  }
455 
456  // Setting the _new_scale_factor different from the _scale_factor
457  // will force a repack operation on close.
458  _new_scale_factor = scale_factor;
459 }
460 
461 ////////////////////////////////////////////////////////////////////
462 // Function: Multifile::add_subfile
463 // Access: Published
464 // Description: Adds a file on disk as a subfile to the Multifile.
465 // The file named by filename will be read and added to
466 // the Multifile at the next call to flush(). If there
467 // already exists a subfile with the indicated name, it
468 // is replaced without examining its contents (but see
469 // also update_subfile).
470 //
471 // Filename::set_binary() or set_text() must have been
472 // called previously to specify the nature of the source
473 // file. If set_text() was called, the text flag will
474 // be set on the subfile.
475 //
476 // Returns the subfile name on success (it might have
477 // been modified slightly), or empty string on failure.
478 ////////////////////////////////////////////////////////////////////
479 string Multifile::
480 add_subfile(const string &subfile_name, const Filename &filename,
481  int compression_level) {
482  nassertr(is_write_valid(), string());
483 
484  Filename fname = filename;
485  if (multifile_always_binary) {
486  fname.set_binary();
487  }
488 
489  nassertr(fname.is_binary_or_text(), string());
490 
491  if (!fname.exists()) {
492  return string();
493  }
494  string name = standardize_subfile_name(subfile_name);
495  if (!name.empty()) {
496  Subfile *subfile = new Subfile;
497  subfile->_name = name;
498  subfile->_source_filename = fname;
499  if (fname.is_text()) {
500  subfile->_flags |= SF_text;
501  }
502 
503  add_new_subfile(subfile, compression_level);
504  }
505 
506  _timestamp = time(NULL);
507  _timestamp_dirty = true;
508 
509  return name;
510 }
511 
512 ////////////////////////////////////////////////////////////////////
513 // Function: Multifile::add_subfile
514 // Access: Public
515 // Description: Adds a file from a stream as a subfile to the Multifile.
516 // The indicated istream will be read and its contents
517 // added to the Multifile at the next call to flush().
518 // The file will be added as a binary subfile.
519 //
520 // Note that the istream must remain untouched and
521 // unused by any other code until flush() is called. At
522 // that time, the Multifile will read the entire
523 // contents of the istream from the current file
524 // position to the end of the file. Subsequently, the
525 // Multifile will *not* close or delete the istream. It
526 // is the caller's responsibility to ensure that the
527 // istream pointer does not destruct during the lifetime
528 // of the Multifile.
529 //
530 // Returns the subfile name on success (it might have
531 // been modified slightly), or empty string on failure.
532 ////////////////////////////////////////////////////////////////////
533 string Multifile::
534 add_subfile(const string &subfile_name, istream *subfile_data,
535  int compression_level) {
536  nassertr(is_write_valid(), string());
537 
538  string name = standardize_subfile_name(subfile_name);
539  if (!name.empty()) {
540  Subfile *subfile = new Subfile;
541  subfile->_name = name;
542  subfile->_source = subfile_data;
543  add_new_subfile(subfile, compression_level);
544  }
545 
546  return name;
547 }
548 
549 ////////////////////////////////////////////////////////////////////
550 // Function: Multifile::update_subfile
551 // Access: Published
552 // Description: Adds a file on disk to the subfile. If a subfile
553 // already exists with the same name, its contents are
554 // compared byte-for-byte to the disk file, and it is
555 // replaced only if it is different; otherwise, the
556 // multifile is left unchanged.
557 //
558 // Filename::set_binary() or set_text() must have been
559 // called previously to specify the nature of the source
560 // file. If set_text() was called, the text flag will
561 // be set on the subfile.
562 ////////////////////////////////////////////////////////////////////
563 string Multifile::
564 update_subfile(const string &subfile_name, const Filename &filename,
565  int compression_level) {
566  nassertr(is_write_valid(), string());
567 
568  Filename fname = filename;
569  if (multifile_always_binary) {
570  fname.set_binary();
571  }
572 
573  nassertr(fname.is_binary_or_text(), string());
574 
575  if (!fname.exists()) {
576  return string();
577  }
578  string name = standardize_subfile_name(subfile_name);
579  if (!name.empty()) {
580  int index = find_subfile(name);
581  if (index >= 0) {
582  // The subfile already exists; compare it to the source file.
583  if (compare_subfile(index, fname)) {
584  // The files are identical; do nothing.
585  return name;
586  }
587  }
588 
589  // The subfile does not already exist or it is different from the
590  // source file. Add the new source file.
591  Subfile *subfile = new Subfile;
592  subfile->_name = name;
593  subfile->_source_filename = fname;
594  if (fname.is_text()) {
595  subfile->_flags |= SF_text;
596  }
597 
598  add_new_subfile(subfile, compression_level);
599  }
600 
601  _timestamp = time(NULL);
602  _timestamp_dirty = true;
603 
604  return name;
605 }
606 
607 #ifdef HAVE_OPENSSL
608 ////////////////////////////////////////////////////////////////////
609 // Function: Multifile::CertRecord::Constructor
610 // Access: Public
611 // Description: Ownership of the X509 object is passed into the
612 // CertRecord; it will be freed when the CertRecord
613 // destructs.
614 ////////////////////////////////////////////////////////////////////
615 Multifile::CertRecord::
616 CertRecord(X509 *cert) :
617  _cert(cert)
618 {
619 }
620 
621 ////////////////////////////////////////////////////////////////////
622 // Function: Multifile::CertRecord::Copy Constructor
623 // Access: Public
624 // Description:
625 ////////////////////////////////////////////////////////////////////
626 Multifile::CertRecord::
627 CertRecord(const Multifile::CertRecord &copy) :
628  _cert(X509_dup(copy._cert))
629 {
630 }
631 
632 ////////////////////////////////////////////////////////////////////
633 // Function: Multifile::CertRecord::Destructor
634 // Access: Public
635 // Description:
636 ////////////////////////////////////////////////////////////////////
637 Multifile::CertRecord::
638 ~CertRecord() {
639  X509_free(_cert);
640 }
641 
642 ////////////////////////////////////////////////////////////////////
643 // Function: Multifile::CertRecord::Copy Assignment
644 // Access: Public
645 // Description:
646 ////////////////////////////////////////////////////////////////////
647 void Multifile::CertRecord::
648 operator = (const Multifile::CertRecord &other) {
649  X509_free(_cert);
650  _cert = X509_dup(other._cert);
651 }
652 #endif // HAVE_OPENSSL
653 
654 #ifdef HAVE_OPENSSL
655 ////////////////////////////////////////////////////////////////////
656 // Function: Multifile::add_signature
657 // Access: Published
658 // Description: Adds a new signature to the Multifile. This
659 // signature associates the indicated certificate with
660 // the current contents of the Multifile. When the
661 // Multifile is read later, the signature will still be
662 // present only if the Multifile is unchanged; any
663 // subsequent changes to the Multifile will
664 // automatically invalidate and remove the signature.
665 //
666 // The chain filename may be empty if the certificate
667 // does not require an authenticating certificate chain
668 // (e.g. because it is self-signed).
669 //
670 // The specified private key must match the certificate,
671 // and the Multifile must be open in read-write mode.
672 // The private key is only used for generating the
673 // signature; it is not written to the Multifile and
674 // cannot be retrieved from the Multifile later.
675 // (However, the certificate *can* be retrieved from the
676 // Multifile later, to identify the entity that created
677 // the signature.)
678 //
679 // This implicitly causes a repack() operation if one is
680 // needed. Returns true on success, false on failure.
681 //
682 // This flavor of add_signature() reads the certificate
683 // and private key from a PEM-formatted file, for
684 // instance as generated by the openssl command. If the
685 // private key file is password-encrypted, the third
686 // parameter will be used as the password to decrypt it.
687 ////////////////////////////////////////////////////////////////////
688 bool Multifile::
689 add_signature(const Filename &certificate, const Filename &chain,
690  const Filename &pkey, const string &password) {
692 
693  if (chain.empty() && pkey.empty()) {
694  // If the second two filenames are empty, assume we're going for
695  // the composite mode, where everything's stuffed into the first
696  // file.
697  return add_signature(certificate, password);
698  }
699 
700  CertChain cert_chain;
701 
702  // Read the certificate file from VFS. First, read the complete
703  // file into memory.
704  string certificate_data;
705  if (!vfs->read_file(certificate, certificate_data, true)) {
706  express_cat.info()
707  << "Could not read " << certificate << ".\n";
708  return false;
709  }
710 
711  // Create an in-memory BIO to read the "file" from the buffer.
712  BIO *certificate_mbio = BIO_new_mem_buf((void *)certificate_data.data(), certificate_data.size());
713  X509 *x509 = PEM_read_bio_X509(certificate_mbio, NULL, NULL, (void *)"");
714  BIO_free(certificate_mbio);
715  if (x509 == NULL) {
716  express_cat.info()
717  << "Could not read certificate in " << certificate << ".\n";
718  return false;
719  }
720 
721  // Store the first X509--the actual certificate--as the first record
722  // in our CertChain object.
723  cert_chain.push_back(CertRecord(x509));
724 
725  // Read the rest of the certificates in the chain file.
726  if (!chain.empty()) {
727  string chain_data;
728  if (!vfs->read_file(chain, chain_data, true)) {
729  express_cat.info()
730  << "Could not read " << chain << ".\n";
731  return false;
732  }
733 
734  BIO *chain_mbio = BIO_new_mem_buf((void *)chain_data.data(), chain_data.size());
735  X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
736  while (c != NULL) {
737  cert_chain.push_back(c);
738  c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
739  }
740  BIO_free(chain_mbio);
741 
742  if (cert_chain.size() == 1) {
743  express_cat.info()
744  << "Could not read certificate chain in " << chain << ".\n";
745  return false;
746  }
747  }
748 
749  // Now do the same thing with the private key. This one may be
750  // password-encrypted on disk.
751  string pkey_data;
752  if (!vfs->read_file(pkey, pkey_data, true)) {
753  express_cat.info()
754  << "Could not read " << pkey << ".\n";
755  return false;
756  }
757 
758  BIO *pkey_mbio = BIO_new_mem_buf((void *)pkey_data.data(), pkey_data.size());
759  EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
760  (void *)password.c_str());
761  BIO_free(pkey_mbio);
762  if (evp_pkey == NULL) {
763  express_cat.info()
764  << "Could not read private key in " << pkey << ".\n";
765  return false;
766  }
767 
768  bool result = add_signature(cert_chain, evp_pkey);
769 
770  EVP_PKEY_free(evp_pkey);
771 
772  return result;
773 }
774 #endif // HAVE_OPENSSL
775 
776 #ifdef HAVE_OPENSSL
777 ////////////////////////////////////////////////////////////////////
778 // Function: Multifile::add_signature
779 // Access: Published
780 // Description: Adds a new signature to the Multifile. This
781 // signature associates the indicated certificate with
782 // the current contents of the Multifile. When the
783 // Multifile is read later, the signature will still be
784 // present only if the Multifile is unchanged; any
785 // subsequent changes to the Multifile will
786 // automatically invalidate and remove the signature.
787 //
788 // This flavor of add_signature() reads the certificate,
789 // private key, and certificate chain from the same
790 // PEM-formatted file. It takes the first private key
791 // found as the intended key, and then uses the first
792 // certificate found that matches that key as the
793 // signing certificate. Any other certificates in the
794 // file are taken to be part of the chain.
795 ////////////////////////////////////////////////////////////////////
796 bool Multifile::
797 add_signature(const Filename &composite, const string &password) {
799 
800  // First, read the complete file into memory.
801  string composite_data;
802  if (!vfs->read_file(composite, composite_data, true)) {
803  express_cat.info()
804  << "Could not read " << composite << ".\n";
805  return false;
806  }
807 
808  // Get the private key.
809  BIO *pkey_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
810  EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
811  (void *)password.c_str());
812  BIO_free(pkey_mbio);
813  if (evp_pkey == NULL) {
814  express_cat.info()
815  << "Could not read private key in " << composite << ".\n";
816  return false;
817  }
818 
819  // Now read all of the certificates.
820  CertChain cert_chain;
821 
822  BIO *chain_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
823  X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
824  while (c != NULL) {
825  cert_chain.push_back(c);
826  c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
827  }
828  BIO_free(chain_mbio);
829 
830  if (cert_chain.empty()) {
831  express_cat.info()
832  << "Could not read certificates in " << composite << ".\n";
833  return false;
834  }
835 
836  // Now find the certificate that matches the signature, and move it
837  // to the front of the chain.
838  size_t i;
839  bool found_match = false;
840  for (i = 0; i < cert_chain.size(); ++i) {
841  X509 *c = cert_chain[i]._cert;
842  if (X509_check_private_key(c, evp_pkey)) {
843  found_match = true;
844  if (i != 0) {
845  // Move this entry to the beginning.
846  cert_chain.insert(cert_chain.begin(), cert_chain[i]);
847  cert_chain.erase(cert_chain.begin() + i + 1);
848  }
849  break;
850  }
851  }
852 
853  if (!found_match) {
854  express_cat.info()
855  << "No certificates in " << composite << " match key.\n";
856  return false;
857  }
858 
859  bool result = add_signature(cert_chain, evp_pkey);
860 
861  EVP_PKEY_free(evp_pkey);
862 
863  return result;
864 }
865 #endif // HAVE_OPENSSL
866 
867 #ifdef HAVE_OPENSSL
868 ////////////////////////////////////////////////////////////////////
869 // Function: Multifile::add_signature
870 // Access: Published
871 // Description: Adds a new signature to the Multifile. This
872 // signature associates the indicated certificate with
873 // the current contents of the Multifile. When the
874 // Multifile is read later, the signature will still be
875 // present only if the Multifile is unchanged; any
876 // subsequent changes to the Multifile will
877 // automatically invalidate and remove the signature.
878 //
879 // If chain is non-NULL, it represents the certificate
880 // chain that validates the certificate.
881 //
882 // The specified private key must match the certificate,
883 // and the Multifile must be open in read-write mode.
884 // The private key is only used for generating the
885 // signature; it is not written to the Multifile and
886 // cannot be retrieved from the Multifile later.
887 // (However, the certificate *can* be retrieved from the
888 // Multifile later, to identify the entity that created
889 // the signature.)
890 //
891 // This implicitly causes a repack() operation if one is
892 // needed. Returns true on success, false on failure.
893 ////////////////////////////////////////////////////////////////////
894 bool Multifile::
895 add_signature(X509 *certificate, STACK_OF(X509) *chain, EVP_PKEY *pkey) {
896  // Convert the certificate and chain into our own CertChain
897  // structure.
898  CertChain cert_chain;
899  cert_chain.push_back(CertRecord(certificate));
900  if (chain != NULL) {
901  int num = sk_X509_num(chain);
902  for (int i = 0; i < num; ++i) {
903  cert_chain.push_back(CertRecord((X509 *)sk_X509_value(chain, i)));
904  }
905  }
906 
907  return add_signature(cert_chain, pkey);
908 }
909 #endif // HAVE_OPENSSL
910 
911 #ifdef HAVE_OPENSSL
912 ////////////////////////////////////////////////////////////////////
913 // Function: Multifile::add_signature
914 // Access: Published
915 // Description: Adds a new signature to the Multifile. This
916 // signature associates the indicated certificate with
917 // the current contents of the Multifile. When the
918 // Multifile is read later, the signature will still be
919 // present only if the Multifile is unchanged; any
920 // subsequent changes to the Multifile will
921 // automatically invalidate and remove the signature.
922 //
923 // The signature certificate is the first certificate on
924 // the CertChain object. Any remaining certificates are
925 // support certificates to authenticate the first one.
926 //
927 // The specified private key must match the certificate,
928 // and the Multifile must be open in read-write mode.
929 // The private key is only used for generating the
930 // signature; it is not written to the Multifile and
931 // cannot be retrieved from the Multifile later.
932 // (However, the certificate *can* be retrieved from the
933 // Multifile later, to identify the entity that created
934 // the signature.)
935 //
936 // This implicitly causes a repack() operation if one is
937 // needed. Returns true on success, false on failure.
938 ////////////////////////////////////////////////////////////////////
939 bool Multifile::
940 add_signature(const Multifile::CertChain &cert_chain, EVP_PKEY *pkey) {
941  if (_needs_repack) {
942  if (!repack()) {
943  return false;
944  }
945  } else {
946  if (!flush()) {
947  return false;
948  }
949  }
950 
951  if (cert_chain.empty()) {
952  express_cat.info()
953  << "No certificate given.\n";
954  return false;
955  }
956 
957  if (pkey == NULL) {
958  express_cat.info()
959  << "No private key given.\n";
960  return false;
961  }
962 
963  if (!X509_check_private_key(cert_chain[0]._cert, pkey)) {
964  express_cat.info()
965  << "Private key does not match certificate.\n";
966  return false;
967  }
968 
969  // Now encode that list of certs to a stream in DER form.
970  stringstream der_stream;
971  StreamWriter der_writer(der_stream);
972  der_writer.add_uint32(cert_chain.size());
973 
974  CertChain::const_iterator ci;
975  for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
976  X509 *cert = (*ci)._cert;
977 
978  int der_len = i2d_X509(cert, NULL);
979  unsigned char *der_buf = new unsigned char[der_len];
980  unsigned char *p = der_buf;
981  i2d_X509(cert, &p);
982  der_writer.append_data(der_buf, der_len);
983  delete[] der_buf;
984  }
985 
986  // Create a temporary Subfile for writing out the signature.
987  der_stream.seekg(0);
988  Subfile *subfile = new Subfile;
989  subfile->_pkey = pkey;
990  subfile->_flags |= SF_signature;
991  subfile->_source = &der_stream;
992 
993  // Write the new Subfile at the end. The cert_special subfiles
994  // always go at the end, because they're not the part of the file
995  // that's signed.
996  nassertr(_new_subfiles.empty(), false);
997  _new_subfiles.push_back(subfile);
998  bool result = flush();
999 
1000  delete subfile;
1001 
1002  return result;
1003 }
1004 #endif // HAVE_OPENSSL
1005 
1006 #ifdef HAVE_OPENSSL
1007 ////////////////////////////////////////////////////////////////////
1008 // Function: Multifile::get_num_signatures
1009 // Access: Published
1010 // Description: Returns the number of matching signatures found on
1011 // the Multifile. These signatures may be iterated via
1012 // get_signature() and related methods.
1013 //
1014 // A signature on this list is guaranteed to match the
1015 // Multifile contents, proving that the Multifile has
1016 // been unmodified since the signature was applied.
1017 // However, this does not guarantee that the certificate
1018 // itself is actually from who it says it is from; only
1019 // that it matches the Multifile contents. See
1020 // validate_signature_certificate() to authenticate a
1021 // particular certificate.
1022 ////////////////////////////////////////////////////////////////////
1023 int Multifile::
1024 get_num_signatures() const {
1025  ((Multifile *)this)->check_signatures();
1026  return _signatures.size();
1027 }
1028 #endif // HAVE_OPENSSL
1029 
1030 #ifdef HAVE_OPENSSL
1031 ////////////////////////////////////////////////////////////////////
1032 // Function: Multifile::get_signature
1033 // Access: Published
1034 // Description: Returns the nth signature found on the Multifile.
1035 // See the comments in get_num_signatures().
1036 ////////////////////////////////////////////////////////////////////
1037 const Multifile::CertChain &Multifile::
1038 get_signature(int n) const {
1039  ((Multifile *)this)->check_signatures();
1040  static CertChain error_chain;
1041  nassertr(n >= 0 && n < (int)_signatures.size(), error_chain);
1042  return _signatures[n];
1043 }
1044 #endif // HAVE_OPENSSL
1045 
1046 #ifdef HAVE_OPENSSL
1047 ////////////////////////////////////////////////////////////////////
1048 // Function: Multifile::get_signature_subject_name
1049 // Access: Published
1050 // Description: Returns the "subject name" for the nth signature found
1051 // on the Multifile. This is a string formatted
1052 // according to RFC2253 that should more-or-less
1053 // identify a particular certificate; when paired with
1054 // the public key (see get_signature_public_key()), it
1055 // can uniquely identify a certificate. See the
1056 // comments in get_num_signatures().
1057 ////////////////////////////////////////////////////////////////////
1058 string Multifile::
1059 get_signature_subject_name(int n) const {
1060  const CertChain &cert_chain = get_signature(n);
1061  nassertr(!cert_chain.empty(), string());
1062 
1063  X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
1064  if (xname != NULL) {
1065  // We use "print" to dump the output to a memory BIO. Is
1066  // there an easier way to extract the X509_NAME text? Curse
1067  // these incomplete docs.
1068  BIO *mbio = BIO_new(BIO_s_mem());
1069  X509_NAME_print_ex(mbio, xname, 0, XN_FLAG_RFC2253);
1070 
1071  char *pp;
1072  long pp_size = BIO_get_mem_data(mbio, &pp);
1073  string name(pp, pp_size);
1074  BIO_free(mbio);
1075  return name;
1076  }
1077 
1078  return string();
1079 }
1080 #endif // HAVE_OPENSSL
1081 
1082 #ifdef HAVE_OPENSSL
1083 ////////////////////////////////////////////////////////////////////
1084 // Function: Multifile::get_signature_friendly_name
1085 // Access: Published
1086 // Description: Returns a "friendly name" for the nth signature found
1087 // on the Multifile. This attempts to extract out the
1088 // most meaningful part of the subject name. It returns
1089 // the emailAddress, if it is defined; otherwise, it
1090 // returns the commonName.
1091 
1092 // See the comments in get_num_signatures().
1093 ////////////////////////////////////////////////////////////////////
1094 string Multifile::
1095 get_signature_friendly_name(int n) const {
1096  const CertChain &cert_chain = get_signature(n);
1097  nassertr(!cert_chain.empty(), string());
1098 
1099  static const int nid_choices[] = {
1100  NID_pkcs9_emailAddress,
1101  NID_subject_alt_name,
1102  NID_commonName,
1103  -1,
1104  };
1105 
1106  // Choose the first NID that exists on the cert.
1107  for (int ni = 0; nid_choices[ni] != -1; ++ni) {
1108  int nid = nid_choices[ni];
1109 
1110  // A complex OpenSSL interface to extract out the name in utf-8.
1111  X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
1112  if (xname != NULL) {
1113  int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
1114  if (pos != -1) {
1115  // We just get the first common name. I guess it's possible to
1116  // have more than one; not sure what that means in this context.
1117  X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
1118  if (xentry != NULL) {
1119  ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
1120  if (data != NULL) {
1121  // We use "print" to dump the output to a memory BIO. Is
1122  // there an easier way to decode the ASN1_STRING? Curse
1123  // these incomplete docs.
1124  BIO *mbio = BIO_new(BIO_s_mem());
1125  ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
1126 
1127  char *pp;
1128  long pp_size = BIO_get_mem_data(mbio, &pp);
1129  string name(pp, pp_size);
1130  BIO_free(mbio);
1131  return name;
1132  }
1133  }
1134  }
1135  }
1136  }
1137 
1138  return string();
1139 }
1140 #endif // HAVE_OPENSSL
1141 
1142 #ifdef HAVE_OPENSSL
1143 ////////////////////////////////////////////////////////////////////
1144 // Function: Multifile::get_signature_public_key
1145 // Access: Published
1146 // Description: Returns the public key used for the nth signature
1147 // found on the Multifile. This is encoded in DER form
1148 // and returned as a string of hex digits.
1149 //
1150 // This can be used, in conjunction with the subject
1151 // name (see get_signature_subject_name()), to uniquely
1152 // identify a particular certificate and its subsequent
1153 // reissues. See the comments in get_num_signatures().
1154 ////////////////////////////////////////////////////////////////////
1155 string Multifile::
1156 get_signature_public_key(int n) const {
1157  const CertChain &cert_chain = get_signature(n);
1158  nassertr(!cert_chain.empty(), string());
1159 
1160  EVP_PKEY *pkey = X509_get_pubkey(cert_chain[0]._cert);
1161  if (pkey != NULL) {
1162  int key_len = i2d_PublicKey(pkey, NULL);
1163  unsigned char *key_buf = new unsigned char[key_len];
1164  unsigned char *p = key_buf;
1165  i2d_PublicKey(pkey, &p);
1166  string result;
1167  for (int i = 0; i < key_len; ++i) {
1168  result += tohex(key_buf[i] >> 4);
1169  result += tohex(key_buf[i]);
1170  }
1171  delete[] key_buf;
1172  return result;
1173  }
1174 
1175  return string();
1176 }
1177 #endif // HAVE_OPENSSL
1178 
1179 #ifdef HAVE_OPENSSL
1180 ////////////////////////////////////////////////////////////////////
1181 // Function: Multifile::print_signature_certificate
1182 // Access: Published
1183 // Description: Writes the certificate for the nth signature, in
1184 // user-readable verbose form, to the indicated stream.
1185 // See the comments in get_num_signatures().
1186 ////////////////////////////////////////////////////////////////////
1187 void Multifile::
1188 print_signature_certificate(int n, ostream &out) const {
1189  const CertChain &cert_chain = get_signature(n);
1190  nassertv(!cert_chain.empty());
1191 
1192  BIO *mbio = BIO_new(BIO_s_mem());
1193  X509_print(mbio, cert_chain[0]._cert);
1194 
1195  char *pp;
1196  long pp_size = BIO_get_mem_data(mbio, &pp);
1197  out.write(pp, pp_size);
1198  BIO_free(mbio);
1199 }
1200 #endif // HAVE_OPENSSL
1201 
1202 #ifdef HAVE_OPENSSL
1203 ////////////////////////////////////////////////////////////////////
1204 // Function: Multifile::write_signature_certificate
1205 // Access: Published
1206 // Description: Writes the certificate for the nth signature, in
1207 // PEM form, to the indicated stream. See the comments
1208 // in get_num_signatures().
1209 ////////////////////////////////////////////////////////////////////
1210 void Multifile::
1211 write_signature_certificate(int n, ostream &out) const {
1212  const CertChain &cert_chain = get_signature(n);
1213  nassertv(!cert_chain.empty());
1214 
1215  BIO *mbio = BIO_new(BIO_s_mem());
1216 
1217  CertChain::const_iterator ci;
1218  for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
1219  X509 *c = (*ci)._cert;
1220  X509_print(mbio, c);
1221  PEM_write_bio_X509(mbio, c);
1222  }
1223 
1224  char *pp;
1225  long pp_size = BIO_get_mem_data(mbio, &pp);
1226  out.write(pp, pp_size);
1227  BIO_free(mbio);
1228 }
1229 #endif // HAVE_OPENSSL
1230 
1231 #ifdef HAVE_OPENSSL
1232 ////////////////////////////////////////////////////////////////////
1233 // Function: Multifile::validate_signature_certificate
1234 // Access: Published
1235 // Description: Checks that the certificate used for the nth
1236 // signature is a valid, authorized certificate with
1237 // some known certificate authority. Returns 0 if it
1238 // is valid, -1 if there is some error, or the
1239 // corresponding OpenSSL error code if it is invalid,
1240 // out-of-date, or self-signed.
1241 ////////////////////////////////////////////////////////////////////
1242 int Multifile::
1243 validate_signature_certificate(int n) const {
1244  int verify_result = -1;
1245 
1246  const CertChain &chain = get_signature(n);
1247  nassertr(!chain.empty(), false);
1248 
1249  OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
1250 
1251  // Copy our CertChain structure into an X509 pointer and
1252  // accompanying STACK_OF(X509) pointer.
1253  X509 *x509 = chain[0]._cert;
1254  STACK_OF(X509) *stack = NULL;
1255  if (chain.size() > 1) {
1256  stack = sk_X509_new(NULL);
1257  for (size_t n = 1; n < chain.size(); ++n) {
1258  sk_X509_push(stack, chain[n]._cert);
1259  }
1260  }
1261 
1262  // Create the X509_STORE_CTX for verifying the cert and chain.
1263  X509_STORE_CTX *ctx = X509_STORE_CTX_new();
1264  X509_STORE_CTX_init(ctx, sslw->get_x509_store(), x509, stack);
1265  X509_STORE_CTX_set_cert(ctx, x509);
1266 
1267  if (X509_verify_cert(ctx)) {
1268  verify_result = 0;
1269  } else {
1270  verify_result = X509_STORE_CTX_get_error(ctx);
1271  }
1272 
1273  if (express_cat.is_debug()) {
1274  express_cat.debug()
1275  << get_signature_subject_name(n) << ": validate " << verify_result
1276  << "\n";
1277  }
1278 
1279  sk_X509_free(stack);
1280  X509_STORE_CTX_cleanup(ctx);
1281  X509_STORE_CTX_free(ctx);
1282 
1283  return verify_result;
1284 }
1285 #endif // HAVE_OPENSSL
1286 
1287 ////////////////////////////////////////////////////////////////////
1288 // Function: Multifile::flush
1289 // Access: Published
1290 // Description: Writes all contents of the Multifile to disk. Until
1291 // flush() is called, add_subfile() and remove_subfile()
1292 // do not actually do anything to disk. At this point,
1293 // all of the recently-added subfiles are read and their
1294 // contents are added to the end of the Multifile, and
1295 // the recently-removed subfiles are marked gone from
1296 // the Multifile.
1297 //
1298 // This may result in a suboptimal index. To guarantee
1299 // that the index is written at the beginning of the
1300 // file, call repack() instead of flush().
1301 //
1302 // It is not necessary to call flush() explicitly unless
1303 // you are concerned about reading the recently-added
1304 // subfiles immediately.
1305 //
1306 // Returns true on success, false on failure.
1307 ////////////////////////////////////////////////////////////////////
1308 bool Multifile::
1310  if (!is_write_valid()) {
1311  return false;
1312  }
1313 
1314  bool new_file = (_next_index == (streampos)0);
1315  if (new_file) {
1316  // If we don't have an index yet, we don't have a header. Write
1317  // the header.
1318  if (!write_header()) {
1319  return false;
1320  }
1321 
1322  } else {
1323  if (_file_minor_ver != _current_minor_ver) {
1324  // If we *do* have an index already, but this is an old version
1325  // multifile, we have to completely rewrite it anyway.
1326  return repack();
1327  }
1328  }
1329 
1330  nassertr(_write != (ostream *)NULL, false);
1331 
1332  // First, mark out all of the removed subfiles.
1333  PendingSubfiles::iterator pi;
1334  for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1335  Subfile *subfile = (*pi);
1336  subfile->rewrite_index_flags(*_write);
1337  delete subfile;
1338  }
1339  _removed_subfiles.clear();
1340 
1341  bool wrote_ok = true;
1342 
1343  if (!_new_subfiles.empty() || new_file) {
1344  // Add a few more files to the end. We always add subfiles at the
1345  // end of the multifile, so go there first.
1346  sort(_new_subfiles.begin(), _new_subfiles.end(), IndirectLess<Subfile>());
1347  if (_last_index != (streampos)0) {
1348  _write->seekp(0, ios::end);
1349  if (_write->fail()) {
1350  express_cat.info()
1351  << "Unable to seek Multifile " << _multifile_name << ".\n";
1352  return false;
1353  }
1354  _next_index = _write->tellp();
1355  _next_index = pad_to_streampos(_next_index);
1356 
1357  // And update the forward link from the last_index to point to
1358  // this new index location.
1359  _write->seekp(_last_index);
1360  StreamWriter writer(_write, false);
1361  writer.add_uint32(streampos_to_word(_next_index));
1362  }
1363 
1364  _write->seekp(_next_index);
1365  nassertr(_next_index == _write->tellp(), false);
1366 
1367  // Ok, here we are at the end of the file. Write out the
1368  // recently-added subfiles here. First, count up the index size.
1369  for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1370  Subfile *subfile = (*pi);
1371  _last_index = _next_index;
1372  _next_index = subfile->write_index(*_write, _next_index, this);
1373  nassertr(_next_index == _write->tellp(), false);
1374  _next_index = pad_to_streampos(_next_index);
1375  nassertr(_next_index == _write->tellp(), false);
1376  }
1377 
1378  // Now we're at the end of the index. Write a 0 here to mark the
1379  // end.
1380  StreamWriter writer(_write, false);
1381  writer.add_uint32(0);
1382  _next_index += 4;
1383  nassertr(_next_index == _write->tellp(), false);
1384  _next_index = pad_to_streampos(_next_index);
1385 
1386  // All right, now write out each subfile's data.
1387  for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1388  Subfile *subfile = (*pi);
1389 
1390  if (_read != (IStreamWrapper *)NULL) {
1391  _read->acquire();
1392  _next_index = subfile->write_data(*_write, _read->get_istream(),
1393  _next_index, this);
1394  _read->release();
1395 
1396  } else {
1397  _next_index = subfile->write_data(*_write, NULL, _next_index, this);
1398  }
1399 
1400  nassertr(_next_index == _write->tellp(), false);
1401  _next_index = pad_to_streampos(_next_index);
1402  if (subfile->is_data_invalid()) {
1403  wrote_ok = false;
1404  }
1405 
1406  if (!subfile->is_cert_special()) {
1407  _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
1408  }
1409  nassertr(_next_index == _write->tellp(), false);
1410  }
1411 
1412  // Now go back and fill in the proper addresses for the data start.
1413  // We didn't do it in the first pass, because we don't really want
1414  // to keep all those file handles open, and so we didn't have to
1415  // determine each file's length ahead of time.
1416  for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
1417  Subfile *subfile = (*pi);
1418  subfile->rewrite_index_data_start(*_write, this);
1419  }
1420 
1421  _new_subfiles.clear();
1422  }
1423 
1424  // Also update the overall timestamp.
1425  if (_timestamp_dirty) {
1426  nassertr(!_write->fail(), false);
1427  static const size_t timestamp_pos = _header_prefix.size() + _header_size + 2 + 2 + 4;
1428  _write->seekp(timestamp_pos);
1429  nassertr(!_write->fail(), false);
1430 
1431  StreamWriter writer(*_write);
1432  if (_record_timestamp) {
1433  writer.add_uint32(_timestamp);
1434  } else {
1435  writer.add_uint32(0);
1436  }
1437  _timestamp_dirty = false;
1438  }
1439 
1440  _write->flush();
1441  if (!wrote_ok || _write->fail()) {
1442  express_cat.info()
1443  << "Unable to update Multifile " << _multifile_name << ".\n";
1444  close();
1445  return false;
1446  }
1447 
1448  return true;
1449 }
1450 
1451 ////////////////////////////////////////////////////////////////////
1452 // Function: Multifile::repack
1453 // Access: Published
1454 // Description: Forces a complete rewrite of the Multifile and all of
1455 // its contents, so that its index will appear at the
1456 // beginning of the file with all of the subfiles listed
1457 // in alphabetical order. This is considered optimal
1458 // for reading, and is the standard configuration; but
1459 // it is not essential to do this.
1460 //
1461 // It is only valid to call this if the Multifile was
1462 // opened using open_read_write() and an explicit
1463 // filename, rather than an iostream. Also, we must
1464 // have write permission to the directory containing the
1465 // Multifile.
1466 //
1467 // Returns true on success, false on failure.
1468 ////////////////////////////////////////////////////////////////////
1469 bool Multifile::
1471  if (_next_index == (streampos)0) {
1472  // If the Multifile hasn't yet been written, this is really just a
1473  // flush operation.
1474  _needs_repack = false;
1475  return flush();
1476  }
1477 
1478  nassertr(is_write_valid() && is_read_valid(), false);
1479  nassertr(!_multifile_name.empty(), false);
1480 
1481  // First, we open a temporary filename to copy the Multifile to.
1482  Filename dirname = _multifile_name.get_dirname();
1483  if (dirname.empty()) {
1484  dirname = ".";
1485  }
1486  Filename temp_filename = Filename::temporary(dirname, "mftemp");
1487  temp_filename.set_binary();
1488  pofstream temp;
1489  if (!temp_filename.open_write(temp)) {
1490  express_cat.info()
1491  << "Unable to open temporary file " << temp_filename << "\n";
1492  return false;
1493  }
1494 
1495  // Now we scrub our internal structures so it looks like we're a
1496  // brand new Multifile.
1497  PendingSubfiles::iterator pi;
1498  for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
1499  Subfile *subfile = (*pi);
1500  delete subfile;
1501  }
1502  _removed_subfiles.clear();
1503  _new_subfiles.clear();
1504  copy(_subfiles.begin(), _subfiles.end(), back_inserter(_new_subfiles));
1505  _next_index = 0;
1506  _last_index = 0;
1507  _last_data_byte = 0;
1508  _scale_factor = _new_scale_factor;
1509 
1510  // And we write our contents to our new temporary file.
1511  _write = &temp;
1512  if (!flush()) {
1513  temp.close();
1514  temp_filename.unlink();
1515  return false;
1516  }
1517 
1518  // Now close everything, and move the temporary file back over our
1519  // original file.
1520  Filename orig_name = _multifile_name;
1521  temp.close();
1522  close();
1523  orig_name.unlink();
1524  if (!temp_filename.rename_to(orig_name)) {
1525  express_cat.info()
1526  << "Unable to rename temporary file " << temp_filename << " to "
1527  << orig_name << ".\n";
1528  return false;
1529  }
1530 
1531  if (!open_read_write(orig_name)) {
1532  express_cat.info()
1533  << "Unable to read newly repacked " << _multifile_name
1534  << ".\n";
1535  return false;
1536  }
1537 
1538  return true;
1539 }
1540 
1541 ////////////////////////////////////////////////////////////////////
1542 // Function: Multifile::get_num_subfiles
1543 // Access: Published
1544 // Description: Returns the number of subfiles within the Multifile.
1545 // The subfiles may be accessed in alphabetical order by
1546 // iterating through [0 .. get_num_subfiles()).
1547 ////////////////////////////////////////////////////////////////////
1548 int Multifile::
1550  return _subfiles.size();
1551 }
1552 
1553 ////////////////////////////////////////////////////////////////////
1554 // Function: Multifile::find_subfile
1555 // Access: Published
1556 // Description: Returns the index of the subfile with the indicated
1557 // name, or -1 if the named subfile is not within the
1558 // Multifile.
1559 ////////////////////////////////////////////////////////////////////
1560 int Multifile::
1561 find_subfile(const string &subfile_name) const {
1562  Subfile find_subfile;
1563  find_subfile._name = standardize_subfile_name(subfile_name);
1564  Subfiles::const_iterator fi;
1565  fi = _subfiles.find(&find_subfile);
1566  if (fi == _subfiles.end()) {
1567  // Not present.
1568  return -1;
1569  }
1570  return (fi - _subfiles.begin());
1571 }
1572 
1573 ////////////////////////////////////////////////////////////////////
1574 // Function: Multifile::has_directory
1575 // Access: Published
1576 // Description: Returns true if the indicated subfile name is the
1577 // directory prefix to one or more files within the
1578 // Multifile. That is, the Multifile contains at least
1579 // one file named "subfile_name/...".
1580 ////////////////////////////////////////////////////////////////////
1581 bool Multifile::
1582 has_directory(const string &subfile_name) const {
1583  string prefix = subfile_name;
1584  if (!prefix.empty()) {
1585  prefix += '/';
1586  }
1587  Subfile find_subfile;
1588  find_subfile._name = prefix;
1589  Subfiles::const_iterator fi;
1590  fi = _subfiles.upper_bound(&find_subfile);
1591  if (fi == _subfiles.end()) {
1592  // Not present.
1593  return false;
1594  }
1595 
1596  // At least one subfile exists whose name sorts after prefix. If it
1597  // contains prefix as the initial substring, then we have a match.
1598  Subfile *subfile = (*fi);
1599  return (subfile->_name.length() > prefix.length() &&
1600  subfile->_name.substr(0, prefix.length()) == prefix);
1601 }
1602 
1603 ////////////////////////////////////////////////////////////////////
1604 // Function: Multifile::scan_directory
1605 // Access: Published
1606 // Description: Considers subfile_name to be the name of a
1607 // subdirectory within the Multifile, but not a file
1608 // itself; fills the given vector up with the sorted list
1609 // of subdirectories or files within the named
1610 // directory.
1611 //
1612 // Note that directories do not exist explicitly within
1613 // a Multifile; this just checks for the existence of
1614 // files with the given initial prefix.
1615 //
1616 // Returns true if successful, false otherwise.
1617 ////////////////////////////////////////////////////////////////////
1618 bool Multifile::
1619 scan_directory(vector_string &contents, const string &subfile_name) const {
1620  string prefix = subfile_name;
1621  if (!prefix.empty()) {
1622  prefix += '/';
1623  }
1624  Subfile find_subfile;
1625  find_subfile._name = prefix;
1626  Subfiles::const_iterator fi;
1627  fi = _subfiles.upper_bound(&find_subfile);
1628 
1629  string previous = "";
1630  while (fi != _subfiles.end()) {
1631  Subfile *subfile = (*fi);
1632  if (!(subfile->_name.length() > prefix.length() &&
1633  subfile->_name.substr(0, prefix.length()) == prefix)) {
1634  // We've reached the end of the list of subfiles beneath the
1635  // indicated directory prefix.
1636  return true;
1637  }
1638 
1639  size_t slash = subfile->_name.find('/', prefix.length());
1640  string basename = subfile->_name.substr(prefix.length(), slash - prefix.length());
1641  if (basename != previous) {
1642  contents.push_back(basename);
1643  previous = basename;
1644  }
1645  ++fi;
1646  }
1647 
1648  return true;
1649 }
1650 
1651 ////////////////////////////////////////////////////////////////////
1652 // Function: Multifile::remove_subfile
1653 // Access: Published
1654 // Description: Removes the nth subfile from the Multifile. This
1655 // will cause all subsequent index numbers to decrease
1656 // by one. The file will not actually be removed from
1657 // the disk until the next call to flush().
1658 //
1659 // Note that this does not actually remove the data from
1660 // the indicated subfile; it simply removes it from the
1661 // index. The Multifile will not be reduced in size
1662 // after this operation, until the next call to
1663 // repack().
1664 ////////////////////////////////////////////////////////////////////
1665 void Multifile::
1666 remove_subfile(int index) {
1667  nassertv(is_write_valid());
1668  nassertv(index >= 0 && index < (int)_subfiles.size());
1669  Subfile *subfile = _subfiles[index];
1670  subfile->_flags |= SF_deleted;
1671  _removed_subfiles.push_back(subfile);
1672  _subfiles.erase(_subfiles.begin() + index);
1673 
1674  _timestamp = time(NULL);
1675  _timestamp_dirty = true;
1676 
1677  _needs_repack = true;
1678 }
1679 
1680 ////////////////////////////////////////////////////////////////////
1681 // Function: Multifile::get_subfile_name
1682 // Access: Published
1683 // Description: Returns the name of the nth subfile.
1684 ////////////////////////////////////////////////////////////////////
1685 const string &Multifile::
1686 get_subfile_name(int index) const {
1687 #ifndef NDEBUG
1688  static string empty_string;
1689  nassertr(index >= 0 && index < (int)_subfiles.size(), empty_string);
1690 #endif
1691  return _subfiles[index]->_name;
1692 }
1693 
1694 ////////////////////////////////////////////////////////////////////
1695 // Function: Multifile::get_subfile_length
1696 // Access: Published
1697 // Description: Returns the uncompressed data length of the nth
1698 // subfile. This might return 0 if the subfile has
1699 // recently been added and flush() has not yet been
1700 // called.
1701 ////////////////////////////////////////////////////////////////////
1702 size_t Multifile::
1703 get_subfile_length(int index) const {
1704  nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
1705  return _subfiles[index]->_uncompressed_length;
1706 }
1707 
1708 ////////////////////////////////////////////////////////////////////
1709 // Function: Multifile::get_subfile_timestamp
1710 // Access: Published
1711 // Description: Returns the modification time of the nth
1712 // subfile. If this is called on an older .mf file,
1713 // which did not store individual timestamps in the file
1714 // (or if get_record_timestamp() is false), this will
1715 // return the modification time of the overall
1716 // multifile.
1717 ////////////////////////////////////////////////////////////////////
1718 time_t Multifile::
1719 get_subfile_timestamp(int index) const {
1720  nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
1721  if (!get_record_timestamp()) {
1722  return get_timestamp();
1723  } else {
1724  return _subfiles[index]->_timestamp;
1725  }
1726 }
1727 
1728 ////////////////////////////////////////////////////////////////////
1729 // Function: Multifile::is_subfile_compressed
1730 // Access: Published
1731 // Description: Returns true if the indicated subfile has been
1732 // compressed when stored within the archive, false
1733 // otherwise.
1734 ////////////////////////////////////////////////////////////////////
1735 bool Multifile::
1736 is_subfile_compressed(int index) const {
1737  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1738  return (_subfiles[index]->_flags & SF_compressed) != 0;
1739 }
1740 
1741 ////////////////////////////////////////////////////////////////////
1742 // Function: Multifile::is_subfile_encrypted
1743 // Access: Published
1744 // Description: Returns true if the indicated subfile has been
1745 // encrypted when stored within the archive, false
1746 // otherwise.
1747 ////////////////////////////////////////////////////////////////////
1748 bool Multifile::
1749 is_subfile_encrypted(int index) const {
1750  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1751  return (_subfiles[index]->_flags & SF_encrypted) != 0;
1752 }
1753 
1754 ////////////////////////////////////////////////////////////////////
1755 // Function: Multifile::is_subfile_text
1756 // Access: Published
1757 // Description: Returns true if the indicated subfile represents text
1758 // data, or false if it represents binary data. If the
1759 // file is text data, it may have been processed by
1760 // end-of-line conversion when it was added. (But the
1761 // actual bits in the multifile will represent the
1762 // standard Unix end-of-line convention, e.g. \n instead
1763 // of \r\n.)
1764 ////////////////////////////////////////////////////////////////////
1765 bool Multifile::
1766 is_subfile_text(int index) const {
1767  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1768  return (_subfiles[index]->_flags & SF_text) != 0;
1769 }
1770 
1771 ////////////////////////////////////////////////////////////////////
1772 // Function: Multifile::get_index_end
1773 // Access: Published
1774 // Description: Returns the first byte that is guaranteed to follow
1775 // any index byte already written to disk in the
1776 // Multifile.
1777 //
1778 // This number is largely meaningless in many cases, but
1779 // if needs_repack() is false, and the file is flushed,
1780 // this will indicate the number of bytes in the header
1781 // + index. Everything at this byte position and later
1782 // will be actual data.
1783 ////////////////////////////////////////////////////////////////////
1784 streampos Multifile::
1785 get_index_end() const {
1786  return normalize_streampos(_next_index + (streampos)4);
1787 }
1788 
1789 ////////////////////////////////////////////////////////////////////
1790 // Function: Multifile::get_subfile_internal_start
1791 // Access: Published
1792 // Description: Returns the starting byte position within the
1793 // Multifile at which the indicated subfile begins.
1794 // This may be used, with get_subfile_internal_length(),
1795 // for low-level access to the subfile, but usually it
1796 // is better to use open_read_subfile() instead (which
1797 // automatically decrypts and/or uncompresses the
1798 // subfile data).
1799 ////////////////////////////////////////////////////////////////////
1800 streampos Multifile::
1801 get_subfile_internal_start(int index) const {
1802  nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
1803  return _subfiles[index]->_data_start;
1804 }
1805 
1806 ////////////////////////////////////////////////////////////////////
1807 // Function: Multifile::get_subfile_internal_length
1808 // Access: Published
1809 // Description: Returns the number of bytes the indicated subfile
1810 // consumes within the archive. For compressed
1811 // subfiles, this will generally be smaller than
1812 // get_subfile_length(); for encrypted (but
1813 // noncompressed) subfiles, it may be slightly
1814 // different, for noncompressed and nonencrypted
1815 // subfiles, it will be equal.
1816 ////////////////////////////////////////////////////////////////////
1817 size_t Multifile::
1819  nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
1820  return _subfiles[index]->_data_length;
1821 }
1822 
1823 ////////////////////////////////////////////////////////////////////
1824 // Function: Multifile::open_read_subfile
1825 // Access: Published
1826 // Description: Returns an istream that may be used to read the
1827 // indicated subfile. You may seek() within this
1828 // istream to your heart's content; even though it will
1829 // be a reference to the already-opened pfstream of the
1830 // Multifile itself, byte 0 appears to be the beginning
1831 // of the subfile and EOF appears to be the end of the
1832 // subfile.
1833 //
1834 // The returned istream will have been allocated via
1835 // new; you should pass the pointer to
1836 // close_read_subfile() when you are finished with it to
1837 // delete it and release its resources.
1838 //
1839 // Any future calls to repack() or close() (or the
1840 // Multifile destructor) will invalidate all currently
1841 // open subfile pointers.
1842 //
1843 // The return value will be NULL if the stream cannot be
1844 // opened for some reason.
1845 ////////////////////////////////////////////////////////////////////
1846 istream *Multifile::
1847 open_read_subfile(int index) {
1848  nassertr(is_read_valid(), NULL);
1849  nassertr(index >= 0 && index < (int)_subfiles.size(), NULL);
1850  Subfile *subfile = _subfiles[index];
1851 
1852  if (subfile->_source != (istream *)NULL ||
1853  !subfile->_source_filename.empty()) {
1854  // The subfile has not yet been copied into the physical
1855  // Multifile. Force a flush operation to incorporate it.
1856  flush();
1857 
1858  // That shouldn't change the subfile index or delete the subfile
1859  // pointer.
1860  nassertr(subfile == _subfiles[index], NULL);
1861  }
1862 
1863  return open_read_subfile(subfile);
1864 }
1865 
1866 ////////////////////////////////////////////////////////////////////
1867 // Function: Multifile::close_read_subfile
1868 // Access: Published, Static
1869 // Description: Closes a file opened by a previous call to
1870 // open_read_subfile(). This really just deletes the
1871 // istream pointer, but it is recommended to use this
1872 // interface instead of deleting it explicitly, to help
1873 // work around compiler issues.
1874 ////////////////////////////////////////////////////////////////////
1875 void Multifile::
1876 close_read_subfile(istream *stream) {
1877  if (stream != (istream *)NULL) {
1878  // For some reason--compiler bug in gcc 3.2?--explicitly deleting
1879  // the stream pointer does not call the appropriate global delete
1880  // function; instead apparently calling the system delete
1881  // function. So we call the delete function by hand instead.
1882 #if !defined(WIN32_VC) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
1883  stream->~istream();
1884  (*global_operator_delete)(stream);
1885 #else
1886  delete stream;
1887 #endif
1888  }
1889 }
1890 
1891 ////////////////////////////////////////////////////////////////////
1892 // Function: Multifile::extract_subfile
1893 // Access: Published
1894 // Description: Extracts the nth subfile into a file with the given
1895 // name.
1896 ////////////////////////////////////////////////////////////////////
1897 bool Multifile::
1898 extract_subfile(int index, const Filename &filename) {
1899  nassertr(is_read_valid(), false);
1900  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1901 
1902  Filename fname = filename;
1903  if (multifile_always_binary) {
1904  fname.set_binary();
1905  }
1906 
1907  if (!fname.is_binary_or_text()) {
1908  // If we haven't specified binary or text, infer it from the type
1909  // of the subfile.
1910  if ((_subfiles[index]->_flags & SF_text) != 0) {
1911  fname.set_text();
1912  } else {
1913  fname.set_binary();
1914  }
1915  }
1916  fname.make_dir();
1917  pofstream out;
1918  if (!fname.open_write(out, true)) {
1919  express_cat.info()
1920  << "Unable to write to file " << filename << "\n";
1921  return false;
1922  }
1923 
1924  return extract_subfile_to(index, out);
1925 }
1926 
1927 ////////////////////////////////////////////////////////////////////
1928 // Function: Multifile::extract_subfile_to
1929 // Access: Public
1930 // Description: Extracts the nth subfile to the indicated ostream.
1931 ////////////////////////////////////////////////////////////////////
1932 bool Multifile::
1933 extract_subfile_to(int index, ostream &out) {
1934  nassertr(is_read_valid(), false);
1935  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1936 
1937  istream *in = open_read_subfile(index);
1938  if (in == (istream *)NULL) {
1939  return false;
1940  }
1941 
1942  static const size_t buffer_size = 4096;
1943  char buffer[buffer_size];
1944 
1945  in->read(buffer, buffer_size);
1946  size_t count = in->gcount();
1947  while (count != 0) {
1948  out.write(buffer, count);
1949  in->read(buffer, buffer_size);
1950  count = in->gcount();
1951  }
1952 
1953  bool failed = (in->fail() && !in->eof());
1954  close_read_subfile(in);
1955  nassertr(!failed, false);
1956 
1957  return (!out.fail());
1958 }
1959 
1960 ////////////////////////////////////////////////////////////////////
1961 // Function: Multifile::compare_subfile
1962 // Access: Published
1963 // Description: Performs a byte-for-byte comparison of the indicated
1964 // file on disk with the nth subfile. Returns true if
1965 // the files are equivalent, or false if they are
1966 // different (or the file is missing).
1967 //
1968 // If Filename::set_binary() or set_text() has already
1969 // been called, it specifies the nature of the source
1970 // file. If this is different from the text flag of the
1971 // subfile, the comparison will always return false.
1972 // If this has not been specified, it will be set from
1973 // the text flag of the subfile.
1974 ////////////////////////////////////////////////////////////////////
1975 bool Multifile::
1976 compare_subfile(int index, const Filename &filename) {
1977  nassertr(is_read_valid(), false);
1978  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
1979 
1980  if (!filename.exists()) {
1981  express_cat.info()
1982  << "File is missing: " << filename << "\n";
1983  return false;
1984  }
1985 
1986  Filename fname = filename;
1987  if (fname.is_binary()) {
1988  // If we've specified a binary file, it had better be a binary
1989  // subfile.
1990  if ((_subfiles[index]->_flags & SF_text) != 0) {
1991  if (express_cat.is_debug()) {
1992  express_cat.debug()
1993  << "File is not binary: " << filename << "\n";
1994  }
1995  return false;
1996  }
1997 
1998  } else if (fname.is_text()) {
1999  // If we've specified a text file, it had better be a text
2000  // subfile.
2001  if ((_subfiles[index]->_flags & SF_text) == 0) {
2002  if (express_cat.is_debug()) {
2003  express_cat.debug()
2004  << "File is not text: " << filename << "\n";
2005  }
2006  return false;
2007  }
2008 
2009  } else {
2010  // If we haven't specified binary or text, infer it from the type
2011  // of the subfile.
2012  if ((_subfiles[index]->_flags & SF_text) != 0) {
2013  fname.set_text();
2014  } else {
2015  fname.set_binary();
2016  }
2017  }
2018 
2019  istream *in1 = open_read_subfile(index);
2020  if (in1 == (istream *)NULL) {
2021  return false;
2022  }
2023 
2024  pifstream in2;
2025 
2026  if (!fname.open_read(in2)) {
2027  express_cat.info()
2028  << "Cannot read " << filename << "\n";
2029  return false;
2030  }
2031 
2032  if (fname.is_binary()) {
2033  // Check the file size.
2034  in2.seekg(0, ios::end);
2035  streampos file_size = in2.tellg();
2036 
2037  if (file_size != (streampos)get_subfile_length(index)) {
2038  // The files have different sizes.
2039  close_read_subfile(in1);
2040  return false;
2041  }
2042  }
2043 
2044  // Check the file data, byte-for-byte.
2045  in2.seekg(0);
2046  int byte1 = in1->get();
2047  int byte2 = in2.get();
2048  while (!in1->fail() && !in1->eof() &&
2049  !in2.fail() && !in2.eof()) {
2050  if (byte1 != byte2) {
2051  close_read_subfile(in1);
2052  return false;
2053  }
2054  byte1 = in1->get();
2055  byte2 = in2.get();
2056  }
2057 
2058  bool failed = (in1->fail() && !in1->eof()) || (in2.fail() && !in2.eof());
2059  close_read_subfile(in1);
2060 
2061  nassertr(!failed, false);
2062 
2063  return true;
2064 }
2065 
2066 ////////////////////////////////////////////////////////////////////
2067 // Function: Multifile::output
2068 // Access: Published
2069 // Description:
2070 ////////////////////////////////////////////////////////////////////
2071 void Multifile::
2072 output(ostream &out) const {
2073  out << "Multifile " << _multifile_name << ", " << get_num_subfiles()
2074  << " subfiles.\n";
2075 }
2076 
2077 ////////////////////////////////////////////////////////////////////
2078 // Function: Multifile::ls
2079 // Access: Published
2080 // Description: Shows a list of all subfiles within the Multifile.
2081 ////////////////////////////////////////////////////////////////////
2082 void Multifile::
2083 ls(ostream &out) const {
2084  int num_subfiles = get_num_subfiles();
2085  for (int i = 0; i < num_subfiles; i++) {
2086  string subfile_name = get_subfile_name(i);
2087  out << subfile_name << "\n";
2088  }
2089 }
2090 
2091 ////////////////////////////////////////////////////////////////////
2092 // Function: Multifile::set_header_prefix
2093 // Access: Published
2094 // Description: Sets the string which is written to the Multifile
2095 // before the Multifile header. This string must begin
2096 // with a hash mark and end with a newline character;
2097 // and if it includes embedded newline characters, each
2098 // one must be followed by a hash mark. If these
2099 // conditions are not initially true, the string will be
2100 // modified as necessary to make it so.
2101 //
2102 // This is primarily useful as a simple hack to allow
2103 // p3d applications to be run directly from the command
2104 // line on Unix-like systems.
2105 //
2106 // The return value is true if successful, or false on
2107 // failure (for instance, because the header prefix
2108 // violates the above rules).
2109 ////////////////////////////////////////////////////////////////////
2110 void Multifile::
2111 set_header_prefix(const string &header_prefix) {
2112  string new_header_prefix = header_prefix;
2113 
2114  if (!new_header_prefix.empty()) {
2115  // It must begin with a hash mark.
2116  if (new_header_prefix[0] != '#') {
2117  new_header_prefix = string("#") + new_header_prefix;
2118  }
2119 
2120  // It must end with a newline.
2121  if (new_header_prefix[new_header_prefix.size() - 1] != '\n') {
2122  new_header_prefix += string("\n");
2123  }
2124 
2125  // Embedded newlines must be followed by a hash mark.
2126  size_t newline = new_header_prefix.find('\n');
2127  while (newline < new_header_prefix.size() - 1) {
2128  if (new_header_prefix[newline + 1] != '#') {
2129  new_header_prefix = new_header_prefix.substr(0, newline + 1) + string("#") + new_header_prefix.substr(newline + 1);
2130  }
2131  newline = new_header_prefix.find('#', newline);
2132  }
2133  }
2134 
2135  if (_header_prefix != new_header_prefix) {
2136  _header_prefix = new_header_prefix;
2137  _needs_repack = true;
2138  }
2139 }
2140 
2141 
2142 ////////////////////////////////////////////////////////////////////
2143 // Function: Multifile::read_subfile
2144 // Access: Public
2145 // Description: Fills a string with the entire contents of
2146 // the indicated subfile.
2147 ////////////////////////////////////////////////////////////////////
2148 bool Multifile::
2149 read_subfile(int index, string &result) {
2150  result = string();
2151 
2152  // We use a temporary pvector, because dynamic accumulation of a
2153  // pvector seems to be many times faster than that of a string, at
2154  // least on the Windows implementation of STL.
2156  if (!read_subfile(index, pv)) {
2157  return false;
2158  }
2159 
2160  if (!pv.empty()) {
2161  result.append((const char *)&pv[0], pv.size());
2162  }
2163 
2164  return true;
2165 }
2166 
2167 ////////////////////////////////////////////////////////////////////
2168 // Function: Multifile::read_subfile
2169 // Access: Public
2170 // Description: Fills a pvector with the entire contents of
2171 // the indicated subfile.
2172 ////////////////////////////////////////////////////////////////////
2173 bool Multifile::
2175  nassertr(is_read_valid(), false);
2176  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
2177  result.clear();
2178 
2179  // Now look up the particular Subfile we are reading.
2180  nassertr(is_read_valid(), false);
2181  nassertr(index >= 0 && index < (int)_subfiles.size(), false);
2182  Subfile *subfile = _subfiles[index];
2183 
2184  if (subfile->_source != (istream *)NULL ||
2185  !subfile->_source_filename.empty()) {
2186  // The subfile has not yet been copied into the physical
2187  // Multifile. Force a flush operation to incorporate it.
2188  flush();
2189 
2190  // That shouldn't change the subfile index or delete the subfile
2191  // pointer.
2192  nassertr(subfile == _subfiles[index], false);
2193  }
2194 
2195  result.reserve(subfile->_uncompressed_length);
2196 
2197  bool success = true;
2198  if (subfile->_flags & (SF_encrypted | SF_compressed)) {
2199  // If the subfile is encrypted or compressed, we can't read it
2200  // directly. Fall back to the generic implementation.
2201  istream *in = open_read_subfile(index);
2202  if (in == (istream *)NULL) {
2203  return false;
2204  }
2205 
2206  success = VirtualFile::simple_read_file(in, result);
2207  close_read_subfile(in);
2208 
2209  } else {
2210  // But if the subfile is just a plain file, we can just read the
2211  // data directly from the Multifile, without paying the cost of an
2212  // ISubStream.
2213  static const size_t buffer_size = 4096;
2214  char buffer[buffer_size];
2215 
2216  streamsize pos = _offset + subfile->_data_start;
2217  size_t max_bytes = subfile->_data_length;
2218  streamsize count = 0;
2219  bool eof = true;
2220 
2221  streamsize num_bytes = (streamsize)min(buffer_size, max_bytes);
2222  _read->seek_read(pos, buffer, num_bytes, count, eof);
2223  while (count != 0) {
2224  thread_consider_yield();
2225  nassertr(count <= (streamsize)max_bytes, false);
2226  result.insert(result.end(), buffer, buffer + (size_t)count);
2227  max_bytes -= (size_t)count;
2228  pos += count;
2229 
2230  num_bytes = (streamsize)min(buffer_size, max_bytes);
2231  _read->seek_read(pos, buffer, num_bytes, count, eof);
2232  }
2233 
2234  success = !eof;
2235  }
2236 
2237  if (!success) {
2238  ostringstream message;
2239  message << "I/O error reading from " << get_multifile_name() << " at "
2240  << get_subfile_name(index);
2241  nassert_raise(message.str());
2242  return false;
2243  }
2244 
2245  return true;
2246 }
2247 
2248 ////////////////////////////////////////////////////////////////////
2249 // Function: Multifile::pad_to_streampos
2250 // Access: Private
2251 // Description: Assumes the _write pointer is at the indicated fpos,
2252 // rounds the fpos up to the next legitimate address
2253 // (using normalize_streampos()), and writes enough
2254 // zeroes to the stream to fill the gap. Returns the
2255 // new fpos.
2256 ////////////////////////////////////////////////////////////////////
2257 streampos Multifile::
2258 pad_to_streampos(streampos fpos) {
2259  nassertr(_write != (ostream *)NULL, fpos);
2260  nassertr(_write->tellp() == fpos, fpos);
2261  streampos new_fpos = normalize_streampos(fpos);
2262  while (fpos < new_fpos) {
2263  _write->put(0);
2264  fpos += 1; // VC++ doesn't define streampos++ (!)
2265  }
2266  nassertr(_write->tellp() == fpos, fpos);
2267  return fpos;
2268 }
2269 
2270 ////////////////////////////////////////////////////////////////////
2271 // Function: Multifile::add_new_subfile
2272 // Access: Private
2273 // Description: Adds a newly-allocated Subfile pointer to the
2274 // Multifile.
2275 ////////////////////////////////////////////////////////////////////
2276 void Multifile::
2277 add_new_subfile(Subfile *subfile, int compression_level) {
2278  if (compression_level != 0) {
2279 #ifndef HAVE_ZLIB
2280  express_cat.warning()
2281  << "zlib not compiled in; cannot generated compressed multifiles.\n";
2282  compression_level = 0;
2283 #else // HAVE_ZLIB
2284  subfile->_flags |= SF_compressed;
2285  subfile->_compression_level = compression_level;
2286 #endif // HAVE_ZLIB
2287  }
2288 
2289 #ifdef HAVE_OPENSSL
2290  if (_encryption_flag) {
2291  subfile->_flags |= SF_encrypted;
2292  }
2293 #endif // HAVE_OPENSSL
2294 
2295  if (_next_index != (streampos)0) {
2296  // If we're adding a Subfile to an already-existing Multifile, we
2297  // will eventually need to repack the file.
2298  _needs_repack = true;
2299  }
2300 
2301  pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
2302  if (!insert_result.second) {
2303  // Hmm, unable to insert. There must already be a subfile by that
2304  // name. Remove the old one.
2305  Subfile *old_subfile = (*insert_result.first);
2306  old_subfile->_flags |= SF_deleted;
2307 
2308  // Maybe it was just added to the _new_subfiles list. In this case, remove it from that list.
2309  PendingSubfiles::iterator ni = find(_new_subfiles.begin(), _new_subfiles.end(), old_subfile);
2310  if (ni != _new_subfiles.end()) {
2311  _new_subfiles.erase(ni);
2312 
2313  } else {
2314  // Otherwise, add it to the _removed_subfiles list, so we can remove the old one.
2315  _removed_subfiles.push_back(old_subfile);
2316  }
2317 
2318  (*insert_result.first) = subfile;
2319  }
2320 
2321  _new_subfiles.push_back(subfile);
2322 }
2323 
2324 ////////////////////////////////////////////////////////////////////
2325 // Function: Multifile::open_read_subfile
2326 // Access: Private
2327 // Description: This variant of open_read_subfile() is used
2328 // internally only, and accepts a pointer to the
2329 // internal Subfile object, which is assumed to be valid
2330 // and written to the multifile.
2331 ////////////////////////////////////////////////////////////////////
2332 istream *Multifile::
2333 open_read_subfile(Subfile *subfile) {
2334  nassertr(subfile->_source == (istream *)NULL &&
2335  subfile->_source_filename.empty(), NULL);
2336 
2337  // Return an ISubStream object that references into the open
2338  // Multifile istream.
2339  nassertr(subfile->_data_start != (streampos)0, NULL);
2340  istream *stream =
2341  new ISubStream(_read, _offset + subfile->_data_start,
2342  _offset + subfile->_data_start + (streampos)subfile->_data_length);
2343 
2344  if ((subfile->_flags & SF_encrypted) != 0) {
2345 #ifndef HAVE_OPENSSL
2346  express_cat.error()
2347  << "OpenSSL not compiled in; cannot read encrypted multifiles.\n";
2348  delete stream;
2349  return NULL;
2350 #else // HAVE_OPENSSL
2351  // The subfile is encrypted. So actually, return an
2352  // IDecryptStream that wraps around the ISubStream.
2353  IDecryptStream *wrapper =
2354  new IDecryptStream(stream, true, _encryption_password);
2355  stream = wrapper;
2356 
2357  // Validate the password by confirming that the encryption header
2358  // matches.
2359  char this_header[_encrypt_header_size];
2360  stream->read(this_header, _encrypt_header_size);
2361  if (stream->fail() || stream->gcount() != (unsigned)_encrypt_header_size ||
2362  memcmp(this_header, _encrypt_header, _encrypt_header_size) != 0) {
2363  express_cat.error()
2364  << "Unable to decrypt subfile " << subfile->_name << ".\n";
2365  delete stream;
2366  return NULL;
2367  }
2368 #endif // HAVE_OPENSSL
2369  }
2370 
2371  if ((subfile->_flags & SF_compressed) != 0) {
2372 #ifndef HAVE_ZLIB
2373  express_cat.error()
2374  << "zlib not compiled in; cannot read compressed multifiles.\n";
2375  delete stream;
2376  return NULL;
2377 #else // HAVE_ZLIB
2378  // Oops, the subfile is compressed. So actually, return an
2379  // IDecompressStream that wraps around the ISubStream.
2380  IDecompressStream *wrapper = new IDecompressStream(stream, true);
2381  stream = wrapper;
2382 #endif // HAVE_ZLIB
2383  }
2384 
2385  if (stream->fail()) {
2386  // Hmm, some inexplicable problem.
2387  delete stream;
2388  return NULL;
2389  }
2390 
2391  return stream;
2392 }
2393 
2394 ////////////////////////////////////////////////////////////////////
2395 // Function: Multifile::standardize_subfile_name
2396 // Access: Private
2397 // Description: Returns the standard form of the subfile name.
2398 ////////////////////////////////////////////////////////////////////
2399 string Multifile::
2400 standardize_subfile_name(const string &subfile_name) const {
2401  Filename name = subfile_name;
2402  name.standardize();
2403  if (name.empty() || name == "/") {
2404  // Invalid empty name.
2405  return string();
2406  }
2407 
2408  if (name[0] == '/') {
2409  return name.get_fullpath().substr(1);
2410  } else if (name.length() > 2 && name[0] == '.' && name[1] == '/') {
2411  return name.get_fullpath().substr(2);
2412  } else {
2413  return name.get_fullpath();
2414  }
2415 }
2416 
2417 ////////////////////////////////////////////////////////////////////
2418 // Function: Multifile::clear_subfiles
2419 // Access: Private
2420 // Description: Removes the set of subfiles from the tables and frees
2421 // their associated memory.
2422 ////////////////////////////////////////////////////////////////////
2423 void Multifile::
2424 clear_subfiles() {
2425  PendingSubfiles::iterator pi;
2426  for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
2427  Subfile *subfile = (*pi);
2428  subfile->rewrite_index_flags(*_write);
2429  delete subfile;
2430  }
2431  _removed_subfiles.clear();
2432 
2433  // We don't have to delete the ones in _new_subfiles, because these
2434  // also appear in _subfiles.
2435  _new_subfiles.clear();
2436 
2437 #ifdef HAVE_OPENSSL
2438  for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2439  Subfile *subfile = (*pi);
2440  delete subfile;
2441  }
2442  _cert_special.clear();
2443 
2444  _signatures.clear();
2445 #endif // HAVE_OPENSSL
2446 
2447  Subfiles::iterator fi;
2448  for (fi = _subfiles.begin(); fi != _subfiles.end(); ++fi) {
2449  Subfile *subfile = (*fi);
2450  delete subfile;
2451  }
2452  _subfiles.clear();
2453 }
2454 
2455 ////////////////////////////////////////////////////////////////////
2456 // Function: Multifile::read_index
2457 // Access: Private
2458 // Description: Reads the Multifile header and index. Returns true
2459 // if successful, false if the Multifile is not valid.
2460 ////////////////////////////////////////////////////////////////////
2461 bool Multifile::
2462 read_index() {
2463  nassertr(_read != (IStreamWrapper *)NULL, false);
2464 
2465  // We acquire the IStreamWrapper lock for the duration of this
2466  // method.
2467  _read->acquire();
2468  istream *read = _read->get_istream();
2469 
2470  char this_header[_header_size];
2471  read->seekg(_offset);
2472 
2473  // Here's a special case: if the multifile begins with a hash
2474  // character, then we continue reading and discarding lines of ASCII
2475  // text, until we come across a nonempty line that does not begin
2476  // with a hash character. This allows a P3D application (which is a
2477  // multifile) to be run directly on the command line on Unix-based
2478  // systems.
2479  _header_prefix = string();
2480  int ch = read->get();
2481 
2482  if (ch == '#') {
2483  while (ch != EOF && ch == '#') {
2484  // Skip to the end of the line.
2485  while (ch != EOF && ch != '\n') {
2486  _header_prefix += ch;
2487  ch = read->get();
2488  }
2489  // Skip to the first non-whitespace character of the line.
2490  while (ch != EOF && (isspace(ch) || ch == '\r')) {
2491  _header_prefix += ch;
2492  ch = read->get();
2493  }
2494  }
2495  }
2496 
2497  // Now read the actual Multifile header.
2498  this_header[0] = ch;
2499  read->read(this_header + 1, _header_size - 1);
2500  if (read->fail() || read->gcount() != (unsigned)(_header_size - 1)) {
2501  express_cat.info()
2502  << "Unable to read Multifile header " << _multifile_name << ".\n";
2503  _read->release();
2504  close();
2505  return false;
2506  }
2507 
2508  if (memcmp(this_header, _header, _header_size) != 0) {
2509  express_cat.info()
2510  << _multifile_name << " is not a Multifile.\n";
2511  _read->release();
2512  close();
2513  return false;
2514  }
2515 
2516  // Now get the version numbers out.
2517  StreamReader reader(read, false);
2518  _file_major_ver = reader.get_int16();
2519  _file_minor_ver = reader.get_int16();
2520  _scale_factor = reader.get_uint32();
2521  _new_scale_factor = _scale_factor;
2522 
2523  if (read->eof() || read->fail()) {
2524  express_cat.info()
2525  << _multifile_name << " header is truncated.\n";
2526  _read->release();
2527  close();
2528  return false;
2529  }
2530 
2531  if (_file_major_ver != _current_major_ver ||
2532  (_file_major_ver == _current_major_ver &&
2533  _file_minor_ver > _current_minor_ver)) {
2534  express_cat.info()
2535  << _multifile_name << " has version " << _file_major_ver << "."
2536  << _file_minor_ver << ", expecting version "
2537  << _current_major_ver << "." << _current_minor_ver << ".\n";
2538  _read->release();
2539  close();
2540  return false;
2541  }
2542 
2543  _record_timestamp = true;
2544  if (_file_minor_ver >= 1) {
2545  time_t read_timestamp = reader.get_uint32();
2546  if (read_timestamp == 0) {
2547  // If we read a 0 timestamp from the file, that implies that we
2548  // don't want to record a timestamp in this particular file.
2549  _record_timestamp = false;
2550  } else {
2551  _timestamp = read_timestamp;
2552  }
2553  _timestamp_dirty = false;
2554  }
2555 
2556  // Now read the index out.
2557  _next_index = read->tellg() - _offset;
2558  _next_index = normalize_streampos(_next_index);
2559  read->seekg(_next_index + _offset);
2560  _last_index = 0;
2561  _last_data_byte = 0;
2562  streampos index_forward;
2563  streamoff bytes_skipped = 0;
2564  bool read_cert_special = false;
2565 
2566  Subfile *subfile = new Subfile;
2567  index_forward = subfile->read_index(*read, _next_index, this);
2568  while (index_forward != (streampos)0) {
2569  _last_index = _next_index;
2570  if (subfile->is_deleted()) {
2571  // Ignore deleted Subfiles in the index.
2572  _needs_repack = true;
2573  delete subfile;
2574  } else if (subfile->is_cert_special()) {
2575  // Certificate chains and signature files get stored in a
2576  // special list.
2577  _cert_special.push_back(subfile);
2578  read_cert_special = true;
2579  } else {
2580  _subfiles.push_back(subfile);
2581  }
2582  if (!subfile->is_cert_special()) {
2583  if (bytes_skipped != 0) {
2584  // If the index entries don't follow exactly sequentially
2585  // (except for the cert special files), the file ought to be
2586  // repacked.
2587  _needs_repack = true;
2588  }
2589  if (read_cert_special) {
2590  // If we read a normal subfile following a cert_special entry,
2591  // the file ought to be repacked (certificates have to go at
2592  // the end).
2593  _needs_repack = true;
2594  }
2595  _last_data_byte = max(_last_data_byte, subfile->get_last_byte_pos());
2596  }
2597  streampos curr_pos = normalize_streampos(read->tellg() - _offset);
2598  bytes_skipped = index_forward - curr_pos;
2599  read->seekg(index_forward + _offset);
2600  _next_index = index_forward;
2601  subfile = new Subfile;
2602  index_forward = subfile->read_index(*read, _next_index, this);
2603  }
2604  if (subfile->is_index_invalid()) {
2605  express_cat.info()
2606  << "Error reading index for " << _multifile_name << ".\n";
2607  _read->release();
2608  close();
2609  delete subfile;
2610  return false;
2611  }
2612 
2613  // Check if the list is already sorted. If it is not, we need a
2614  // repack.
2615  for (size_t si = 1; si < _subfiles.size() && !_needs_repack; ++si) {
2616  if (*_subfiles[si] < *_subfiles[si - 1]) {
2617  _needs_repack = true;
2618  }
2619  }
2620 
2621  if (_needs_repack) {
2622  // At least sort them now.
2623  size_t before_size = _subfiles.size();
2624  _subfiles.sort();
2625  size_t after_size = _subfiles.size();
2626 
2627  // If these don't match, the same filename appeared twice in the
2628  // index, which shouldn't be possible.
2629  nassertr(before_size == after_size, true);
2630  }
2631 
2632  delete subfile;
2633  _read->release();
2634  return true;
2635 }
2636 
2637 ////////////////////////////////////////////////////////////////////
2638 // Function: Multifile::write_header
2639 // Access: Private
2640 // Description: Writes just the header part of the Multifile, not the
2641 // index.
2642 ////////////////////////////////////////////////////////////////////
2643 bool Multifile::
2644 write_header() {
2645  _file_major_ver = _current_major_ver;
2646  _file_minor_ver = _current_minor_ver;
2647 
2648  nassertr(_write != (ostream *)NULL, false);
2649  nassertr(_write->tellp() == (streampos)0, false);
2650  _write->write(_header_prefix.data(), _header_prefix.size());
2651  _write->write(_header, _header_size);
2652  StreamWriter writer(_write, false);
2653  writer.add_int16(_current_major_ver);
2654  writer.add_int16(_current_minor_ver);
2655  writer.add_uint32(_scale_factor);
2656 
2657  if (_record_timestamp) {
2658  writer.add_uint32(_timestamp);
2659  } else {
2660  writer.add_uint32(0);
2661  _timestamp_dirty = false;
2662  }
2663 
2664  _next_index = _write->tellp();
2665  _next_index = pad_to_streampos(_next_index);
2666  _last_index = 0;
2667 
2668  if (_write->fail()) {
2669  express_cat.info()
2670  << "Unable to write header for " << _multifile_name << ".\n";
2671  close();
2672  return false;
2673  }
2674 
2675  return true;
2676 }
2677 
2678 ////////////////////////////////////////////////////////////////////
2679 // Function: Multifile::check_signatures
2680 // Access: Private
2681 // Description: Walks through the list of _cert_special entries in
2682 // the Multifile, moving any valid signatures found to
2683 // _signatures. After this call, _cert_special will be
2684 // empty.
2685 //
2686 // This does not check the validity of the certificates
2687 // themselves. It only checks that they correctly sign
2688 // the Multifile contents.
2689 ////////////////////////////////////////////////////////////////////
2690 void Multifile::
2691 check_signatures() {
2692 #ifdef HAVE_OPENSSL
2693  PendingSubfiles::iterator pi;
2694 
2695  for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
2696  Subfile *subfile = (*pi);
2697  nassertv((subfile->_flags & SF_signature) != 0);
2698 
2699  // Extract the signature data and certificate separately.
2700  istream *stream = open_read_subfile(subfile);
2701  nassertv(stream != NULL);
2702  StreamReader reader(*stream);
2703  size_t sig_size = reader.get_uint32();
2704  string sig_string = reader.extract_bytes(sig_size);
2705 
2706  size_t num_certs = reader.get_uint32();
2707 
2708  // Read the remaining buffer of certificate data.
2709  pvector<unsigned char> buffer;
2710  bool success = VirtualFile::simple_read_file(stream, buffer);
2711  nassertv(success);
2712  close_read_subfile(stream);
2713 
2714  // Now convert each of the certificates to an X509 object, and
2715  // store it in our CertChain.
2716  CertChain chain;
2717  EVP_PKEY *pkey = NULL;
2718  if (!buffer.empty()) {
2719 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
2720  // Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
2721  const unsigned char *bp, *bp_end;
2722 #else
2723  // Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
2724  unsigned char *bp, *bp_end;
2725 #endif
2726  bp = (unsigned char *)&buffer[0];
2727  bp_end = bp + buffer.size();
2728  X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
2729  while (num_certs > 0 && x509 != NULL) {
2730  chain.push_back(CertRecord(x509));
2731  --num_certs;
2732  x509 = d2i_X509(NULL, &bp, bp_end - bp);
2733  }
2734  if (num_certs != 0 || x509 != NULL) {
2735  express_cat.warning()
2736  << "Extra data in signature record.\n";
2737  }
2738  }
2739 
2740  if (!chain.empty()) {
2741  pkey = X509_get_pubkey(chain[0]._cert);
2742  }
2743 
2744  if (pkey != NULL) {
2745  EVP_MD_CTX *md_ctx;
2746 #ifdef SSL_097
2747  md_ctx = EVP_MD_CTX_create();
2748 #else
2749  md_ctx = new EVP_MD_CTX;
2750 #endif
2751  EVP_VerifyInit(md_ctx, EVP_sha1());
2752 
2753  nassertv(_read != NULL);
2754  _read->acquire();
2755  istream *read = _read->get_istream();
2756 
2757  // Read and hash the multifile contents, but only up till
2758  // _last_data_byte.
2759  read->seekg(_offset);
2760  streampos bytes_remaining = _last_data_byte;
2761  static const size_t buffer_size = 4096;
2762  char buffer[buffer_size];
2763  read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2764  size_t count = read->gcount();
2765  while (count != 0) {
2766  nassertv(count <= buffer_size);
2767  EVP_VerifyUpdate(md_ctx, buffer, count);
2768  bytes_remaining -= count;
2769  read->read(buffer, min((streampos)buffer_size, bytes_remaining));
2770  count = read->gcount();
2771  }
2772  nassertv(bytes_remaining == (streampos)0);
2773  _read->release();
2774 
2775  // Now check that the signature matches the hash.
2776  int verify_result =
2777  EVP_VerifyFinal(md_ctx,
2778  (unsigned char *)sig_string.data(),
2779  sig_string.size(), pkey);
2780  if (verify_result == 1) {
2781  // The signature matches; save the certificate and its chain.
2782  _signatures.push_back(chain);
2783  } else {
2784  // Bad match.
2785  _needs_repack = true;
2786  }
2787  }
2788  }
2789 #endif // HAVE_OPENSSL
2790 
2791  _cert_special.clear();
2792 }
2793 
2794 ////////////////////////////////////////////////////////////////////
2795 // Function: Multifile::Subfile::read_index
2796 // Access: Public
2797 // Description: Reads the index record for the Subfile from the
2798 // indicated istream. Assumes the istream has already
2799 // been positioned to the indicated stream position,
2800 // fpos, the start of the index record. Returns the
2801 // position within the file of the next index record.
2802 ////////////////////////////////////////////////////////////////////
2803 streampos Multifile::Subfile::
2804 read_index(istream &read, streampos fpos, Multifile *multifile) {
2805  nassertr(read.tellg() - multifile->_offset == fpos, fpos);
2806 
2807  // First, get the next stream position. We do this separately,
2808  // because if it is zero, we don't get anything else.
2809  StreamReader reader(read);
2810 
2811  streampos next_index = multifile->word_to_streampos(reader.get_uint32());
2812  if (read.eof() || read.fail()) {
2813  _flags |= SF_index_invalid;
2814  return 0;
2815  }
2816 
2817  if (next_index == (streampos)0) {
2818  return 0;
2819  }
2820 
2821  // Now get the rest of the index.
2822 
2823  _index_start = fpos;
2824  _index_length = 0;
2825 
2826  _data_start = multifile->word_to_streampos(reader.get_uint32());
2827  _data_length = reader.get_uint32();
2828  _flags = reader.get_uint16();
2829  if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2830  _uncompressed_length = reader.get_uint32();
2831  } else {
2832  _uncompressed_length = _data_length;
2833  }
2834  if (multifile->_file_minor_ver < 1) {
2835  _timestamp = multifile->get_timestamp();
2836  } else {
2837  _timestamp = reader.get_uint32();
2838  if (_timestamp == 0) {
2839  _timestamp = multifile->get_timestamp();
2840  }
2841  }
2842 
2843  size_t name_length = reader.get_uint16();
2844  if (read.eof() || read.fail()) {
2845  _flags |= SF_index_invalid;
2846  return 0;
2847  }
2848 
2849  // And finally, get the rest of the name.
2850  char *name_buffer = (char *)PANDA_MALLOC_ARRAY(name_length);
2851  nassertr(name_buffer != (char *)NULL, next_index);
2852  for (size_t ni = 0; ni < name_length; ni++) {
2853  name_buffer[ni] = read.get() ^ 0xff;
2854  }
2855  _name = string(name_buffer, name_length);
2856  PANDA_FREE_ARRAY(name_buffer);
2857 
2858  if (read.eof() || read.fail()) {
2859  _flags |= SF_index_invalid;
2860  return 0;
2861  }
2862 
2863  _index_length = read.tellg() - fpos - multifile->_offset;
2864  return next_index;
2865 }
2866 
2867 ////////////////////////////////////////////////////////////////////
2868 // Function: Multifile::Subfile::write_index
2869 // Access: Public
2870 // Description: Writes the index record for the Subfile to the
2871 // indicated ostream. Assumes the istream has already
2872 // been positioned to the indicated stream position,
2873 // fpos, the start of the index record, and that this is
2874 // the effective end of the file. Returns the position
2875 // within the file of the next index record.
2876 //
2877 // The _index_start member is updated by this operation.
2878 ////////////////////////////////////////////////////////////////////
2879 streampos Multifile::Subfile::
2880 write_index(ostream &write, streampos fpos, Multifile *multifile) {
2881  nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2882 
2883  _index_start = fpos;
2884  _index_length = 0;
2885 
2886  // This will be the contents of this particular index record. We
2887  // build it up first since it will be variable length.
2888  Datagram dg;
2889  dg.add_uint32(multifile->streampos_to_word(_data_start));
2890  dg.add_uint32(_data_length);
2891  dg.add_uint16(_flags);
2892  if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
2893  dg.add_uint32(_uncompressed_length);
2894  }
2895  dg.add_uint32(_timestamp);
2896  dg.add_uint16(_name.length());
2897 
2898  // For no real good reason, we'll invert all the bits in the name.
2899  // The only reason we do this is to make it inconvenient for a
2900  // casual browser of the Multifile to discover the names of the
2901  // files stored within it. Naturally, this isn't real obfuscation
2902  // or security.
2903  string::iterator ni;
2904  for (ni = _name.begin(); ni != _name.end(); ++ni) {
2905  dg.add_int8((*ni) ^ 0xff);
2906  }
2907 
2908  size_t this_index_size = 4 + dg.get_length();
2909 
2910  // Plus, we will write out the next index address first.
2911  streampos next_index = fpos + (streampos)this_index_size;
2912 
2913  Datagram idg;
2914  idg.add_uint32(multifile->streampos_to_word(next_index));
2915 
2916  write.write((const char *)idg.get_data(), idg.get_length());
2917  write.write((const char *)dg.get_data(), dg.get_length());
2918 
2919  _index_length = write.tellp() - fpos - multifile->_offset;
2920  return next_index;
2921 }
2922 
2923 ////////////////////////////////////////////////////////////////////
2924 // Function: Multifile::Subfile::write_data
2925 // Access: Public
2926 // Description: Writes the data record for the Subfile to the
2927 // indicated ostream: the actual contents of the
2928 // Subfile. Assumes the istream has already been
2929 // positioned to the indicated stream position, fpos,
2930 // the start of the data record, and that this is the
2931 // effective end of the file. Returns the position
2932 // within the file of the next data record.
2933 //
2934 // The _data_start, _data_length, and
2935 // _uncompressed_length members are updated by this
2936 // operation.
2937 //
2938 // If the "read" pointer is non-NULL, it is the readable
2939 // istream of a Multifile in which the Subfile might
2940 // already be packed. This is used for reading the
2941 // contents of the Subfile during a repack() operation.
2942 ////////////////////////////////////////////////////////////////////
2943 streampos Multifile::Subfile::
2944 write_data(ostream &write, istream *read, streampos fpos,
2945  Multifile *multifile) {
2946  nassertr(write.tellp() - multifile->_offset == fpos, fpos);
2947 
2948  istream *source = _source;
2949  pifstream source_file;
2950  if (source == (istream *)NULL && !_source_filename.empty()) {
2951  // If we have a filename, open it up and read that.
2952  if (!_source_filename.open_read(source_file)) {
2953  // Unable to open the source file.
2954  express_cat.info()
2955  << "Unable to read " << _source_filename << ".\n";
2956  _flags |= SF_data_invalid;
2957  _data_length = 0;
2958  _uncompressed_length = 0;
2959  } else {
2960  source = &source_file;
2961  }
2962  }
2963 
2964  if (source == (istream *)NULL) {
2965  // We don't have any source data. Perhaps we're reading from an
2966  // already-packed Subfile (e.g. during repack()).
2967  if (read == (istream *)NULL) {
2968  // No, we're just screwed.
2969  express_cat.info()
2970  << "No source for subfile " << _name << ".\n";
2971  _flags |= SF_data_invalid;
2972  } else {
2973  // Read the data from the original Multifile.
2974  read->seekg(_data_start + multifile->_offset);
2975  for (size_t p = 0; p < _data_length; p++) {
2976  int byte = read->get();
2977  if (read->eof() || read->fail()) {
2978  // Unexpected EOF or other failure on the source file.
2979  express_cat.info()
2980  << "Unexpected EOF for subfile " << _name << ".\n";
2981  _flags |= SF_data_invalid;
2982  break;
2983  }
2984  write.put(byte);
2985  }
2986  }
2987  } else {
2988  // We do have source data. Copy it in, and also measure its
2989  // length.
2990  ostream *putter = &write;
2991  bool delete_putter = false;
2992 
2993 #ifndef HAVE_OPENSSL
2994  // Without OpenSSL, we can't support encryption. The flag had
2995  // better not be set.
2996  nassertr((_flags & SF_encrypted) == 0, fpos);
2997 
2998 #else // HAVE_OPENSSL
2999  if ((_flags & SF_encrypted) != 0) {
3000  // Write it encrypted.
3001  OEncryptStream *encrypt = new OEncryptStream;
3002  encrypt->set_iteration_count(multifile->_encryption_iteration_count);
3003  encrypt->open(putter, delete_putter, multifile->_encryption_password);
3004 
3005  putter = encrypt;
3006  delete_putter = true;
3007 
3008  // Also write the encrypt_header to the beginning of the
3009  // encrypted stream, so we can validate the password on
3010  // decryption.
3011  putter->write(_encrypt_header, _encrypt_header_size);
3012  }
3013 #endif // HAVE_OPENSSL
3014 
3015 #ifndef HAVE_ZLIB
3016  // Without ZLIB, we can't support compression. The flag had
3017  // better not be set.
3018  nassertr((_flags & SF_compressed) == 0, fpos);
3019 #else // HAVE_ZLIB
3020  if ((_flags & SF_compressed) != 0) {
3021  // Write it compressed.
3022  putter = new OCompressStream(putter, delete_putter, _compression_level);
3023  delete_putter = true;
3024  }
3025 #endif // HAVE_ZLIB
3026 
3027  streampos write_start = fpos;
3028  _uncompressed_length = 0;
3029 
3030 #ifndef HAVE_OPENSSL
3031  // We also need OpenSSL for signatures.
3032  nassertr((_flags & SF_signature) == 0, fpos);
3033 
3034 #else // HAVE_OPENSSL
3035  if ((_flags & SF_signature) != 0) {
3036  // If it's a special signature record, precede the record data
3037  // (the certificate itself) with the signature data generated
3038  // against the multifile contents.
3039 
3040  // In order to generate a signature, we need to have a valid
3041  // read pointer.
3042  nassertr(read != NULL, fpos);
3043 
3044  // And we also need to have a private key.
3045  nassertr(_pkey != NULL, fpos);
3046 
3047  EVP_MD_CTX *md_ctx;
3048 #ifdef SSL_097
3049  md_ctx = EVP_MD_CTX_create();
3050 #else
3051  md_ctx = new EVP_MD_CTX;
3052 #endif
3053  EVP_SignInit(md_ctx, EVP_sha1());
3054 
3055  // Read and hash the multifile contents, but only up till
3056  // _last_data_byte.
3057  nassertr(multifile->_last_data_byte < fpos, fpos);
3058  read->seekg(multifile->_offset);
3059  streampos bytes_remaining = multifile->_last_data_byte;
3060  static const size_t buffer_size = 4096;
3061  char buffer[buffer_size];
3062  read->read(buffer, min((streampos)buffer_size, bytes_remaining));
3063  size_t count = read->gcount();
3064  while (count != 0) {
3065  nassertr(count <= buffer_size, fpos);
3066  EVP_SignUpdate(md_ctx, buffer, count);
3067  bytes_remaining -= count;
3068  read->read(buffer, min((streampos)buffer_size, bytes_remaining));
3069  count = read->gcount();
3070  }
3071  nassertr(bytes_remaining == (streampos)0, fpos);
3072 
3073  // Now generate and write out the signature.
3074  unsigned int max_size = EVP_PKEY_size(_pkey);
3075  unsigned char *sig_data = new unsigned char[max_size];
3076  unsigned int sig_size;
3077  if (!EVP_SignFinal(md_ctx, sig_data, &sig_size, _pkey)) {
3078  OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr();
3079  sslw->notify_ssl_errors();
3080  }
3081  nassertr(sig_size <= max_size, fpos);
3082 
3083  StreamWriter writer(*putter);
3084  writer.add_uint32(sig_size);
3085  putter->write((char *)sig_data, sig_size);
3086  _uncompressed_length += 4 + sig_size;
3087 
3088  delete[] sig_data;
3089 
3090 #ifdef SSL_097
3091  EVP_MD_CTX_destroy(md_ctx);
3092 #else
3093  delete md_ctx;
3094 #endif
3095  }
3096 #endif // HAVE_OPENSSL
3097 
3098  // Finally, we can write out the data itself.
3099  static const size_t buffer_size = 4096;
3100  char buffer[buffer_size];
3101 
3102  source->read(buffer, buffer_size);
3103  size_t count = source->gcount();
3104  while (count != 0) {
3105  _uncompressed_length += count;
3106  putter->write(buffer, count);
3107  source->read(buffer, buffer_size);
3108  count = source->gcount();
3109  }
3110 
3111  if (delete_putter) {
3112  delete putter;
3113  }
3114 
3115  streampos write_end = write.tellp() - multifile->_offset;
3116  _data_length = (size_t)(write_end - write_start);
3117  }
3118 
3119  // We can't set _data_start until down here, after we have read the
3120  // Subfile. (In case we are running during repack()).
3121  _data_start = fpos;
3122 
3123  // Get the modification timestamp for this subfile. This is read
3124  // from the source file, if we have a filename; otherwise, it's the
3125  // current time.
3126  if (!_source_filename.empty()) {
3127  _timestamp = _source_filename.get_timestamp();
3128  }
3129  if (_timestamp == 0) {
3130  _timestamp = time(NULL);
3131  }
3132 
3133  _source = (istream *)NULL;
3134  _source_filename = Filename();
3135  source_file.close();
3136 
3137  return fpos + (streampos)_data_length;
3138 }
3139 
3140 ////////////////////////////////////////////////////////////////////
3141 // Function: Multifile::Subfile::rewrite_index_data_start
3142 // Access: Public
3143 // Description: Seeks within the indicate pfstream back to the index
3144 // record and rewrites just the _data_start and
3145 // _data_length part of the index record.
3146 ////////////////////////////////////////////////////////////////////
3147 void Multifile::Subfile::
3148 rewrite_index_data_start(ostream &write, Multifile *multifile) {
3149  nassertv(_index_start != (streampos)0);
3150 
3151  static const size_t data_start_offset = 4;
3152  size_t data_start_pos = _index_start + (streampos)data_start_offset;
3153  write.seekp(data_start_pos + multifile->_offset);
3154  nassertv(!write.fail());
3155 
3156  StreamWriter writer(write);
3157  writer.add_uint32(multifile->streampos_to_word(_data_start));
3158  writer.add_uint32(_data_length);
3159  writer.add_uint16(_flags);
3160  if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
3161  writer.add_uint32(_uncompressed_length);
3162  }
3163  if (multifile->_record_timestamp) {
3164  writer.add_uint32(_timestamp);
3165  } else {
3166  writer.add_uint32(0);
3167  }
3168 }
3169 
3170 ////////////////////////////////////////////////////////////////////
3171 // Function: Multifile::Subfile::rewrite_index_flags
3172 // Access: Public
3173 // Description: Seeks within the indicated ostream back to the index
3174 // record and rewrites just the _flags part of the
3175 // index record.
3176 ////////////////////////////////////////////////////////////////////
3177 void Multifile::Subfile::
3178 rewrite_index_flags(ostream &write) {
3179  // If the subfile has never even been recorded to disk, we don't
3180  // need to do anything at all in this function.
3181  if (_index_start != (streampos)0) {
3182  static const size_t flags_offset = 4 + 4 + 4;
3183  size_t flags_pos = _index_start + (streampos)flags_offset;
3184  write.seekp(flags_pos);
3185  nassertv(!write.fail());
3186 
3187  StreamWriter writer(write);
3188  writer.add_uint16(_flags);
3189  }
3190 }
A StreamWriter object is used to write sequential binary data directly to an ostream.
Definition: streamWriter.h:33
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the streamWriter.
Definition: streamWriter.I:394
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
size_t get_subfile_internal_length(int index) const
Returns the number of bytes the indicated subfile consumes within the archive.
Definition: multifile.cxx:1818
time_t get_timestamp() const
Returns a time_t value that represents the time the file was last modified, to within whatever precis...
Definition: filename.cxx:1605
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:398
istream * open_read_subfile(int index)
Returns an istream that may be used to read the indicated subfile.
Definition: multifile.cxx:1847
bool open_write(const Filename &multifile_name)
Opens the named Multifile on disk for writing.
Definition: multifile.cxx:252
size_type_0 size() const
Returns the number of elements in the ordered vector.
void clear()
Removes all elements from the ordered vector.
void add_int8(PN_int8 value)
Adds a signed 8-bit integer to the datagram.
Definition: datagram.I:128
time_t get_timestamp() const
Returns the modification timestamp of the overall Multifile.
Definition: multifile.I:86
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
string extract_bytes(size_t size)
Extracts the indicated number of bytes in the stream and returns them as a string.
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:183
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:507
static void close_read_subfile(istream *stream)
Closes a file opened by a previous call to open_read_subfile().
Definition: multifile.cxx:1876
bool is_subfile_compressed(int index) const
Returns true if the indicated subfile has been compressed when stored within the archive, false otherwise.
Definition: multifile.cxx:1736
void set_header_prefix(const string &header_prefix)
Sets the string which is written to the Multifile before the Multifile header.
Definition: multifile.cxx:2111
int find_subfile(const string &subfile_name) const
Returns the index of the subfile with the indicated name, or -1 if the named subfile is not within th...
Definition: multifile.cxx:1561
string read_subfile(int index)
Returns a string that contains the entire contents of the indicated subfile.
Definition: multifile.I:363
streampos get_index_end() const
Returns the first byte that is guaranteed to follow any index byte already written to disk in the Mul...
Definition: multifile.cxx:1785
This class provides a locking wrapper around a combination ostream/istream pointer.
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
bool is_write_valid() const
Returns true if the Multifile has been opened for write mode and there have been no errors...
Definition: multifile.I:60
bool is_subfile_encrypted(int index) const
Returns true if the indicated subfile has been encrypted when stored within the archive, false otherwise.
Definition: multifile.cxx:1749
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
Definition: filename.cxx:945
bool is_binary() const
Returns true if the Filename has been indicated to represent a binary file via a previous call to set...
Definition: filename.I:521
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
string update_subfile(const string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk to the subfile.
Definition: multifile.cxx:564
void seek_read(streamsize pos, char *buffer, streamsize num_bytes, streamsize &read_bytes, bool &eof)
Atomically seeks to a particular offset from the beginning of the file, and reads a number of bytes f...
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
Definition: filename.cxx:439
bool repack()
Forces a complete rewrite of the Multifile and all of its contents, so that its index will appear at ...
Definition: multifile.cxx:1470
void release()
Releases the internal lock.
Definition: streamWrapper.I:65
bool scan_directory(vector_string &contents, const string &subfile_name) const
Considers subfile_name to be the name of a subdirectory within the Multifile, but not a file itself; ...
Definition: multifile.cxx:1619
size_t get_subfile_length(int index) const
Returns the uncompressed data length of the nth subfile.
Definition: multifile.cxx:1703
An istream object that presents a subwindow into another istream.
Definition: subStream.h:34
void add_int16(PN_int16 value)
Adds a signed 16-bit integer to the stream.
Definition: streamWriter.I:126
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This class provides a locking wrapper around an arbitrary istream pointer.
Definition: streamWrapper.h:53
time_t get_subfile_timestamp(int index) const
Returns the modification time of the nth subfile.
Definition: multifile.cxx:1719
bool has_directory(const string &subfile_name) const
Returns true if the indicated subfile name is the directory prefix to one or more files within the Mu...
Definition: multifile.cxx:1582
void set_scale_factor(size_t scale_factor)
Changes the internal scale factor for this Multifile.
Definition: multifile.cxx:442
bool open_read_write(const Filename &multifile_name)
Opens the named Multifile on disk for reading and writing.
Definition: multifile.cxx:303
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void sort()
Maps to sort_unique().
bool is_read_valid() const
Returns true if the Multifile has been opened for read mode and there have been no errors...
Definition: multifile.I:48
bool open_read_write(fstream &stream, bool truncate=false) const
Opens the indicated fstream for read/write access to the file, if possible.
Definition: filename.cxx:2143
string add_subfile(const string &subfile_name, const Filename &filename, int compression_level)
Adds a file on disk as a subfile to the Multifile.
Definition: multifile.cxx:480
bool flush()
Writes all contents of the Multifile to disk.
Definition: multifile.cxx:1309
bool get_record_timestamp() const
Returns the flag indicating whether timestamps should be recorded within the Multifile or not...
Definition: multifile.I:121
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the stream.
Definition: streamWriter.I:170
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2554
bool compare_subfile(int index, const Filename &filename)
Performs a byte-for-byte comparison of the indicated file on disk with the nth subfile.
Definition: multifile.cxx:1976
bool extract_subfile(int index, const Filename &filename)
Extracts the nth subfile into a file with the given name.
Definition: multifile.cxx:1898
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
istream * get_istream() const
Returns the istream this object is wrapping.
void remove_subfile(int index)
Removes the nth subfile from the Multifile.
Definition: multifile.cxx:1666
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
bool extract_subfile_to(int index, ostream &out)
Extracts the nth subfile to the indicated ostream.
Definition: multifile.cxx:1933
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
Definition: filename.I:548
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
void ls(ostream &out=cout) const
Shows a list of all subfiles within the Multifile.
Definition: multifile.cxx:2083
bool open_read(const Filename &multifile_name, const streampos &offset=0)
Opens the named Multifile on disk for reading.
Definition: multifile.cxx:190
PN_int16 get_int16()
Extracts a signed 16-bit integer.
Definition: streamReader.I:127
void acquire()
Acquires the internal lock.
Definition: streamWrapper.I:47
A file that contains a set of files.
Definition: multifile.h:34
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
void close()
Closes the Multifile if it is open.
Definition: multifile.cxx:376
This is a convenience class to specialize ConfigVariable as an integer type.
static bool simple_read_file(istream *stream, pvector< unsigned char > &result)
Fills up the indicated pvector with the contents of the just-opened file.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
Definition: multifile.I:23
bool is_subfile_text(int index) const
Returns true if the indicated subfile represents text data, or false if it represents binary data...
Definition: multifile.cxx:1766
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:30
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
streampos get_subfile_internal_start(int index) const
Returns the starting byte position within the Multifile at which the indicated subfile begins...
Definition: multifile.cxx:1801
bool rename_to(const Filename &other) const
Renames the file to the indicated new filename.
Definition: filename.cxx:2577
const string & get_subfile_name(int index) const
Returns the name of the nth subfile.
Definition: multifile.cxx:1686
bool is_binary_or_text() const
Returns true either is_binary() or is_text() is true; that is, that the filename has been specified a...
Definition: filename.I:534
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
size_t get_length() const
Returns the number of bytes in the datagram.
Definition: datagram.I:457
int get_num_subfiles() const
Returns the number of subfiles within the Multifile.
Definition: multifile.cxx:1549
const void * get_data() const
Returns a pointer to the beginning of the datagram&#39;s data.
Definition: datagram.I:447