Panda3D
configPage.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file configPage.cxx
10  * @author drose
11  * @date 2004-10-15
12  */
13 
14 #include "configPage.h"
15 #include "configDeclaration.h"
16 #include "configVariableCore.h"
17 #include "configVariableManager.h"
18 #include "prcKeyRegistry.h"
19 #include "config_prc.h"
20 #include "encryptStream.h"
21 
22 #include <ctype.h>
23 
24 #ifdef HAVE_OPENSSL
25 #include <openssl/evp.h>
26 #endif
27 
28 using std::istream;
29 using std::ostream;
30 using std::string;
31 
32 ConfigPage *ConfigPage::_default_page = nullptr;
33 ConfigPage *ConfigPage::_local_page = nullptr;
34 
35 /**
36  * The constructor is private because a ConfigPage should be constructed via
37  * the ConfigPageManager make_page() interface.
38  */
39 ConfigPage::
40 ConfigPage(const string &name, bool implicit_load, int page_seq) :
41  _name(name),
42  _implicit_load(implicit_load),
43  _page_seq(page_seq),
44  _sort(implicit_load ? 10 : 0),
45  _next_decl_seq(1),
46  _trust_level(0)
47 {
48 }
49 
50 /**
51  * The destructor is private because a ConfigPage should be deleted via the
52  * ConfigPageManager delete_page() interface.
53  */
54 ConfigPage::
55 ~ConfigPage() {
56  clear();
57 }
58 
59 /**
60  * Returns a pointer to the global "default page". This is the ConfigPage
61  * that lists all variables' original default values.
62  */
65  if (_default_page == nullptr) {
66  _default_page = new ConfigPage("default", false, 0);
67  }
68  return _default_page;
69 }
70 
71 /**
72  * Returns a pointer to the global "local page". This is the ConfigPage that
73  * lists the locally-assigned values for any variables in the world that have
74  * such a local assignment.
75  */
78  if (_local_page == nullptr) {
79  _local_page = new ConfigPage("local", false, 0);
80  }
81  return _local_page;
82 }
83 
84 /**
85  * Changes the explicit sort order of this particular ConfigPage. Lower-
86  * numbered pages supercede higher-numbered pages. Initially, all explicitly-
87  * loaded pages have sort value 0, and implicitly-loaded pages (found on disk)
88  * have sort value 10; you may set an individual page higher or lower to
89  * influence its priority relative to other pages.
90  */
91 void ConfigPage::
92 set_sort(int sort) {
93  if (_sort != sort) {
94  _sort = sort;
95  ConfigPageManager::get_global_ptr()->mark_unsorted();
96  }
97 }
98 
99 /**
100  * Removes all of the declarations from the page.
101  */
103 clear() {
104  Declarations::iterator di;
105  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
106  ConfigDeclaration *decl = (*di);
107  delete decl;
108  }
109  _declarations.clear();
110  _trust_level = 0;
111  _signature = string();
112 }
113 
114 /**
115  * Reads the contents of a complete prc file, as returned by the indicated
116  * istream, into the current page file. Returns true on success, or false on
117  * some I/O error.
118  *
119  * This is a low-level interface. Normally you do not need to call it
120  * directly. See the global functions load_prc_file() and unload_prc_file(),
121  * defined in panda/src/putil, for a higher-level interface.
122  */
124 read_prc(istream &in) {
125  // We must empty the page before we start to read it; otherwise trust level
126  // is meaningless.
127  clear();
128 
129  // We avoid getline() here because of its notorious problem with last lines
130  // that lack a trailing newline character.
131  static const size_t buffer_size = 1024;
132  char buffer[buffer_size];
133 
134 #ifdef HAVE_OPENSSL
135  // Set up the evp context for verifying the signature, if we find one.
136  _md_ctx = EVP_MD_CTX_create();
137  EVP_VerifyInit((EVP_MD_CTX *)_md_ctx, EVP_sha1());
138 #endif // HAVE_OPENSSL
139 
140  string prev_line;
141 
142  in.read(buffer, buffer_size);
143  size_t count = in.gcount();
144  while (count != 0) {
145  char *buffer_end = buffer + count;
146 
147  // Look for the first line in the buffer..
148  char *newline = (char *)memchr((void *)buffer, '\n', count);
149  if (newline == nullptr) {
150  // The buffer was one long line. Huh.
151  prev_line += string(buffer, count);
152 
153  } else {
154  // Process the first line in the buffer.
155  size_t length = newline - buffer;
156  read_prc_line(prev_line + string(buffer, length + 1));
157 
158  // Now look for the next line, etc.
159  char *start = newline + 1;
160  newline = (char *)memchr((void *)start, '\n', buffer_end - start);
161  while (newline != nullptr) {
162  length = newline - start;
163  read_prc_line(string(start, length + 1));
164  start = newline + 1;
165  newline = (char *)memchr((void *)start, '\n', buffer_end - start);
166  }
167 
168  // The remaining text in the buffer is the start of the next line.
169  length = buffer_end - start;
170  prev_line = string(start, length);
171  }
172 
173  if (in.fail() || in.eof()) {
174  // If we got a failure reading the buffer last time, don't keep reading
175  // again. Irix seems to require this test; otherwise, it repeatedly
176  // returns the same text at the end of the file.
177  count = 0;
178 
179  } else {
180  in.read(buffer, buffer_size);
181  count = in.gcount();
182  }
183  }
184 
185  if (!prev_line.empty()) {
186  read_prc_line(prev_line);
187  }
188 
189 #ifdef HAVE_OPENSSL
190  // Now validate the signature and free the SSL structures.
191  if (!_signature.empty()) {
192  PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr();
193  int num_keys = pkr->get_num_keys();
194  for (int i = 1; i < num_keys && _trust_level == 0; i++) {
195  EVP_PKEY *pkey = pkr->get_key(i);
196  if (pkey != nullptr) {
197  int verify_result =
198  EVP_VerifyFinal((EVP_MD_CTX *)_md_ctx,
199  (unsigned char *)_signature.data(),
200  _signature.size(), pkey);
201  if (verify_result == 1) {
202  _trust_level = i;
203  }
204  }
205  }
206  if (_trust_level == 0) {
207  prc_cat->info()
208  << "invalid signature found in " << get_name() << "\n";
209  }
210  }
211  EVP_MD_CTX_destroy((EVP_MD_CTX *)_md_ctx);
212 #endif // HAVE_OPENSSL
213 
214  bool failed = (in.fail() && !in.eof());
215 
216  return !failed;
217 }
218 
219 /**
220  * Automatically decrypts and reads the stream, given the indicated password.
221  * Note that if the password is incorrect, the result may be garbage.
222  */
224 read_encrypted_prc(istream &in, const string &password) {
225 #ifdef HAVE_OPENSSL
226  IDecryptStream decrypt(&in, false, password);
227  return read_prc(decrypt);
228 #else
229  return false;
230 #endif // HAVE_OPENSSL
231 }
232 
233 /**
234  * Adds the indicated variable/value pair as a new declaration on the page.
235  */
237 make_declaration(const string &variable, const string &value) {
238  ConfigVariableManager *variable_mgr = ConfigVariableManager::get_global_ptr();
239  return make_declaration(variable_mgr->make_variable(variable), value);
240 }
241 
242 /**
243  * Adds the indicated variable/value pair as a new declaration on the page.
244  */
246 make_declaration(ConfigVariableCore *variable, const string &value) {
248  (this, variable, value, _next_decl_seq);
249  _next_decl_seq++;
250 
251  _declarations.push_back(decl);
252  make_dirty();
253 
254  return decl;
255 }
256 
257 /**
258  * Removes the indicated declaration from the page and deletes it. Returns
259  * true if the declaration is successfully removed, false if it was not on the
260  * page.
261  */
264  Declarations::iterator di;
265  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
266  if ((*di) == decl) {
267  _declarations.erase(di);
268  delete decl;
269  make_dirty();
270  return true;
271  }
272  }
273 
274  return false;
275 }
276 
277 /**
278  * Returns the number of declarations on the page.
279  */
280 size_t ConfigPage::
281 get_num_declarations() const {
282  return _declarations.size();
283 }
284 
285 /**
286  * Returns the nth declaration on the page.
287  */
289 get_declaration(size_t n) const {
290  nassertr(n < _declarations.size(), nullptr);
291  return _declarations[n];
292 }
293 
294 /**
295  * Returns a modifiable pointer to the nth declaration on the page. Any
296  * modifications will appear in the output, if the page is written out with
297  * ConfigPage::write().
298  */
300 modify_declaration(size_t n) {
301  nassertr(n < _declarations.size(), nullptr);
302  return _declarations[n];
303 }
304 
305 /**
306  * Returns the variable named by the nth declaration on the page.
307  */
309 get_variable_name(size_t n) const {
310  nassertr(n < _declarations.size(), string());
311  return _declarations[n]->get_variable()->get_name();
312 }
313 
314 /**
315  * Returns the value assigned by the nth declaration on the page.
316  */
318 get_string_value(size_t n) const {
319  nassertr(n < _declarations.size(), string());
320  return _declarations[n]->get_string_value();
321 }
322 
323 /**
324  * Returns true if the nth active variable on the page has been used by code,
325  * false otherwise.
326  */
328 is_variable_used(size_t n) const {
329  nassertr(n < _declarations.size(), false);
330  return _declarations[n]->get_variable()->is_used();
331 }
332 
333 /**
334  *
335  */
336 void ConfigPage::
337 output(ostream &out) const {
338  out << "ConfigPage " << get_name() << ", " << get_num_declarations()
339  << " declarations.";
340 }
341 
342 /**
343  * Outputs the first few hex digits of the signature.
344  */
346 output_brief_signature(ostream &out) const {
347  size_t num_bytes = std::min(_signature.size(), (size_t)8);
348  for (size_t p = 0; p < num_bytes; ++p) {
349  unsigned int byte = _signature[p];
350 
351  unsigned int hi = (byte >> 4) & 0xf;
352  if (hi >= 10) {
353  out << (char)((hi - 10) + 'a');
354  } else {
355  out << (char)(hi + '0');
356  }
357 
358  unsigned int lo = byte & 0xf;
359  if (lo >= 10) {
360  out << (char)((lo - 10) + 'a');
361  } else {
362  out << (char)(lo + '0');
363  }
364  }
365 }
366 
367 /**
368  *
369  */
370 void ConfigPage::
371 write(ostream &out) const {
372  Declarations::const_iterator di;
373  for (di = _declarations.begin(); di != _declarations.end(); ++di) {
374  (*di)->write(out);
375  }
376 }
377 
378 /**
379  * Handles reading in a single line from a .prc file. This is called
380  * internally by read_prc() for each line.
381  */
382 void ConfigPage::
383 read_prc_line(const string &line) {
384  if (line.substr(0, 7) == "##!sig ") {
385  // This is a signature. Accumulate it into the signature and return, and
386  // don't count it as contributing to the hash.
387  for (size_t p = 7; p < line.length() - 1; p += 2) {
388  unsigned char digit = (hex_digit(line[p]) << 4) | hex_digit(line[p + 1]);
389  _signature += digit;
390  }
391  return;
392  }
393 
394 #ifdef HAVE_OPENSSL
395  // Accumulate any line that's not itself a signature into the hash, so we
396  // can validate the signature at the end.
397  EVP_VerifyUpdate((EVP_MD_CTX *)_md_ctx, line.data(), line.size());
398 #endif // HAVE_OPENSSL
399 
400  // Separate the line into a variable and a value.
401  size_t p = 0;
402  while (p < line.length() && isspace((unsigned char)line[p])) {
403  p++;
404  }
405 
406  if (p == line.length() || line[p] == '#') {
407  // Blank line or comment; do nothing.
408  return;
409  }
410 
411  size_t variable_begin = p;
412  while (p < line.length() && !isspace((unsigned char)line[p])) {
413  p++;
414  }
415  size_t variable_end = p;
416 
417  while (p < line.length() && isspace((unsigned char)line[p])) {
418  p++;
419  }
420  size_t value_begin = p;
421 
422  // Is there an embedded comment on this line?
423  p = line.find(" #", value_begin);
424  if (p == string::npos) {
425  // No, the value extends all the way to the end of the line.
426  p = line.length();
427  }
428 
429  // The value extends from here to the end of the line (or to the start of
430  // the embedded comment), so trim whitespace backwards off from there.
431  while (p > value_begin && isspace((unsigned char)line[p - 1])) {
432  p--;
433  }
434  size_t value_end = p;
435 
436  string variable = line.substr(variable_begin, variable_end - variable_begin);
437  string value = line.substr(value_begin, value_end - value_begin);
438 
439  make_declaration(variable, value);
440 }
441 
442 /**
443  * Decodes a hex digit into its numeric value.
444  */
445 unsigned int ConfigPage::
446 hex_digit(unsigned char digit) {
447  if (isalpha(digit)) {
448  return tolower(digit) - 'a' + 10;
449  } else if (isdigit(digit)) {
450  return digit - '0';
451  } else {
452  return 0;
453  }
454 }
A single declaration of a config variable, typically defined as one line in a .prc file,...
void mark_unsorted()
This method is meant to be used internally to this module; there is no need to call it directly.
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
std::string get_string_value(size_t n) const
Returns the value assigned by the nth declaration on the page.
Definition: configPage.cxx:318
bool delete_declaration(ConfigDeclaration *decl)
Removes the indicated declaration from the page and deletes it.
Definition: configPage.cxx:263
const ConfigDeclaration * get_declaration(size_t n) const
Returns the nth declaration on the page.
Definition: configPage.cxx:289
ConfigDeclaration * make_declaration(const std::string &variable, const std::string &value)
Adds the indicated variable/value pair as a new declaration on the page.
Definition: configPage.cxx:237
get_num_declarations
Returns the number of declarations on the page.
Definition: configPage.h:77
bool read_encrypted_prc(std::istream &in, const std::string &password)
Automatically decrypts and reads the stream, given the indicated password.
Definition: configPage.cxx:224
modify_declaration
Returns a modifiable pointer to the nth declaration on the page.
Definition: configPage.h:77
std::string get_variable_name(size_t n) const
Returns the variable named by the nth declaration on the page.
Definition: configPage.cxx:309
static ConfigPage * get_default_page()
Returns a pointer to the global "default page".
Definition: configPage.cxx:64
static ConfigPage * get_local_page()
Returns a pointer to the global "local page".
Definition: configPage.cxx:77
bool read_prc(std::istream &in)
Reads the contents of a complete prc file, as returned by the indicated istream, into the current pag...
Definition: configPage.cxx:124
set_sort
Changes the explicit sort order of this particular ConfigPage.
Definition: configPage.h:52
void output_brief_signature(std::ostream &out) const
Outputs the first few hex digits of the signature.
Definition: configPage.cxx:346
void clear()
Removes all of the declarations from the page.
Definition: configPage.cxx:103
bool is_variable_used(size_t n) const
Returns true if the nth active variable on the page has been used by code, false otherwise.
Definition: configPage.cxx:328
get_name
Returns the name of the page.
Definition: configPage.h:43
The internal definition of a ConfigVariable.
A global object that maintains the set of ConfigVariables (actually, ConfigVariableCores) everywhere ...
ConfigVariableCore * make_variable(const std::string &name)
Creates and returns a new, undefined ConfigVariableCore with the indicated name; or if a variable wit...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.