Changeset 3369336
- Timestamp:
- 09/28/2025 09:29:12 PM (6 months ago)
- Location:
- mihdan-no-external-links
- Files:
-
- 2 added
- 52 edited
- 1 copied
-
tags/5.1.6 (copied) (copied from mihdan-no-external-links/trunk)
-
tags/5.1.6/admin/Admin.php (modified) (23 diffs)
-
tags/5.1.6/admin/LogTable.php (modified) (4 diffs)
-
tags/5.1.6/admin/MaskTable.php (modified) (2 diffs)
-
tags/5.1.6/admin/SiteHealth.php (modified) (2 diffs)
-
tags/5.1.6/includes/Main.php (modified) (5 diffs)
-
tags/5.1.6/mihdan-no-external-links.php (modified) (2 diffs)
-
tags/5.1.6/readme.txt (modified) (2 diffs)
-
tags/5.1.6/vendor/composer/installed.json (modified) (3 diffs)
-
tags/5.1.6/vendor/composer/installed.php (modified) (3 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/BACKERS.md (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php (modified) (2 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php (modified) (2 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php (added)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php (modified) (3 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php (modified) (3 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php (modified) (3 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/File/X509.php (modified) (5 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php (modified) (2 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php (modified) (2 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php (modified) (1 diff)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php (modified) (2 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php (modified) (14 diffs)
-
tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php (modified) (55 diffs)
-
trunk/admin/Admin.php (modified) (23 diffs)
-
trunk/admin/LogTable.php (modified) (4 diffs)
-
trunk/admin/MaskTable.php (modified) (2 diffs)
-
trunk/admin/SiteHealth.php (modified) (2 diffs)
-
trunk/includes/Main.php (modified) (5 diffs)
-
trunk/mihdan-no-external-links.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/vendor/composer/installed.json (modified) (3 diffs)
-
trunk/vendor/composer/installed.php (modified) (3 diffs)
-
trunk/vendor/phpseclib/phpseclib/BACKERS.md (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php (modified) (2 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php (modified) (2 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/EC/Formats/Signature/IEEE.php (added)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php (modified) (3 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php (modified) (3 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php (modified) (3 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/File/X509.php (modified) (5 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php (modified) (2 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php (modified) (2 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php (modified) (1 diff)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php (modified) (2 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php (modified) (14 diffs)
-
trunk/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php (modified) (55 diffs)
Legend:
- Unmodified
- Added
- Removed
-
mihdan-no-external-links/tags/5.1.6/admin/Admin.php
r3096422 r3369336 155 155 * 156 156 * @since 4.0.0 157 */ 158 public function enqueue_scripts( $hook ): void { 157 * 158 * @param string $hook The current admin page. 159 */ 160 public function enqueue_scripts( string $hook ): void { 159 161 wp_enqueue_script( 160 162 $this->plugin_name, … … 436 438 register_setting( 437 439 $this->plugin_name . '-settings', 438 $this->options_prefix . 'masking_type' 440 $this->options_prefix . 'masking_type', 441 [ 442 'sanitize_callback' => 'sanitize_text_field', 443 ] 439 444 ); 440 445 441 446 register_setting( 442 447 $this->plugin_name . '-settings', 443 $this->options_prefix . 'redirect_time' 448 $this->options_prefix . 'redirect_time', 449 [ 450 'sanitize_callback' => 'sanitize_text_field', 451 ] 444 452 ); 445 453 … … 455 463 register_setting( 456 464 $this->plugin_name . '-settings', 457 $this->options_prefix . 'mask_links' 465 $this->options_prefix . 'mask_links', 466 [ 467 'sanitize_callback' => 'sanitize_text_field', 468 ] 458 469 ); 459 470 460 471 register_setting( 461 472 $this->plugin_name . '-settings', 462 $this->options_prefix . 'mask_posts_pages' 473 $this->options_prefix . 'mask_posts_pages', 474 [ 475 'sanitize_callback' => 'intval', 476 ] 463 477 ); 464 478 465 479 register_setting( 466 480 $this->plugin_name . '-settings', 467 $this->options_prefix . 'mask_comments' 481 $this->options_prefix . 'mask_comments', 482 [ 483 'sanitize_callback' => 'intval', 484 ] 468 485 ); 469 486 470 487 register_setting( 471 488 $this->plugin_name . '-settings', 472 $this->options_prefix . 'mask_comment_author' 489 $this->options_prefix . 'mask_comment_author', 490 [ 491 'sanitize_callback' => 'intval', 492 ] 473 493 ); 474 494 475 495 register_setting( 476 496 $this->plugin_name . '-settings', 477 $this->options_prefix . 'mask_rss' 497 $this->options_prefix . 'mask_rss', 498 [ 499 'sanitize_callback' => 'intval', 500 ] 478 501 ); 479 502 480 503 register_setting( 481 504 $this->plugin_name . '-settings', 482 $this->options_prefix . 'mask_rss_comments' 505 $this->options_prefix . 'mask_rss_comments', 506 [ 507 'sanitize_callback' => 'intval', 508 ] 483 509 ); 484 510 … … 494 520 register_setting( 495 521 $this->plugin_name . '-settings', 496 $this->options_prefix . 'nofollow' 522 $this->options_prefix . 'nofollow', 523 [ 524 'sanitize_callback' => 'intval', 525 ] 497 526 ); 498 527 499 528 register_setting( 500 529 $this->plugin_name . '-settings', 501 $this->options_prefix . 'target_blank' 530 $this->options_prefix . 'target_blank', 531 [ 532 'sanitize_callback' => 'intval', 533 ] 502 534 ); 503 535 504 536 register_setting( 505 537 $this->plugin_name . '-settings', 506 $this->options_prefix . 'noindex_tag' 538 $this->options_prefix . 'noindex_tag', 539 [ 540 'sanitize_callback' => 'intval', 541 ] 507 542 ); 508 543 509 544 register_setting( 510 545 $this->plugin_name . '-settings', 511 $this->options_prefix . 'noindex_comment' 546 $this->options_prefix . 'noindex_comment', 547 [ 548 'sanitize_callback' => 'intval', 549 ] 512 550 ); 513 551 … … 528 566 register_setting( 529 567 $this->plugin_name . '-settings-seo-hide', 530 $this->options_prefix . 'seo_hide' 568 $this->options_prefix . 'seo_hide', 569 [ 570 'sanitize_callback' => 'intval', 571 ] 531 572 ); 532 573 … … 565 606 register_setting( 566 607 $this->plugin_name . '-settings-seo-hide', 567 $this->options_prefix . 'seo_hide_include_list' 608 $this->options_prefix . 'seo_hide_include_list', 609 [ 610 'sanitize_callback' => 'wp_kses_post', 611 ] 568 612 ); 569 613 … … 585 629 register_setting( 586 630 $this->plugin_name . '-settings-seo-hide', 587 $this->options_prefix . 'seo_hide_mode' 631 $this->options_prefix . 'seo_hide_mode', 632 [ 633 'sanitize_callback' => 'sanitize_text_field', 634 ] 588 635 ); 589 636 … … 607 654 register_setting( 608 655 $this->plugin_name . '-settings-seo-hide', 609 $this->options_prefix . 'seo_hide_exclude_list' 656 $this->options_prefix . 'seo_hide_exclude_list', 657 [ 658 'sanitize_callback' => [ $this, 'wp_kses_post' ], 659 ] 610 660 ); 611 661 … … 621 671 register_setting( 622 672 $this->plugin_name . '-settings-advanced', 623 $this->options_prefix . 'logging' 673 $this->options_prefix . 'logging', 674 [ 675 'sanitize_callback' => 'intval', 676 ] 624 677 ); 625 678 626 679 register_setting( 627 680 $this->plugin_name . '-settings-advanced', 628 $this->options_prefix . 'log_duration' 681 $this->options_prefix . 'log_duration', 682 [ 683 'sanitize_callback' => 'intval', 684 ] 629 685 ); 630 686 … … 639 695 register_setting( 640 696 $this->plugin_name . '-settings-advanced', 641 $this->options_prefix . 'anonymize_links' 697 $this->options_prefix . 'anonymize_links', 698 [ 699 'sanitize_callback' => 'intval', 700 ] 642 701 ); 643 702 644 703 register_setting( 645 704 $this->plugin_name . '-settings-advanced', 646 $this->options_prefix . 'anonymous_link_provider' 705 $this->options_prefix . 'anonymous_link_provider', 706 [ 707 'sanitize_callback' => 'sanitize_text_field', 708 ] 647 709 ); 648 710 … … 657 719 register_setting( 658 720 $this->plugin_name . '-settings-advanced', 659 $this->options_prefix . 'bot_targeting' 721 $this->options_prefix . 'bot_targeting', 722 [ 723 'sanitize_callback' => 'sanitize_text_field', 724 ] 660 725 ); 661 726 662 727 register_setting( 663 728 $this->plugin_name . '-settings-advanced', 664 $this->options_prefix . 'bots_selector' 729 $this->options_prefix . 'bots_selector', 730 [ 731 'sanitize_callback' => [ $this, 'sanitize_text_array_deep' ], 732 ] 665 733 ); 666 734 … … 676 744 register_setting( 677 745 $this->plugin_name . '-settings-advanced', 678 $this->options_prefix . 'check_referrer' 746 $this->options_prefix . 'check_referrer', 747 [ 748 'sanitize_callback' => 'intval', 749 ] 679 750 ); 680 751 681 752 register_setting( 682 753 $this->plugin_name . '-settings-advanced', 683 $this->options_prefix . 'remove_all_links' 754 $this->options_prefix . 'remove_all_links', 755 [ 756 'sanitize_callback' => 'intval', 757 ] 684 758 ); 685 759 686 760 register_setting( 687 761 $this->plugin_name . '-settings-advanced', 688 $this->options_prefix . 'links_to_text' 762 $this->options_prefix . 'links_to_text', 763 [ 764 'sanitize_callback' => 'intval', 765 ] 689 766 ); 690 767 … … 700 777 register_setting( 701 778 $this->plugin_name . '-settings-advanced', 702 $this->options_prefix . 'debug_mode' 779 $this->options_prefix . 'debug_mode', 780 [ 781 'sanitize_callback' => 'intval', 782 ] 703 783 ); 704 784 … … 731 811 register_setting( 732 812 $this->plugin_name . '-settings-links', 733 $this->options_prefix . 'link_structure' 813 $this->options_prefix . 'link_structure', 814 [ 815 'sanitize_callback' => 'sanitize_text_field', 816 ] 734 817 ); 735 818 … … 801 884 register_setting( 802 885 $this->plugin_name . '-settings-links', 803 $this->options_prefix . 'link_encoding' 886 $this->options_prefix . 'link_encoding', 887 [ 888 'sanitize_callback' => 'sanitize_text_field', 889 ] 804 890 ); 805 891 … … 876 962 register_setting( 877 963 $this->plugin_name . '-settings-links', 878 $this->options_prefix . 'link_shortening' 964 $this->options_prefix . 'link_shortening', 965 [ 966 'sanitize_callback' => 'sanitize_text_field', 967 ] 879 968 ); 880 969 … … 962 1051 register_setting( 963 1052 $this->plugin_name . '-settings-advanced', 964 $this->options_prefix . 'redirect_message' 1053 $this->options_prefix . 'redirect_message', 1054 [ 1055 'sanitize_callback' => 'wp_kses_post', 1056 ] 965 1057 ); 966 1058 … … 976 1068 register_setting( 977 1069 $this->plugin_name . '-settings-advanced', 978 $this->options_prefix . 'redirect_page' 1070 $this->options_prefix . 'redirect_page', 1071 [ 1072 'sanitize_callback' => 'intval', 1073 ] 979 1074 ); 980 1075 … … 995 1090 register_setting( 996 1091 $this->plugin_name . '-settings-include-exclude', 997 $this->options_prefix . 'inclusion_list' 1092 $this->options_prefix . 'inclusion_list', 1093 [ 1094 'sanitize_callback' => 'wp_kses_post', 1095 ] 998 1096 ); 999 1097 … … 1014 1112 register_setting( 1015 1113 $this->plugin_name . '-settings-include-exclude', 1016 $this->options_prefix . 'exclusion_list' 1114 $this->options_prefix . 'exclusion_list', 1115 [ 1116 'sanitize_callback' => 'wp_kses_post', 1117 ] 1017 1118 ); 1018 1119 … … 1033 1134 register_setting( 1034 1135 $this->plugin_name . '-settings-include-exclude', 1035 $this->options_prefix . 'skip_auth' 1136 $this->options_prefix . 'skip_auth', 1137 [ 1138 'sanitize_callback' => 'intval', 1139 ] 1036 1140 ); 1037 1141 … … 1052 1156 register_setting( 1053 1157 $this->plugin_name . '-settings-include-exclude', 1054 $this->options_prefix . 'skip_follow' 1158 $this->options_prefix . 'skip_follow', 1159 [ 1160 'sanitize_callback' => 'intval', 1161 ] 1055 1162 ); 1056 1163 … … 2250 2357 return $actions; 2251 2358 } 2359 2360 /** 2361 * Sanitizes an array of strings from user input or from the database. 2362 * 2363 * @param array $array Array to sanitize. 2364 * 2365 * @return array 2366 */ 2367 public function sanitize_text_array_deep( array $array ): array { 2368 return array_map( 'sanitize_text_field', $array ); 2369 } 2252 2370 } -
mihdan-no-external-links/tags/5.1.6/admin/LogTable.php
r2783436 r3369336 99 99 'delete' => sprintf( 100 100 '<a href="?page=%s&action=%s&log=%s&_wpnonce=%s">Delete</a>', 101 isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1,101 isset( $_REQUEST['page'] ) ? sanitize_text_field( $_REQUEST['page'] ) : 1, 102 102 'delete', 103 103 absint( $item['id'] ), … … 294 294 : ''; 295 295 296 if ( ! empty( $nonce ) && ! wp_verify_nonce( $nonce, $this->options_prefix . 'delete_log' ) ) { 297 wp_die( esc_html__( 'Are you sure you want to do this?' ) ); 298 } 299 300 if ( 'delete' === $this->current_action() ) { 296 if ( 'delete' === $this->current_action() && wp_verify_nonce( $nonce, $this->options_prefix . 'delete_log' ) ) { 301 297 302 298 $delete_count = 0; … … 312 308 } 313 309 314 if ( 315 ( isset( $_POST['action'] ) && 'bulk-delete' === $_POST['action'] ) || 316 ( isset( $_POST['action2'] ) && 'bulk-delete' === $_POST['action2'] ) 317 ) { 310 if ( 'bulk-delete' === $this->current_action() && wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { 318 311 319 312 $delete_ids = ! empty( $_POST['bulk-delete'] ) 320 ? array_map( ' sanitize_text_field', wp_unslash( $_POST['bulk-delete'] ) )313 ? array_map( 'intval', wp_unslash( $_POST['bulk-delete'] ) ) 321 314 : []; 322 315 … … 335 328 exit; 336 329 } 337 338 330 } 339 331 -
mihdan-no-external-links/tags/5.1.6/admin/MaskTable.php
r2783436 r3369336 241 241 'cb' => '<input type="checkbox" />', 242 242 'title' => __( 'URL', $this->plugin_name ), 243 'mask' => __( 'Mask' ),244 'numeric' => __( 'Numeric' ),243 'mask' => __( 'Mask', $this->plugin_name ), 244 'numeric' => __( 'Numeric', $this->plugin_name ), 245 245 ]; 246 246 … … 317 317 318 318 if ( ! empty( $nonce ) && ! wp_verify_nonce( $nonce, $this->options_prefix . 'delete_mask' ) ) { 319 wp_die( esc_html__( 'Are you sure you want to do this?' ) );319 wp_die( esc_html__( 'Are you sure you want to do this?', $this->plugin_name ) ); 320 320 } 321 321 -
mihdan-no-external-links/tags/5.1.6/admin/SiteHealth.php
r2782193 r3369336 75 75 'status' => 'good', 76 76 'badge' => [ 77 'label' => __( 'Performance' ),77 'label' => __( 'Performance', $this->plugin_name ), 78 78 'color' => 'blue', 79 79 ], … … 86 86 'status' => 'critical', 87 87 'badge' => [ 88 'label' => __( 'Performance' ),88 'label' => __( 'Performance', $this->plugin_name ), 89 89 'color' => 'red', 90 90 ], -
mihdan-no-external-links/tags/5.1.6/includes/Main.php
r3007441 r3369336 116 116 $this->define_admin_hooks(); 117 117 $this->define_public_hooks(); 118 119 118 } 120 119 … … 264 263 $plugin_i18n = new I18n( $this->get_plugin_name() ); 265 264 266 $this->loader->add_action( ' plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );265 $this->loader->add_action( 'init', $plugin_i18n, 'load_plugin_textdomain' ); 267 266 268 267 } … … 342 341 'skip_follow' => false, 343 342 'redirect_page' => 0, 344 'redirect_message' => __( 345 'You will be redirected in 3 seconds. If your browser does not automatically redirect you, please <a href="%linkurl%">click here</a>.', 346 $this->plugin_name 347 ), 343 'redirect_message' => 'You will be redirected in 3 seconds. If your browser does not automatically redirect you, please <a href="%linkurl%">click here</a>.', 348 344 'output_buffer' => $output_buffer, 349 345 ); 350 346 351 347 $this->options = $this->validate_options( $options ); 352 353 348 } 354 349 … … 547 542 548 543 $this->loader->add_filter( 'set-screen-option', $this->admin, 'mask_page_set_screen_options', null, 3 ); 549 $hook_name = vsprintf( 550 'load-%s_page_%s-masks', 551 [ strtolower( sanitize_file_name( __( 'No External Links', $this->plugin_name ) ) ), $this->get_plugin_name() ] 552 ); 544 545 $hook_name = sprintf( 'load-no-external-links_page_%s-masks', $this->get_plugin_name() ); 553 546 554 547 $this->loader->add_action( $hook_name, $this->admin, 'mask_page_screen_options' ); … … 556 549 $this->loader->add_filter( 'set-screen-option', $this->admin, 'log_page_set_screen_options', null, 3 ); 557 550 558 $hook_name = vsprintf( 559 'load-%s_page_%s-logs', 560 [ strtolower( sanitize_file_name( __( 'No External Links', $this->plugin_name ) ) ), $this->get_plugin_name() ] 561 ); 551 $hook_name = sprintf( 'load-no-external-links_page_%s-logs', $this->get_plugin_name() ); 562 552 563 553 $this->loader->add_action( $hook_name, $this->admin, 'log_page_screen_options' ); -
mihdan-no-external-links/tags/5.1.6/mihdan-no-external-links.php
r3369209 r3369336 11 11 * Plugin URI: https://www.kobzarev.com/projects/no-external-links/ 12 12 * Description: Convert external links into internal links, site wide or post/page specific. Add NoFollow, Click logging, and more... 13 * Version: 5.1. 513 * Version: 5.1.6 14 14 * Author: Mikhail Kobzarev 15 15 * Author URI: https://www.kobzarev.com/ … … 29 29 30 30 const MIHDAN_NO_EXTERNAL_LINKS_DIR = __DIR__; 31 const MIHDAN_NO_EXTERNAL_LINKS_VERSION = '5.1. 5';31 const MIHDAN_NO_EXTERNAL_LINKS_VERSION = '5.1.6'; 32 32 const MIHDAN_NO_EXTERNAL_LINKS_SLUG = 'mihdan-no-external-links'; 33 33 -
mihdan-no-external-links/tags/5.1.6/readme.txt
r3369209 r3369336 5 5 Requires at least: 5.7.4 6 6 Tested up to: 6.8 7 Stable tag: 5.1. 57 Stable tag: 5.1.6 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 63 63 64 64 == Changelog == 65 66 = 5.1.6 (28.09.2025) = 67 * Resolved #[39](https://github.com/mihdan/mihdan-no-external-links/issues/39) 68 * Resolved #[40](https://github.com/mihdan/mihdan-no-external-links/issues/40) 69 * The error "Function _load_textdomain_just_in_time was called incorrectly" has been fixed 65 70 66 71 = 5.1.5 (28.09.2025) = -
mihdan-no-external-links/tags/5.1.6/vendor/composer/installed.json
r2900866 r3369336 197 197 { 198 198 "name": "phpseclib/phpseclib", 199 "version": "3.0. 19",200 "version_normalized": "3.0. 19.0",199 "version": "3.0.34", 200 "version_normalized": "3.0.34.0", 201 201 "source": { 202 202 "type": "git", 203 203 "url": "https://github.com/phpseclib/phpseclib.git", 204 "reference": " cc181005cf548bfd8a4896383bb825d859259f95"204 "reference": "56c79f16a6ae17e42089c06a2144467acc35348a" 205 205 }, 206 206 "dist": { 207 207 "type": "zip", 208 "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ cc181005cf548bfd8a4896383bb825d859259f95",209 "reference": " cc181005cf548bfd8a4896383bb825d859259f95",208 "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a", 209 "reference": "56c79f16a6ae17e42089c06a2144467acc35348a", 210 210 "shasum": "" 211 211 }, … … 225 225 "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." 226 226 }, 227 "time": "2023- 03-05T17:13:09+00:00",227 "time": "2023-11-27T11:13:31+00:00", 228 228 "type": "library", 229 229 "installation-source": "dist", … … 290 290 "support": { 291 291 "issues": "https://github.com/phpseclib/phpseclib/issues", 292 "source": "https://github.com/phpseclib/phpseclib/tree/3.0. 19"292 "source": "https://github.com/phpseclib/phpseclib/tree/3.0.34" 293 293 }, 294 294 "funding": [ -
mihdan-no-external-links/tags/5.1.6/vendor/composer/installed.php
r3369209 r3369336 2 2 'root' => array( 3 3 'name' => 'mihdan/mihdan-no-external-links', 4 'pretty_version' => '5.1. 5',5 'version' => '5.1. 5.0',6 'reference' => ' a6d16d367a6ceb1bbe468219125173021c653f99',4 'pretty_version' => '5.1.6', 5 'version' => '5.1.6.0', 6 'reference' => 'eda3a90f6434e53d5238700433c3b2aa66195b62', 7 7 'type' => 'wordpress-plugin', 8 8 'install_path' => __DIR__ . '/../../', … … 12 12 'versions' => array( 13 13 'mihdan/mihdan-no-external-links' => array( 14 'pretty_version' => '5.1. 5',15 'version' => '5.1. 5.0',16 'reference' => ' a6d16d367a6ceb1bbe468219125173021c653f99',14 'pretty_version' => '5.1.6', 15 'version' => '5.1.6.0', 16 'reference' => 'eda3a90f6434e53d5238700433c3b2aa66195b62', 17 17 'type' => 'wordpress-plugin', 18 18 'install_path' => __DIR__ . '/../../', … … 48 48 ), 49 49 'phpseclib/phpseclib' => array( 50 'pretty_version' => '3.0. 19',51 'version' => '3.0. 19.0',52 'reference' => ' cc181005cf548bfd8a4896383bb825d859259f95',50 'pretty_version' => '3.0.34', 51 'version' => '3.0.34.0', 52 'reference' => '56c79f16a6ae17e42089c06a2144467acc35348a', 53 53 'type' => 'library', 54 54 'install_path' => __DIR__ . '/../phpseclib/phpseclib', -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/BACKERS.md
r2900866 r3369336 14 14 - Tharyrok 15 15 - [cjhaas](https://github.com/cjhaas) 16 - [istiak-tridip](https://github.com/istiak-tridip) -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php
r2900866 r3369336 131 131 * @param string $key 132 132 * @param string $password optional 133 * @return AsymmetricKey133 * @return \phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey 134 134 */ 135 135 public static function load($key, $password = false) -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php
r2900866 r3369336 142 142 case 'RC2': 143 143 $cipher = new RC2('cbc'); 144 $cipher->setKeyLength(64); 144 145 break; 145 146 case '3-KeyTripleDES': … … 219 220 switch ($algo) { 220 221 case 'desCBC': 221 $cipher = new TripleDES('cbc');222 $cipher = new DES('cbc'); 222 223 break; 223 224 case 'des-EDE3-CBC': -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php
r2900866 r3369336 669 669 // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster 670 670 case (PHP_OS & "\xDF\xDF\xDF") === 'WIN': 671 case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':671 case !(is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") == 'ARM'): 672 672 case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: 673 673 self::$use_reg_intval = true; 674 674 break; 675 case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM':675 case is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") == 'ARM': 676 676 switch (true) { 677 677 /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors: … … 918 918 * @param string $password 919 919 * @param string $method 920 * @param string[]...$func_args920 * @param int|string ...$func_args 921 921 * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length 922 922 * @throws \RuntimeException if bcrypt is being used and a salt isn't provided -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
r2900866 r3369336 842 842 self::ENCRYPTION_NONE 843 843 ]; 844 $ numSelected= 0;844 $encryptedCount = 0; 845 845 $selected = 0; 846 846 foreach ($masks as $mask) { 847 847 if ($padding & $mask) { 848 848 $selected = $mask; 849 $ numSelected++;849 $encryptedCount++; 850 850 } 851 851 } 852 if ($ numSelected> 1) {852 if ($encryptedCount > 1) { 853 853 throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected'); 854 854 } … … 860 860 self::SIGNATURE_PKCS1 861 861 ]; 862 $ numSelected= 0;862 $signatureCount = 0; 863 863 $selected = 0; 864 864 foreach ($masks as $mask) { 865 865 if ($padding & $mask) { 866 866 $selected = $mask; 867 $ numSelected++;867 $signatureCount++; 868 868 } 869 869 } 870 if ($ numSelected> 1) {870 if ($signatureCount > 1) { 871 871 throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected'); 872 872 } … … 874 874 875 875 $new = clone $this; 876 $new->encryptionPadding = $encryptionPadding; 877 $new->signaturePadding = $signaturePadding; 876 if ($encryptedCount) { 877 $new->encryptionPadding = $encryptionPadding; 878 } 879 if ($signatureCount) { 880 $new->signaturePadding = $signaturePadding; 881 } 878 882 return $new; 879 883 } -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
r2782199 r3369336 834 834 // Generating encrypt code: 835 835 $init_encrypt .= ' 836 static $tables;837 836 if (empty($tables)) { 838 837 $tables = &$this->getTables(); … … 891 890 // Generating decrypt code: 892 891 $init_decrypt .= ' 893 static $invtables;894 892 if (empty($invtables)) { 895 893 $invtables = &$this->getInvTables(); … … 948 946 $this->inline_crypt = $this->createInlineCryptFunction( 949 947 [ 950 'init_crypt' => ' ',948 'init_crypt' => 'static $tables; static $invtables;', 951 949 'init_encrypt' => $init_encrypt, 952 950 'init_decrypt' => $init_decrypt, -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
r2782199 r3369336 22 22 namespace phpseclib3\File; 23 23 24 use DateTime;25 24 use phpseclib3\Common\Functions\Strings; 26 25 use phpseclib3\File\ASN1\Element; … … 206 205 } 207 206 208 return [ self::decode_ber($encoded)];207 return [$decoded]; 209 208 } 210 209 … … 1404 1403 } 1405 1404 break; 1406 case ($c & 0x80000000) != 0:1405 case ($c & (PHP_INT_SIZE == 8 ? 0x80000000 : (1 << 31))) != 0: 1407 1406 return false; 1408 1407 case $c >= 0x04000000: -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/File/X509.php
r2900866 r3369336 165 165 * @var array 166 166 */ 167 private $CAs ;167 private $CAs = []; 168 168 169 169 /** … … 316 316 'id-at-role' => '2.5.4.72', 317 317 'id-at-postalAddress' => '2.5.4.16', 318 'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3', 319 'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2', 320 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1', 321 'id-at-businessCategory' => '2.5.4.15', 318 322 319 323 //'id-domainComponent' => '0.9.2342.19200300.100.1.25', … … 1039 1043 foreach ($names as $name) { 1040 1044 foreach ($name as $key => $value) { 1041 $value = str_replace(['.', '*'], ['\.', '[^.]*'], $value); 1045 $value = preg_quote($value); 1046 $value = str_replace('\*', '[^.]*', $value); 1042 1047 switch ($key) { 1043 1048 case 'dNSName': … … 1539 1544 { 1540 1545 switch (strtolower($propName)) { 1546 case 'jurisdictionofincorporationcountryname': 1547 case 'jurisdictioncountryname': 1548 case 'jurisdictionc': 1549 return 'jurisdictionOfIncorporationCountryName'; 1550 case 'jurisdictionofincorporationstateorprovincename': 1551 case 'jurisdictionstateorprovincename': 1552 case 'jurisdictionst': 1553 return 'jurisdictionOfIncorporationStateOrProvinceName'; 1554 case 'jurisdictionlocalityname': 1555 case 'jurisdictionl': 1556 return 'jurisdictionLocalityName'; 1557 case 'id-at-businesscategory': 1558 case 'businesscategory': 1559 return 'id-at-businessCategory'; 1541 1560 case 'id-at-countryname': 1542 1561 case 'countryname': … … 2031 2050 return false; 2032 2051 } 2033 if (empty($this->CAs)) {2034 return $chain;2035 }2036 2052 while (true) { 2037 2053 $currentCert = $chain[count($chain) - 1]; -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
r2900866 r3369336 101 101 self::$mainEngine = $fqmain; 102 102 103 if (!in_array('Default', $modexps)) {104 $modexps[] = 'DefaultEngine';105 }106 107 103 $found = false; 108 104 foreach ($modexps as $modexp) { … … 141 137 if (!isset(self::$mainEngine)) { 142 138 $engines = [ 143 ['GMP' ],139 ['GMP', ['DefaultEngine']], 144 140 ['PHP64', ['OpenSSL']], 145 141 ['BCMath', ['OpenSSL']], 146 ['PHP32', ['OpenSSL']] 142 ['PHP32', ['OpenSSL']], 143 ['PHP64', ['DefaultEngine']], 144 ['PHP32', ['DefaultEngine']] 147 145 ]; 146 148 147 foreach ($engines as $engine) { 149 148 try { 150 self::setEngine($engine[0], isset($engine[1]) ? $engine[1] : []);151 break;149 self::setEngine($engine[0], $engine[1]); 150 return; 152 151 } catch (\Exception $e) { 153 152 } 154 153 } 154 155 throw new \UnexpectedValueException('No valid BigInteger found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available so either disable JIT or install GMP / BCMath'); 155 156 } 156 157 } -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php
r2900866 r3369336 645 645 } 646 646 647 if ($this->compare($n) > 0) { 648 list(, $temp) = $this->divide($n); 649 return $temp->powModInner($e, $n); 650 } 651 647 652 return $this->powModInner($e, $n); 648 653 } -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php
r2782199 r3369336 1327 1327 return array_reverse($vals); 1328 1328 } 1329 1330 /** 1331 * @return bool 1332 */ 1333 protected static function testJITOnWindows() 1334 { 1335 // see https://github.com/php/php-src/issues/11917 1336 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) { 1337 $status = opcache_get_status(); 1338 if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) { 1339 return true; 1340 } 1341 } 1342 return false; 1343 } 1329 1344 } -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php
r2782199 r3369336 81 81 $step = count($vals) & 3; 82 82 if ($step) { 83 $digit = floor($digit / pow(2, 2 * $step));83 $digit = (int) floor($digit / pow(2, 2 * $step)); 84 84 } 85 85 if ($step != 3) { 86 $digit &= static::MAX_DIGIT;86 $digit = (int) fmod($digit, static::BASE_FULL); 87 87 $i++; 88 88 } … … 103 103 public static function isValidEngine() 104 104 { 105 return PHP_INT_SIZE >= 4 ;105 return PHP_INT_SIZE >= 4 && !self::testJITOnWindows(); 106 106 } 107 107 -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php
r2782199 r3369336 104 104 public static function isValidEngine() 105 105 { 106 return PHP_INT_SIZE >= 8 ;106 return PHP_INT_SIZE >= 8 && !self::testJITOnWindows(); 107 107 } 108 108 -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php
r2782199 r3369336 49 49 { 50 50 $m = array_shift($indices); 51 if ($m > 571) { 52 /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines 53 altho theoretically there may be legit reasons to use binary finite fields with larger degrees 54 imposing a limit on the maximum size is both reasonable and precedented. in particular, 55 http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says 56 "implementations SHOULD check that the packet length is reasonable in order for the implementation to 57 avoid denial of service and/or buffer overflow attacks" */ 58 throw new \OutOfBoundsException('Degrees larger than 571 are not supported'); 59 } 51 60 $val = str_repeat('0', $m) . '1'; 52 61 foreach ($indices as $index) { -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php
r2900866 r3369336 264 264 265 265 while (!$t->equals($one)) { 266 for ($i = =clone $one; $i->compare($m) < 0; $i = $i->add($one)) {266 for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) { 267 267 if ($t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) { 268 268 break; … … 313 313 public function toBytes() 314 314 { 315 $length = static::$modulo[$this->instanceID]->getLengthInBytes(); 316 return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); 315 if (isset(static::$modulo[$this->instanceID])) { 316 $length = static::$modulo[$this->instanceID]->getLengthInBytes(); 317 return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); 318 } 319 return $this->value->toBytes(); 317 320 } 318 321 -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
r2900866 r3369336 94 94 * @access private 95 95 */ 96 private $packet_types = [];96 private static $packet_types = []; 97 97 98 98 /** … … 103 103 * @access private 104 104 */ 105 private $status_codes = [];105 private static $status_codes = []; 106 106 107 107 /** @var array<int, string> */ 108 private $attributes;108 private static $attributes; 109 109 110 110 /** @var array<int, string> */ 111 private $open_flags;111 private static $open_flags; 112 112 113 113 /** @var array<int, string> */ 114 private $open_flags5;114 private static $open_flags5; 115 115 116 116 /** @var array<int, string> */ 117 private $file_types;117 private static $file_types; 118 118 119 119 /** … … 351 351 * Connects to an SFTP server 352 352 * 353 * @param string $host 353 * $host can either be a string, representing the host, or a stream resource. 354 * 355 * @param mixed $host 354 356 * @param int $port 355 357 * @param int $timeout … … 361 363 $this->max_sftp_packet = 1 << 15; 362 364 363 $this->packet_types = [ 364 1 => 'NET_SFTP_INIT', 365 2 => 'NET_SFTP_VERSION', 366 3 => 'NET_SFTP_OPEN', 367 4 => 'NET_SFTP_CLOSE', 368 5 => 'NET_SFTP_READ', 369 6 => 'NET_SFTP_WRITE', 370 7 => 'NET_SFTP_LSTAT', 371 9 => 'NET_SFTP_SETSTAT', 372 10 => 'NET_SFTP_FSETSTAT', 373 11 => 'NET_SFTP_OPENDIR', 374 12 => 'NET_SFTP_READDIR', 375 13 => 'NET_SFTP_REMOVE', 376 14 => 'NET_SFTP_MKDIR', 377 15 => 'NET_SFTP_RMDIR', 378 16 => 'NET_SFTP_REALPATH', 379 17 => 'NET_SFTP_STAT', 380 18 => 'NET_SFTP_RENAME', 381 19 => 'NET_SFTP_READLINK', 382 20 => 'NET_SFTP_SYMLINK', 383 21 => 'NET_SFTP_LINK', 384 385 101 => 'NET_SFTP_STATUS', 386 102 => 'NET_SFTP_HANDLE', 387 103 => 'NET_SFTP_DATA', 388 104 => 'NET_SFTP_NAME', 389 105 => 'NET_SFTP_ATTRS', 390 391 200 => 'NET_SFTP_EXTENDED' 392 ]; 393 $this->status_codes = [ 394 0 => 'NET_SFTP_STATUS_OK', 395 1 => 'NET_SFTP_STATUS_EOF', 396 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 397 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 398 4 => 'NET_SFTP_STATUS_FAILURE', 399 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 400 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 401 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 402 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 403 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 404 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 405 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 406 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 407 13 => 'NET_SFTP_STATUS_NO_MEDIA', 408 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 409 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 410 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 411 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 412 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 413 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 414 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 415 21 => 'NET_SFTP_STATUS_LINK_LOOP', 416 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 417 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 418 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 419 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 420 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 421 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 422 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 423 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 424 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 425 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' 426 ]; 427 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 428 // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why 429 $this->attributes = [ 430 0x00000001 => 'NET_SFTP_ATTR_SIZE', 431 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 432 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 433 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 434 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 435 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ 436 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 437 0x00000040 => 'NET_SFTP_ATTR_ACL', 438 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 439 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ 440 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ 441 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 442 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 443 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 444 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 445 0x00008000 => 'NET_SFTP_ATTR_CTIME', 446 // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers 447 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in 448 // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. 449 // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. 450 (PHP_INT_SIZE == 4 ? -1 : 0xFFFFFFFF) => 'NET_SFTP_ATTR_EXTENDED' 451 ]; 452 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 453 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name 454 // the array for that $this->open5_flags and similarly alter the constant names. 455 $this->open_flags = [ 456 0x00000001 => 'NET_SFTP_OPEN_READ', 457 0x00000002 => 'NET_SFTP_OPEN_WRITE', 458 0x00000004 => 'NET_SFTP_OPEN_APPEND', 459 0x00000008 => 'NET_SFTP_OPEN_CREATE', 460 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 461 0x00000020 => 'NET_SFTP_OPEN_EXCL', 462 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 463 ]; 464 // SFTPv5+ changed the flags up: 465 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 466 $this->open_flags5 = [ 467 // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened 468 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 469 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 470 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 471 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 472 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', 473 // the rest of the flags are not supported 474 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" 475 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 476 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 477 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 478 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 479 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 480 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 481 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 482 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 483 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 484 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 485 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 486 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', 487 ]; 488 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 489 // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation 490 $this->file_types = [ 491 1 => 'NET_SFTP_TYPE_REGULAR', 492 2 => 'NET_SFTP_TYPE_DIRECTORY', 493 3 => 'NET_SFTP_TYPE_SYMLINK', 494 4 => 'NET_SFTP_TYPE_SPECIAL', 495 5 => 'NET_SFTP_TYPE_UNKNOWN', 496 // the following types were first defined for use in SFTPv5+ 497 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 498 6 => 'NET_SFTP_TYPE_SOCKET', 499 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 500 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 501 9 => 'NET_SFTP_TYPE_FIFO' 502 ]; 503 $this->define_array( 504 $this->packet_types, 505 $this->status_codes, 506 $this->attributes, 507 $this->open_flags, 508 $this->open_flags5, 509 $this->file_types 510 ); 365 if (empty(self::$packet_types)) { 366 self::$packet_types = [ 367 1 => 'NET_SFTP_INIT', 368 2 => 'NET_SFTP_VERSION', 369 3 => 'NET_SFTP_OPEN', 370 4 => 'NET_SFTP_CLOSE', 371 5 => 'NET_SFTP_READ', 372 6 => 'NET_SFTP_WRITE', 373 7 => 'NET_SFTP_LSTAT', 374 9 => 'NET_SFTP_SETSTAT', 375 10 => 'NET_SFTP_FSETSTAT', 376 11 => 'NET_SFTP_OPENDIR', 377 12 => 'NET_SFTP_READDIR', 378 13 => 'NET_SFTP_REMOVE', 379 14 => 'NET_SFTP_MKDIR', 380 15 => 'NET_SFTP_RMDIR', 381 16 => 'NET_SFTP_REALPATH', 382 17 => 'NET_SFTP_STAT', 383 18 => 'NET_SFTP_RENAME', 384 19 => 'NET_SFTP_READLINK', 385 20 => 'NET_SFTP_SYMLINK', 386 21 => 'NET_SFTP_LINK', 387 388 101 => 'NET_SFTP_STATUS', 389 102 => 'NET_SFTP_HANDLE', 390 103 => 'NET_SFTP_DATA', 391 104 => 'NET_SFTP_NAME', 392 105 => 'NET_SFTP_ATTRS', 393 394 200 => 'NET_SFTP_EXTENDED' 395 ]; 396 self::$status_codes = [ 397 0 => 'NET_SFTP_STATUS_OK', 398 1 => 'NET_SFTP_STATUS_EOF', 399 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 400 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 401 4 => 'NET_SFTP_STATUS_FAILURE', 402 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 403 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 404 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 405 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 406 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 407 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 408 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 409 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 410 13 => 'NET_SFTP_STATUS_NO_MEDIA', 411 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 412 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 413 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 414 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 415 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 416 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 417 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 418 21 => 'NET_SFTP_STATUS_LINK_LOOP', 419 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 420 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 421 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 422 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 423 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 424 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 425 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 426 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 427 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 428 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' 429 ]; 430 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 431 // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why 432 self::$attributes = [ 433 0x00000001 => 'NET_SFTP_ATTR_SIZE', 434 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 435 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 436 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 437 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 438 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ 439 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 440 0x00000040 => 'NET_SFTP_ATTR_ACL', 441 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 442 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ 443 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ 444 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 445 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 446 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 447 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 448 0x00008000 => 'NET_SFTP_ATTR_CTIME', 449 // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers 450 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in 451 // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. 452 // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. 453 (PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED' 454 ]; 455 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 456 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name 457 // the array for that $this->open5_flags and similarly alter the constant names. 458 self::$open_flags = [ 459 0x00000001 => 'NET_SFTP_OPEN_READ', 460 0x00000002 => 'NET_SFTP_OPEN_WRITE', 461 0x00000004 => 'NET_SFTP_OPEN_APPEND', 462 0x00000008 => 'NET_SFTP_OPEN_CREATE', 463 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 464 0x00000020 => 'NET_SFTP_OPEN_EXCL', 465 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 466 ]; 467 // SFTPv5+ changed the flags up: 468 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 469 self::$open_flags5 = [ 470 // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened 471 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 472 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 473 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 474 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 475 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', 476 // the rest of the flags are not supported 477 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" 478 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 479 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 480 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 481 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 482 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 483 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 484 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 485 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 486 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 487 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 488 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 489 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', 490 ]; 491 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 492 // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation 493 self::$file_types = [ 494 1 => 'NET_SFTP_TYPE_REGULAR', 495 2 => 'NET_SFTP_TYPE_DIRECTORY', 496 3 => 'NET_SFTP_TYPE_SYMLINK', 497 4 => 'NET_SFTP_TYPE_SPECIAL', 498 5 => 'NET_SFTP_TYPE_UNKNOWN', 499 // the following types were first defined for use in SFTPv5+ 500 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 501 6 => 'NET_SFTP_TYPE_SOCKET', 502 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 503 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 504 9 => 'NET_SFTP_TYPE_FIFO' 505 ]; 506 self::define_array( 507 self::$packet_types, 508 self::$status_codes, 509 self::$attributes, 510 self::$open_flags, 511 self::$open_flags5, 512 self::$file_types 513 ); 514 } 511 515 512 516 if (!defined('NET_SFTP_QUEUE_SIZE')) { … … 544 548 private function partial_init_sftp_connection() 545 549 { 546 $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; 547 548 $packet = Strings::packSSH2( 549 'CsN3', 550 NET_SSH2_MSG_CHANNEL_OPEN, 551 'session', 552 self::CHANNEL, 553 $this->window_size, 554 0x4000 555 ); 556 557 $this->send_binary_packet($packet); 558 559 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; 560 561 $response = $this->get_channel_packet(self::CHANNEL, true); 550 $response = $this->openChannel(self::CHANNEL, true); 562 551 if ($response === true && $this->isTimeout()) { 563 552 return false; … … 816 805 } 817 806 818 $error = $this->status_codes[$status];807 $error = self::$status_codes[$status]; 819 808 820 809 if ($this->version > 2) { … … 2139 2128 if ($start >= 0) { 2140 2129 $offset = $start; 2141 } elseif ($mode & self::RESUME) {2130 } elseif ($mode & (self::RESUME | self::RESUME_START)) { 2142 2131 // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called 2143 2132 $size = $this->stat($remote_file)['size']; … … 2211 2200 fseek($fp, $local_start); 2212 2201 $size -= $local_start; 2202 } elseif ($mode & self::RESUME) { 2203 fseek($fp, $offset); 2204 $size -= $offset; 2213 2205 } 2214 2206 } elseif ($dataCallback) { … … 2498 2490 } 2499 2491 2500 if ($length > 0 && $length <= $offset - $start) {2501 if ($local_file === false) {2502 $content = substr($content, 0, $length);2503 } else {2504 ftruncate($fp, $length + $res_offset);2505 }2506 }2507 2508 2492 if ($fclose_check) { 2509 2493 fclose($fp); … … 2842 2826 2843 2827 /** 2828 * Recursively go through rawlist() output to get the total filesize 2829 * 2830 * @return int 2831 */ 2832 private static function recursiveFilesize(array $files) 2833 { 2834 $size = 0; 2835 foreach ($files as $name => $file) { 2836 if ($name == '.' || $name == '..') { 2837 continue; 2838 } 2839 $size += is_array($file) ? 2840 self::recursiveFilesize($file) : 2841 $file->size; 2842 } 2843 return $size; 2844 } 2845 2846 /** 2844 2847 * Gets file size 2845 2848 * 2846 2849 * @param string $path 2850 * @param bool $recursive 2847 2851 * @return mixed 2848 2852 */ 2849 public function filesize($path) 2850 { 2851 return $this->get_stat_cache_prop($path, 'size'); 2853 public function filesize($path, $recursive = false) 2854 { 2855 return !$recursive || $this->filetype($path) != 'dir' ? 2856 $this->get_stat_cache_prop($path, 'size') : 2857 self::recursiveFilesize($this->rawlist($path, true)); 2852 2858 } 2853 2859 … … 3042 3048 } 3043 3049 3044 foreach ( $this->attributes as $key => $value) {3050 foreach (self::$attributes as $key => $value) { 3045 3051 switch ($flags & $key) { 3046 3052 case NET_SFTP_ATTR_UIDGID: … … 3273 3279 3274 3280 if (defined('NET_SFTP_LOGGING')) { 3275 $packet_type = '-> ' . $this->packet_types[$type] .3281 $packet_type = '-> ' . self::$packet_types[$type] . 3276 3282 ' (' . round($stop - $start, 4) . 's)'; 3277 3283 $this->append_log($packet_type, $data); … … 3377 3383 3378 3384 if (defined('NET_SFTP_LOGGING')) { 3379 $packet_type = '<- ' . $this->packet_types[$this->packet_type] .3385 $packet_type = '<- ' . self::$packet_types[$this->packet_type] . 3380 3386 ' (' . round($stop - $start, 4) . 's)'; 3381 3387 $this->append_log($packet_type, $packet); … … 3421 3427 * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') 3422 3428 * 3423 * @return array|string 3429 * @return array|string|false 3424 3430 */ 3425 3431 public function getSFTPLog() -
mihdan-no-external-links/tags/5.1.6/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
r2900866 r3369336 554 554 * @access private 555 555 */ 556 private $message_numbers = [];556 private static $message_numbers = []; 557 557 558 558 /** … … 563 563 * @access private 564 564 */ 565 private $disconnect_reasons = [];565 private static $disconnect_reasons = []; 566 566 567 567 /** … … 572 572 * @access private 573 573 */ 574 private $channel_open_failure_reasons = [];574 private static $channel_open_failure_reasons = []; 575 575 576 576 /** … … 582 582 * @access private 583 583 */ 584 private $terminal_modes = [];584 private static $terminal_modes = []; 585 585 586 586 /** … … 592 592 * @access private 593 593 */ 594 private $channel_extended_data_type_codes = [];594 private static $channel_extended_data_type_codes = []; 595 595 596 596 /** … … 648 648 649 649 /** 650 * The identifier of the interactive channel which was opened most recently 651 * 652 * @see self::getInteractiveChannelId() 653 * @var int 654 */ 655 private $channel_id_last_interactive = 0; 656 657 /** 650 658 * Packet Size 651 659 * … … 837 845 */ 838 846 private $request_pty = false; 839 840 /**841 * Flag set while exec() is running when using enablePTY()842 *843 * @var bool844 */845 private $in_request_pty_exec = false;846 847 /**848 * Flag set after startSubsystem() is called849 *850 * @var bool851 */852 private $in_subsystem;853 847 854 848 /** … … 1095 1089 1096 1090 /** 1091 * How many channels are currently opened 1092 * 1093 * @var int 1094 */ 1095 private $channelCount = 0; 1096 1097 /** 1098 * Does the server support multiple channels? If not then error out 1099 * when multiple channels are attempted to be opened 1100 * 1101 * @var bool 1102 */ 1103 private $errorOnMultipleChannels; 1104 1105 /** 1097 1106 * Default Constructor. 1098 1107 * … … 1106 1115 public function __construct($host, $port = 22, $timeout = 10) 1107 1116 { 1108 $this->message_numbers = [ 1109 1 => 'NET_SSH2_MSG_DISCONNECT', 1110 2 => 'NET_SSH2_MSG_IGNORE', 1111 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 1112 4 => 'NET_SSH2_MSG_DEBUG', 1113 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 1114 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 1115 20 => 'NET_SSH2_MSG_KEXINIT', 1116 21 => 'NET_SSH2_MSG_NEWKEYS', 1117 30 => 'NET_SSH2_MSG_KEXDH_INIT', 1118 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 1119 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 1120 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 1121 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 1122 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 1123 1124 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 1125 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 1126 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 1127 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 1128 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 1129 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 1130 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 1131 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 1132 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 1133 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 1134 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 1135 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 1136 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 1137 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' 1138 ]; 1139 $this->disconnect_reasons = [ 1140 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 1141 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 1142 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 1143 4 => 'NET_SSH2_DISCONNECT_RESERVED', 1144 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 1145 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 1146 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 1147 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 1148 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 1149 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 1150 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 1151 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 1152 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 1153 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 1154 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' 1155 ]; 1156 $this->channel_open_failure_reasons = [ 1157 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' 1158 ]; 1159 $this->terminal_modes = [ 1160 0 => 'NET_SSH2_TTY_OP_END' 1161 ]; 1162 $this->channel_extended_data_type_codes = [ 1163 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' 1164 ]; 1165 1166 $this->define_array( 1167 $this->message_numbers, 1168 $this->disconnect_reasons, 1169 $this->channel_open_failure_reasons, 1170 $this->terminal_modes, 1171 $this->channel_extended_data_type_codes, 1172 [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], 1173 [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], 1174 [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 1175 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], 1176 // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} 1177 [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 1178 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 1179 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 1180 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 1181 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], 1182 // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) 1183 [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 1184 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] 1185 ); 1117 if (empty(self::$message_numbers)) { 1118 self::$message_numbers = [ 1119 1 => 'NET_SSH2_MSG_DISCONNECT', 1120 2 => 'NET_SSH2_MSG_IGNORE', 1121 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 1122 4 => 'NET_SSH2_MSG_DEBUG', 1123 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 1124 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 1125 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308 1126 20 => 'NET_SSH2_MSG_KEXINIT', 1127 21 => 'NET_SSH2_MSG_NEWKEYS', 1128 30 => 'NET_SSH2_MSG_KEXDH_INIT', 1129 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 1130 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 1131 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 1132 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 1133 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 1134 1135 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 1136 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 1137 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 1138 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 1139 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 1140 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 1141 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 1142 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 1143 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 1144 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 1145 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 1146 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 1147 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 1148 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' 1149 ]; 1150 self::$disconnect_reasons = [ 1151 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 1152 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 1153 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 1154 4 => 'NET_SSH2_DISCONNECT_RESERVED', 1155 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 1156 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 1157 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 1158 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 1159 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 1160 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 1161 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 1162 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 1163 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 1164 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 1165 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' 1166 ]; 1167 self::$channel_open_failure_reasons = [ 1168 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' 1169 ]; 1170 self::$terminal_modes = [ 1171 0 => 'NET_SSH2_TTY_OP_END' 1172 ]; 1173 self::$channel_extended_data_type_codes = [ 1174 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' 1175 ]; 1176 1177 self::define_array( 1178 self::$message_numbers, 1179 self::$disconnect_reasons, 1180 self::$channel_open_failure_reasons, 1181 self::$terminal_modes, 1182 self::$channel_extended_data_type_codes, 1183 [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], 1184 [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], 1185 [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 1186 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], 1187 // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} 1188 [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 1189 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 1190 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 1191 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 1192 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], 1193 // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) 1194 [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 1195 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] 1196 ); 1197 } 1186 1198 1187 1199 /** … … 1268 1280 { 1269 1281 $this->send_kex_first = false; 1282 } 1283 1284 /** 1285 * stream_select wrapper 1286 * 1287 * Quoting https://stackoverflow.com/a/14262151/569976, 1288 * "The general approach to `EINTR` is to simply handle the error and retry the operation again" 1289 * 1290 * This wrapper does that loop 1291 */ 1292 private static function stream_select(&$read, &$write, &$except, $seconds, $microseconds = null) 1293 { 1294 $remaining = $seconds + $microseconds / 1000000; 1295 $start = microtime(true); 1296 while (true) { 1297 $result = @stream_select($read, $write, $except, $seconds, $microseconds); 1298 if ($result !== false) { 1299 return $result; 1300 } 1301 $elapsed = microtime(true) - $start; 1302 $seconds = (int) ($remaining - floor($elapsed)); 1303 $microseconds = (int) (1000000 * ($remaining - $seconds)); 1304 if ($elapsed >= $remaining) { 1305 return false; 1306 } 1307 } 1270 1308 } 1271 1309 … … 1334 1372 $sec = (int) floor($this->curTimeout); 1335 1373 $usec = (int) (1000000 * ($this->curTimeout - $sec)); 1336 if ( @stream_select($read, $write, $except, $sec, $usec) === false) {1374 if (static::stream_select($read, $write, $except, $sec, $usec) === false) { 1337 1375 throw new \RuntimeException('Connection timed out whilst receiving server identification string'); 1338 1376 } … … 1388 1426 throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers"); 1389 1427 } 1428 1429 // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see 1430 // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info. 1431 // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses 1432 // when consolekit was incorporated. 1433 // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the 1434 // issues with how Ubuntu incorporated consolekit 1435 $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#'; 1436 $match = preg_match($pattern, $this->server_identifier, $matches); 1437 $match = $match && version_compare('5.8', $matches[1], '<='); 1438 $match = $match && version_compare('6.9', $matches[1], '>='); 1439 $this->errorOnMultipleChannels = $match; 1390 1440 1391 1441 if (!$this->send_id_string_first) { … … 1487 1537 SSH2::getSupportedCompressionAlgorithms(); 1488 1538 1539 $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c')); 1540 1489 1541 // some SSH servers have buggy implementations of some of the above algorithms 1490 1542 switch (true) { … … 1501 1553 $c2s_mac_algorithms, 1502 1554 ['hmac-sha1-96', 'hmac-md5-96'] 1555 )); 1556 } 1557 break; 1558 case substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_': 1559 if (!isset($preferred['server_to_client']['crypt'])) { 1560 $s2c_encryption_algorithms = array_values(array_diff( 1561 $s2c_encryption_algorithms, 1562 ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] 1563 )); 1564 } 1565 if (!isset($preferred['client_to_server']['crypt'])) { 1566 $c2s_encryption_algorithms = array_values(array_diff( 1567 $c2s_encryption_algorithms, 1568 ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] 1503 1569 )); 1504 1570 } … … 2122 2188 * 2123 2189 * @param string $username 2124 * @param string| AsymmetricKey|array[]|Agent|null ...$args2190 * @param string|PrivateKey|array[]|Agent|null ...$args 2125 2191 * @return bool 2126 2192 * @see self::_login() … … 2147 2213 * 2148 2214 * @param string $username 2149 * @param string ...$args2215 * @param string|PrivateKey|array[]|Agent|null ...$args 2150 2216 * @return bool 2151 2217 * @see self::_login_helper() … … 2267 2333 } 2268 2334 $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); 2269 throw new ConnectionClosedException('Connection closed by server'); 2270 } 2271 2272 list($type, $service) = Strings::unpackSSH2('Cs', $response); 2335 throw $e; 2336 } 2337 2338 list($type) = Strings::unpackSSH2('C', $response); 2339 2340 if ($type == NET_SSH2_MSG_EXT_INFO) { 2341 list($nr_extensions) = Strings::unpackSSH2('N', $response); 2342 for ($i = 0; $i < $nr_extensions; $i++) { 2343 list($extension_name, $extension_value) = Strings::unpackSSH2('ss', $response); 2344 if ($extension_name == 'server-sig-algs') { 2345 $this->supported_private_key_algorithms = explode(',', $extension_value); 2346 } 2347 } 2348 2349 $response = $this->get_binary_packet(); 2350 list($type) = Strings::unpackSSH2('C', $response); 2351 } 2352 2353 list($service) = Strings::unpackSSH2('s', $response); 2354 2273 2355 if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') { 2274 2356 $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); … … 2546 2628 $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; 2547 2629 if (isset($this->preferred['hostkey'])) { 2548 $algos = array_intersect($ this->preferred['hostkey'], $algos);2630 $algos = array_intersect($algos, $this->preferred['hostkey']); 2549 2631 } 2550 2632 $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms); … … 2730 2812 } 2731 2813 2732 if ($this->in_request_pty_exec) { 2733 throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); 2734 } 2735 2736 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to 2737 // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, 2738 // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. 2739 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info 2740 $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; 2741 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy 2742 // uses 0x4000, that's what will be used here, as well. 2743 $packet_size = 0x4000; 2744 2745 $packet = Strings::packSSH2( 2746 'CsN3', 2747 NET_SSH2_MSG_CHANNEL_OPEN, 2748 'session', 2749 self::CHANNEL_EXEC, 2750 $this->window_size_server_to_client[self::CHANNEL_EXEC], 2751 $packet_size 2752 ); 2753 $this->send_binary_packet($packet); 2754 2755 $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; 2756 2757 $this->get_channel_packet(self::CHANNEL_EXEC); 2814 //if ($this->isPTYOpen()) { 2815 // throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); 2816 //} 2817 2818 $this->openChannel(self::CHANNEL_EXEC); 2758 2819 2759 2820 if ($this->request_pty === true) { … … 2780 2841 throw new \RuntimeException('Unable to request pseudo-terminal'); 2781 2842 } 2782 2783 $this->in_request_pty_exec = true;2784 2843 } 2785 2844 … … 2811 2870 $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; 2812 2871 2813 if ($this->in_request_pty_exec) { 2872 if ($this->request_pty === true) { 2873 $this->channel_id_last_interactive = self::CHANNEL_EXEC; 2814 2874 return true; 2815 2875 } … … 2837 2897 2838 2898 /** 2839 * Creates an interactive shell 2840 * 2841 * @see self::read() 2842 * @see self::write() 2899 * How many channels are currently open? 2900 * 2901 * @return int 2902 */ 2903 public function getOpenChannelCount() 2904 { 2905 return $this->channelCount; 2906 } 2907 2908 /** 2909 * Opens a channel 2910 * 2911 * @param string $channel 2912 * @param bool $skip_extended 2843 2913 * @return bool 2844 * @throws \UnexpectedValueException on receipt of unexpected packets 2845 * @throws \RuntimeException on other errors 2846 */ 2847 private function initShell() 2848 { 2849 if ($this->in_request_pty_exec === true) { 2850 return true; 2851 } 2852 2853 $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; 2914 */ 2915 protected function openChannel($channel, $skip_extended = false) 2916 { 2917 if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) { 2918 throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again'); 2919 } 2920 2921 $this->channelCount++; 2922 2923 if ($this->channelCount > 1 && $this->errorOnMultipleChannels) { 2924 throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels"); 2925 } 2926 2927 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to 2928 // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, 2929 // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. 2930 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info 2931 $this->window_size_server_to_client[$channel] = $this->window_size; 2932 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy 2933 // uses 0x4000, that's what will be used here, as well. 2854 2934 $packet_size = 0x4000; 2855 2935 … … 2858 2938 NET_SSH2_MSG_CHANNEL_OPEN, 2859 2939 'session', 2860 self::CHANNEL_SHELL,2861 $this->window_size_server_to_client[ self::CHANNEL_SHELL],2940 $channel, 2941 $this->window_size_server_to_client[$channel], 2862 2942 $packet_size 2863 2943 ); … … 2865 2945 $this->send_binary_packet($packet); 2866 2946 2867 $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; 2868 2869 $this->get_channel_packet(self::CHANNEL_SHELL); 2947 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN; 2948 2949 return $this->get_channel_packet($channel, $skip_extended); 2950 } 2951 2952 /** 2953 * Creates an interactive shell 2954 * 2955 * Returns bool(true) if the shell was opened. 2956 * Returns bool(false) if the shell was already open. 2957 * 2958 * @see self::isShellOpen() 2959 * @see self::read() 2960 * @see self::write() 2961 * @return bool 2962 * @throws InsufficientSetupException if not authenticated 2963 * @throws \UnexpectedValueException on receipt of unexpected packets 2964 * @throws \RuntimeException on other errors 2965 */ 2966 public function openShell() 2967 { 2968 if (!$this->isAuthenticated()) { 2969 throw new InsufficientSetupException('Operation disallowed prior to login()'); 2970 } 2971 2972 $this->openChannel(self::CHANNEL_SHELL); 2870 2973 2871 2974 $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); … … 2908 3011 $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; 2909 3012 3013 $this->channel_id_last_interactive = self::CHANNEL_SHELL; 3014 2910 3015 $this->bitmap |= self::MASK_SHELL; 2911 3016 … … 2914 3019 2915 3020 /** 2916 * Return the channel to be used with read() / write() 2917 * 3021 * Return the channel to be used with read(), write(), and reset(), if none were specified 3022 * @deprecated for lack of transparency in intended channel target, to be potentially replaced 3023 * with method which guarantees open-ness of all yielded channels and throws 3024 * error for multiple open channels 2918 3025 * @see self::read() 2919 3026 * @see self::write() … … 2923 3030 { 2924 3031 switch (true) { 2925 case $this->i n_subsystem:3032 case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM): 2926 3033 return self::CHANNEL_SUBSYSTEM; 2927 case $this->i n_request_pty_exec:3034 case $this->is_channel_status_data(self::CHANNEL_EXEC): 2928 3035 return self::CHANNEL_EXEC; 2929 3036 default: 2930 3037 return self::CHANNEL_SHELL; 2931 3038 } 3039 } 3040 3041 /** 3042 * Indicates the DATA status on the given channel 3043 * 3044 * @param int $channel The channel number to evaluate 3045 * @return bool 3046 */ 3047 private function is_channel_status_data($channel) 3048 { 3049 return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA; 2932 3050 } 2933 3051 … … 2988 3106 * if $mode == self::READ_REGEX, a regular expression. 2989 3107 * 3108 * If not specifying a channel, an open interactive channel will be selected, or, if there are 3109 * no open channels, an interactive shell will be created. If there are multiple open 3110 * interactive channels, a legacy behavior will apply in which channel selection prioritizes 3111 * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive 3112 * channels, callers are discouraged from relying on this legacy behavior and should specify 3113 * the intended channel. 3114 * 2990 3115 * @see self::write() 2991 3116 * @param string $expect 2992 * @param int $mode 3117 * @param int $mode One of the self::READ_* constants 3118 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 2993 3119 * @return string|bool|null 2994 3120 * @throws \RuntimeException on connection error 2995 */ 2996 public function read($expect = '', $mode = self::READ_SIMPLE) 2997 { 3121 * @throws InsufficientSetupException on unexpected channel status, possibly due to closure 3122 */ 3123 public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null) 3124 { 3125 if (!$this->isAuthenticated()) { 3126 throw new InsufficientSetupException('Operation disallowed prior to login()'); 3127 } 3128 2998 3129 $this->curTimeout = $this->timeout; 2999 3130 $this->is_timeout = false; 3000 3131 3001 if (!$this->isAuthenticated()) { 3002 throw new InsufficientSetupException('Operation disallowed prior to login()'); 3003 } 3004 3005 if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) { 3006 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3007 } 3008 3009 $channel = $this->get_interactive_channel(); 3132 if ($channel === null) { 3133 $channel = $this->get_interactive_channel(); 3134 } 3135 3136 if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) { 3137 if ($channel != self::CHANNEL_SHELL) { 3138 throw new InsufficientSetupException('Data is not available on channel'); 3139 } elseif (!$this->openShell()) { 3140 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3141 } 3142 } 3010 3143 3011 3144 if ($mode == self::READ_NEXT) { … … 3025 3158 $response = $this->get_channel_packet($channel); 3026 3159 if ($response === true) { 3027 $this->in_request_pty_exec = false;3028 3160 return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); 3029 3161 } … … 3035 3167 /** 3036 3168 * Inputs a command into an interactive shell. 3169 * 3170 * If not specifying a channel, an open interactive channel will be selected, or, if there are 3171 * no open channels, an interactive shell will be created. If there are multiple open 3172 * interactive channels, a legacy behavior will apply in which channel selection prioritizes 3173 * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive 3174 * channels, callers are discouraged from relying on this legacy behavior and should specify 3175 * the intended channel. 3037 3176 * 3038 3177 * @see SSH2::read() 3039 3178 * @param string $cmd 3179 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 3040 3180 * @return void 3041 3181 * @throws \RuntimeException on connection error 3042 */ 3043 public function write($cmd) 3182 * @throws InsufficientSetupException on unexpected channel status, possibly due to closure 3183 */ 3184 public function write($cmd, $channel = null) 3044 3185 { 3045 3186 if (!$this->isAuthenticated()) { … … 3047 3188 } 3048 3189 3049 if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) { 3050 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3051 } 3052 3053 $this->send_channel_packet($this->get_interactive_channel(), $cmd); 3190 if ($channel === null) { 3191 $channel = $this->get_interactive_channel(); 3192 } 3193 3194 if (!$this->is_channel_status_data($channel)) { 3195 if ($channel != self::CHANNEL_SHELL) { 3196 throw new InsufficientSetupException('Data is not available on channel'); 3197 } elseif (!$this->openShell()) { 3198 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3199 } 3200 } 3201 3202 $this->send_channel_packet($channel, $cmd); 3054 3203 } 3055 3204 … … 3069 3218 public function startSubsystem($subsystem) 3070 3219 { 3071 $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; 3072 3073 $packet = Strings::packSSH2( 3074 'CsN3', 3075 NET_SSH2_MSG_CHANNEL_OPEN, 3076 'session', 3077 self::CHANNEL_SUBSYSTEM, 3078 $this->window_size, 3079 0x4000 3080 ); 3081 3082 $this->send_binary_packet($packet); 3083 3084 $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; 3085 3086 $this->get_channel_packet(self::CHANNEL_SUBSYSTEM); 3220 $this->openChannel(self::CHANNEL_SUBSYSTEM); 3087 3221 3088 3222 $packet = Strings::packSSH2( … … 3104 3238 $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; 3105 3239 3106 $this->bitmap |= self::MASK_SHELL; 3107 $this->in_subsystem = true; 3240 $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM; 3108 3241 3109 3242 return true; … … 3118 3251 public function stopSubsystem() 3119 3252 { 3120 $this->in_subsystem = false; 3121 $this->close_channel(self::CHANNEL_SUBSYSTEM); 3253 if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) { 3254 $this->close_channel(self::CHANNEL_SUBSYSTEM); 3255 } 3122 3256 return true; 3123 3257 } … … 3128 3262 * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call 3129 3263 * 3130 */ 3131 public function reset() 3132 { 3133 $this->close_channel($this->get_interactive_channel()); 3264 * If not specifying a channel, an open interactive channel will be selected. If there are 3265 * multiple open interactive channels, a legacy behavior will apply in which channel selection 3266 * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple 3267 * interactive channels, callers are discouraged from relying on this legacy behavior and 3268 * should specify the intended channel. 3269 * 3270 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 3271 * @return void 3272 */ 3273 public function reset($channel = null) 3274 { 3275 if ($channel === null) { 3276 $channel = $this->get_interactive_channel(); 3277 } 3278 if ($this->isInteractiveChannelOpen($channel)) { 3279 $this->close_channel($channel); 3280 } 3134 3281 } 3135 3282 … … 3177 3324 public function isConnected() 3178 3325 { 3179 return ( bool) ($this->bitmap & self::MASK_CONNECTED);3326 return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock); 3180 3327 } 3181 3328 … … 3188 3335 { 3189 3336 return (bool) ($this->bitmap & self::MASK_LOGIN); 3337 } 3338 3339 /** 3340 * Is the interactive shell active? 3341 * 3342 * @return bool 3343 */ 3344 public function isShellOpen() 3345 { 3346 return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL); 3347 } 3348 3349 /** 3350 * Is the exec pty active? 3351 * 3352 * @return bool 3353 */ 3354 public function isPTYOpen() 3355 { 3356 return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC); 3357 } 3358 3359 /** 3360 * Is the given interactive channel active? 3361 * 3362 * @param int $channel Channel id returned by self::getInteractiveChannelId() 3363 * @return bool 3364 */ 3365 public function isInteractiveChannelOpen($channel) 3366 { 3367 return $this->isAuthenticated() && $this->is_channel_status_data($channel); 3368 } 3369 3370 /** 3371 * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status. 3372 * Returns 0 if no interactive channel has been opened. 3373 * 3374 * @see self::isInteractiveChannelOpen() 3375 * @return int 3376 */ 3377 public function getInteractiveChannelId() 3378 { 3379 return $this->channel_id_last_interactive; 3190 3380 } 3191 3381 … … 3206 3396 } 3207 3397 3208 $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;3209 $packet_size = 0x4000;3210 $packet = Strings::packSSH2(3211 'CsN3',3212 NET_SSH2_MSG_CHANNEL_OPEN,3213 'session',3214 self::CHANNEL_KEEP_ALIVE,3215 $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],3216 $packet_size3217 );3218 3219 3398 try { 3220 $this->send_binary_packet($packet); 3221 3222 $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; 3223 3224 $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE); 3399 $this->openChannel(self::CHANNEL_KEEP_ALIVE); 3225 3400 } catch (\RuntimeException $e) { 3226 3401 return $this->reconnect(); … … 3262 3437 $this->retry_connect = true; 3263 3438 $this->get_seq_no = $this->send_seq_no = 0; 3439 $this->channel_status = []; 3440 $this->channel_id_last_interactive = 0; 3264 3441 } 3265 3442 … … 3284 3461 if (!$this->curTimeout) { 3285 3462 if ($this->keepAlive <= 0) { 3286 @stream_select($read, $write, $except, null);3463 static::stream_select($read, $write, $except, null); 3287 3464 } else { 3288 if (! @stream_select($read, $write, $except, $this->keepAlive)) {3465 if (!static::stream_select($read, $write, $except, $this->keepAlive)) { 3289 3466 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); 3290 3467 return $this->get_binary_packet(true); … … 3300 3477 3301 3478 if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { 3302 if (! @stream_select($read, $write, $except, $this->keepAlive)) {3479 if (!static::stream_select($read, $write, $except, $this->keepAlive)) { 3303 3480 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); 3304 3481 $elapsed = microtime(true) - $start; … … 3314 3491 3315 3492 // this can return a "stream_select(): unable to select [4]: Interrupted system call" error 3316 if (! @stream_select($read, $write, $except, $sec, $usec)) {3493 if (!static::stream_select($read, $write, $except, $sec, $usec)) { 3317 3494 $this->is_timeout = true; 3318 3495 return true; … … 3505 3682 if (defined('NET_SSH2_LOGGING')) { 3506 3683 $current = microtime(true); 3507 $message_number = isset( $this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';3684 $message_number = isset(self::$message_numbers[ord($payload[0])]) ? self::$message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; 3508 3685 $message_number = '<- ' . $message_number . 3509 3686 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; … … 3587 3764 Strings::shift($payload, 1); 3588 3765 list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload); 3589 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n$message";3766 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . self::$disconnect_reasons[$reason_code] . "\r\n$message"; 3590 3767 $this->bitmap = 0; 3591 3768 return false; … … 3774 3951 public function disablePTY() 3775 3952 { 3776 if ($this->i n_request_pty_exec) {3953 if ($this->isPTYOpen()) { 3777 3954 $this->close_channel(self::CHANNEL_EXEC); 3778 $this->in_request_pty_exec = false;3779 3955 } 3780 3956 $this->request_pty = false; … … 3802 3978 * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION 3803 3979 * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS 3980 * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE 3804 3981 * 3805 3982 * bool(false) is returned if: … … 3969 4146 } 3970 4147 case NET_SSH2_MSG_CHANNEL_CLOSE: 3971 return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended); 4148 if ($client_channel == $channel && $type == NET_SSH2_MSG_CHANNEL_CLOSE) { 4149 return true; 4150 } 4151 return $this->get_channel_packet($client_channel, $skip_extended); 3972 4152 } 3973 4153 } … … 4004 4184 $this->curTimeout = 5; 4005 4185 4006 if ($this->bitmap & self::MASK_SHELL) { 4007 $this->bitmap &= ~self::MASK_SHELL; 4008 } 4186 $this->close_channel_bitmap($channel); 4187 4009 4188 if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { 4010 4189 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); … … 4012 4191 4013 4192 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; 4193 $this->channelCount--; 4194 4014 4195 if ($client_channel == $channel) { 4015 4196 return true; … … 4158 4339 if (defined('NET_SSH2_LOGGING')) { 4159 4340 $current = microtime(true); 4160 $message_number = isset( $this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';4341 $message_number = isset(self::$message_numbers[ord($logged[0])]) ? self::$message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; 4161 4342 $message_number = '-> ' . $message_number . 4162 4343 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; … … 4167 4348 if (strlen($packet) != $sent) { 4168 4349 $this->bitmap = 0; 4169 throw new \RuntimeException("Only $sent of " . strlen($packet) . " bytes were sent"); 4350 $message = $sent === false ? 4351 'Unable to write ' . strlen($packet) . ' bytes' : 4352 "Only $sent of " . strlen($packet) . " bytes were sent"; 4353 throw new \RuntimeException($message); 4170 4354 } 4171 4355 } … … 4343 4527 4344 4528 $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; 4529 $this->channelCount--; 4345 4530 4346 4531 $this->curTimeout = 5; 4347 4532 4348 4533 while (!is_bool($this->get_channel_packet($client_channel))) { 4349 }4350 4351 if ($this->is_timeout) {4352 $this->disconnect();4353 4534 } 4354 4535 … … 4357 4538 } 4358 4539 4359 if ($this->bitmap & self::MASK_SHELL) { 4360 $this->bitmap &= ~self::MASK_SHELL; 4540 $this->close_channel_bitmap($client_channel); 4541 } 4542 4543 /** 4544 * Maintains execution state bitmap in response to channel closure 4545 * 4546 * @param int $client_channel The channel number to maintain closure status of 4547 * @return void 4548 */ 4549 private function close_channel_bitmap($client_channel) 4550 { 4551 switch ($client_channel) { 4552 case self::CHANNEL_SHELL: 4553 // Shell status has been maintained in the bitmap for backwards 4554 // compatibility sake, but can be removed going forward 4555 if ($this->bitmap & self::MASK_SHELL) { 4556 $this->bitmap &= ~self::MASK_SHELL; 4557 } 4558 break; 4361 4559 } 4362 4560 } … … 4396 4594 * @access protected 4397 4595 */ 4398 protected function define_array(...$args)4596 protected static function define_array(...$args) 4399 4597 { 4400 4598 foreach ($args as $arg) { … … 4799 4997 ] 4800 4998 ]; 4999 } 5000 5001 /** 5002 * Force multiple channels (even if phpseclib has decided to disable them) 5003 */ 5004 public function forceMultipleChannels() 5005 { 5006 $this->errorOnMultipleChannels = false; 4801 5007 } 4802 5008 -
mihdan-no-external-links/trunk/admin/Admin.php
r3096422 r3369336 155 155 * 156 156 * @since 4.0.0 157 */ 158 public function enqueue_scripts( $hook ): void { 157 * 158 * @param string $hook The current admin page. 159 */ 160 public function enqueue_scripts( string $hook ): void { 159 161 wp_enqueue_script( 160 162 $this->plugin_name, … … 436 438 register_setting( 437 439 $this->plugin_name . '-settings', 438 $this->options_prefix . 'masking_type' 440 $this->options_prefix . 'masking_type', 441 [ 442 'sanitize_callback' => 'sanitize_text_field', 443 ] 439 444 ); 440 445 441 446 register_setting( 442 447 $this->plugin_name . '-settings', 443 $this->options_prefix . 'redirect_time' 448 $this->options_prefix . 'redirect_time', 449 [ 450 'sanitize_callback' => 'sanitize_text_field', 451 ] 444 452 ); 445 453 … … 455 463 register_setting( 456 464 $this->plugin_name . '-settings', 457 $this->options_prefix . 'mask_links' 465 $this->options_prefix . 'mask_links', 466 [ 467 'sanitize_callback' => 'sanitize_text_field', 468 ] 458 469 ); 459 470 460 471 register_setting( 461 472 $this->plugin_name . '-settings', 462 $this->options_prefix . 'mask_posts_pages' 473 $this->options_prefix . 'mask_posts_pages', 474 [ 475 'sanitize_callback' => 'intval', 476 ] 463 477 ); 464 478 465 479 register_setting( 466 480 $this->plugin_name . '-settings', 467 $this->options_prefix . 'mask_comments' 481 $this->options_prefix . 'mask_comments', 482 [ 483 'sanitize_callback' => 'intval', 484 ] 468 485 ); 469 486 470 487 register_setting( 471 488 $this->plugin_name . '-settings', 472 $this->options_prefix . 'mask_comment_author' 489 $this->options_prefix . 'mask_comment_author', 490 [ 491 'sanitize_callback' => 'intval', 492 ] 473 493 ); 474 494 475 495 register_setting( 476 496 $this->plugin_name . '-settings', 477 $this->options_prefix . 'mask_rss' 497 $this->options_prefix . 'mask_rss', 498 [ 499 'sanitize_callback' => 'intval', 500 ] 478 501 ); 479 502 480 503 register_setting( 481 504 $this->plugin_name . '-settings', 482 $this->options_prefix . 'mask_rss_comments' 505 $this->options_prefix . 'mask_rss_comments', 506 [ 507 'sanitize_callback' => 'intval', 508 ] 483 509 ); 484 510 … … 494 520 register_setting( 495 521 $this->plugin_name . '-settings', 496 $this->options_prefix . 'nofollow' 522 $this->options_prefix . 'nofollow', 523 [ 524 'sanitize_callback' => 'intval', 525 ] 497 526 ); 498 527 499 528 register_setting( 500 529 $this->plugin_name . '-settings', 501 $this->options_prefix . 'target_blank' 530 $this->options_prefix . 'target_blank', 531 [ 532 'sanitize_callback' => 'intval', 533 ] 502 534 ); 503 535 504 536 register_setting( 505 537 $this->plugin_name . '-settings', 506 $this->options_prefix . 'noindex_tag' 538 $this->options_prefix . 'noindex_tag', 539 [ 540 'sanitize_callback' => 'intval', 541 ] 507 542 ); 508 543 509 544 register_setting( 510 545 $this->plugin_name . '-settings', 511 $this->options_prefix . 'noindex_comment' 546 $this->options_prefix . 'noindex_comment', 547 [ 548 'sanitize_callback' => 'intval', 549 ] 512 550 ); 513 551 … … 528 566 register_setting( 529 567 $this->plugin_name . '-settings-seo-hide', 530 $this->options_prefix . 'seo_hide' 568 $this->options_prefix . 'seo_hide', 569 [ 570 'sanitize_callback' => 'intval', 571 ] 531 572 ); 532 573 … … 565 606 register_setting( 566 607 $this->plugin_name . '-settings-seo-hide', 567 $this->options_prefix . 'seo_hide_include_list' 608 $this->options_prefix . 'seo_hide_include_list', 609 [ 610 'sanitize_callback' => 'wp_kses_post', 611 ] 568 612 ); 569 613 … … 585 629 register_setting( 586 630 $this->plugin_name . '-settings-seo-hide', 587 $this->options_prefix . 'seo_hide_mode' 631 $this->options_prefix . 'seo_hide_mode', 632 [ 633 'sanitize_callback' => 'sanitize_text_field', 634 ] 588 635 ); 589 636 … … 607 654 register_setting( 608 655 $this->plugin_name . '-settings-seo-hide', 609 $this->options_prefix . 'seo_hide_exclude_list' 656 $this->options_prefix . 'seo_hide_exclude_list', 657 [ 658 'sanitize_callback' => [ $this, 'wp_kses_post' ], 659 ] 610 660 ); 611 661 … … 621 671 register_setting( 622 672 $this->plugin_name . '-settings-advanced', 623 $this->options_prefix . 'logging' 673 $this->options_prefix . 'logging', 674 [ 675 'sanitize_callback' => 'intval', 676 ] 624 677 ); 625 678 626 679 register_setting( 627 680 $this->plugin_name . '-settings-advanced', 628 $this->options_prefix . 'log_duration' 681 $this->options_prefix . 'log_duration', 682 [ 683 'sanitize_callback' => 'intval', 684 ] 629 685 ); 630 686 … … 639 695 register_setting( 640 696 $this->plugin_name . '-settings-advanced', 641 $this->options_prefix . 'anonymize_links' 697 $this->options_prefix . 'anonymize_links', 698 [ 699 'sanitize_callback' => 'intval', 700 ] 642 701 ); 643 702 644 703 register_setting( 645 704 $this->plugin_name . '-settings-advanced', 646 $this->options_prefix . 'anonymous_link_provider' 705 $this->options_prefix . 'anonymous_link_provider', 706 [ 707 'sanitize_callback' => 'sanitize_text_field', 708 ] 647 709 ); 648 710 … … 657 719 register_setting( 658 720 $this->plugin_name . '-settings-advanced', 659 $this->options_prefix . 'bot_targeting' 721 $this->options_prefix . 'bot_targeting', 722 [ 723 'sanitize_callback' => 'sanitize_text_field', 724 ] 660 725 ); 661 726 662 727 register_setting( 663 728 $this->plugin_name . '-settings-advanced', 664 $this->options_prefix . 'bots_selector' 729 $this->options_prefix . 'bots_selector', 730 [ 731 'sanitize_callback' => [ $this, 'sanitize_text_array_deep' ], 732 ] 665 733 ); 666 734 … … 676 744 register_setting( 677 745 $this->plugin_name . '-settings-advanced', 678 $this->options_prefix . 'check_referrer' 746 $this->options_prefix . 'check_referrer', 747 [ 748 'sanitize_callback' => 'intval', 749 ] 679 750 ); 680 751 681 752 register_setting( 682 753 $this->plugin_name . '-settings-advanced', 683 $this->options_prefix . 'remove_all_links' 754 $this->options_prefix . 'remove_all_links', 755 [ 756 'sanitize_callback' => 'intval', 757 ] 684 758 ); 685 759 686 760 register_setting( 687 761 $this->plugin_name . '-settings-advanced', 688 $this->options_prefix . 'links_to_text' 762 $this->options_prefix . 'links_to_text', 763 [ 764 'sanitize_callback' => 'intval', 765 ] 689 766 ); 690 767 … … 700 777 register_setting( 701 778 $this->plugin_name . '-settings-advanced', 702 $this->options_prefix . 'debug_mode' 779 $this->options_prefix . 'debug_mode', 780 [ 781 'sanitize_callback' => 'intval', 782 ] 703 783 ); 704 784 … … 731 811 register_setting( 732 812 $this->plugin_name . '-settings-links', 733 $this->options_prefix . 'link_structure' 813 $this->options_prefix . 'link_structure', 814 [ 815 'sanitize_callback' => 'sanitize_text_field', 816 ] 734 817 ); 735 818 … … 801 884 register_setting( 802 885 $this->plugin_name . '-settings-links', 803 $this->options_prefix . 'link_encoding' 886 $this->options_prefix . 'link_encoding', 887 [ 888 'sanitize_callback' => 'sanitize_text_field', 889 ] 804 890 ); 805 891 … … 876 962 register_setting( 877 963 $this->plugin_name . '-settings-links', 878 $this->options_prefix . 'link_shortening' 964 $this->options_prefix . 'link_shortening', 965 [ 966 'sanitize_callback' => 'sanitize_text_field', 967 ] 879 968 ); 880 969 … … 962 1051 register_setting( 963 1052 $this->plugin_name . '-settings-advanced', 964 $this->options_prefix . 'redirect_message' 1053 $this->options_prefix . 'redirect_message', 1054 [ 1055 'sanitize_callback' => 'wp_kses_post', 1056 ] 965 1057 ); 966 1058 … … 976 1068 register_setting( 977 1069 $this->plugin_name . '-settings-advanced', 978 $this->options_prefix . 'redirect_page' 1070 $this->options_prefix . 'redirect_page', 1071 [ 1072 'sanitize_callback' => 'intval', 1073 ] 979 1074 ); 980 1075 … … 995 1090 register_setting( 996 1091 $this->plugin_name . '-settings-include-exclude', 997 $this->options_prefix . 'inclusion_list' 1092 $this->options_prefix . 'inclusion_list', 1093 [ 1094 'sanitize_callback' => 'wp_kses_post', 1095 ] 998 1096 ); 999 1097 … … 1014 1112 register_setting( 1015 1113 $this->plugin_name . '-settings-include-exclude', 1016 $this->options_prefix . 'exclusion_list' 1114 $this->options_prefix . 'exclusion_list', 1115 [ 1116 'sanitize_callback' => 'wp_kses_post', 1117 ] 1017 1118 ); 1018 1119 … … 1033 1134 register_setting( 1034 1135 $this->plugin_name . '-settings-include-exclude', 1035 $this->options_prefix . 'skip_auth' 1136 $this->options_prefix . 'skip_auth', 1137 [ 1138 'sanitize_callback' => 'intval', 1139 ] 1036 1140 ); 1037 1141 … … 1052 1156 register_setting( 1053 1157 $this->plugin_name . '-settings-include-exclude', 1054 $this->options_prefix . 'skip_follow' 1158 $this->options_prefix . 'skip_follow', 1159 [ 1160 'sanitize_callback' => 'intval', 1161 ] 1055 1162 ); 1056 1163 … … 2250 2357 return $actions; 2251 2358 } 2359 2360 /** 2361 * Sanitizes an array of strings from user input or from the database. 2362 * 2363 * @param array $array Array to sanitize. 2364 * 2365 * @return array 2366 */ 2367 public function sanitize_text_array_deep( array $array ): array { 2368 return array_map( 'sanitize_text_field', $array ); 2369 } 2252 2370 } -
mihdan-no-external-links/trunk/admin/LogTable.php
r2783436 r3369336 99 99 'delete' => sprintf( 100 100 '<a href="?page=%s&action=%s&log=%s&_wpnonce=%s">Delete</a>', 101 isset( $_REQUEST['page'] ) ? absint( $_REQUEST['page'] ) : 1,101 isset( $_REQUEST['page'] ) ? sanitize_text_field( $_REQUEST['page'] ) : 1, 102 102 'delete', 103 103 absint( $item['id'] ), … … 294 294 : ''; 295 295 296 if ( ! empty( $nonce ) && ! wp_verify_nonce( $nonce, $this->options_prefix . 'delete_log' ) ) { 297 wp_die( esc_html__( 'Are you sure you want to do this?' ) ); 298 } 299 300 if ( 'delete' === $this->current_action() ) { 296 if ( 'delete' === $this->current_action() && wp_verify_nonce( $nonce, $this->options_prefix . 'delete_log' ) ) { 301 297 302 298 $delete_count = 0; … … 312 308 } 313 309 314 if ( 315 ( isset( $_POST['action'] ) && 'bulk-delete' === $_POST['action'] ) || 316 ( isset( $_POST['action2'] ) && 'bulk-delete' === $_POST['action2'] ) 317 ) { 310 if ( 'bulk-delete' === $this->current_action() && wp_verify_nonce( $nonce, 'bulk-' . $this->_args['plural'] ) ) { 318 311 319 312 $delete_ids = ! empty( $_POST['bulk-delete'] ) 320 ? array_map( ' sanitize_text_field', wp_unslash( $_POST['bulk-delete'] ) )313 ? array_map( 'intval', wp_unslash( $_POST['bulk-delete'] ) ) 321 314 : []; 322 315 … … 335 328 exit; 336 329 } 337 338 330 } 339 331 -
mihdan-no-external-links/trunk/admin/MaskTable.php
r2783436 r3369336 241 241 'cb' => '<input type="checkbox" />', 242 242 'title' => __( 'URL', $this->plugin_name ), 243 'mask' => __( 'Mask' ),244 'numeric' => __( 'Numeric' ),243 'mask' => __( 'Mask', $this->plugin_name ), 244 'numeric' => __( 'Numeric', $this->plugin_name ), 245 245 ]; 246 246 … … 317 317 318 318 if ( ! empty( $nonce ) && ! wp_verify_nonce( $nonce, $this->options_prefix . 'delete_mask' ) ) { 319 wp_die( esc_html__( 'Are you sure you want to do this?' ) );319 wp_die( esc_html__( 'Are you sure you want to do this?', $this->plugin_name ) ); 320 320 } 321 321 -
mihdan-no-external-links/trunk/admin/SiteHealth.php
r2782193 r3369336 75 75 'status' => 'good', 76 76 'badge' => [ 77 'label' => __( 'Performance' ),77 'label' => __( 'Performance', $this->plugin_name ), 78 78 'color' => 'blue', 79 79 ], … … 86 86 'status' => 'critical', 87 87 'badge' => [ 88 'label' => __( 'Performance' ),88 'label' => __( 'Performance', $this->plugin_name ), 89 89 'color' => 'red', 90 90 ], -
mihdan-no-external-links/trunk/includes/Main.php
r3007441 r3369336 116 116 $this->define_admin_hooks(); 117 117 $this->define_public_hooks(); 118 119 118 } 120 119 … … 264 263 $plugin_i18n = new I18n( $this->get_plugin_name() ); 265 264 266 $this->loader->add_action( ' plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );265 $this->loader->add_action( 'init', $plugin_i18n, 'load_plugin_textdomain' ); 267 266 268 267 } … … 342 341 'skip_follow' => false, 343 342 'redirect_page' => 0, 344 'redirect_message' => __( 345 'You will be redirected in 3 seconds. If your browser does not automatically redirect you, please <a href="%linkurl%">click here</a>.', 346 $this->plugin_name 347 ), 343 'redirect_message' => 'You will be redirected in 3 seconds. If your browser does not automatically redirect you, please <a href="%linkurl%">click here</a>.', 348 344 'output_buffer' => $output_buffer, 349 345 ); 350 346 351 347 $this->options = $this->validate_options( $options ); 352 353 348 } 354 349 … … 547 542 548 543 $this->loader->add_filter( 'set-screen-option', $this->admin, 'mask_page_set_screen_options', null, 3 ); 549 $hook_name = vsprintf( 550 'load-%s_page_%s-masks', 551 [ strtolower( sanitize_file_name( __( 'No External Links', $this->plugin_name ) ) ), $this->get_plugin_name() ] 552 ); 544 545 $hook_name = sprintf( 'load-no-external-links_page_%s-masks', $this->get_plugin_name() ); 553 546 554 547 $this->loader->add_action( $hook_name, $this->admin, 'mask_page_screen_options' ); … … 556 549 $this->loader->add_filter( 'set-screen-option', $this->admin, 'log_page_set_screen_options', null, 3 ); 557 550 558 $hook_name = vsprintf( 559 'load-%s_page_%s-logs', 560 [ strtolower( sanitize_file_name( __( 'No External Links', $this->plugin_name ) ) ), $this->get_plugin_name() ] 561 ); 551 $hook_name = sprintf( 'load-no-external-links_page_%s-logs', $this->get_plugin_name() ); 562 552 563 553 $this->loader->add_action( $hook_name, $this->admin, 'log_page_screen_options' ); -
mihdan-no-external-links/trunk/mihdan-no-external-links.php
r3369209 r3369336 11 11 * Plugin URI: https://www.kobzarev.com/projects/no-external-links/ 12 12 * Description: Convert external links into internal links, site wide or post/page specific. Add NoFollow, Click logging, and more... 13 * Version: 5.1. 513 * Version: 5.1.6 14 14 * Author: Mikhail Kobzarev 15 15 * Author URI: https://www.kobzarev.com/ … … 29 29 30 30 const MIHDAN_NO_EXTERNAL_LINKS_DIR = __DIR__; 31 const MIHDAN_NO_EXTERNAL_LINKS_VERSION = '5.1. 5';31 const MIHDAN_NO_EXTERNAL_LINKS_VERSION = '5.1.6'; 32 32 const MIHDAN_NO_EXTERNAL_LINKS_SLUG = 'mihdan-no-external-links'; 33 33 -
mihdan-no-external-links/trunk/readme.txt
r3369209 r3369336 5 5 Requires at least: 5.7.4 6 6 Tested up to: 6.8 7 Stable tag: 5.1. 57 Stable tag: 5.1.6 8 8 Requires PHP: 7.4 9 9 License: GPLv2 or later … … 63 63 64 64 == Changelog == 65 66 = 5.1.6 (28.09.2025) = 67 * Resolved #[39](https://github.com/mihdan/mihdan-no-external-links/issues/39) 68 * Resolved #[40](https://github.com/mihdan/mihdan-no-external-links/issues/40) 69 * The error "Function _load_textdomain_just_in_time was called incorrectly" has been fixed 65 70 66 71 = 5.1.5 (28.09.2025) = -
mihdan-no-external-links/trunk/vendor/composer/installed.json
r2900866 r3369336 197 197 { 198 198 "name": "phpseclib/phpseclib", 199 "version": "3.0. 19",200 "version_normalized": "3.0. 19.0",199 "version": "3.0.34", 200 "version_normalized": "3.0.34.0", 201 201 "source": { 202 202 "type": "git", 203 203 "url": "https://github.com/phpseclib/phpseclib.git", 204 "reference": " cc181005cf548bfd8a4896383bb825d859259f95"204 "reference": "56c79f16a6ae17e42089c06a2144467acc35348a" 205 205 }, 206 206 "dist": { 207 207 "type": "zip", 208 "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ cc181005cf548bfd8a4896383bb825d859259f95",209 "reference": " cc181005cf548bfd8a4896383bb825d859259f95",208 "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a", 209 "reference": "56c79f16a6ae17e42089c06a2144467acc35348a", 210 210 "shasum": "" 211 211 }, … … 225 225 "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." 226 226 }, 227 "time": "2023- 03-05T17:13:09+00:00",227 "time": "2023-11-27T11:13:31+00:00", 228 228 "type": "library", 229 229 "installation-source": "dist", … … 290 290 "support": { 291 291 "issues": "https://github.com/phpseclib/phpseclib/issues", 292 "source": "https://github.com/phpseclib/phpseclib/tree/3.0. 19"292 "source": "https://github.com/phpseclib/phpseclib/tree/3.0.34" 293 293 }, 294 294 "funding": [ -
mihdan-no-external-links/trunk/vendor/composer/installed.php
r3369209 r3369336 2 2 'root' => array( 3 3 'name' => 'mihdan/mihdan-no-external-links', 4 'pretty_version' => '5.1. 5',5 'version' => '5.1. 5.0',6 'reference' => ' a6d16d367a6ceb1bbe468219125173021c653f99',4 'pretty_version' => '5.1.6', 5 'version' => '5.1.6.0', 6 'reference' => 'eda3a90f6434e53d5238700433c3b2aa66195b62', 7 7 'type' => 'wordpress-plugin', 8 8 'install_path' => __DIR__ . '/../../', … … 12 12 'versions' => array( 13 13 'mihdan/mihdan-no-external-links' => array( 14 'pretty_version' => '5.1. 5',15 'version' => '5.1. 5.0',16 'reference' => ' a6d16d367a6ceb1bbe468219125173021c653f99',14 'pretty_version' => '5.1.6', 15 'version' => '5.1.6.0', 16 'reference' => 'eda3a90f6434e53d5238700433c3b2aa66195b62', 17 17 'type' => 'wordpress-plugin', 18 18 'install_path' => __DIR__ . '/../../', … … 48 48 ), 49 49 'phpseclib/phpseclib' => array( 50 'pretty_version' => '3.0. 19',51 'version' => '3.0. 19.0',52 'reference' => ' cc181005cf548bfd8a4896383bb825d859259f95',50 'pretty_version' => '3.0.34', 51 'version' => '3.0.34.0', 52 'reference' => '56c79f16a6ae17e42089c06a2144467acc35348a', 53 53 'type' => 'library', 54 54 'install_path' => __DIR__ . '/../phpseclib/phpseclib', -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/BACKERS.md
r2900866 r3369336 14 14 - Tharyrok 15 15 - [cjhaas](https://github.com/cjhaas) 16 - [istiak-tridip](https://github.com/istiak-tridip) -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/AsymmetricKey.php
r2900866 r3369336 131 131 * @param string $key 132 132 * @param string $password optional 133 * @return AsymmetricKey133 * @return \phpseclib3\Crypt\Common\PublicKey|\phpseclib3\Crypt\Common\PrivateKey 134 134 */ 135 135 public static function load($key, $password = false) -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/Formats/Keys/PKCS8.php
r2900866 r3369336 142 142 case 'RC2': 143 143 $cipher = new RC2('cbc'); 144 $cipher->setKeyLength(64); 144 145 break; 145 146 case '3-KeyTripleDES': … … 219 220 switch ($algo) { 220 221 case 'desCBC': 221 $cipher = new TripleDES('cbc');222 $cipher = new DES('cbc'); 222 223 break; 223 224 case 'des-EDE3-CBC': -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Common/SymmetricKey.php
r2900866 r3369336 669 669 // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster 670 670 case (PHP_OS & "\xDF\xDF\xDF") === 'WIN': 671 case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':671 case !(is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") == 'ARM'): 672 672 case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: 673 673 self::$use_reg_intval = true; 674 674 break; 675 case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM':675 case is_string(php_uname('m')) && (php_uname('m') & "\xDF\xDF\xDF") == 'ARM': 676 676 switch (true) { 677 677 /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors: … … 918 918 * @param string $password 919 919 * @param string $method 920 * @param string[]...$func_args920 * @param int|string ...$func_args 921 921 * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length 922 922 * @throws \RuntimeException if bcrypt is being used and a salt isn't provided -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
r2900866 r3369336 842 842 self::ENCRYPTION_NONE 843 843 ]; 844 $ numSelected= 0;844 $encryptedCount = 0; 845 845 $selected = 0; 846 846 foreach ($masks as $mask) { 847 847 if ($padding & $mask) { 848 848 $selected = $mask; 849 $ numSelected++;849 $encryptedCount++; 850 850 } 851 851 } 852 if ($ numSelected> 1) {852 if ($encryptedCount > 1) { 853 853 throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected'); 854 854 } … … 860 860 self::SIGNATURE_PKCS1 861 861 ]; 862 $ numSelected= 0;862 $signatureCount = 0; 863 863 $selected = 0; 864 864 foreach ($masks as $mask) { 865 865 if ($padding & $mask) { 866 866 $selected = $mask; 867 $ numSelected++;867 $signatureCount++; 868 868 } 869 869 } 870 if ($ numSelected> 1) {870 if ($signatureCount > 1) { 871 871 throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected'); 872 872 } … … 874 874 875 875 $new = clone $this; 876 $new->encryptionPadding = $encryptionPadding; 877 $new->signaturePadding = $signaturePadding; 876 if ($encryptedCount) { 877 $new->encryptionPadding = $encryptionPadding; 878 } 879 if ($signatureCount) { 880 $new->signaturePadding = $signaturePadding; 881 } 878 882 return $new; 879 883 } -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
r2782199 r3369336 834 834 // Generating encrypt code: 835 835 $init_encrypt .= ' 836 static $tables;837 836 if (empty($tables)) { 838 837 $tables = &$this->getTables(); … … 891 890 // Generating decrypt code: 892 891 $init_decrypt .= ' 893 static $invtables;894 892 if (empty($invtables)) { 895 893 $invtables = &$this->getInvTables(); … … 948 946 $this->inline_crypt = $this->createInlineCryptFunction( 949 947 [ 950 'init_crypt' => ' ',948 'init_crypt' => 'static $tables; static $invtables;', 951 949 'init_encrypt' => $init_encrypt, 952 950 'init_decrypt' => $init_decrypt, -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
r2782199 r3369336 22 22 namespace phpseclib3\File; 23 23 24 use DateTime;25 24 use phpseclib3\Common\Functions\Strings; 26 25 use phpseclib3\File\ASN1\Element; … … 206 205 } 207 206 208 return [ self::decode_ber($encoded)];207 return [$decoded]; 209 208 } 210 209 … … 1404 1403 } 1405 1404 break; 1406 case ($c & 0x80000000) != 0:1405 case ($c & (PHP_INT_SIZE == 8 ? 0x80000000 : (1 << 31))) != 0: 1407 1406 return false; 1408 1407 case $c >= 0x04000000: -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/File/X509.php
r2900866 r3369336 165 165 * @var array 166 166 */ 167 private $CAs ;167 private $CAs = []; 168 168 169 169 /** … … 316 316 'id-at-role' => '2.5.4.72', 317 317 'id-at-postalAddress' => '2.5.4.16', 318 'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3', 319 'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2', 320 'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1', 321 'id-at-businessCategory' => '2.5.4.15', 318 322 319 323 //'id-domainComponent' => '0.9.2342.19200300.100.1.25', … … 1039 1043 foreach ($names as $name) { 1040 1044 foreach ($name as $key => $value) { 1041 $value = str_replace(['.', '*'], ['\.', '[^.]*'], $value); 1045 $value = preg_quote($value); 1046 $value = str_replace('\*', '[^.]*', $value); 1042 1047 switch ($key) { 1043 1048 case 'dNSName': … … 1539 1544 { 1540 1545 switch (strtolower($propName)) { 1546 case 'jurisdictionofincorporationcountryname': 1547 case 'jurisdictioncountryname': 1548 case 'jurisdictionc': 1549 return 'jurisdictionOfIncorporationCountryName'; 1550 case 'jurisdictionofincorporationstateorprovincename': 1551 case 'jurisdictionstateorprovincename': 1552 case 'jurisdictionst': 1553 return 'jurisdictionOfIncorporationStateOrProvinceName'; 1554 case 'jurisdictionlocalityname': 1555 case 'jurisdictionl': 1556 return 'jurisdictionLocalityName'; 1557 case 'id-at-businesscategory': 1558 case 'businesscategory': 1559 return 'id-at-businessCategory'; 1541 1560 case 'id-at-countryname': 1542 1561 case 'countryname': … … 2031 2050 return false; 2032 2051 } 2033 if (empty($this->CAs)) {2034 return $chain;2035 }2036 2052 while (true) { 2037 2053 $currentCert = $chain[count($chain) - 1]; -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
r2900866 r3369336 101 101 self::$mainEngine = $fqmain; 102 102 103 if (!in_array('Default', $modexps)) {104 $modexps[] = 'DefaultEngine';105 }106 107 103 $found = false; 108 104 foreach ($modexps as $modexp) { … … 141 137 if (!isset(self::$mainEngine)) { 142 138 $engines = [ 143 ['GMP' ],139 ['GMP', ['DefaultEngine']], 144 140 ['PHP64', ['OpenSSL']], 145 141 ['BCMath', ['OpenSSL']], 146 ['PHP32', ['OpenSSL']] 142 ['PHP32', ['OpenSSL']], 143 ['PHP64', ['DefaultEngine']], 144 ['PHP32', ['DefaultEngine']] 147 145 ]; 146 148 147 foreach ($engines as $engine) { 149 148 try { 150 self::setEngine($engine[0], isset($engine[1]) ? $engine[1] : []);151 break;149 self::setEngine($engine[0], $engine[1]); 150 return; 152 151 } catch (\Exception $e) { 153 152 } 154 153 } 154 155 throw new \UnexpectedValueException('No valid BigInteger found. This is only possible when JIT is enabled on Windows and neither the GMP or BCMath extensions are available so either disable JIT or install GMP / BCMath'); 155 156 } 156 157 } -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/Engine.php
r2900866 r3369336 645 645 } 646 646 647 if ($this->compare($n) > 0) { 648 list(, $temp) = $this->divide($n); 649 return $temp->powModInner($e, $n); 650 } 651 647 652 return $this->powModInner($e, $n); 648 653 } -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP.php
r2782199 r3369336 1327 1327 return array_reverse($vals); 1328 1328 } 1329 1330 /** 1331 * @return bool 1332 */ 1333 protected static function testJITOnWindows() 1334 { 1335 // see https://github.com/php/php-src/issues/11917 1336 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && function_exists('opcache_get_status') && PHP_VERSION_ID < 80213 && !defined('PHPSECLIB_ALLOW_JIT')) { 1337 $status = opcache_get_status(); 1338 if ($status && isset($status['jit']) && $status['jit']['enabled'] && $status['jit']['on']) { 1339 return true; 1340 } 1341 } 1342 return false; 1343 } 1329 1344 } -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP32.php
r2782199 r3369336 81 81 $step = count($vals) & 3; 82 82 if ($step) { 83 $digit = floor($digit / pow(2, 2 * $step));83 $digit = (int) floor($digit / pow(2, 2 * $step)); 84 84 } 85 85 if ($step != 3) { 86 $digit &= static::MAX_DIGIT;86 $digit = (int) fmod($digit, static::BASE_FULL); 87 87 $i++; 88 88 } … … 103 103 public static function isValidEngine() 104 104 { 105 return PHP_INT_SIZE >= 4 ;105 return PHP_INT_SIZE >= 4 && !self::testJITOnWindows(); 106 106 } 107 107 -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP64.php
r2782199 r3369336 104 104 public static function isValidEngine() 105 105 { 106 return PHP_INT_SIZE >= 8 ;106 return PHP_INT_SIZE >= 8 && !self::testJITOnWindows(); 107 107 } 108 108 -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/BinaryField.php
r2782199 r3369336 49 49 { 50 50 $m = array_shift($indices); 51 if ($m > 571) { 52 /* sect571r1 and sect571k1 are the largest binary curves that https://www.secg.org/sec2-v2.pdf defines 53 altho theoretically there may be legit reasons to use binary finite fields with larger degrees 54 imposing a limit on the maximum size is both reasonable and precedented. in particular, 55 http://tools.ietf.org/html/rfc4253#section-6.1 (The Secure Shell (SSH) Transport Layer Protocol) says 56 "implementations SHOULD check that the packet length is reasonable in order for the implementation to 57 avoid denial of service and/or buffer overflow attacks" */ 58 throw new \OutOfBoundsException('Degrees larger than 571 are not supported'); 59 } 51 60 $val = str_repeat('0', $m) . '1'; 52 61 foreach ($indices as $index) { -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Math/PrimeField/Integer.php
r2900866 r3369336 264 264 265 265 while (!$t->equals($one)) { 266 for ($i = =clone $one; $i->compare($m) < 0; $i = $i->add($one)) {266 for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) { 267 267 if ($t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) { 268 268 break; … … 313 313 public function toBytes() 314 314 { 315 $length = static::$modulo[$this->instanceID]->getLengthInBytes(); 316 return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); 315 if (isset(static::$modulo[$this->instanceID])) { 316 $length = static::$modulo[$this->instanceID]->getLengthInBytes(); 317 return str_pad($this->value->toBytes(), $length, "\0", STR_PAD_LEFT); 318 } 319 return $this->value->toBytes(); 317 320 } 318 321 -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
r2900866 r3369336 94 94 * @access private 95 95 */ 96 private $packet_types = [];96 private static $packet_types = []; 97 97 98 98 /** … … 103 103 * @access private 104 104 */ 105 private $status_codes = [];105 private static $status_codes = []; 106 106 107 107 /** @var array<int, string> */ 108 private $attributes;108 private static $attributes; 109 109 110 110 /** @var array<int, string> */ 111 private $open_flags;111 private static $open_flags; 112 112 113 113 /** @var array<int, string> */ 114 private $open_flags5;114 private static $open_flags5; 115 115 116 116 /** @var array<int, string> */ 117 private $file_types;117 private static $file_types; 118 118 119 119 /** … … 351 351 * Connects to an SFTP server 352 352 * 353 * @param string $host 353 * $host can either be a string, representing the host, or a stream resource. 354 * 355 * @param mixed $host 354 356 * @param int $port 355 357 * @param int $timeout … … 361 363 $this->max_sftp_packet = 1 << 15; 362 364 363 $this->packet_types = [ 364 1 => 'NET_SFTP_INIT', 365 2 => 'NET_SFTP_VERSION', 366 3 => 'NET_SFTP_OPEN', 367 4 => 'NET_SFTP_CLOSE', 368 5 => 'NET_SFTP_READ', 369 6 => 'NET_SFTP_WRITE', 370 7 => 'NET_SFTP_LSTAT', 371 9 => 'NET_SFTP_SETSTAT', 372 10 => 'NET_SFTP_FSETSTAT', 373 11 => 'NET_SFTP_OPENDIR', 374 12 => 'NET_SFTP_READDIR', 375 13 => 'NET_SFTP_REMOVE', 376 14 => 'NET_SFTP_MKDIR', 377 15 => 'NET_SFTP_RMDIR', 378 16 => 'NET_SFTP_REALPATH', 379 17 => 'NET_SFTP_STAT', 380 18 => 'NET_SFTP_RENAME', 381 19 => 'NET_SFTP_READLINK', 382 20 => 'NET_SFTP_SYMLINK', 383 21 => 'NET_SFTP_LINK', 384 385 101 => 'NET_SFTP_STATUS', 386 102 => 'NET_SFTP_HANDLE', 387 103 => 'NET_SFTP_DATA', 388 104 => 'NET_SFTP_NAME', 389 105 => 'NET_SFTP_ATTRS', 390 391 200 => 'NET_SFTP_EXTENDED' 392 ]; 393 $this->status_codes = [ 394 0 => 'NET_SFTP_STATUS_OK', 395 1 => 'NET_SFTP_STATUS_EOF', 396 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 397 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 398 4 => 'NET_SFTP_STATUS_FAILURE', 399 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 400 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 401 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 402 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 403 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 404 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 405 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 406 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 407 13 => 'NET_SFTP_STATUS_NO_MEDIA', 408 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 409 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 410 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 411 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 412 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 413 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 414 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 415 21 => 'NET_SFTP_STATUS_LINK_LOOP', 416 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 417 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 418 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 419 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 420 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 421 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 422 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 423 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 424 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 425 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' 426 ]; 427 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 428 // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why 429 $this->attributes = [ 430 0x00000001 => 'NET_SFTP_ATTR_SIZE', 431 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 432 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 433 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 434 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 435 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ 436 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 437 0x00000040 => 'NET_SFTP_ATTR_ACL', 438 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 439 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ 440 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ 441 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 442 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 443 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 444 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 445 0x00008000 => 'NET_SFTP_ATTR_CTIME', 446 // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers 447 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in 448 // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. 449 // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. 450 (PHP_INT_SIZE == 4 ? -1 : 0xFFFFFFFF) => 'NET_SFTP_ATTR_EXTENDED' 451 ]; 452 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 453 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name 454 // the array for that $this->open5_flags and similarly alter the constant names. 455 $this->open_flags = [ 456 0x00000001 => 'NET_SFTP_OPEN_READ', 457 0x00000002 => 'NET_SFTP_OPEN_WRITE', 458 0x00000004 => 'NET_SFTP_OPEN_APPEND', 459 0x00000008 => 'NET_SFTP_OPEN_CREATE', 460 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 461 0x00000020 => 'NET_SFTP_OPEN_EXCL', 462 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 463 ]; 464 // SFTPv5+ changed the flags up: 465 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 466 $this->open_flags5 = [ 467 // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened 468 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 469 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 470 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 471 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 472 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', 473 // the rest of the flags are not supported 474 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" 475 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 476 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 477 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 478 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 479 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 480 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 481 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 482 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 483 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 484 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 485 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 486 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', 487 ]; 488 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 489 // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation 490 $this->file_types = [ 491 1 => 'NET_SFTP_TYPE_REGULAR', 492 2 => 'NET_SFTP_TYPE_DIRECTORY', 493 3 => 'NET_SFTP_TYPE_SYMLINK', 494 4 => 'NET_SFTP_TYPE_SPECIAL', 495 5 => 'NET_SFTP_TYPE_UNKNOWN', 496 // the following types were first defined for use in SFTPv5+ 497 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 498 6 => 'NET_SFTP_TYPE_SOCKET', 499 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 500 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 501 9 => 'NET_SFTP_TYPE_FIFO' 502 ]; 503 $this->define_array( 504 $this->packet_types, 505 $this->status_codes, 506 $this->attributes, 507 $this->open_flags, 508 $this->open_flags5, 509 $this->file_types 510 ); 365 if (empty(self::$packet_types)) { 366 self::$packet_types = [ 367 1 => 'NET_SFTP_INIT', 368 2 => 'NET_SFTP_VERSION', 369 3 => 'NET_SFTP_OPEN', 370 4 => 'NET_SFTP_CLOSE', 371 5 => 'NET_SFTP_READ', 372 6 => 'NET_SFTP_WRITE', 373 7 => 'NET_SFTP_LSTAT', 374 9 => 'NET_SFTP_SETSTAT', 375 10 => 'NET_SFTP_FSETSTAT', 376 11 => 'NET_SFTP_OPENDIR', 377 12 => 'NET_SFTP_READDIR', 378 13 => 'NET_SFTP_REMOVE', 379 14 => 'NET_SFTP_MKDIR', 380 15 => 'NET_SFTP_RMDIR', 381 16 => 'NET_SFTP_REALPATH', 382 17 => 'NET_SFTP_STAT', 383 18 => 'NET_SFTP_RENAME', 384 19 => 'NET_SFTP_READLINK', 385 20 => 'NET_SFTP_SYMLINK', 386 21 => 'NET_SFTP_LINK', 387 388 101 => 'NET_SFTP_STATUS', 389 102 => 'NET_SFTP_HANDLE', 390 103 => 'NET_SFTP_DATA', 391 104 => 'NET_SFTP_NAME', 392 105 => 'NET_SFTP_ATTRS', 393 394 200 => 'NET_SFTP_EXTENDED' 395 ]; 396 self::$status_codes = [ 397 0 => 'NET_SFTP_STATUS_OK', 398 1 => 'NET_SFTP_STATUS_EOF', 399 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', 400 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', 401 4 => 'NET_SFTP_STATUS_FAILURE', 402 5 => 'NET_SFTP_STATUS_BAD_MESSAGE', 403 6 => 'NET_SFTP_STATUS_NO_CONNECTION', 404 7 => 'NET_SFTP_STATUS_CONNECTION_LOST', 405 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', 406 9 => 'NET_SFTP_STATUS_INVALID_HANDLE', 407 10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', 408 11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', 409 12 => 'NET_SFTP_STATUS_WRITE_PROTECT', 410 13 => 'NET_SFTP_STATUS_NO_MEDIA', 411 14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', 412 15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', 413 16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', 414 17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', 415 18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', 416 19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', 417 20 => 'NET_SFTP_STATUS_INVALID_FILENAME', 418 21 => 'NET_SFTP_STATUS_LINK_LOOP', 419 22 => 'NET_SFTP_STATUS_CANNOT_DELETE', 420 23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', 421 24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', 422 25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', 423 26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', 424 27 => 'NET_SFTP_STATUS_DELETE_PENDING', 425 28 => 'NET_SFTP_STATUS_FILE_CORRUPT', 426 29 => 'NET_SFTP_STATUS_OWNER_INVALID', 427 30 => 'NET_SFTP_STATUS_GROUP_INVALID', 428 31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' 429 ]; 430 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1 431 // the order, in this case, matters quite a lot - see \phpseclib3\Net\SFTP::_parseAttributes() to understand why 432 self::$attributes = [ 433 0x00000001 => 'NET_SFTP_ATTR_SIZE', 434 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 435 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+ 436 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 437 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 438 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+ 439 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME', 440 0x00000040 => 'NET_SFTP_ATTR_ACL', 441 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES', 442 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+ 443 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+ 444 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT', 445 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE', 446 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT', 447 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME', 448 0x00008000 => 'NET_SFTP_ATTR_CTIME', 449 // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers 450 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in 451 // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. 452 // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. 453 (PHP_INT_SIZE == 4 ? (-1 << 31) : 0x80000000) => 'NET_SFTP_ATTR_EXTENDED' 454 ]; 455 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 456 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name 457 // the array for that $this->open5_flags and similarly alter the constant names. 458 self::$open_flags = [ 459 0x00000001 => 'NET_SFTP_OPEN_READ', 460 0x00000002 => 'NET_SFTP_OPEN_WRITE', 461 0x00000004 => 'NET_SFTP_OPEN_APPEND', 462 0x00000008 => 'NET_SFTP_OPEN_CREATE', 463 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', 464 0x00000020 => 'NET_SFTP_OPEN_EXCL', 465 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4 466 ]; 467 // SFTPv5+ changed the flags up: 468 // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3 469 self::$open_flags5 = [ 470 // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened 471 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW', 472 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE', 473 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING', 474 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE', 475 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING', 476 // the rest of the flags are not supported 477 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored" 478 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC', 479 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE', 480 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ', 481 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE', 482 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE', 483 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY', 484 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW', 485 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE', 486 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO', 487 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP', 488 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM', 489 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER', 490 ]; 491 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 492 // see \phpseclib3\Net\SFTP::_parseLongname() for an explanation 493 self::$file_types = [ 494 1 => 'NET_SFTP_TYPE_REGULAR', 495 2 => 'NET_SFTP_TYPE_DIRECTORY', 496 3 => 'NET_SFTP_TYPE_SYMLINK', 497 4 => 'NET_SFTP_TYPE_SPECIAL', 498 5 => 'NET_SFTP_TYPE_UNKNOWN', 499 // the following types were first defined for use in SFTPv5+ 500 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2 501 6 => 'NET_SFTP_TYPE_SOCKET', 502 7 => 'NET_SFTP_TYPE_CHAR_DEVICE', 503 8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', 504 9 => 'NET_SFTP_TYPE_FIFO' 505 ]; 506 self::define_array( 507 self::$packet_types, 508 self::$status_codes, 509 self::$attributes, 510 self::$open_flags, 511 self::$open_flags5, 512 self::$file_types 513 ); 514 } 511 515 512 516 if (!defined('NET_SFTP_QUEUE_SIZE')) { … … 544 548 private function partial_init_sftp_connection() 545 549 { 546 $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; 547 548 $packet = Strings::packSSH2( 549 'CsN3', 550 NET_SSH2_MSG_CHANNEL_OPEN, 551 'session', 552 self::CHANNEL, 553 $this->window_size, 554 0x4000 555 ); 556 557 $this->send_binary_packet($packet); 558 559 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; 560 561 $response = $this->get_channel_packet(self::CHANNEL, true); 550 $response = $this->openChannel(self::CHANNEL, true); 562 551 if ($response === true && $this->isTimeout()) { 563 552 return false; … … 816 805 } 817 806 818 $error = $this->status_codes[$status];807 $error = self::$status_codes[$status]; 819 808 820 809 if ($this->version > 2) { … … 2139 2128 if ($start >= 0) { 2140 2129 $offset = $start; 2141 } elseif ($mode & self::RESUME) {2130 } elseif ($mode & (self::RESUME | self::RESUME_START)) { 2142 2131 // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called 2143 2132 $size = $this->stat($remote_file)['size']; … … 2211 2200 fseek($fp, $local_start); 2212 2201 $size -= $local_start; 2202 } elseif ($mode & self::RESUME) { 2203 fseek($fp, $offset); 2204 $size -= $offset; 2213 2205 } 2214 2206 } elseif ($dataCallback) { … … 2498 2490 } 2499 2491 2500 if ($length > 0 && $length <= $offset - $start) {2501 if ($local_file === false) {2502 $content = substr($content, 0, $length);2503 } else {2504 ftruncate($fp, $length + $res_offset);2505 }2506 }2507 2508 2492 if ($fclose_check) { 2509 2493 fclose($fp); … … 2842 2826 2843 2827 /** 2828 * Recursively go through rawlist() output to get the total filesize 2829 * 2830 * @return int 2831 */ 2832 private static function recursiveFilesize(array $files) 2833 { 2834 $size = 0; 2835 foreach ($files as $name => $file) { 2836 if ($name == '.' || $name == '..') { 2837 continue; 2838 } 2839 $size += is_array($file) ? 2840 self::recursiveFilesize($file) : 2841 $file->size; 2842 } 2843 return $size; 2844 } 2845 2846 /** 2844 2847 * Gets file size 2845 2848 * 2846 2849 * @param string $path 2850 * @param bool $recursive 2847 2851 * @return mixed 2848 2852 */ 2849 public function filesize($path) 2850 { 2851 return $this->get_stat_cache_prop($path, 'size'); 2853 public function filesize($path, $recursive = false) 2854 { 2855 return !$recursive || $this->filetype($path) != 'dir' ? 2856 $this->get_stat_cache_prop($path, 'size') : 2857 self::recursiveFilesize($this->rawlist($path, true)); 2852 2858 } 2853 2859 … … 3042 3048 } 3043 3049 3044 foreach ( $this->attributes as $key => $value) {3050 foreach (self::$attributes as $key => $value) { 3045 3051 switch ($flags & $key) { 3046 3052 case NET_SFTP_ATTR_UIDGID: … … 3273 3279 3274 3280 if (defined('NET_SFTP_LOGGING')) { 3275 $packet_type = '-> ' . $this->packet_types[$type] .3281 $packet_type = '-> ' . self::$packet_types[$type] . 3276 3282 ' (' . round($stop - $start, 4) . 's)'; 3277 3283 $this->append_log($packet_type, $data); … … 3377 3383 3378 3384 if (defined('NET_SFTP_LOGGING')) { 3379 $packet_type = '<- ' . $this->packet_types[$this->packet_type] .3385 $packet_type = '<- ' . self::$packet_types[$this->packet_type] . 3380 3386 ' (' . round($stop - $start, 4) . 's)'; 3381 3387 $this->append_log($packet_type, $packet); … … 3421 3427 * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') 3422 3428 * 3423 * @return array|string 3429 * @return array|string|false 3424 3430 */ 3425 3431 public function getSFTPLog() -
mihdan-no-external-links/trunk/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
r2900866 r3369336 554 554 * @access private 555 555 */ 556 private $message_numbers = [];556 private static $message_numbers = []; 557 557 558 558 /** … … 563 563 * @access private 564 564 */ 565 private $disconnect_reasons = [];565 private static $disconnect_reasons = []; 566 566 567 567 /** … … 572 572 * @access private 573 573 */ 574 private $channel_open_failure_reasons = [];574 private static $channel_open_failure_reasons = []; 575 575 576 576 /** … … 582 582 * @access private 583 583 */ 584 private $terminal_modes = [];584 private static $terminal_modes = []; 585 585 586 586 /** … … 592 592 * @access private 593 593 */ 594 private $channel_extended_data_type_codes = [];594 private static $channel_extended_data_type_codes = []; 595 595 596 596 /** … … 648 648 649 649 /** 650 * The identifier of the interactive channel which was opened most recently 651 * 652 * @see self::getInteractiveChannelId() 653 * @var int 654 */ 655 private $channel_id_last_interactive = 0; 656 657 /** 650 658 * Packet Size 651 659 * … … 837 845 */ 838 846 private $request_pty = false; 839 840 /**841 * Flag set while exec() is running when using enablePTY()842 *843 * @var bool844 */845 private $in_request_pty_exec = false;846 847 /**848 * Flag set after startSubsystem() is called849 *850 * @var bool851 */852 private $in_subsystem;853 847 854 848 /** … … 1095 1089 1096 1090 /** 1091 * How many channels are currently opened 1092 * 1093 * @var int 1094 */ 1095 private $channelCount = 0; 1096 1097 /** 1098 * Does the server support multiple channels? If not then error out 1099 * when multiple channels are attempted to be opened 1100 * 1101 * @var bool 1102 */ 1103 private $errorOnMultipleChannels; 1104 1105 /** 1097 1106 * Default Constructor. 1098 1107 * … … 1106 1115 public function __construct($host, $port = 22, $timeout = 10) 1107 1116 { 1108 $this->message_numbers = [ 1109 1 => 'NET_SSH2_MSG_DISCONNECT', 1110 2 => 'NET_SSH2_MSG_IGNORE', 1111 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 1112 4 => 'NET_SSH2_MSG_DEBUG', 1113 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 1114 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 1115 20 => 'NET_SSH2_MSG_KEXINIT', 1116 21 => 'NET_SSH2_MSG_NEWKEYS', 1117 30 => 'NET_SSH2_MSG_KEXDH_INIT', 1118 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 1119 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 1120 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 1121 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 1122 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 1123 1124 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 1125 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 1126 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 1127 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 1128 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 1129 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 1130 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 1131 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 1132 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 1133 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 1134 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 1135 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 1136 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 1137 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' 1138 ]; 1139 $this->disconnect_reasons = [ 1140 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 1141 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 1142 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 1143 4 => 'NET_SSH2_DISCONNECT_RESERVED', 1144 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 1145 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 1146 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 1147 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 1148 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 1149 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 1150 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 1151 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 1152 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 1153 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 1154 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' 1155 ]; 1156 $this->channel_open_failure_reasons = [ 1157 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' 1158 ]; 1159 $this->terminal_modes = [ 1160 0 => 'NET_SSH2_TTY_OP_END' 1161 ]; 1162 $this->channel_extended_data_type_codes = [ 1163 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' 1164 ]; 1165 1166 $this->define_array( 1167 $this->message_numbers, 1168 $this->disconnect_reasons, 1169 $this->channel_open_failure_reasons, 1170 $this->terminal_modes, 1171 $this->channel_extended_data_type_codes, 1172 [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], 1173 [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], 1174 [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 1175 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], 1176 // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} 1177 [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 1178 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 1179 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 1180 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 1181 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], 1182 // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) 1183 [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 1184 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] 1185 ); 1117 if (empty(self::$message_numbers)) { 1118 self::$message_numbers = [ 1119 1 => 'NET_SSH2_MSG_DISCONNECT', 1120 2 => 'NET_SSH2_MSG_IGNORE', 1121 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', 1122 4 => 'NET_SSH2_MSG_DEBUG', 1123 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', 1124 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', 1125 7 => 'NET_SSH2_MSG_EXT_INFO', // RFC 8308 1126 20 => 'NET_SSH2_MSG_KEXINIT', 1127 21 => 'NET_SSH2_MSG_NEWKEYS', 1128 30 => 'NET_SSH2_MSG_KEXDH_INIT', 1129 31 => 'NET_SSH2_MSG_KEXDH_REPLY', 1130 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', 1131 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', 1132 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', 1133 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', 1134 1135 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', 1136 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', 1137 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', 1138 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', 1139 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', 1140 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', 1141 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', 1142 94 => 'NET_SSH2_MSG_CHANNEL_DATA', 1143 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', 1144 96 => 'NET_SSH2_MSG_CHANNEL_EOF', 1145 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', 1146 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', 1147 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', 1148 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' 1149 ]; 1150 self::$disconnect_reasons = [ 1151 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', 1152 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', 1153 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', 1154 4 => 'NET_SSH2_DISCONNECT_RESERVED', 1155 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', 1156 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', 1157 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', 1158 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', 1159 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', 1160 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', 1161 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', 1162 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', 1163 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', 1164 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', 1165 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' 1166 ]; 1167 self::$channel_open_failure_reasons = [ 1168 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' 1169 ]; 1170 self::$terminal_modes = [ 1171 0 => 'NET_SSH2_TTY_OP_END' 1172 ]; 1173 self::$channel_extended_data_type_codes = [ 1174 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' 1175 ]; 1176 1177 self::define_array( 1178 self::$message_numbers, 1179 self::$disconnect_reasons, 1180 self::$channel_open_failure_reasons, 1181 self::$terminal_modes, 1182 self::$channel_extended_data_type_codes, 1183 [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], 1184 [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], 1185 [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', 1186 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], 1187 // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} 1188 [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', 1189 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', 1190 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', 1191 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', 1192 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], 1193 // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) 1194 [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', 1195 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] 1196 ); 1197 } 1186 1198 1187 1199 /** … … 1268 1280 { 1269 1281 $this->send_kex_first = false; 1282 } 1283 1284 /** 1285 * stream_select wrapper 1286 * 1287 * Quoting https://stackoverflow.com/a/14262151/569976, 1288 * "The general approach to `EINTR` is to simply handle the error and retry the operation again" 1289 * 1290 * This wrapper does that loop 1291 */ 1292 private static function stream_select(&$read, &$write, &$except, $seconds, $microseconds = null) 1293 { 1294 $remaining = $seconds + $microseconds / 1000000; 1295 $start = microtime(true); 1296 while (true) { 1297 $result = @stream_select($read, $write, $except, $seconds, $microseconds); 1298 if ($result !== false) { 1299 return $result; 1300 } 1301 $elapsed = microtime(true) - $start; 1302 $seconds = (int) ($remaining - floor($elapsed)); 1303 $microseconds = (int) (1000000 * ($remaining - $seconds)); 1304 if ($elapsed >= $remaining) { 1305 return false; 1306 } 1307 } 1270 1308 } 1271 1309 … … 1334 1372 $sec = (int) floor($this->curTimeout); 1335 1373 $usec = (int) (1000000 * ($this->curTimeout - $sec)); 1336 if ( @stream_select($read, $write, $except, $sec, $usec) === false) {1374 if (static::stream_select($read, $write, $except, $sec, $usec) === false) { 1337 1375 throw new \RuntimeException('Connection timed out whilst receiving server identification string'); 1338 1376 } … … 1388 1426 throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers"); 1389 1427 } 1428 1429 // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see 1430 // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info. 1431 // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses 1432 // when consolekit was incorporated. 1433 // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the 1434 // issues with how Ubuntu incorporated consolekit 1435 $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#'; 1436 $match = preg_match($pattern, $this->server_identifier, $matches); 1437 $match = $match && version_compare('5.8', $matches[1], '<='); 1438 $match = $match && version_compare('6.9', $matches[1], '>='); 1439 $this->errorOnMultipleChannels = $match; 1390 1440 1391 1441 if (!$this->send_id_string_first) { … … 1487 1537 SSH2::getSupportedCompressionAlgorithms(); 1488 1538 1539 $kex_algorithms = array_merge($kex_algorithms, array('ext-info-c')); 1540 1489 1541 // some SSH servers have buggy implementations of some of the above algorithms 1490 1542 switch (true) { … … 1501 1553 $c2s_mac_algorithms, 1502 1554 ['hmac-sha1-96', 'hmac-md5-96'] 1555 )); 1556 } 1557 break; 1558 case substr($this->server_identifier, 0, 24) == 'SSH-2.0-TurboFTP_SERVER_': 1559 if (!isset($preferred['server_to_client']['crypt'])) { 1560 $s2c_encryption_algorithms = array_values(array_diff( 1561 $s2c_encryption_algorithms, 1562 ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] 1563 )); 1564 } 1565 if (!isset($preferred['client_to_server']['crypt'])) { 1566 $c2s_encryption_algorithms = array_values(array_diff( 1567 $c2s_encryption_algorithms, 1568 ['aes128-gcm@openssh.com', 'aes256-gcm@openssh.com'] 1503 1569 )); 1504 1570 } … … 2122 2188 * 2123 2189 * @param string $username 2124 * @param string| AsymmetricKey|array[]|Agent|null ...$args2190 * @param string|PrivateKey|array[]|Agent|null ...$args 2125 2191 * @return bool 2126 2192 * @see self::_login() … … 2147 2213 * 2148 2214 * @param string $username 2149 * @param string ...$args2215 * @param string|PrivateKey|array[]|Agent|null ...$args 2150 2216 * @return bool 2151 2217 * @see self::_login_helper() … … 2267 2333 } 2268 2334 $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); 2269 throw new ConnectionClosedException('Connection closed by server'); 2270 } 2271 2272 list($type, $service) = Strings::unpackSSH2('Cs', $response); 2335 throw $e; 2336 } 2337 2338 list($type) = Strings::unpackSSH2('C', $response); 2339 2340 if ($type == NET_SSH2_MSG_EXT_INFO) { 2341 list($nr_extensions) = Strings::unpackSSH2('N', $response); 2342 for ($i = 0; $i < $nr_extensions; $i++) { 2343 list($extension_name, $extension_value) = Strings::unpackSSH2('ss', $response); 2344 if ($extension_name == 'server-sig-algs') { 2345 $this->supported_private_key_algorithms = explode(',', $extension_value); 2346 } 2347 } 2348 2349 $response = $this->get_binary_packet(); 2350 list($type) = Strings::unpackSSH2('C', $response); 2351 } 2352 2353 list($service) = Strings::unpackSSH2('s', $response); 2354 2273 2355 if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') { 2274 2356 $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); … … 2546 2628 $algos = ['rsa-sha2-256', 'rsa-sha2-512', 'ssh-rsa']; 2547 2629 if (isset($this->preferred['hostkey'])) { 2548 $algos = array_intersect($ this->preferred['hostkey'], $algos);2630 $algos = array_intersect($algos, $this->preferred['hostkey']); 2549 2631 } 2550 2632 $algo = self::array_intersect_first($algos, $this->supported_private_key_algorithms); … … 2730 2812 } 2731 2813 2732 if ($this->in_request_pty_exec) { 2733 throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); 2734 } 2735 2736 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to 2737 // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, 2738 // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. 2739 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info 2740 $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; 2741 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy 2742 // uses 0x4000, that's what will be used here, as well. 2743 $packet_size = 0x4000; 2744 2745 $packet = Strings::packSSH2( 2746 'CsN3', 2747 NET_SSH2_MSG_CHANNEL_OPEN, 2748 'session', 2749 self::CHANNEL_EXEC, 2750 $this->window_size_server_to_client[self::CHANNEL_EXEC], 2751 $packet_size 2752 ); 2753 $this->send_binary_packet($packet); 2754 2755 $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; 2756 2757 $this->get_channel_packet(self::CHANNEL_EXEC); 2814 //if ($this->isPTYOpen()) { 2815 // throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); 2816 //} 2817 2818 $this->openChannel(self::CHANNEL_EXEC); 2758 2819 2759 2820 if ($this->request_pty === true) { … … 2780 2841 throw new \RuntimeException('Unable to request pseudo-terminal'); 2781 2842 } 2782 2783 $this->in_request_pty_exec = true;2784 2843 } 2785 2844 … … 2811 2870 $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; 2812 2871 2813 if ($this->in_request_pty_exec) { 2872 if ($this->request_pty === true) { 2873 $this->channel_id_last_interactive = self::CHANNEL_EXEC; 2814 2874 return true; 2815 2875 } … … 2837 2897 2838 2898 /** 2839 * Creates an interactive shell 2840 * 2841 * @see self::read() 2842 * @see self::write() 2899 * How many channels are currently open? 2900 * 2901 * @return int 2902 */ 2903 public function getOpenChannelCount() 2904 { 2905 return $this->channelCount; 2906 } 2907 2908 /** 2909 * Opens a channel 2910 * 2911 * @param string $channel 2912 * @param bool $skip_extended 2843 2913 * @return bool 2844 * @throws \UnexpectedValueException on receipt of unexpected packets 2845 * @throws \RuntimeException on other errors 2846 */ 2847 private function initShell() 2848 { 2849 if ($this->in_request_pty_exec === true) { 2850 return true; 2851 } 2852 2853 $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; 2914 */ 2915 protected function openChannel($channel, $skip_extended = false) 2916 { 2917 if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) { 2918 throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again'); 2919 } 2920 2921 $this->channelCount++; 2922 2923 if ($this->channelCount > 1 && $this->errorOnMultipleChannels) { 2924 throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels"); 2925 } 2926 2927 // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to 2928 // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, 2929 // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. 2930 // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info 2931 $this->window_size_server_to_client[$channel] = $this->window_size; 2932 // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy 2933 // uses 0x4000, that's what will be used here, as well. 2854 2934 $packet_size = 0x4000; 2855 2935 … … 2858 2938 NET_SSH2_MSG_CHANNEL_OPEN, 2859 2939 'session', 2860 self::CHANNEL_SHELL,2861 $this->window_size_server_to_client[ self::CHANNEL_SHELL],2940 $channel, 2941 $this->window_size_server_to_client[$channel], 2862 2942 $packet_size 2863 2943 ); … … 2865 2945 $this->send_binary_packet($packet); 2866 2946 2867 $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; 2868 2869 $this->get_channel_packet(self::CHANNEL_SHELL); 2947 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN; 2948 2949 return $this->get_channel_packet($channel, $skip_extended); 2950 } 2951 2952 /** 2953 * Creates an interactive shell 2954 * 2955 * Returns bool(true) if the shell was opened. 2956 * Returns bool(false) if the shell was already open. 2957 * 2958 * @see self::isShellOpen() 2959 * @see self::read() 2960 * @see self::write() 2961 * @return bool 2962 * @throws InsufficientSetupException if not authenticated 2963 * @throws \UnexpectedValueException on receipt of unexpected packets 2964 * @throws \RuntimeException on other errors 2965 */ 2966 public function openShell() 2967 { 2968 if (!$this->isAuthenticated()) { 2969 throw new InsufficientSetupException('Operation disallowed prior to login()'); 2970 } 2971 2972 $this->openChannel(self::CHANNEL_SHELL); 2870 2973 2871 2974 $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); … … 2908 3011 $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; 2909 3012 3013 $this->channel_id_last_interactive = self::CHANNEL_SHELL; 3014 2910 3015 $this->bitmap |= self::MASK_SHELL; 2911 3016 … … 2914 3019 2915 3020 /** 2916 * Return the channel to be used with read() / write() 2917 * 3021 * Return the channel to be used with read(), write(), and reset(), if none were specified 3022 * @deprecated for lack of transparency in intended channel target, to be potentially replaced 3023 * with method which guarantees open-ness of all yielded channels and throws 3024 * error for multiple open channels 2918 3025 * @see self::read() 2919 3026 * @see self::write() … … 2923 3030 { 2924 3031 switch (true) { 2925 case $this->i n_subsystem:3032 case $this->is_channel_status_data(self::CHANNEL_SUBSYSTEM): 2926 3033 return self::CHANNEL_SUBSYSTEM; 2927 case $this->i n_request_pty_exec:3034 case $this->is_channel_status_data(self::CHANNEL_EXEC): 2928 3035 return self::CHANNEL_EXEC; 2929 3036 default: 2930 3037 return self::CHANNEL_SHELL; 2931 3038 } 3039 } 3040 3041 /** 3042 * Indicates the DATA status on the given channel 3043 * 3044 * @param int $channel The channel number to evaluate 3045 * @return bool 3046 */ 3047 private function is_channel_status_data($channel) 3048 { 3049 return isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA; 2932 3050 } 2933 3051 … … 2988 3106 * if $mode == self::READ_REGEX, a regular expression. 2989 3107 * 3108 * If not specifying a channel, an open interactive channel will be selected, or, if there are 3109 * no open channels, an interactive shell will be created. If there are multiple open 3110 * interactive channels, a legacy behavior will apply in which channel selection prioritizes 3111 * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive 3112 * channels, callers are discouraged from relying on this legacy behavior and should specify 3113 * the intended channel. 3114 * 2990 3115 * @see self::write() 2991 3116 * @param string $expect 2992 * @param int $mode 3117 * @param int $mode One of the self::READ_* constants 3118 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 2993 3119 * @return string|bool|null 2994 3120 * @throws \RuntimeException on connection error 2995 */ 2996 public function read($expect = '', $mode = self::READ_SIMPLE) 2997 { 3121 * @throws InsufficientSetupException on unexpected channel status, possibly due to closure 3122 */ 3123 public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null) 3124 { 3125 if (!$this->isAuthenticated()) { 3126 throw new InsufficientSetupException('Operation disallowed prior to login()'); 3127 } 3128 2998 3129 $this->curTimeout = $this->timeout; 2999 3130 $this->is_timeout = false; 3000 3131 3001 if (!$this->isAuthenticated()) { 3002 throw new InsufficientSetupException('Operation disallowed prior to login()'); 3003 } 3004 3005 if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) { 3006 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3007 } 3008 3009 $channel = $this->get_interactive_channel(); 3132 if ($channel === null) { 3133 $channel = $this->get_interactive_channel(); 3134 } 3135 3136 if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) { 3137 if ($channel != self::CHANNEL_SHELL) { 3138 throw new InsufficientSetupException('Data is not available on channel'); 3139 } elseif (!$this->openShell()) { 3140 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3141 } 3142 } 3010 3143 3011 3144 if ($mode == self::READ_NEXT) { … … 3025 3158 $response = $this->get_channel_packet($channel); 3026 3159 if ($response === true) { 3027 $this->in_request_pty_exec = false;3028 3160 return Strings::shift($this->interactiveBuffer, strlen($this->interactiveBuffer)); 3029 3161 } … … 3035 3167 /** 3036 3168 * Inputs a command into an interactive shell. 3169 * 3170 * If not specifying a channel, an open interactive channel will be selected, or, if there are 3171 * no open channels, an interactive shell will be created. If there are multiple open 3172 * interactive channels, a legacy behavior will apply in which channel selection prioritizes 3173 * an active subsystem, the exec pty, and, lastly, the shell. If using multiple interactive 3174 * channels, callers are discouraged from relying on this legacy behavior and should specify 3175 * the intended channel. 3037 3176 * 3038 3177 * @see SSH2::read() 3039 3178 * @param string $cmd 3179 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 3040 3180 * @return void 3041 3181 * @throws \RuntimeException on connection error 3042 */ 3043 public function write($cmd) 3182 * @throws InsufficientSetupException on unexpected channel status, possibly due to closure 3183 */ 3184 public function write($cmd, $channel = null) 3044 3185 { 3045 3186 if (!$this->isAuthenticated()) { … … 3047 3188 } 3048 3189 3049 if (!($this->bitmap & self::MASK_SHELL) && !$this->initShell()) { 3050 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3051 } 3052 3053 $this->send_channel_packet($this->get_interactive_channel(), $cmd); 3190 if ($channel === null) { 3191 $channel = $this->get_interactive_channel(); 3192 } 3193 3194 if (!$this->is_channel_status_data($channel)) { 3195 if ($channel != self::CHANNEL_SHELL) { 3196 throw new InsufficientSetupException('Data is not available on channel'); 3197 } elseif (!$this->openShell()) { 3198 throw new \RuntimeException('Unable to initiate an interactive shell session'); 3199 } 3200 } 3201 3202 $this->send_channel_packet($channel, $cmd); 3054 3203 } 3055 3204 … … 3069 3218 public function startSubsystem($subsystem) 3070 3219 { 3071 $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; 3072 3073 $packet = Strings::packSSH2( 3074 'CsN3', 3075 NET_SSH2_MSG_CHANNEL_OPEN, 3076 'session', 3077 self::CHANNEL_SUBSYSTEM, 3078 $this->window_size, 3079 0x4000 3080 ); 3081 3082 $this->send_binary_packet($packet); 3083 3084 $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; 3085 3086 $this->get_channel_packet(self::CHANNEL_SUBSYSTEM); 3220 $this->openChannel(self::CHANNEL_SUBSYSTEM); 3087 3221 3088 3222 $packet = Strings::packSSH2( … … 3104 3238 $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; 3105 3239 3106 $this->bitmap |= self::MASK_SHELL; 3107 $this->in_subsystem = true; 3240 $this->channel_id_last_interactive = self::CHANNEL_SUBSYSTEM; 3108 3241 3109 3242 return true; … … 3118 3251 public function stopSubsystem() 3119 3252 { 3120 $this->in_subsystem = false; 3121 $this->close_channel(self::CHANNEL_SUBSYSTEM); 3253 if ($this->isInteractiveChannelOpen(self::CHANNEL_SUBSYSTEM)) { 3254 $this->close_channel(self::CHANNEL_SUBSYSTEM); 3255 } 3122 3256 return true; 3123 3257 } … … 3128 3262 * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call 3129 3263 * 3130 */ 3131 public function reset() 3132 { 3133 $this->close_channel($this->get_interactive_channel()); 3264 * If not specifying a channel, an open interactive channel will be selected. If there are 3265 * multiple open interactive channels, a legacy behavior will apply in which channel selection 3266 * prioritizes an active subsystem, the exec pty, and, lastly, the shell. If using multiple 3267 * interactive channels, callers are discouraged from relying on this legacy behavior and 3268 * should specify the intended channel. 3269 * 3270 * @param int|null $channel Channel id returned by self::getInteractiveChannelId() 3271 * @return void 3272 */ 3273 public function reset($channel = null) 3274 { 3275 if ($channel === null) { 3276 $channel = $this->get_interactive_channel(); 3277 } 3278 if ($this->isInteractiveChannelOpen($channel)) { 3279 $this->close_channel($channel); 3280 } 3134 3281 } 3135 3282 … … 3177 3324 public function isConnected() 3178 3325 { 3179 return ( bool) ($this->bitmap & self::MASK_CONNECTED);3326 return ($this->bitmap & self::MASK_CONNECTED) && is_resource($this->fsock) && !feof($this->fsock); 3180 3327 } 3181 3328 … … 3188 3335 { 3189 3336 return (bool) ($this->bitmap & self::MASK_LOGIN); 3337 } 3338 3339 /** 3340 * Is the interactive shell active? 3341 * 3342 * @return bool 3343 */ 3344 public function isShellOpen() 3345 { 3346 return $this->isInteractiveChannelOpen(self::CHANNEL_SHELL); 3347 } 3348 3349 /** 3350 * Is the exec pty active? 3351 * 3352 * @return bool 3353 */ 3354 public function isPTYOpen() 3355 { 3356 return $this->isInteractiveChannelOpen(self::CHANNEL_EXEC); 3357 } 3358 3359 /** 3360 * Is the given interactive channel active? 3361 * 3362 * @param int $channel Channel id returned by self::getInteractiveChannelId() 3363 * @return bool 3364 */ 3365 public function isInteractiveChannelOpen($channel) 3366 { 3367 return $this->isAuthenticated() && $this->is_channel_status_data($channel); 3368 } 3369 3370 /** 3371 * Returns a channel identifier, presently of the last interactive channel opened, regardless of current status. 3372 * Returns 0 if no interactive channel has been opened. 3373 * 3374 * @see self::isInteractiveChannelOpen() 3375 * @return int 3376 */ 3377 public function getInteractiveChannelId() 3378 { 3379 return $this->channel_id_last_interactive; 3190 3380 } 3191 3381 … … 3206 3396 } 3207 3397 3208 $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;3209 $packet_size = 0x4000;3210 $packet = Strings::packSSH2(3211 'CsN3',3212 NET_SSH2_MSG_CHANNEL_OPEN,3213 'session',3214 self::CHANNEL_KEEP_ALIVE,3215 $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],3216 $packet_size3217 );3218 3219 3398 try { 3220 $this->send_binary_packet($packet); 3221 3222 $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; 3223 3224 $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE); 3399 $this->openChannel(self::CHANNEL_KEEP_ALIVE); 3225 3400 } catch (\RuntimeException $e) { 3226 3401 return $this->reconnect(); … … 3262 3437 $this->retry_connect = true; 3263 3438 $this->get_seq_no = $this->send_seq_no = 0; 3439 $this->channel_status = []; 3440 $this->channel_id_last_interactive = 0; 3264 3441 } 3265 3442 … … 3284 3461 if (!$this->curTimeout) { 3285 3462 if ($this->keepAlive <= 0) { 3286 @stream_select($read, $write, $except, null);3463 static::stream_select($read, $write, $except, null); 3287 3464 } else { 3288 if (! @stream_select($read, $write, $except, $this->keepAlive)) {3465 if (!static::stream_select($read, $write, $except, $this->keepAlive)) { 3289 3466 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); 3290 3467 return $this->get_binary_packet(true); … … 3300 3477 3301 3478 if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { 3302 if (! @stream_select($read, $write, $except, $this->keepAlive)) {3479 if (!static::stream_select($read, $write, $except, $this->keepAlive)) { 3303 3480 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); 3304 3481 $elapsed = microtime(true) - $start; … … 3314 3491 3315 3492 // this can return a "stream_select(): unable to select [4]: Interrupted system call" error 3316 if (! @stream_select($read, $write, $except, $sec, $usec)) {3493 if (!static::stream_select($read, $write, $except, $sec, $usec)) { 3317 3494 $this->is_timeout = true; 3318 3495 return true; … … 3505 3682 if (defined('NET_SSH2_LOGGING')) { 3506 3683 $current = microtime(true); 3507 $message_number = isset( $this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';3684 $message_number = isset(self::$message_numbers[ord($payload[0])]) ? self::$message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; 3508 3685 $message_number = '<- ' . $message_number . 3509 3686 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; … … 3587 3764 Strings::shift($payload, 1); 3588 3765 list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload); 3589 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n$message";3766 $this->errors[] = 'SSH_MSG_DISCONNECT: ' . self::$disconnect_reasons[$reason_code] . "\r\n$message"; 3590 3767 $this->bitmap = 0; 3591 3768 return false; … … 3774 3951 public function disablePTY() 3775 3952 { 3776 if ($this->i n_request_pty_exec) {3953 if ($this->isPTYOpen()) { 3777 3954 $this->close_channel(self::CHANNEL_EXEC); 3778 $this->in_request_pty_exec = false;3779 3955 } 3780 3956 $this->request_pty = false; … … 3802 3978 * - if the channel status is CHANNEL_OPEN and the response was CHANNEL_OPEN_CONFIRMATION 3803 3979 * - if the channel status is CHANNEL_REQUEST and the response was CHANNEL_SUCCESS 3980 * - if the channel status is CHANNEL_CLOSE and the response was CHANNEL_CLOSE 3804 3981 * 3805 3982 * bool(false) is returned if: … … 3969 4146 } 3970 4147 case NET_SSH2_MSG_CHANNEL_CLOSE: 3971 return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended); 4148 if ($client_channel == $channel && $type == NET_SSH2_MSG_CHANNEL_CLOSE) { 4149 return true; 4150 } 4151 return $this->get_channel_packet($client_channel, $skip_extended); 3972 4152 } 3973 4153 } … … 4004 4184 $this->curTimeout = 5; 4005 4185 4006 if ($this->bitmap & self::MASK_SHELL) { 4007 $this->bitmap &= ~self::MASK_SHELL; 4008 } 4186 $this->close_channel_bitmap($channel); 4187 4009 4188 if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { 4010 4189 $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); … … 4012 4191 4013 4192 $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; 4193 $this->channelCount--; 4194 4014 4195 if ($client_channel == $channel) { 4015 4196 return true; … … 4158 4339 if (defined('NET_SSH2_LOGGING')) { 4159 4340 $current = microtime(true); 4160 $message_number = isset( $this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';4341 $message_number = isset(self::$message_numbers[ord($logged[0])]) ? self::$message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; 4161 4342 $message_number = '-> ' . $message_number . 4162 4343 ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; … … 4167 4348 if (strlen($packet) != $sent) { 4168 4349 $this->bitmap = 0; 4169 throw new \RuntimeException("Only $sent of " . strlen($packet) . " bytes were sent"); 4350 $message = $sent === false ? 4351 'Unable to write ' . strlen($packet) . ' bytes' : 4352 "Only $sent of " . strlen($packet) . " bytes were sent"; 4353 throw new \RuntimeException($message); 4170 4354 } 4171 4355 } … … 4343 4527 4344 4528 $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; 4529 $this->channelCount--; 4345 4530 4346 4531 $this->curTimeout = 5; 4347 4532 4348 4533 while (!is_bool($this->get_channel_packet($client_channel))) { 4349 }4350 4351 if ($this->is_timeout) {4352 $this->disconnect();4353 4534 } 4354 4535 … … 4357 4538 } 4358 4539 4359 if ($this->bitmap & self::MASK_SHELL) { 4360 $this->bitmap &= ~self::MASK_SHELL; 4540 $this->close_channel_bitmap($client_channel); 4541 } 4542 4543 /** 4544 * Maintains execution state bitmap in response to channel closure 4545 * 4546 * @param int $client_channel The channel number to maintain closure status of 4547 * @return void 4548 */ 4549 private function close_channel_bitmap($client_channel) 4550 { 4551 switch ($client_channel) { 4552 case self::CHANNEL_SHELL: 4553 // Shell status has been maintained in the bitmap for backwards 4554 // compatibility sake, but can be removed going forward 4555 if ($this->bitmap & self::MASK_SHELL) { 4556 $this->bitmap &= ~self::MASK_SHELL; 4557 } 4558 break; 4361 4559 } 4362 4560 } … … 4396 4594 * @access protected 4397 4595 */ 4398 protected function define_array(...$args)4596 protected static function define_array(...$args) 4399 4597 { 4400 4598 foreach ($args as $arg) { … … 4799 4997 ] 4800 4998 ]; 4999 } 5000 5001 /** 5002 * Force multiple channels (even if phpseclib has decided to disable them) 5003 */ 5004 public function forceMultipleChannels() 5005 { 5006 $this->errorOnMultipleChannels = false; 4801 5007 } 4802 5008
Note: See TracChangeset
for help on using the changeset viewer.