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