1<?php 2 3require_once('../config.inc.php'); 4 5require_once(HTML2PS_DIR.'stubs.common.inc.php'); 6require_once(HTML2PS_DIR.'error.php'); 7require_once(HTML2PS_DIR.'config.parse.php'); 8 9parse_config_file('../html2ps.config'); 10 11define('CHECK_STATUS_FAILED', 0); 12define('CHECK_STATUS_WARNING', 1); 13define('CHECK_STATUS_SUCCESS', 2); 14 15error_reporting(E_ALL); 16ini_set("display_errors","1"); 17 18$__g_registered_checks = array(); 19 20function out_header() { 21 readfile('systemcheck.header.tpl'); 22} 23 24function out_footer() { 25 readfile('systemcheck.footer.tpl'); 26} 27 28function status2class($status) { 29 $mapping = array(CHECK_STATUS_FAILED => "failed", 30 CHECK_STATUS_WARNING => "warning", 31 CHECK_STATUS_SUCCESS => "success"); 32 if (isset($mapping[$status])) { 33 return $mapping[$status]; 34 }; 35 36 error_log(sprintf("Unknown status code passed to 'status2class': %s", $status)); 37 return "unknown"; 38} 39 40function out_check_list() { 41 $checks = ManagerChecks::getChecks(); 42 foreach ($checks as $check) { 43 $title = htmlspecialchars($check->title()); 44 $message = nl2br($check->getMessage()); 45 $status_class = status2class($check->getStatus()); 46 47 print <<<EOF 48<div class="check"> 49<div class="title ${status_class}">${title}</div> 50<div class="message">${message}</div> 51</div> 52EOF; 53 }; 54} 55 56class ManagerChecks { 57 function register($check) { 58 global $__g_registered_checks; 59 $__g_registered_checks[] = $check; 60 } 61 62 function run() { 63 global $__g_registered_checks; 64 $size = count($__g_registered_checks); 65 for ($i=0; $i<$size; $i++) { 66 $__g_registered_checks[$i]->run(); 67 }; 68 } 69 70 function getChecks() { 71 global $__g_registered_checks; 72 return $__g_registered_checks; 73 } 74} 75 76class CheckSimple { 77 var $_message; 78 79 /** 80 * Invariants 81 */ 82 function title() { 83 error_no_method('title', get_class($this)); 84 } 85 86 function description() { 87 error_no_method('description', get_class($this)); 88 } 89 90 /** 91 * Start checking 92 */ 93 function run() { 94 error_no_method('run', get_class($this)); 95 } 96 97 /** 98 * Get check status code; status code should be one of the following 99 * predefined constants: 100 * CHECK_STATUS_FAILED - check failed, script will not work unless this issue is fixed 101 * CHECK_STATUS_WARNING - check succeeded, script may encounter minor issues 102 * CHECK_STATUS_SUCCESS - check succeeded without any problems 103 * 104 * @return Integer Status code 105 */ 106 function getStatus() { 107 error_no_method('status', get_class($this)); 108 } 109 110 /** 111 * Returns a short human-readable message describing results of the 112 * check run. By default, this message is generated in 'run' method 113 * (overridden in CheckSimple children) and stored via 'setMessage' 114 * 115 * @return String description of the test results 116 */ 117 function getMessage() { 118 return $this->_message; 119 } 120 121 function setMessage($message) { 122 $this->_message = $message; 123 } 124} 125 126/** 127 */ 128class CheckBinary extends CheckSimple { 129 var $_success; 130 131 function setSuccess($success) { 132 $this->_success = $success; 133 } 134 135 function getSuccess() { 136 return $this->_success; 137 } 138} 139 140/** 141 */ 142class CheckBinaryRequired extends CheckBinary { 143 function getStatus() { 144 if ($this->getSuccess()) { 145 return CHECK_STATUS_SUCCESS; 146 } else { 147 return CHECK_STATUS_FAILED; 148 }; 149 } 150} 151 152/** 153 */ 154class CheckBinaryRecommended extends CheckBinary { 155 function getStatus() { 156 if ($this->getSuccess()) { 157 return CHECK_STATUS_SUCCESS; 158 } else { 159 return CHECK_STATUS_WARNING; 160 }; 161 } 162} 163 164/** 165 */ 166class CheckTriState extends CheckSimple { 167 var $_status; 168 169 function getStatus() { 170 return $this->_status; 171 } 172 173 function setStatus($status) { 174 $this->_status = $status; 175 } 176} 177 178/** 179 * Actual checks 180 */ 181 182/** 183 * PHP version 184 */ 185class CheckPHPVersion extends CheckTriState { 186 function title() { 187 return "PHP Version"; 188 } 189 190 function description() { 191 return ""; 192 } 193 194 function run() { 195 // > "4.3.0"; 196 } 197} 198// ManagerChecks::register(new CheckPHPVersion()); 199 200/** 201 * Required / recommended extensions 202 */ 203 204/** 205 * Presense of DOM/XML extensions 206 */ 207class CheckDOM extends CheckTriState { 208 function title() { 209 return "XML DOM extension"; 210 } 211 212 function description() { 213 return "HTML files are parsed using XML DOM extensions"; 214 } 215 216 function run() { 217 if (function_exists('domxml_open_mem') || 218 class_exists('DOMDocument')) { 219 $this->setStatus(CHECK_STATUS_SUCCESS); 220 $this->setMessage('Native XML DOM extension found'); 221 return; 222 }; 223 224 if (file_exists(HTML2PS_DIR.'classes/include.php')) { 225 $this->setStatus(CHECK_STATUS_WARNING); 226 $this->setMessage('No native XML DOM extension found, falling back to Active-State DOM XML. Note that it is <b>highly recommended to use native PHP XML DOM extension</b>.'); 227 return; 228 }; 229 230 $this->setStatus(CHECK_STATUS_FAILED); 231 $this->setMessage('No XML DOM extension found'); 232 } 233} 234 235/** 236 * Presense of PDFLIB extension 237 */ 238class CheckPDFLIB extends CheckBinaryRecommended { 239} 240 241/** 242 * Presense of Curl extension 243 */ 244class CheckCurl extends CheckBinaryRecommended { 245 function title() { 246 return "Curl PHP Extension"; 247 } 248 249 function description() { 250 return "Curl PHP extension is recommended for fetching files via HTTP protocol"; 251 } 252 253 function run() { 254 $this->setSuccess(false); 255 256 if (!extension_loaded('curl')) { 257 $this->setMessage('Missing Curl extension. Script will use pure-PHP fallback (allow_url_fopen=On is required!). <b>Proxy support is not available</b>'); 258 return; 259 }; 260 261 $version = curl_version(); 262 // PHP 5.0.1 and greater return array instead of string 263 if (is_array($version)) { 264 $version = $version['version']; 265 }; 266 $this->setMessage(sprintf('Found Curl extension version %s.', $version['version'])); 267 $this->setSuccess(true); 268 } 269} 270 271/** 272 * Presense of GD extension 273 */ 274class CheckGD extends CheckBinaryRequired { 275 function title() { 276 return "GD PHP Extension"; 277 } 278 279 function description() { 280 return "GD PHP extension is required for graphic file processing"; 281 } 282 283 function run() { 284 $this->setSuccess(false); 285 286 if (!extension_loaded('gd')) { 287 $this->setMessage('Missing GD extension. Please refer to <a href="http://php.net/gd">PHP.net instructions</a> on installing/enabling this extension.'); 288 return; 289 }; 290 291 $gd_info = gd_info(); 292 $gd_version_string = $gd_info['GD Version']; 293 294 /** 295 * Extract version number if it is a bundled version; otherwise we assume that 296 * version string should contain verions number only 297 */ 298 if (preg_match("/bundled \(([\d\.]+) compatible\)/", $gd_version_string, $matches)) { 299 $gd_version = $matches[1]; 300 } else { 301 $gd_version = $gd_version_string; 302 }; 303 304 if (!function_exists('imagecreatetruecolor')) { 305 $this->setMessage("GD version 2.0.1+ required for 'imagecreatetruecolor' function to work"); 306 return; 307 }; 308 309 $this->setMessage("Found GD version $gd_version."); 310 $this->setSuccess(true); 311 } 312} 313 314/** 315 * Presense of ZLIB extension (compressed files) 316 */ 317class CheckZLIB extends CheckBinaryRecommended { 318} 319 320/** 321 * System limits & settings 322 */ 323 324/** 325 * Execution time limit 326 */ 327class CheckMaxExecutionTime extends CheckTriState { 328} 329 330/** 331 * Memory limit 332 */ 333class CheckMemoryLimit extends CheckTriState { 334} 335 336/** 337 * Allow_url_fopen setting 338 */ 339class CheckAllowURLFopen extends CheckBinaryRecommended { 340 function title() { 341 return "allow_url_fopen ini setting"; 342 } 343 344 function description() { 345 return "allow_url_fopen should be enabled when CURL extension is not available"; 346 } 347 348 function run() { 349 $this->setSuccess(false); 350 351 $setting = ini_get('allow_url_fopen'); 352 if (!$setting) { 353 $this->setMessage('<a href="http://php.net/filesystem">allow_url_fopen</a> is disabled. You will not be able to fetch files via HTTP without CURL extension.'); 354 return; 355 } 356 357 $this->setMessage('allow_url_fopen is enabled'); 358 $this->setSuccess(true); 359 } 360} 361 362 363/** 364 * pcre.backtrack_limit setting (PHP 5.2) 365 */ 366class CheckPCREBacktrack extends CheckBinaryRecommended { 367 function title() { 368 return "pcre.backtrack_limit ini setting"; 369 } 370 371 function description() { 372 return "It is recommended to increase pcre.backtrack_limit value to 1,000,000"; 373 } 374 375 function run() { 376 $this->setSuccess(false); 377 378 $version = explode('.', PHP_VERSION); 379 if ($version[0] < 5 || 380 ($version[0] == 5 && $version[1] < 2)) { 381 $this->setMessage('pcre.backtrack_limit is not available in PHP prior to 5.2.0'); 382 $this->setSuccess(true); 383 return; 384 }; 385 386 $setting = ini_get('pcre.backtrack_limit'); 387 if ($setting < 1000000) { 388 $this->setMessage(sprintf('<a href="http://php.net/filesystem">pcre.backtrack_limit</a> is set to %s (less than 1,000,000). You could experience issues converting large pages.', 389 $setting)); 390 return; 391 } 392 393 $this->setMessage('pcre.backtrack_limit is greater than 1,000,000'); 394 $this->setSuccess(true); 395 } 396} 397 398 399/** 400 * Access/permissions 401 */ 402 403/** 404 * permissions on cache directory 405 */ 406class CheckPermissionsCache extends CheckBinaryRequired { 407 function title() { 408 return "Permissions on 'cache' subdirectory"; 409 } 410 411 function description() { 412 return "Script should have full access to 'cache' subdirectory to keep cached files there"; 413 } 414 415 function run() { 416 if (!file_exists(HTML2PS_DIR.'/cache/')) { 417 $this->setMessage("'cache' subdirectory is missing"); 418 $this->setSuccess(false); 419 return; 420 }; 421 422 if (!is_readable(HTML2PS_DIR.'/cache/')) { 423 $this->setMessage("'cache' subdirectory is not readable"); 424 $this->setSuccess(false); 425 return; 426 }; 427 428 if (!is_writable(HTML2PS_DIR.'/cache/')) { 429 $this->setMessage("'cache' subdirectory is not writable"); 430 $this->setSuccess(false); 431 return; 432 }; 433 434 if (!is_executable(HTML2PS_DIR.'/cache/') && PHP_OS != "WINNT") { 435 $this->setMessage("'cache' subdirectory is not executable"); 436 $this->setSuccess(false); 437 return; 438 }; 439 440 $this->setMessage("'cache' subdirectory is fully accessible to the script"); 441 $this->setSuccess(true); 442 } 443} 444 445/** 446 * Permissions on 'out' directory 447 */ 448class CheckPermissionsOut extends CheckBinaryRecommended { 449 function title() { 450 return "Permissions on 'out' subdirectory"; 451 } 452 453 function description() { 454 return "Script should have full access to 'out' subdirectory to put generated files there"; 455 } 456 457 function run() { 458 if (!file_exists(HTML2PS_DIR.'/out/')) { 459 $this->setMessage("'out' subdirectory is missing"); 460 $this->setSuccess(false); 461 return; 462 }; 463 464 if (!is_readable(HTML2PS_DIR.'/out/')) { 465 $this->setMessage("'out' subdirectory is not readable"); 466 $this->setSuccess(false); 467 return; 468 }; 469 470 if (!is_writable(HTML2PS_DIR.'/out/')) { 471 $this->setMessage("'out' subdirectory is not writable"); 472 $this->setSuccess(false); 473 return; 474 }; 475 476 if (!is_executable(HTML2PS_DIR.'/out/') && PHP_OS != "WINNT") { 477 $this->setMessage("'out' subdirectory is not executable"); 478 $this->setSuccess(false); 479 return; 480 }; 481 482 $this->setMessage("'out' subdirectory is fully accessible to the script"); 483 $this->setSuccess(true); 484 } 485} 486 487/** 488 * Permissions on 'temp' directory (system-dependent) 489 */ 490class CheckPermissionsTemp extends CheckBinaryRequired { 491 function title() { 492 return "Permissions on 'temp' subdirectory"; 493 } 494 495 function description() { 496 return "Script should have full access to 'temp' subdirectory to keep temporary files there"; 497 } 498 499 function run() { 500 if (!file_exists(HTML2PS_DIR.'/temp/')) { 501 $this->setMessage("'temp' subdirectory is missing"); 502 $this->setSuccess(false); 503 return; 504 }; 505 506 if (!is_readable(HTML2PS_DIR.'/temp/')) { 507 $this->setMessage("'temp' subdirectory is not readable"); 508 $this->setSuccess(false); 509 return; 510 }; 511 512 if (!is_writable(HTML2PS_DIR.'/temp/')) { 513 $this->setMessage("'temp' subdirectory is not writable"); 514 $this->setSuccess(false); 515 return; 516 }; 517 518 if (!is_executable(HTML2PS_DIR.'/temp/') && PHP_OS != "WINNT") { 519 $this->setMessage("'temp' subdirectory is not executable"); 520 $this->setSuccess(false); 521 return; 522 }; 523 524 $this->setMessage("'temp' subdirectory is fully accessible to the script"); 525 $this->setSuccess(true); 526 } 527} 528 529/** 530 * Permissions/availability of GS executable 531 */ 532 533/** 534 * Permissions of fonts directory 535 */ 536class CheckPermissionsFonts extends CheckBinaryRequired { 537 function title() { 538 return "Permissions on 'fonts' subdirectory"; 539 } 540 541 function description() { 542 return "Script should be able to read 'fonts' subdirectory to access installed fonts"; 543 } 544 545 function run() { 546 if (!file_exists(HTML2PS_DIR.'/fonts/')) { 547 $this->setMessage("'fonts' subdirectory is missing"); 548 $this->setSuccess(false); 549 return; 550 }; 551 552 if (!is_readable(HTML2PS_DIR.'/fonts/')) { 553 $this->setMessage("'fonts' subdirectory is not readable"); 554 $this->setSuccess(false); 555 return; 556 }; 557 558 if (!is_executable(HTML2PS_DIR.'/fonts/') && PHP_OS != "WINNT") { 559 $this->setMessage("'fonts' subdirectory is not executable"); 560 $this->setSuccess(false); 561 return; 562 }; 563 564 $this->setMessage("'fonts' subdirectory is readable and executable by the script"); 565 $this->setSuccess(true); 566 } 567} 568 569/** 570 * Permissions/presence of Type1 fonts repository 571 */ 572class CheckPermissionsType1 extends CheckBinaryRecommended { 573 function title() { 574 return "Permissions on Type1 fonts directory"; 575 } 576 577 function description() { 578 return "Script should be able to access Type1 fonts directory containing font metrics in order to generate Postscript files"; 579 } 580 581 function run() { 582 if (!file_exists(TYPE1_FONTS_REPOSITORY)) { 583 $this->setMessage("Type1 fonts directory (".TYPE1_FONTS_REPOSITORY.") is missing. You will not be able to generate postscript files."); 584 $this->setSuccess(false); 585 return; 586 }; 587 588 if (!is_readable(TYPE1_FONTS_REPOSITORY)) { 589 $this->setMessage("Type1 fonts directory (".TYPE1_FONTS_REPOSITORY.") is not readable. You will not be able to generate postscript files."); 590 $this->setSuccess(false); 591 return; 592 }; 593 594 if (!is_executable(HTML2PS_DIR.'/fonts/') && PHP_OS != "WINNT") { 595 $this->setMessage("Type1 fonts directory (".TYPE1_FONTS_REPOSITORY.") is not executable. You will not be able to generate postscript files."); 596 $this->setSuccess(false); 597 return; 598 }; 599 600 $this->setMessage("Type1 fonts directory is readable and executable by the script"); 601 $this->setSuccess(true); 602 } 603} 604 605/** 606 * Fonts 607 */ 608 609/** 610 * Permissions/presense of TTF files 611 */ 612class CheckPresenceTTF extends CheckBinaryRecommended { 613 function title() { 614 return "Presense of registered TTF files"; 615 } 616 617 function description() { 618 return "TrueType fonts registered in html2ps.config should be present in order to generate PDF files with these fonts."; 619 } 620 621 function run() { 622 $message = ""; 623 $this->setSuccess(true); 624 625 global $g_font_resolver_pdf; 626 foreach ($g_font_resolver_pdf->ttf_mappings as $file) { 627 $fullname = HTML2PS_DIR.'/fonts/'.$file; 628 629 if (!file_exists($fullname)) { 630 $message .= "Font ".$fullname." is missing. You will not be able to generate PDF files with this font.\n"; 631 $this->setSuccess(false); 632 } elseif (!file_exists($fullname)) { 633 $message .= "Font ".$fullname." is not readable. You will not be able to generate PDF files with this font.\n"; 634 $this->setSuccess(false); 635 } else { 636 $message .= "Font ".$fullname." is present and readable.\n"; 637 }; 638 }; 639 640 $this->setMessage($message); 641 } 642} 643 644/** 645 * Permissions/presense of Type1 fonts 646 */ 647 648/** 649 * Permissions/presense of AFM files for Type1 fonts 650 */ 651class CheckPresenceType1AFM extends CheckBinaryRecommended { 652 function title() { 653 return "Presense of registered TTF files"; 654 } 655 656 function description() { 657 return "TrueType fonts registered in html2ps.config should be present in order to generate PDF files with these fonts."; 658 } 659 660 function run() { 661 $message = ""; 662 $this->setSuccess(true); 663 664 global $g_font_resolver; 665 foreach ($g_font_resolver->afm_mappings as $file) { 666 $fullname = TYPE1_FONTS_REPOSITORY.$file.'.afm'; 667 668 if (!file_exists($fullname)) { 669 $message .= "Font ".$fullname." is missing. You will not be able to generate PDF files with this font.\n"; 670 $this->setSuccess(false); 671 } elseif (!file_exists($fullname)) { 672 $message .= "Font ".$fullname." is not readable. You will not be able to generate PDF files with this font.\n"; 673 $this->setSuccess(false); 674 } else { 675 $message .= "Font ".$fullname." is present and readable.\n"; 676 }; 677 }; 678 679 $this->setMessage($message); 680 } 681} 682 683/** 684 * Graphics 685 */ 686 687/** 688 * Generic 689 */ 690class CheckGDFormat extends CheckBinaryRequired { 691 function title() { 692 return "GD ".$this->_getFormatName()." Support"; 693 } 694 695 function description() { 696 return "GD ".$this->_getFormatName()." Support is required for reading images in ".$this->_getFormatName()." format"; 697 } 698 699 function run() { 700 $this->setSuccess(false); 701 702 if (!extension_loaded('gd')) { 703 $this->setMessage('Missing GD extension. Please refer to <a href="http://php.net/gd">PHP.net instructions</a> on installing/enabling this extension.'); 704 return; 705 }; 706 707 $gd_info = gd_info(); 708 if (!$gd_info[$this->_getInfoKey()]) { 709 $this->setMessage("No ".$this->_getFormatName()." support, some images will not be displayed"); 710 return; 711 }; 712 713 $this->setMessage($this->_getFormatName()." support enabled"); 714 $this->setSuccess(true); 715 } 716} 717 718/** 719 * JPEG support 720 */ 721class CheckGDJPEG extends CheckGDFormat { 722 function _getFormatName() { 723 return "JPEG"; 724 } 725 726 function _getInfoKey() { 727 return "JPG Support"; 728 } 729} 730 731/** 732 * GIF support 733 */ 734class CheckGDGIF extends CheckGDFormat { 735 function _getFormatName() { 736 return "GIF"; 737 } 738 739 function _getInfoKey() { 740 return "GIF Read Support"; 741 } 742} 743 744/** 745 * PNG support 746 */ 747class CheckGDPNG extends CheckGDFormat { 748 function _getFormatName() { 749 return "PNG"; 750 } 751 752 function _getInfoKey() { 753 return "PNG Support"; 754 } 755} 756 757/** 758 * Freetype support 759 */ 760 761/** 762 * Miscellanous 763 */ 764 765/** 766 * Check if outgoing connections are allowed 767 */ 768class CheckOutgoingConnections extends CheckBinary { 769} 770 771ManagerChecks::register(new CheckDOM()); 772ManagerChecks::register(new CheckCurl()); 773ManagerChecks::register(new CheckAllowURLFopen()); 774ManagerChecks::register(new CheckPCREBacktrack()); 775ManagerChecks::register(new CheckGD()); 776ManagerChecks::register(new CheckGDJPEG()); 777ManagerChecks::register(new CheckGDGIF()); 778ManagerChecks::register(new CheckGDPNG()); 779ManagerChecks::register(new CheckPermissionsTemp()); 780ManagerChecks::register(new CheckPermissionsFonts()); 781ManagerChecks::register(new CheckPermissionsType1()); 782ManagerChecks::register(new CheckPresenceTTF()); 783ManagerChecks::register(new CheckPresenceType1AFM()); 784 785ManagerChecks::run(); 786out_header(); 787out_check_list(); 788out_footer(); 789 790?>