Panda3D
|
00001 // Filename: configPage.cxx 00002 // Created by: drose (15Oct04) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "configPage.h" 00016 #include "configDeclaration.h" 00017 #include "configVariableCore.h" 00018 #include "configVariableManager.h" 00019 #include "prcKeyRegistry.h" 00020 #include "config_prc.h" 00021 #include "encryptStream.h" 00022 00023 #include <ctype.h> 00024 00025 #ifdef HAVE_OPENSSL 00026 #include "openssl/evp.h" 00027 #endif 00028 00029 ConfigPage *ConfigPage::_default_page = NULL; 00030 ConfigPage *ConfigPage::_local_page = NULL; 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: ConfigPage::Constructor 00034 // Access: Private 00035 // Description: The constructor is private because a ConfigPage 00036 // should be constructed via the ConfigPageManager 00037 // make_page() interface. 00038 //////////////////////////////////////////////////////////////////// 00039 ConfigPage:: 00040 ConfigPage(const string &name, bool implicit_load, int page_seq) : 00041 _name(name), 00042 _implicit_load(implicit_load), 00043 _page_seq(page_seq), 00044 _next_decl_seq(1), 00045 _trust_level(0) 00046 { 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: ConfigPage::Destructor 00051 // Access: Private 00052 // Description: The destructor is private because a ConfigPage 00053 // should be deleted via the ConfigPageManager 00054 // delete_page() interface. 00055 //////////////////////////////////////////////////////////////////// 00056 ConfigPage:: 00057 ~ConfigPage() { 00058 clear(); 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: ConfigPage::get_default_page 00063 // Access: Published, Static 00064 // Description: Returns a pointer to the global "default page". This 00065 // is the ConfigPage that lists all variables' original 00066 // default values. 00067 //////////////////////////////////////////////////////////////////// 00068 ConfigPage *ConfigPage:: 00069 get_default_page() { 00070 if (_default_page == (ConfigPage *)NULL) { 00071 _default_page = new ConfigPage("default", false, 0); 00072 } 00073 return _default_page; 00074 } 00075 00076 //////////////////////////////////////////////////////////////////// 00077 // Function: ConfigPage::get_local_page 00078 // Access: Published, Static 00079 // Description: Returns a pointer to the global "local page". This 00080 // is the ConfigPage that lists the locally-assigned 00081 // values for any variables in the world that have such 00082 // a local assignment. 00083 //////////////////////////////////////////////////////////////////// 00084 ConfigPage *ConfigPage:: 00085 get_local_page() { 00086 if (_local_page == (ConfigPage *)NULL) { 00087 _local_page = new ConfigPage("local", false, 0); 00088 } 00089 return _local_page; 00090 } 00091 00092 //////////////////////////////////////////////////////////////////// 00093 // Function: ConfigPage::clear 00094 // Access: Published 00095 // Description: Removes all of the declarations from the page. 00096 //////////////////////////////////////////////////////////////////// 00097 void ConfigPage:: 00098 clear() { 00099 Declarations::iterator di; 00100 for (di = _declarations.begin(); di != _declarations.end(); ++di) { 00101 ConfigDeclaration *decl = (*di); 00102 delete decl; 00103 } 00104 _declarations.clear(); 00105 _trust_level = 0; 00106 _signature = string(); 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: ConfigPage::read_prc 00111 // Access: Published 00112 // Description: Reads the contents of a complete prc file, as 00113 // returned by the indicated istream, into the current 00114 // page file. Returns true on success, or false on some 00115 // I/O error. 00116 // 00117 // This is a low-level interface. Normally you do not 00118 // need to call it directly. See the global functions 00119 // load_prc_file() and unload_prc_file(), defined in 00120 // panda/src/putil, for a higher-level interface. 00121 //////////////////////////////////////////////////////////////////// 00122 bool ConfigPage:: 00123 read_prc(istream &in) { 00124 // We must empty the page before we start to read it; otherwise 00125 // trust level is meaningless. 00126 clear(); 00127 00128 // We avoid getline() here because of its notorious problem with 00129 // last lines that lack a trailing newline character. 00130 static const size_t buffer_size = 1024; 00131 char buffer[buffer_size]; 00132 00133 #ifdef HAVE_OPENSSL 00134 // Set up the evp context for verifying the signature, if we find 00135 // one. 00136 #ifdef SSL_097 00137 _md_ctx = EVP_MD_CTX_create(); 00138 #else 00139 _md_ctx = new EVP_MD_CTX; 00140 #endif 00141 EVP_VerifyInit((EVP_MD_CTX *)_md_ctx, EVP_sha1()); 00142 #endif // HAVE_OPENSSL 00143 00144 string prev_line; 00145 00146 in.read(buffer, buffer_size); 00147 size_t count = in.gcount(); 00148 while (count != 0) { 00149 char *buffer_end = buffer + count; 00150 00151 // Look for the first line in the buffer.. 00152 char *newline = (char *)memchr((void *)buffer, '\n', count); 00153 if (newline == (char *)NULL) { 00154 // The buffer was one long line. Huh. 00155 prev_line += string(buffer, count); 00156 00157 } else { 00158 // Process the first line in the buffer. 00159 size_t length = newline - buffer; 00160 read_prc_line(prev_line + string(buffer, length + 1)); 00161 00162 // Now look for the next line, etc. 00163 char *start = newline + 1; 00164 newline = (char *)memchr((void *)start, '\n', buffer_end - start); 00165 while (newline != (char *)NULL) { 00166 length = newline - start; 00167 read_prc_line(string(start, length + 1)); 00168 start = newline + 1; 00169 newline = (char *)memchr((void *)start, '\n', buffer_end - start); 00170 } 00171 00172 // The remaining text in the buffer is the start of the next 00173 // line. 00174 length = buffer_end - start; 00175 prev_line = string(start, length); 00176 } 00177 00178 if (in.fail() || in.eof()) { 00179 // If we got a failure reading the buffer last time, don't keep 00180 // reading again. Irix seems to require this test; otherwise, 00181 // it repeatedly returns the same text at the end of the file. 00182 count = 0; 00183 00184 } else { 00185 in.read(buffer, buffer_size); 00186 count = in.gcount(); 00187 } 00188 } 00189 00190 if (!prev_line.empty()) { 00191 read_prc_line(prev_line); 00192 } 00193 00194 #ifdef HAVE_OPENSSL 00195 // Now validate the signature and free the SSL structures. 00196 if (!_signature.empty()) { 00197 PrcKeyRegistry *pkr = PrcKeyRegistry::get_global_ptr(); 00198 int num_keys = pkr->get_num_keys(); 00199 for (int i = 1; i < num_keys && _trust_level == 0; i++) { 00200 EVP_PKEY *pkey = pkr->get_key(i); 00201 if (pkey != (EVP_PKEY *)NULL) { 00202 int verify_result = 00203 EVP_VerifyFinal((EVP_MD_CTX *)_md_ctx, 00204 (unsigned char *)_signature.data(), 00205 _signature.size(), pkey); 00206 if (verify_result == 1) { 00207 _trust_level = i; 00208 } 00209 } 00210 } 00211 if (_trust_level == 0) { 00212 prc_cat->info() 00213 << "invalid signature found in " << get_name() << "\n"; 00214 } 00215 } 00216 #ifdef SSL_097 00217 EVP_MD_CTX_destroy((EVP_MD_CTX *)_md_ctx); 00218 #else 00219 delete (EVP_MD_CTX *)_md_ctx; 00220 #endif 00221 #endif // HAVE_OPENSSL 00222 00223 bool failed = (in.fail() && !in.eof()); 00224 00225 return !failed; 00226 } 00227 00228 //////////////////////////////////////////////////////////////////// 00229 // Function: ConfigPage::read_encrypted_prc 00230 // Access: Published 00231 // Description: Automatically decrypts and reads the stream, given 00232 // the indicated password. Note that if the password is 00233 // incorrect, the result may be garbage. 00234 //////////////////////////////////////////////////////////////////// 00235 bool ConfigPage:: 00236 read_encrypted_prc(istream &in, const string &password) { 00237 #ifdef HAVE_OPENSSL 00238 IDecryptStream decrypt(&in, false, password); 00239 return read_prc(decrypt); 00240 #else 00241 return false; 00242 #endif // HAVE_OPENSSL 00243 } 00244 00245 //////////////////////////////////////////////////////////////////// 00246 // Function: ConfigPage::make_declaration 00247 // Access: Published 00248 // Description: Adds the indicated variable/value pair as a new 00249 // declaration on the page. 00250 //////////////////////////////////////////////////////////////////// 00251 ConfigDeclaration *ConfigPage:: 00252 make_declaration(const string &variable, const string &value) { 00253 ConfigVariableManager *variable_mgr = ConfigVariableManager::get_global_ptr(); 00254 return make_declaration(variable_mgr->make_variable(variable), value); 00255 } 00256 00257 //////////////////////////////////////////////////////////////////// 00258 // Function: ConfigPage::make_declaration 00259 // Access: Published 00260 // Description: Adds the indicated variable/value pair as a new 00261 // declaration on the page. 00262 //////////////////////////////////////////////////////////////////// 00263 ConfigDeclaration *ConfigPage:: 00264 make_declaration(ConfigVariableCore *variable, const string &value) { 00265 ConfigDeclaration *decl = new ConfigDeclaration 00266 (this, variable, value, _next_decl_seq); 00267 _next_decl_seq++; 00268 00269 _declarations.push_back(decl); 00270 make_dirty(); 00271 00272 return decl; 00273 } 00274 00275 //////////////////////////////////////////////////////////////////// 00276 // Function: ConfigPage::delete_declaration 00277 // Access: Published 00278 // Description: Removes the indicated declaration from the page and 00279 // deletes it. Returns true if the declaration is 00280 // successfully removed, false if it was not on the 00281 // page. 00282 //////////////////////////////////////////////////////////////////// 00283 bool ConfigPage:: 00284 delete_declaration(ConfigDeclaration *decl) { 00285 Declarations::iterator di; 00286 for (di = _declarations.begin(); di != _declarations.end(); ++di) { 00287 if ((*di) == decl) { 00288 _declarations.erase(di); 00289 delete decl; 00290 make_dirty(); 00291 return true; 00292 } 00293 } 00294 00295 return false; 00296 } 00297 00298 //////////////////////////////////////////////////////////////////// 00299 // Function: ConfigPage::get_num_declarations 00300 // Access: Published 00301 // Description: Returns the number of declarations on the page. 00302 //////////////////////////////////////////////////////////////////// 00303 int ConfigPage:: 00304 get_num_declarations() const { 00305 return _declarations.size(); 00306 } 00307 00308 //////////////////////////////////////////////////////////////////// 00309 // Function: ConfigPage::get_declaration 00310 // Access: Published 00311 // Description: Returns the nth declaration on the page. 00312 //////////////////////////////////////////////////////////////////// 00313 const ConfigDeclaration *ConfigPage:: 00314 get_declaration(int n) const { 00315 nassertr(n >= 0 && n < (int)_declarations.size(), (ConfigDeclaration *)NULL); 00316 return _declarations[n]; 00317 } 00318 00319 //////////////////////////////////////////////////////////////////// 00320 // Function: ConfigPage::get_variable_name 00321 // Access: Published 00322 // Description: Returns the variable named by the nth declaration on 00323 // the page. 00324 //////////////////////////////////////////////////////////////////// 00325 string ConfigPage:: 00326 get_variable_name(int n) const { 00327 nassertr(n >= 0 && n < (int)_declarations.size(), string()); 00328 return _declarations[n]->get_variable()->get_name(); 00329 } 00330 00331 //////////////////////////////////////////////////////////////////// 00332 // Function: ConfigPage::get_string_value 00333 // Access: Published 00334 // Description: Returns the value assigned by the nth declaration on 00335 // the page. 00336 //////////////////////////////////////////////////////////////////// 00337 string ConfigPage:: 00338 get_string_value(int n) const { 00339 nassertr(n >= 0 && n < (int)_declarations.size(), string()); 00340 return _declarations[n]->get_string_value(); 00341 } 00342 00343 //////////////////////////////////////////////////////////////////// 00344 // Function: ConfigPage::is_variable_used 00345 // Access: Published 00346 // Description: Returns true if the nth active variable on 00347 // the page has been used by code, false otherwise. 00348 //////////////////////////////////////////////////////////////////// 00349 bool ConfigPage:: 00350 is_variable_used(int n) const { 00351 nassertr(n >= 0 && n < (int)_declarations.size(), false); 00352 return _declarations[n]->get_variable()->is_used(); 00353 } 00354 00355 //////////////////////////////////////////////////////////////////// 00356 // Function: ConfigPage::output 00357 // Access: Published 00358 // Description: 00359 //////////////////////////////////////////////////////////////////// 00360 void ConfigPage:: 00361 output(ostream &out) const { 00362 out << "ConfigPage " << get_name() << ", " << get_num_declarations() 00363 << " declarations."; 00364 } 00365 00366 //////////////////////////////////////////////////////////////////// 00367 // Function: ConfigPage::output_brief_signature 00368 // Access: Published 00369 // Description: Outputs the first few hex digits of the signature. 00370 //////////////////////////////////////////////////////////////////// 00371 void ConfigPage:: 00372 output_brief_signature(ostream &out) const { 00373 size_t num_bytes = min(_signature.size(), (size_t)8); 00374 for (size_t p = 0; p < num_bytes; ++p) { 00375 unsigned int byte = _signature[p]; 00376 00377 unsigned int hi = (byte >> 4) & 0xf; 00378 if (hi >= 10) { 00379 out << (char)((hi - 10) + 'a'); 00380 } else { 00381 out << (char)(hi + '0'); 00382 } 00383 00384 unsigned int lo = byte & 0xf; 00385 if (lo >= 10) { 00386 out << (char)((lo - 10) + 'a'); 00387 } else { 00388 out << (char)(lo + '0'); 00389 } 00390 } 00391 } 00392 00393 //////////////////////////////////////////////////////////////////// 00394 // Function: ConfigPage::write 00395 // Access: Published 00396 // Description: 00397 //////////////////////////////////////////////////////////////////// 00398 void ConfigPage:: 00399 write(ostream &out) const { 00400 Declarations::const_iterator di; 00401 for (di = _declarations.begin(); di != _declarations.end(); ++di) { 00402 (*di)->write(out); 00403 } 00404 } 00405 00406 //////////////////////////////////////////////////////////////////// 00407 // Function: ConfigPage::read_prc_line 00408 // Access: Private 00409 // Description: Handles reading in a single line from a .prc file. 00410 // This is called internally by read_prc() for each 00411 // line. 00412 //////////////////////////////////////////////////////////////////// 00413 void ConfigPage:: 00414 read_prc_line(const string &line) { 00415 if (line.substr(0, 7) == "##!sig ") { 00416 // This is a signature. Accumulate it into the signature and 00417 // return, and don't count it as contributing to the hash. 00418 for (size_t p = 7; p < line.length() - 1; p += 2) { 00419 unsigned char digit = (hex_digit(line[p]) << 4) | hex_digit(line[p + 1]); 00420 _signature += digit; 00421 } 00422 return; 00423 } 00424 00425 #ifdef HAVE_OPENSSL 00426 // Accumulate any line that's not itself a signature into the hash, 00427 // so we can validate the signature at the end. 00428 EVP_VerifyUpdate((EVP_MD_CTX *)_md_ctx, line.data(), line.size()); 00429 #endif // HAVE_OPENSSL 00430 00431 // Separate the line into a variable and a value. 00432 size_t p = 0; 00433 while (p < line.length() && isspace((unsigned char)line[p])) { 00434 p++; 00435 } 00436 00437 if (p == line.length() || line[p] == '#') { 00438 // Blank line or comment; do nothing. 00439 return; 00440 } 00441 00442 size_t variable_begin = p; 00443 while (p < line.length() && !isspace((unsigned char)line[p])) { 00444 p++; 00445 } 00446 size_t variable_end = p; 00447 00448 while (p < line.length() && isspace((unsigned char)line[p])) { 00449 p++; 00450 } 00451 size_t value_begin = p; 00452 00453 // Is there an embedded comment on this line? 00454 p = line.find(" #", value_begin); 00455 if (p == string::npos) { 00456 // No, the value extends all the way to the end of the line. 00457 p = line.length(); 00458 } 00459 00460 // The value extends from here to the end of the line (or to the 00461 // start of the embedded comment), so trim whitespace backwards off 00462 // from there. 00463 while (p > value_begin && isspace((unsigned char)line[p - 1])) { 00464 p--; 00465 } 00466 size_t value_end = p; 00467 00468 string variable = line.substr(variable_begin, variable_end - variable_begin); 00469 string value = line.substr(value_begin, value_end - value_begin); 00470 00471 make_declaration(variable, value); 00472 } 00473 00474 //////////////////////////////////////////////////////////////////// 00475 // Function: ConfigPage::hex_digit 00476 // Access: Private, Static 00477 // Description: Decodes a hex digit into its numeric value. 00478 //////////////////////////////////////////////////////////////////// 00479 unsigned int ConfigPage:: 00480 hex_digit(unsigned char digit) { 00481 if (isalpha(digit)) { 00482 return tolower(digit) - 'a' + 10; 00483 } else if (isdigit(digit)) { 00484 return digit - '0'; 00485 } else { 00486 return 0; 00487 } 00488 }