| @@ -6,9 +6,9 @@ | ||
| 6 | 6 | | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| | |
| 7 | 7 | | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | | |
| 8 | 8 | | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| | |
| 9 | 9 | | | |
| 10 | - | (c) Jerome Bruandet ~ https://code-profiler.com/ | | |
| 10 | + | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ | | |
| 11 | 11 | +=====================================================================+ |
| 12 | 12 | */ |
| 13 | 13 | |
| 14 | 14 | if (! defined('ABSPATH') ) { |
| @@ -72,15 +72,8 @@ | ||
| 72 | 72 | code_profiler_wp_send_json( $response ); |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | code_profiler_log_debug( |
| 76 | - esc_html__('Cleaning up the temporary folder', 'code-profiler') | |
| 77 | - ); | |
| 78 | - | |
| 79 | - // Clean-up temp files left in the profiles folder | |
| 80 | - code_profiler_cleantmpfiles(); | |
| 81 | - | |
| 82 | - code_profiler_log_debug( | |
| 83 | 76 | esc_html__('Retrieving parameters #1', 'code-profiler') |
| 84 | 77 | ); |
| 85 | 78 | |
| 86 | 79 | // Frontend or backend |
| @@ -191,15 +184,18 @@ | ||
| 191 | 184 | ); |
| 192 | 185 | |
| 193 | 186 | // Create security key |
| 194 | 187 | $profiler_key = bin2hex( random_bytes( 16 ) ); |
| 195 | - $cp_options['hash'] = sha1( $profiler_key ); | |
| 188 | + touch( CODE_PROFILER_UPLOAD_DIR .'/key_'. sha1( $profiler_key ) .'.tmp'); | |
| 196 | 189 | |
| 197 | 190 | code_profiler_log_debug( |
| 198 | 191 | esc_html__('Building HTTP query', 'code-profiler') |
| 199 | 192 | ); |
| 200 | 193 | |
| 201 | - // Build query | |
| 194 | + /** | |
| 195 | + * Build the query. | |
| 196 | + */ | |
| 197 | + $raw_url = $url; | |
| 202 | 198 | $url = add_query_arg( [ |
| 203 | 199 | 'CODE_PROFILER_ON' => $microtime, |
| 204 | 200 | 'profiler_key' => $profiler_key |
| 205 | 201 | ], $url ); |
| @@ -231,14 +227,17 @@ | ||
| 231 | 227 | esc_html__('Building custom HTTP headers', 'code-profiler') |
| 232 | 228 | ); |
| 233 | 229 | $is_custom_headers = ''; |
| 234 | 230 | foreach( $custom_headers as $custom_header ) { |
| 235 | - list( $key, $value ) = explode( ':', "$custom_header:" ); | |
| 231 | + if ( strpos( $custom_header, ':') === false ) { | |
| 232 | + continue; | |
| 233 | + } | |
| 234 | + list( $key, $value ) = explode(':', $custom_header, 2 ); | |
| 236 | 235 | // Lowercase header name |
| 237 | 236 | $key = trim( strtolower( $key ) ); |
| 238 | 237 | $value = trim( $value ); |
| 239 | 238 | // We want printable ASCII characters only |
| 240 | - $value = preg_replace('/[\x00-\x1f\x7f-\xff]/', '', $value); | |
| 239 | + $value = code_profiler_ASCII_filter( $value ); | |
| 241 | 240 | if (! empty( $key ) && ! empty( $value ) ) { |
| 242 | 241 | $headers['headers'][ $key ] = $value; |
| 243 | 242 | $is_custom_headers .= "$key: $value\n"; |
| 244 | 243 | } |
| @@ -434,8 +433,32 @@ | ||
| 434 | 433 | } else { |
| 435 | 434 | unset( $cp_options['cookies'] ); |
| 436 | 435 | } |
| 437 | 436 | |
| 437 | + /** | |
| 438 | + * Optional file and folder exclusions. | |
| 439 | + */ | |
| 440 | + $tmp_exclusions = []; | |
| 441 | + if (! empty( $_POST['exclusions'] ) ) { | |
| 442 | + $tmp_array = explode( PHP_EOL, trim( stripslashes( $_POST['exclusions'] ) ) ); | |
| 443 | + foreach( $tmp_array as $item ) { | |
| 444 | + $item = trim( code_profiler_ASCII_filter( $item ) ); | |
| 445 | + if ( $item ) { | |
| 446 | + $tmp_exclusions[] = $item; | |
| 447 | + } | |
| 448 | + } | |
| 449 | + } | |
| 450 | + /** | |
| 451 | + * Remove duplicates. | |
| 452 | + */ | |
| 453 | + $exclusions = array_unique( $tmp_exclusions ); | |
| 454 | + | |
| 455 | + if ( $exclusions) { | |
| 456 | + $cp_options['exclusions'] = json_encode( $exclusions ); | |
| 457 | + } else { | |
| 458 | + unset( $cp_options['exclusions'] ); | |
| 459 | + } | |
| 460 | + | |
| 438 | 461 | update_option('code-profiler', $cp_options ); |
| 439 | 462 | |
| 440 | 463 | code_profiler_log_debug( |
| 441 | 464 | esc_html__('Sending HTTP request', 'code-profiler') |
| @@ -460,25 +483,82 @@ | ||
| 460 | 483 | code_profiler_log_debug( |
| 461 | 484 | esc_html__('Fetching HTTP response', 'code-profiler') |
| 462 | 485 | ); |
| 463 | 486 | |
| 487 | + /** | |
| 488 | + * Always log last HTTP response headers and body, | |
| 489 | + * except sensitive data (cookies & PHP session ID). | |
| 490 | + */ | |
| 491 | + if ( isset( $res['headers'] ) && isset( $res['body'] ) ) { | |
| 492 | + /** | |
| 493 | + * Search for an existing log or create it. | |
| 494 | + */ | |
| 495 | + $last_log = code_profiler_glob( | |
| 496 | + CODE_PROFILER_UPLOAD_DIR, | |
| 497 | + '^last_request\.\d+?\.\d+?\.log$', | |
| 498 | + true | |
| 499 | + ); | |
| 500 | + if ( empty( $last_log[0] ) ) { | |
| 501 | + $last_log[0] = CODE_PROFILER_UPLOAD_DIR .'/last_request.'. microtime( true ) .'.log'; | |
| 502 | + } | |
| 503 | + /** | |
| 504 | + * Parse headers. | |
| 505 | + */ | |
| 506 | + $headers = "HTTP {$res['response']['code']} {$res['response']['message']}\n"; | |
| 507 | + | |
| 508 | + foreach( $res['headers'] as $key => $value ) { | |
| 509 | + /** | |
| 510 | + * Remove cookies. | |
| 511 | + */ | |
| 512 | + if ( $key == 'set-cookie') { | |
| 513 | + $headers .= ucfirst( $key ) .': *** '. __('Removed', 'code-profiler') ." ***\n"; | |
| 514 | + } else { | |
| 515 | + /** | |
| 516 | + * HTTP headers can contain arrays. | |
| 517 | + */ | |
| 518 | + if ( is_array( $value ) ) { | |
| 519 | + foreach( $value as $k => $v ) { | |
| 520 | + $headers .= ucfirst( $key ) .": $v\n"; | |
| 521 | + } | |
| 522 | + } else { | |
| 523 | + $headers .= ucfirst( $key ) .": $value\n"; | |
| 524 | + } | |
| 525 | + } | |
| 526 | + } | |
| 527 | + /** | |
| 528 | + * Save to the log. | |
| 529 | + */ | |
| 530 | + file_put_contents( $last_log[0], | |
| 531 | + "==================================================\n". | |
| 532 | + __('Requested page:', 'code-profiler') ."\n\n". | |
| 533 | + "$raw_url\n". | |
| 534 | + "==================================================\n". | |
| 535 | + __('Response headers:', 'code-profiler') ."\n\n". | |
| 536 | + $headers . | |
| 537 | + "==================================================\n". | |
| 538 | + __('Response body:', 'code-profiler') ."\n\n". | |
| 539 | + print_r( $res['body'], true ). | |
| 540 | + "\n==================================================\n" | |
| 541 | + ); | |
| 542 | + } | |
| 543 | + | |
| 464 | 544 | // HTTP status code |
| 465 | 545 | if (! empty( $cp_options['http_response'] ) ) { |
| 466 | 546 | if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) { |
| 547 | + | |
| 548 | + $msg = ''; | |
| 549 | + | |
| 467 | 550 | $log = esc_html__( |
| 551 | + /* Translators: HTTP response code and message */ | |
| 468 | 552 | 'The website returned the following HTTP status code: %s %s.', 'code-profiler' |
| 469 | 553 | ); |
| 470 | - if ( $res['response']['code'] >= 500 ) { | |
| 471 | - $msg = $log .' '. esc_html__( | |
| 472 | - 'You may find more details about this error in your PHP error log.', | |
| 473 | - 'code-profiler' | |
| 474 | - ); | |
| 475 | - } else { | |
| 476 | - $msg = $log .' '. esc_html__('By default, the profiler will always abort and throw an error '. | |
| 477 | - 'if the server did not return a 200 HTTP status code. You can change that behaviour in '. | |
| 478 | - 'the Settings section if the page you are profiling needs to return a different '. | |
| 479 | - 'code (3xx, 4xx or 5xx).', 'code-profiler'); | |
| 554 | + | |
| 555 | + if ( $res['response']['code'] < 500 ) { | |
| 556 | + $log .= ' '. esc_html__('By default, the profiler will always abort and throw an error if the server did not return a 200 HTTP status code. You can change that behaviour in the "Settings" section if the page you are profiling needs to return a different code (3xx, 4xx or 5xx).', 'code-profiler'); | |
| 480 | 557 | } |
| 558 | + | |
| 559 | + $msg .= $log .' '. esc_html__('You may find more details about this error in your PHP error log and/or in the "Logs" section.', 'code-profiler'); | |
| 560 | + | |
| 481 | 561 | $response['message'] = sprintf( |
| 482 | 562 | $msg, |
| 483 | 563 | (int) $res['response']['code'], |
| 484 | 564 | $res['response']['message'] |
| @@ -485,8 +565,21 @@ | ||
| 485 | 565 | ); |
| 486 | 566 | code_profiler_log_error( |
| 487 | 567 | sprintf( $log, $res['response']['code'], $res['response']['message'] ) |
| 488 | 568 | ); |
| 569 | + | |
| 570 | + // If it is a 301/302 redirection, we write the new URL to the log | |
| 571 | + if ( in_array( $res['response']['code'], [301, 302] ) && | |
| 572 | + isset( $res['headers']['location'] ) ) { | |
| 573 | + | |
| 574 | + code_profiler_log_error( | |
| 575 | + sprintf( | |
| 576 | + /* Translators: URL */ | |
| 577 | + esc_html__('The URL redirects to: %s', 'code-profiler'), | |
| 578 | + $res['headers']['location'] | |
| 579 | + ) | |
| 580 | + ); | |
| 581 | + } | |
| 489 | 582 | code_profiler_wp_send_json( $response ); |
| 490 | 583 | } |
| 491 | 584 | } |
| 492 | 585 | |
| @@ -529,14 +622,8 @@ | ||
| 529 | 622 | function codeprofiler_prepare_report() { |
| 530 | 623 | |
| 531 | 624 | $response = ['status' => 'error']; |
| 532 | 625 | |
| 533 | - code_profiler_log_debug( | |
| 534 | - esc_html__('Entering AJAX endpoint (report preparation)', 'code-profiler') | |
| 535 | - ); | |
| 536 | - | |
| 537 | - code_profiler_hide_errors(); | |
| 538 | - | |
| 539 | 626 | // If this is an AJAX call, make sure it comes from an admin/superadmin. |
| 540 | 627 | if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report') { |
| 541 | 628 | // Admin/Superadmin only |
| 542 | 629 | if (! is_super_admin() ) { |
| @@ -547,8 +634,14 @@ | ||
| 547 | 634 | } |
| 548 | 635 | } |
| 549 | 636 | |
| 550 | 637 | code_profiler_log_debug( |
| 638 | + esc_html__('Entering AJAX endpoint (report preparation)', 'code-profiler') | |
| 639 | + ); | |
| 640 | + | |
| 641 | + code_profiler_hide_errors(); | |
| 642 | + | |
| 643 | + code_profiler_log_debug( | |
| 551 | 644 | esc_html__('Verifying security nonce', 'code-profiler') |
| 552 | 645 | ); |
| 553 | 646 | |
| 554 | 647 | // Verify the security nonce |
| @@ -596,13 +689,8 @@ | ||
| 596 | 689 | // Take a 1s break so that we can spot any potential error |
| 597 | 690 | // in the backend before AJAX refresh the page |
| 598 | 691 | usleep( 1000000 ); |
| 599 | 692 | |
| 600 | - // Clear hash | |
| 601 | - $cp_options = get_option('code-profiler'); | |
| 602 | - unset( $cp_options['hash'] ); | |
| 603 | - update_option('code-profiler', $cp_options ); | |
| 604 | - | |
| 605 | 693 | code_profiler_log_info( |
| 606 | 694 | esc_html__('All done, exiting profiler', 'code-profiler') |
| 607 | 695 | ); |
| 608 | 696 | $response['cp_profile'] = $microtime; |
| @@ -635,9 +723,9 @@ | ||
| 635 | 723 | |
| 636 | 724 | // Admin/Superadmin only |
| 637 | 725 | if (! is_super_admin() ) { |
| 638 | 726 | $response['message'] = esc_html__( |
| 639 | - 'You are not allowed to performed this action', 'code-profiler' | |
| 727 | + 'You are not allowed to performed this action.', 'code-profiler' | |
| 640 | 728 | ); |
| 641 | 729 | wp_send_json( $response ); |
| 642 | 730 | } |
| 643 | 731 | |
| @@ -643,9 +731,9 @@ | ||
| 643 | 731 | |
| 644 | 732 | // Verify the security nonce |
| 645 | 733 | if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'rename-profile') ) { |
| 646 | 734 | $response['message'] = esc_html__( |
| 647 | - 'Missing or wrong security nonce. Reload the page and try again', 'code-profiler' | |
| 735 | + 'Missing or wrong security nonce. Reload the page and try again.', 'code-profiler' | |
| 648 | 736 | ); |
| 649 | 737 | wp_send_json( $response ); |
| 650 | 738 | } |
| 651 | 739 | |
| @@ -667,18 +755,34 @@ | ||
| 667 | 755 | wp_send_json( $response ); |
| 668 | 756 | } |
| 669 | 757 | $profile = $_POST['profile']; |
| 670 | 758 | |
| 671 | - $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$profile*" ); | |
| 759 | + $glob = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, "^$profile", true ); | |
| 760 | + | |
| 761 | + $res = false; | |
| 762 | + | |
| 672 | 763 | if ( is_array( $glob ) ) { |
| 673 | 764 | foreach( $glob as $path ) { |
| 674 | 765 | // preg_quote is needed for Windows servers because ABSPATH will contain backslashes |
| 675 | - if ( preg_match('`^'. preg_quote( CODE_PROFILER_UPLOAD_DIR ) . | |
| 676 | - '/(\d{10}\.\d{4})\..+?\.([a-z]+?\.profile)$`', $path, $match ) ) { | |
| 766 | + if ( preg_match('`^'. preg_quote( CODE_PROFILER_UPLOAD_DIR . DIRECTORY_SEPARATOR ) . | |
| 767 | + '(\d{10}\.\d{4})\..+?\.([a-z]+?\.profile)$`', $path, $match ) ) { | |
| 677 | 768 | |
| 678 | - rename( $path, CODE_PROFILER_UPLOAD_DIR . "/{$match[1]}.$new_name.{$match[2]}" ); | |
| 769 | + $res = rename( $path, CODE_PROFILER_UPLOAD_DIR . "/{$match[1]}.$new_name.{$match[2]}" ); | |
| 770 | + if ( $res === false ) { | |
| 771 | + $response['message'] = esc_html__( | |
| 772 | + 'The operation failed.', 'code-profiler' | |
| 773 | + ); | |
| 774 | + wp_send_json( $response ); | |
| 775 | + } | |
| 679 | 776 | } |
| 680 | 777 | } |
| 778 | + } | |
| 779 | + | |
| 780 | + if ( $res === false ) { | |
| 781 | + $response['message'] = esc_html__( | |
| 782 | + 'The operation failed.', 'code-profiler' | |
| 783 | + ); | |
| 784 | + wp_send_json( $response ); | |
| 681 | 785 | } |
| 682 | 786 | |
| 683 | 787 | $response['status'] = 'success'; |
| 684 | 788 | $response['newname'] = $new_name; |