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 */
63 ConfigPage *ConfigPage::
64 get_default_page() {
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 */
76 ConfigPage *ConfigPage::
77 get_local_page() {
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 */
102 void ConfigPage::
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 */
123 bool ConfigPage::
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 */
223 bool ConfigPage::
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 */
236 ConfigDeclaration *ConfigPage::
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 */
245 ConfigDeclaration *ConfigPage::
246 make_declaration(ConfigVariableCore *variable, const string &value) {
247 ConfigDeclaration *decl = new ConfigDeclaration
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 */
262 bool ConfigPage::
263 delete_declaration(ConfigDeclaration *decl) {
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 */
288 const ConfigDeclaration *ConfigPage::
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 */
299 ConfigDeclaration *ConfigPage::
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 */
308 string ConfigPage::
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 */
317 string ConfigPage::
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 */
327 bool ConfigPage::
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 */
345 void ConfigPage::
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 }
ConfigDeclaration
A single declaration of a config variable, typically defined as one line in a .prc file,...
Definition: configDeclaration.h:34
ConfigPageManager::mark_unsorted
void mark_unsorted()
This method is meant to be used internally to this module; there is no need to call it directly.
Definition: configPageManager.I:158
ConfigPage
A page of ConfigDeclarations that may be loaded or unloaded.
Definition: configPage.h:30
ConfigPage::get_string_value
std::string get_string_value(size_t n) const
Returns the value assigned by the nth declaration on the page.
Definition: configPage.cxx:318
ConfigPage::delete_declaration
bool delete_declaration(ConfigDeclaration *decl)
Removes the indicated declaration from the page and deletes it.
Definition: configPage.cxx:263
ConfigPage::get_declaration
const ConfigDeclaration * get_declaration(size_t n) const
Returns the nth declaration on the page.
Definition: configPage.cxx:289
ConfigPage::make_declaration
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
ConfigPage::get_num_declarations
get_num_declarations
Returns the number of declarations on the page.
Definition: configPage.h:77
ConfigPage::read_encrypted_prc
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
ConfigPage::modify_declaration
modify_declaration
Returns a modifiable pointer to the nth declaration on the page.
Definition: configPage.h:77
ConfigPage::get_variable_name
std::string get_variable_name(size_t n) const
Returns the variable named by the nth declaration on the page.
Definition: configPage.cxx:309
ConfigPage::get_default_page
static ConfigPage * get_default_page()
Returns a pointer to the global "default page".
Definition: configPage.cxx:64
ConfigPage::get_local_page
static ConfigPage * get_local_page()
Returns a pointer to the global "local page".
Definition: configPage.cxx:77
ConfigPage::read_prc
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
ConfigPage::set_sort
set_sort
Changes the explicit sort order of this particular ConfigPage.
Definition: configPage.h:52
ConfigPage::output_brief_signature
void output_brief_signature(std::ostream &out) const
Outputs the first few hex digits of the signature.
Definition: configPage.cxx:346
ConfigPage::clear
void clear()
Removes all of the declarations from the page.
Definition: configPage.cxx:103
ConfigPage::is_variable_used
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
ConfigPage::get_name
get_name
Returns the name of the page.
Definition: configPage.h:43
ConfigVariableCore
The internal definition of a ConfigVariable.
Definition: configVariableCore.h:34
ConfigVariableManager
A global object that maintains the set of ConfigVariables (actually, ConfigVariableCores) everywhere ...
Definition: configVariableManager.h:31
ConfigVariableManager::make_variable
ConfigVariableCore * make_variable(const std::string &name)
Creates and returns a new, undefined ConfigVariableCore with the indicated name; or if a variable wit...
Definition: configVariableManager.cxx:50
configDeclaration.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configPage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableCore.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_prc.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
encryptStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
prcKeyRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.