Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/automattic.zip
Назад
PK E/]��J�G �G jetpack-admin-ui/LICENSE.txtnu ��� This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. PK E/]��� � jetpack-admin-ui/composer.jsonnu ��� { "name": "automattic/jetpack-admin-ui", "description": "Generic Jetpack wp-admin UI elements", "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { "php": ">=7.2" }, "require-dev": { "yoast/phpunit-polyfills": "^4.0.0", "automattic/jetpack-changelogger": "^6.0.5", "automattic/jetpack-logo": "^3.0.5", "automattic/jetpack-test-environment": "@dev", "automattic/phpunit-select-config": "^1.0.3" }, "suggest": { "automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package." }, "autoload": { "classmap": [ "src/" ] }, "scripts": { "phpunit": [ "phpunit-select-config phpunit.#.xml.dist --colors=always" ], "test-coverage": [ "php -dpcov.directory=. ./vendor/bin/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\"" ], "test-php": [ "@composer phpunit" ] }, "minimum-stability": "dev", "prefer-stable": true, "extra": { "autotagger": true, "mirror-repo": "Automattic/jetpack-admin-ui", "textdomain": "jetpack-admin-ui", "changelogger": { "link-template": "https://github.com/Automattic/jetpack-admin-ui/compare/${old}...${new}" }, "branch-alias": { "dev-trunk": "0.5.x-dev" }, "version-constants": { "::PACKAGE_VERSION": "src/class-admin-menu.php" } }, "config": { "allow-plugins": { "roots/wordpress-core-installer": true } } } PK E/]��Y�� � ) jetpack-admin-ui/src/class-admin-menu.phpnu ��� <?php /** * Admin Menu Registration * * @package automattic/jetpack-admin-ui */ namespace Automattic\Jetpack\Admin_UI; /** * This class offers a wrapper to add_submenu_page and makes sure stand-alone plugin's menu items are always added under the Jetpack top level menu. * If the Jetpack top level was not previously registered by other plugin, it will be registered here. */ class Admin_Menu { const PACKAGE_VERSION = '0.5.11'; /** * Whether this class has been initialized * * @var boolean */ private static $initialized = false; /** * List of menu items enqueued to be added * * @var array */ private static $menu_items = array(); /** * Initialize the class and set up the main hook * * @return void */ public static function init() { if ( ! self::$initialized ) { self::$initialized = true; self::handle_akismet_menu(); add_action( 'admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998. add_action( 'network_admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998. } } /** * Handles the Akismet menu item when used alongside other stand-alone plugins * * When Jetpack plugin is present, Akismet menu item is moved under the Jetpack top level menu, but if Akismet is active alongside other stand-alone plugins, * we use this method to move the menu item. */ private static function handle_akismet_menu() { if ( class_exists( 'Akismet_Admin' ) ) { add_action( 'admin_menu', function () { // Prevent Akismet from adding a menu item. remove_action( 'admin_menu', array( 'Akismet_Admin', 'admin_menu' ), 5 ); // Add an Anti-spam menu item for Jetpack. self::add_menu( __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), __( 'Akismet Anti-spam', 'jetpack-admin-ui' ), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ), 6 ); }, 4 ); } } /** * Callback to the admin_menu and network_admin_menu hooks that will register the enqueued menu items * * @return void */ public static function admin_menu_hook_callback() { $can_see_toplevel_menu = true; $jetpack_plugin_present = class_exists( 'Jetpack_React_Page' ); $icon = method_exists( '\Automattic\Jetpack\Assets\Logo', 'get_base64_logo' ) ? ( new \Automattic\Jetpack\Assets\Logo() )->get_base64_logo() : 'dashicons-admin-plugins'; if ( ! $jetpack_plugin_present ) { add_menu_page( 'Jetpack', 'Jetpack', 'edit_posts', 'jetpack', '__return_null', $icon, 3 ); // If Jetpack plugin is not present, user will only be able to see this menu if they have enough capability to at least one of the sub menus being added. $can_see_toplevel_menu = false; } /** * The add_sub_menu function has a bug and will not keep the right order of menu items. * * @see https://core.trac.wordpress.org/ticket/52035 * Let's order the items before registering them. * Since this all happens after the Jetpack plugin menu items were added, all items will be added after Jetpack plugin items - unless position is very low number (smaller than the number of menu items present in Jetpack plugin). */ usort( self::$menu_items, function ( $a, $b ) { $position_a = empty( $a['position'] ) ? 0 : $a['position']; $position_b = empty( $b['position'] ) ? 0 : $b['position']; $result = $position_a <=> $position_b; if ( 0 === $result ) { $result = strcmp( $a['menu_title'], $b['menu_title'] ); } return $result; } ); foreach ( self::$menu_items as $menu_item ) { if ( ! current_user_can( $menu_item['capability'] ) ) { continue; } $can_see_toplevel_menu = true; add_submenu_page( 'jetpack', $menu_item['page_title'], $menu_item['menu_title'], $menu_item['capability'], $menu_item['menu_slug'], $menu_item['function'], $menu_item['position'] ); } if ( ! $jetpack_plugin_present ) { remove_submenu_page( 'jetpack', 'jetpack' ); } if ( ! $can_see_toplevel_menu ) { remove_menu_page( 'jetpack' ); } } /** * Adds a new submenu to the Jetpack Top level menu * * The parameters this method accepts are the same as @see add_submenu_page. This class will * aggreagate all menu items registered by stand-alone plugins and make sure they all go under the same * Jetpack top level menu. It will also handle the top level menu registration in case the Jetpack plugin is not present. * * @param string $page_title The text to be displayed in the title tags of the page when the menu * is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by. Should be unique for this menu * and only include lowercase alphanumeric, dashes, and underscores characters * to be compatible with sanitize_key(). * @param callable|null $function The function to be called to output the content for this page. * @param int $position The position in the menu order this item should appear. Leave empty typically. * * @return string The resulting page's hook_suffix */ public static function add_menu( $page_title, $menu_title, $capability, $menu_slug, $function, $position = null ) { self::init(); self::$menu_items[] = compact( 'page_title', 'menu_title', 'capability', 'menu_slug', 'function', 'position' ); /** * Let's return the page hook so consumers can use. * We know all pages will be under Jetpack top level menu page, so we can hardcode the first part of the string. * Using get_plugin_page_hookname here won't work because the top level page is not registered yet. */ return 'jetpack_page_' . $menu_slug; } /** * Removes an already added submenu * * @param string $menu_slug The slug of the submenu to remove. * * @return array|false The removed submenu on success, false if not found. */ public static function remove_menu( $menu_slug ) { foreach ( self::$menu_items as $index => $menu_item ) { if ( $menu_item['menu_slug'] === $menu_slug ) { unset( self::$menu_items[ $index ] ); return $menu_item; } } return false; } /** * Gets the slug for the first item under the Jetpack top level menu * * @return string|null */ public static function get_top_level_menu_item_slug() { global $submenu; if ( ! empty( $submenu['jetpack'] ) ) { $item = reset( $submenu['jetpack'] ); if ( isset( $item[2] ) ) { return $item[2]; } } } /** * Gets the URL for the first item under the Jetpack top level menu * * @param string $fallback If Jetpack menu is not there or no children is found, return this fallback instead. Default to admin_url(). * @return string */ public static function get_top_level_menu_item_url( $fallback = false ) { $slug = self::get_top_level_menu_item_slug(); if ( $slug ) { $url = menu_page_url( $slug, false ); return $url; } $url = $fallback ? $fallback : admin_url(); return $url; } } PK E/]k �]( ( block-delimiter/composer.jsonnu ��� { "name": "automattic/block-delimiter", "description": "Efficiently work with block structure", "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { "php": ">=7.2" }, "require-dev": { "automattic/jetpack-test-environment": "@dev", "automattic/jetpack-changelogger": "^6.0.6", "automattic/phpunit-select-config": "^1.0.3" }, "autoload": { "classmap": [ "src/" ] }, "autoload-dev": { "classmap": [ "tests/php/lib/" ], "psr-4": { "Automattic\\Block_Delimiter\\Tests\\": "tests/php/" } }, "scripts": { "phpunit": [ "phpunit-select-config phpunit.#.xml.dist --colors=always" ], "test-coverage": [ "php -dpcov.directory=. ./vendor/bin/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\"" ], "test-php": [ "@composer phpunit" ] }, "minimum-stability": "dev", "prefer-stable": true, "extra": { "autotagger": true, "branch-alias": { "dev-trunk": "0.3.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/block-delimiter/compare/v${old}...v${new}" }, "mirror-repo": "Automattic/block-delimiter", "textdomain": "jetpack-block-delimiter" }, "suggest": { "automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package." } } PK E/]��J�G �G block-delimiter/LICENSE.txtnu ��� This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. PK E/]tw�6�u �u - block-delimiter/src/class-block-delimiter.phpnu ��� <?php /** * Efficiently working with block structure. * * @package automattic/block-delimiter */ declare( strict_types = 1 ); namespace Automattic; use Exception; /** * Class for efficiently working with block structure. * * This class follows design values of the HTML API: * - minimize allocations and strive for zero memory overhead * - make costs explicit; pay only for what you need * - follow a streaming, re-entrant design for pausing and aborting * * For usage, jump straight to {@see static::next_delimiter}. */ class Block_Delimiter { /** * Indicates if the last operation failed, otherwise * will be `null` for success. * * @var string|null */ private static $last_error = self::UNINITIALIZED; /** * Indicates failures from decoding JSON attributes. * * @var int */ private $last_json_error = JSON_ERROR_NONE; /** * Holds a reference to the original source text from which to * extract the parsed spans of the delimiter. * * @var string */ private $source_text; /** * Byte offset into source text where entire delimiter begins. * * @var int */ private $delimiter_at; /** * Byte length of full span of delimiter. * * @var int */ private $delimiter_length; /** * Byte offset where namespace span begins. * * @var int */ private $namespace_at; /** * Byte length of namespace span, or `0` if implicitly in the "core" namespace. * * @var int */ private $namespace_length; /** * Byte offset where block name span begins. * * @var int */ private $name_at; /** * Byte length of block name span. * * @var int */ private $name_length; /** * Whether the delimiter contains the block self-closing flag. * * This may be erroneous if present within a block closer, * therefore the {@see self::has_void_flag} can be used by * calling code to perform appropriate error-handling. * * @var bool */ private $has_void_flag = false; /** * Byte offset where JSON attributes span begins. * * @var int */ private $json_at; /** * Byte length of JSON attributes span, or `0` if none are present. * * @var int */ private $json_length; /** * Indicates what kind of block comment delimiter this represents. * * One of: * * - `static::OPENER` If the delimiter is opening a block. * - `static::CLOSER` If the delimiter is closing an open block. * - `static::VOID` If the delimiter represents a void block with no inner content. * * If a parsed comment delimiter contains both the closing and the void * flags then it will be interpreted as a void block to match the behavior * of the official block parser, however, this is a mistake and probably * the block ought to close an open block of the same name, if one is open. * * @var string */ private $type; /** * Finds the next block delimiter in a text document and returns a parsed * block delimiter info record if it parses, otherwise returns `null`. * * Block comment delimiters must be valid HTML comments and may contain JSON. * This search does not determine, however, if the JSON is valid. * * Example delimiters: * * `<!-- wp:paragraph {"dropCap": true} -->` * `<!-- wp:separator /-->` * `<!-- /wp:paragraph -->` * * In the case that a block comment delimiter contains both the void indicator and * also the closing indicator, it will be treated as a void block. * * Example: * * // Find all image block opening delimiters. * $at = 0; * $end = strlen( $html ); * $images = array(); * while ( $at < $end ) { * $delimiter = Block_Delimiter::next_delimiter( $html, $at, $next_at, $next_length ); * if ( ! isset( $delimiter ) ) { * break; * } * * if ( * Block_Delimiter::OPENER === $delimiter->get_delimiter_type() && * $delimiter->is_block_type( 'core/image' ) * ) { * $images[] = $delimiter; * } * * $at = $next_at + $next_length; * } * * @param string $text Input document possibly containing block comment delimiters. * @param int $starting_byte_offset Where in the input document to begin searching. * @param int|null $match_byte_offset Optional. When provided, will be set to the byte offset in * the input document where the delimiter was found, if one * is found, otherwise not set. * @param int|null $match_byte_length Optional. When provided, will be set to the byte length of * the matched delimiter if one is found, otherwise not set. * @return Block_Delimiter|null Parsed block delimiter info record if found, otherwise `null`. */ public static function next_delimiter( string $text, int $starting_byte_offset, ?int &$match_byte_offset = null, ?int &$match_byte_length = null ): ?Block_Delimiter { $end = strlen( $text ); $at = $starting_byte_offset; $delimiter = null; static::$last_error = null; while ( $at < $end ) { /* * Find the next possible opening. * * This follows the behavior in the official block parser, which treats a post * as a list of blocks with nested HTML. If HTML comment syntax appears within * an HTML attribute value, SCRIPT or STYLE element, or in other select places, * which it can do inside of HTML, then the block parsing may break. * * For a more robust parse scan through the document with the HTML API. In * practice, this has not been a problem in the entire history of blocks. */ $comment_opening_at = strpos( $text, '<!--', $at ); if ( false === $comment_opening_at ) { return null; } $opening_whitespace_at = $comment_opening_at + 4; $opening_whitespace_length = strspn( $text, " \t\f\r\n", $opening_whitespace_at ); if ( 0 === $opening_whitespace_length ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $wp_prefix_at = $opening_whitespace_at + $opening_whitespace_length; if ( $wp_prefix_at >= $end ) { static::$last_error = self::INCOMPLETE_INPUT; return null; } $has_closer = false; if ( '/' === $text[ $wp_prefix_at ] ) { $has_closer = true; ++$wp_prefix_at; } if ( 0 !== substr_compare( $text, 'wp:', $wp_prefix_at, 3 ) ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $namespace_at = $wp_prefix_at + 3; if ( $namespace_at >= $end ) { static::$last_error = self::INCOMPLETE_INPUT; return null; } $start_of_namespace = $text[ $namespace_at ]; // The namespace must start with a-z. if ( 'a' > $start_of_namespace || 'z' < $start_of_namespace ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $namespace_length = 1 + strspn( $text, 'abcdefghijklmnopqrstuvwxyz0123456789-_', $namespace_at + 1 ); $separator_at = $namespace_at + $namespace_length; if ( $separator_at >= $end ) { static::$last_error = self::INCOMPLETE_INPUT; return null; } $has_separator = '/' === $text[ $separator_at ]; if ( $has_separator ) { $name_at = $separator_at + 1; $start_of_name = $text[ $name_at ]; if ( 'a' > $start_of_name || 'z' < $start_of_name ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $name_length = 1 + strspn( $text, 'abcdefghijklmnopqrstuvwxyz0123456789-_', $name_at + 1 ); } else { $name_at = $namespace_at; $name_length = $namespace_length; $namespace_length = 0; } $after_name_whitespace_at = $name_at + $name_length; $after_name_whitespace_length = strspn( $text, " \t\f\r\n", $after_name_whitespace_at ); if ( 0 === $after_name_whitespace_length ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $json_at = $after_name_whitespace_at + $after_name_whitespace_length; if ( $json_at >= $end ) { static::$last_error = self::INCOMPLETE_INPUT; return null; } $has_json = '{' === $text[ $json_at ]; $json_length = 0; /* * For the final span of the delimiter it's most efficient to find the end * of the HTML comment and work backwards. This prevents complicated parsing * inside the JSON span, which cannot contain the HTML comment terminator. * * This also matches the behavior in the official block parser, though it * allows for matching invalid JSON content. */ $comment_closing_at = strpos( $text, '-->', $json_at ); if ( false === $comment_closing_at ) { static::$last_error = self::INCOMPLETE_INPUT; return null; } /* * It looks like this logic leaves an error in here, when the position * overlaps the JSON or block name. However, for neither of those is it * possible to parse a valid block if that last overlapping character * is the void flag. This, therefore, will be valid regardless of how * the rest of the comment delimiter is written. */ if ( '/' === $text[ $comment_closing_at - 1 ] ) { $has_void_flag = true; $void_flag_length = 1; } else { $has_void_flag = false; $void_flag_length = 0; } /* * If there's no JSON, then the span of text after the name * until the comment closing must be completely whitespace. */ if ( ! $has_json ) { $max_whitespace_length = $comment_closing_at - $json_at - $void_flag_length; // This shouldn't be possible, but it can't be allowed regardless. if ( $max_whitespace_length < 0 ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } $closing_whitespace_length = strspn( $text, " \t\f\r\n", $json_at, $comment_closing_at - $json_at - $void_flag_length ); if ( 0 === $after_name_whitespace_length + $closing_whitespace_length ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } // This must be a block delimiter! $delimiter = new static(); break; } // There's no JSON, so attempt to find its boundary. $after_json_whitespace_length = 0; for ( $char_at = $comment_closing_at - $void_flag_length - 1; $char_at > $json_at; $char_at-- ) { $char = $text[ $char_at ]; switch ( $char ) { case ' ': case "\t": case "\f": case "\r": case "\n": ++$after_json_whitespace_length; continue 2; case '}': $json_length = $char_at - $json_at + 1; break 2; default: ++$at; continue 3; } } if ( 0 === $json_length || 0 === $after_json_whitespace_length ) { $at = self::find_html_comment_end( $text, $comment_opening_at, $end ); continue; } // This must be a block delimiter! $delimiter = new static(); break; } if ( null === $delimiter ) { return null; } $delimiter->source_text = $text; $delimiter->delimiter_at = $comment_opening_at; $delimiter->delimiter_length = $comment_closing_at + 3 - $comment_opening_at; $delimiter->namespace_at = $namespace_at; $delimiter->namespace_length = $namespace_length; $delimiter->name_at = $name_at; $delimiter->name_length = $name_length; $delimiter->json_at = $json_at; $delimiter->json_length = $json_length; $delimiter->type = $has_closer ? static::CLOSER : ( $has_void_flag ? static::VOID : static::OPENER ); $delimiter->has_void_flag = $has_void_flag; $match_byte_offset = $delimiter->delimiter_at; $match_byte_length = $delimiter->delimiter_length; return $delimiter; } /** * Generator function to traverse block delimiters in a text and also * yield the position information for the delimiter as it scans. * * Example: * * foreach ( Block_Delimiter::scan_delimiters( $post_content ) as $where => $delimiter ) { * echo "Found a {$delimiter->allocate_and_return_block_type()} at {$where[0]} (length is {$where[1]} bytes)\n"; * } * * @param string $text Document potentially containing blocks. * @param ?string $freeform_blocks Optional. 'visit' to visit virtual block delimiters for freeform content. * @return \Generator Visits each block delimiter and provides [ at, length ] => delimiter. */ public static function scan_delimiters( string $text, ?string $freeform_blocks = 'skip' ): \Generator { $at = 0; $depth = 0; /* * Although `phpcs` confidently asserts that `$match_at` and `$match_length` * are undefined, it is not aware enough to realize that they are set by the * call to `next_delimiter` and so it’s necessary to alter the code so it * doesn’t get confused and reject valid code. */ $match_at = 0; $match_length = 0; while ( null !== ( $delimiter = self::next_delimiter( $text, $at, $match_at, $match_length ) ) ) { // Handle top-level text as freeform blocks if ( 0 === $depth && $match_at > $at && 'visit' === $freeform_blocks ) { list( $text_opener, $text_closer ) = static::freeform_pair( $text, $at, $match_at - $at ); ++$depth; yield [ $at, 0 ] => $text_opener; --$depth; yield [ $match_at, 0 ] => $text_closer; } $delimiter_type = $delimiter->get_delimiter_type(); switch ( $delimiter_type ) { case static::OPENER: case static::VOID: ++$depth; break; case static::CLOSER: --$depth; break; } yield [ $match_at, $match_length ] => $delimiter; if ( static::VOID === $delimiter_type ) { --$depth; } $at = $match_at + $match_length; } $end = strlen( $text ); if ( 'visit' === $freeform_blocks && $at < $end ) { list( $text_opener, $text_closer ) = static::freeform_pair( $text, $at, $end - $at ); ++$depth; yield [ $at, 0 ] => $text_opener; --$depth; yield [ $end, 0 ] => $text_closer; } } /** * Constructor function. */ private function __construct() { // This is not to be called from the outside. } /** * Returns the byte-offset after the ending character of an HTML comment, * assuming the proper starting byte offset. * * @since 0.2.1 * * @param string $text Document in which to search for HTML comment end. * @param int $comment_starting_at Where the HTML comment started, the leading `<`. * @param int $search_end Last offset in which to search, for limiting search span. * @return int Offset after the current HTML comment ends, or `$end` if no end was found. */ private static function find_html_comment_end( string $text, int $comment_starting_at, int $search_end ): int { // Find span-of-dashes comments which look like `<!----->`. $span_of_dashes = strspn( $text, '-', $comment_starting_at + 2 ); if ( $comment_starting_at + 2 + $span_of_dashes < $search_end && '>' === $text[ $comment_starting_at + 2 + $span_of_dashes ] ) { return $comment_starting_at + $span_of_dashes + 1; } // Otherwise, there are other characters inside the comment, find the first `-->` or `--!>`. $now_at = $comment_starting_at + 4; while ( $now_at < $search_end ) { $dashes_at = strpos( $text, '--', $now_at ); if ( false === $dashes_at ) { static::$last_error = self::INCOMPLETE_INPUT; return $search_end; } $closer_must_be_at = $dashes_at + 2 + strspn( $text, '-', $dashes_at + 2 ); if ( $closer_must_be_at < $search_end && '!' === $text[ $closer_must_be_at ] ) { $closer_must_be_at++; } if ( $closer_must_be_at < $search_end && '>' === $text[ $closer_must_be_at ] ) { return $closer_must_be_at + 1; } $now_at++; } return $search_end; } /** * Creates a pair of delimiters for freeform text content * since there are no delimiters in a document for them. * * @param string $text Source document. * @param int $at Where the text region starts (byte offset). * @param int $length How long the text region spans (byte count). * @return static[] Opening and closing block delimiters for the text region. */ private static function freeform_pair( string $text, int $at, int $length ): array { $opener = new static(); $opener->source_text = $text; $opener->delimiter_at = $at; $opener->delimiter_length = 0; $opener->namespace_at = $at; $opener->namespace_length = 0; $opener->name_at = $at; $opener->name_length = 0; $opener->json_at = $at; $opener->json_length = 0; $opener->type = static::OPENER; $opener->has_void_flag = false; $closer = clone $opener; $end_at = $at + $length; $closer->delimiter_at = $end_at; $closer->namespace_at = $end_at; $closer->name_at = $end_at; $closer->json_at = $end_at; $closer->type = static::CLOSER; return [ $opener, $closer ]; } /** * Indicates if the last attempt to parse a block comment delimiter * failed, if set, otherwise `null` if the last attempt succeeded. * * @return string|null */ public static function get_last_error() { return static::$last_error; } /** * Indicates if the last attempt to parse a block’s JSON attributes failed. * * @see JSON_ERROR_NONE, JSON_ERROR_DEPTH, etc… * * @return int JSON_ERROR_ code from last attempt to parse block JSON attributes. */ public function get_last_json_error(): int { return $this->last_json_error; } /** * Allocates a substring from the source text containing the delimiter * and releases the reference to the source text. * * Use this function when the delimiter is holding on to the source * text and preventing it from being freed by PHP. This function incurs * a string allocation; if the source text will be retained anyway then * there's no need to detach as that memory cannot be freed. * * This is a low-level function available for controlling the performance * of sensitive hot-paths. You probably don't need this. * * Example: * * function first_block( $html ) { * return Block_Delimiter::next_delimiter( $really_long_html, 0 ); * } * * $delimiter = first_block( $really_long_html_document ); * // `$really_long_html_document` is still retained inside `$delimiter`, which could lead to a memory leak. * * $delimiter->allocate_and_detach_from_source_text(); * // `$really_long_html_document` is no longer referenced, and its memory may be freed or used for something else. * * @return void */ public function allocate_and_detach_from_source_text(): void { $this->source_text = substr( $this->source_text, $this->delimiter_at, $this->delimiter_length ); $byte_delta = $this->delimiter_at; $this->delimiter_at -= $byte_delta; $this->namespace_at -= $byte_delta; $this->name_at -= $byte_delta; $this->json_at -= $byte_delta; } /** * Returns the type of the block comment delimiter. * * One of: * * - `static::OPENER` * - `static::CLOSER` * - `static::VOID` * * @return string type of the block comment delimiter. */ public function get_delimiter_type(): string { return $this->type; } /** * Returns whether the delimiter contains the void flag. * * This should be avoided except in cases of handling errors with * block closers containing the void flag. For normative use, * {@see self::get_delimiter_type}. * * @return bool */ public function has_void_flag(): bool { return $this->has_void_flag; } /** * Indicates if the block delimiter represents a block of the given type. * * Since the "core" namespace may be implicit, it's allowable to pass * either the fully-qualified block type with namespace and block name * as well as the shorthand version only containing the block name, if * the desired block is in the "core" namespace. * * Example: * * $is_core_paragraph = $delimiter->is_block_type( 'paragraph' ); * $is_core_paragraph = $delimiter->is_block_type( 'core/paragraph' ); * $is_formula = $delimiter->is_block_type( 'math-block/formula' ); * * @param string $block_type Block type name for the desired block. * E.g. "paragraph", "core/paragraph", "math-blocks/formula". * @return bool Whether this delimiter represents a block of the given type. */ public function is_block_type( string $block_type ): bool { // This is a core/freeform text block, it’s special. if ( 0 === $this->name_length ) { return 'core/freeform' === $block_type || 'freeform' === $block_type; } $slash_at = strpos( $block_type, '/' ); if ( false === $slash_at ) { $namespace = 'core'; $block_name = $block_type; } else { $namespace = substr( $block_type, 0, $slash_at ); $block_name = substr( $block_type, $slash_at + 1 ); } // Only the 'core' namespace is allowed to be omitted. if ( 0 === $this->namespace_length && 'core' !== $namespace ) { return false; } // If given an explicit namespace, they must match. if ( 0 !== $this->namespace_length && ( strlen( $namespace ) !== $this->namespace_length || 0 !== substr_compare( $this->source_text, $namespace, $this->namespace_at, $this->namespace_length ) ) ) { return false; } // The block name must match. return ( strlen( $block_name ) === $this->name_length && 0 === substr_compare( $this->source_text, $block_name, $this->name_at, $this->name_length ) ); } /** * Allocates a substring for the block type and returns the * fully-qualified name, including the namespace. * * This function allocates a substring for the given block type. This * allocation will be small and likely fine in most cases, but it's * preferable to call {@link static::is_block_type} if only needing * to know whether the delimiter is for a given block type, as that * function is more efficient for this purpose and avoids the allocation. * * Example: * * 'core/paragraph' = $delimiter->allocate_and_return_block_type(); * * @return string Fully-qualified block namespace and type, e.g. "core/paragraph". */ public function allocate_and_return_block_type(): string { // This is a core/freeform text block, it’s special. if ( 0 === $this->name_length ) { return 'core/freeform'; } // This is implicitly in the "core" namespace. if ( 0 === $this->namespace_length ) { $block_name = substr( $this->source_text, $this->name_at, $this->name_length ); return "core/{$block_name}"; } return substr( $this->source_text, $this->namespace_at, $this->namespace_length + $this->name_length + 1 ); } /** * Returns a lazy wrapper around the block attributes, which can be used * for efficiently interacting with the JSON attributes. * * @throws Exception This function is not yet implemented. * * @todo Create a lazy JSON wrapper so specific attributes can be * efficiently queried without parsing everything and loading * the entire object into memory. * @todo After realistic benchmarking, see if JsonStreamingParser\Parser * could be used — it would need to be fast enough for the reduction * in memory use to be worth it, compared to {@see \json_decode}. * * @see \JsonStreamingParser\Parser * * @return never */ public function get_attributes(): void { throw new Exception( 'Lazy attribute parsing not yet supported' ); } /** * Attempts to parse and return the entire JSON attributes from the delimiter, * allocating memory and processing the JSON span in the process. * * This does not return any parsed attributes for a closing block delimiter * even if there is a span of JSON content; this JSON is a parsing error. * * Consider calling {@link static::get_attributes} instead if it's not * necessary to read all the attributes at the same time, as that provides * a more efficient mechanism for typical use cases. * * Since the JSON span inside the comment delimiter may not be valid JSON, * this function will return `null` if it cannot parse the span and set the * {@see static::get_last_json_error} to the appropriate JSON_ERROR_ constant. * * If the delimiter contains no JSON span, it will also return `null`, * but the last error will be set to {@see JSON_ERROR_NONE}. * * Example: * * $delimiter = Block_Delimiter::next_delimiter( '<!-- wp:image {"url": "https://wordpress.org/favicon.ico"} -->', 0 ); * $memory_hungry_and_slow_attributes = $delimiter->allocate_and_return_parsed_attributes(); * $memory_hungry_and_slow_attributes === array( 'url' => 'https://wordpress.org/favicon.ico' ); * * $delimiter = Block_Delimiter::next_delimiter( '<!-- /wp:image {"url": "https://wordpress.org/favicon.ico"} -->', 0 ); * null = $delimiter->allocate_and_return_parsed_attributes(); * JSON_ERROR_NONE = $delimiter->get_last_json_error(); * * $delimiter = Block_Delimiter::next_delimiter( '<!-- wp:separator {} /-->', 0 ); * array() === $delimiter->allocate_and_return_parsed_attributes(); * * $delimiter = Block_Delimiter::next_delimiter( '<!-- wp:separator /-->', 0 ); * null = $delimiter->allocate_and_return_parsed_attributes(); * * $delimiter = Block_Delimiter::next_delimiter( '<!-- wp:image {"url} -->', 0 ); * null = $delimiter->allocate_and_return_parsed_attributes(); * JSON_ERROR_CTRL_CHAR = $delimiter->get_last_json_error(); * * @return array|null Parsed JSON attributes, if present and valid, otherwise `null`. */ public function allocate_and_return_parsed_attributes(): ?array { $this->last_json_error = JSON_ERROR_NONE; if ( static::CLOSER === $this->type ) { return null; } if ( 0 === $this->json_length ) { return null; } $json_span = substr( $this->source_text, $this->json_at, $this->json_length ); $parsed = json_decode( $json_span, null, 512, JSON_OBJECT_AS_ARRAY | JSON_INVALID_UTF8_SUBSTITUTE ); $last_error = json_last_error(); $this->last_json_error = $last_error; return ( JSON_ERROR_NONE === $last_error && is_array( $parsed ) ) ? $parsed : null; } // Debugging methods not meant for production use. /** * Prints a debugging message showing the structure of the parsed delimiter. * * This is not meant to be used in production! * * @access private */ public function debug_print_structure(): void { $c = ( ! defined( 'STDOUT' ) || posix_isatty( STDOUT ) ) ? function ( $color = null ) { return $color; } // phpcs:ignore : function ( $color ) { return ''; }; // phpcs:ignore if ( $this->is_block_type( 'core/freeform' ) ) { $closer = static::CLOSER === $this->get_delimiter_type() ? '/' : ''; echo "{$c( "\e[90m" )}<!-- "; // phpcs:ignore echo "{$c( "\e[0;31m" )}{$closer}"; // phpcs:ignore echo "{$c("\e[90m" )}wp:"; // phpcs:ignore echo "{$c( "\e[0;34m" )}freeform"; // phpcs:ignore echo "{$c( "\e[0;36m" )} {$c("\e[90m")}-->\n"; // phpcs:ignore return; } $namespace = substr( $this->source_text, $this->namespace_at, $this->namespace_length ); $slash = 0 === $this->namespace_length ? '' : '/'; $block_name = substr( $this->source_text, $this->name_at, $this->name_length ); $closer = static::CLOSER === $this->type ? '/' : ''; $json = substr( $this->source_text, $this->json_at, $this->json_length ); $opener_whitespace_at = $this->delimiter_at + 4; $opener_whitespace_length = $this->namespace_at - 3 - $opener_whitespace_at - ( static::CLOSER === $this->type ? 1 : 0 ); $after_name_whitespace_at = $this->name_at + $this->name_length; $after_name_whitespace_length = $this->json_at - $after_name_whitespace_at; $closing_whitespace_at = $this->json_at + $this->json_length; $closing_whitespace_length = $this->delimiter_at + $this->delimiter_length - 3 - $closing_whitespace_at; if ( '/' === $this->source_text[ $this->delimiter_at + $this->delimiter_length - 4 ] ) { $void_flag = '/'; --$closing_whitespace_length; } else { $void_flag = ''; } $w = function ( $whitespace ) use ( $c ) { return $c( "\e[2;90m" ) . str_replace( array( ' ', "\t", "\f", "\r", "\n" ), array( '␣', '␉', '␌', '␍', '' ), $whitespace ); }; echo "{$c( "\e[90m" )}<!--"; // phpcs:ignore echo $w( substr( $this->source_text, $opener_whitespace_at, $opener_whitespace_length ) ); // phpcs:ignore echo "{$c( "\e[0;31m" )}{$closer}"; // phpcs:ignore echo "{$c("\e[90m" )}wp:{$c( "\e[2;34m" )}{$namespace}"; // phpcs:ignore echo "{$c( "\e[2;90m" )}{$slash}"; // phpcs:ignore echo "{$c( "\e[0;34m" )}{$block_name}"; // phpcs:ignore echo $w( substr( $this->source_text, $after_name_whitespace_at, $after_name_whitespace_length ) ); // phpcs:ignore echo "{$c("\e[0;2;32m" )}{$json}"; // phpcs:ignore echo $w( substr( $this->source_text, $closing_whitespace_at, $closing_whitespace_length ) ); // phpcs:ignore echo "{$c( "\e[0;36m" )}{$void_flag}{$c("\e[90m")}-->\n"; // phpcs:ignore } // Constant declarations that would otherwise pollute the top of the class. /** * Indicates that the block comment delimiter closes an open block. */ const CLOSER = 'closer'; /** * Indicates that the parser started parsing a block comment delimiter, but * the input document ended before it could finish. The document was likely truncated. */ const INCOMPLETE_INPUT = 'incomplete-input'; /** * Indicates that the block comment delimiter opens a block. */ const OPENER = 'opener'; /** * Indicates that the parser has not yet attempted to parse a block comment delimiter. */ const UNINITIALIZED = 'uninitialized'; /** * Indicates that the block comment delimiter represents a void block * with no inner content of any kind. */ const VOID = 'void'; } PK E/]�����v �v + block-delimiter/src/class-block-scanner.phpnu ��� <?php /** * Efficiently scan through block structure in document without parsing * the entire block tree and all of its JSON attributes into memory. * * @package automattic/block-delimiter */ declare( strict_types = 1 ); namespace Automattic; use Exception; use WP_HTML_Span; /** * Class for efficiently scanning through block structure in a document * without parsing the entire block tree and JSON attributes into memory. * * This class follows design values of the HTML API: * - minimize allocations and strive for zero memory overhead * - make costs explicit; pay only for what you need * - follow a streaming, re-entrant design for pausing and aborting * * For usage, jump straight to {@see self::next_delimiter}. */ class Block_Scanner { /** * Indicates if the last operation failed, otherwise * will be `null` for success. * * @var string|null */ private $last_error = null; /** * Indicates failures from decoding JSON attributes. * * @var int */ private $last_json_error = JSON_ERROR_NONE; /** * Holds a reference to the original source text from which to * extract the parsed spans of the delimiter. * * @var string */ private $source_text; /** * Byte offset into source text where entire delimiter begins. * * @var int */ private $delimiter_at = 0; /** * Byte length of full span of delimiter. * * @var int */ private $delimiter_length = 0; /** * Byte offset where namespace span begins. * * @var int */ private $namespace_at = 0; /** * Byte length of namespace span, or `0` if implicitly in the "core" namespace. * * @var int */ private $namespace_length = 0; /** * Byte offset where block name span begins. * * @var int */ private $name_at = 0; /** * Byte length of block name span. * * @var int */ private $name_length = 0; /** * Whether the delimiter contains the block self-closing flag. * * This may be erroneous if present within a block closer, * therefore the {@see self::has_void_flag} can be used by * calling code to perform appropriate error-handling. * * @var bool */ private $has_void_flag = false; /** * Byte offset where JSON attributes span begins. * * @var int */ private $json_at; /** * Byte length of JSON attributes span, or `0` if none are present. * * @var int */ private $json_length; /** * Indicates what kind of block comment delimiter this represents. * * One of: * * - `static::OPENER` If the delimiter is opening a block. * - `static::CLOSER` If the delimiter is closing an open block. * - `static::VOID` If the delimiter represents a void block with no inner content. * * If a parsed comment delimiter contains both the closing and the void * flags then it will be interpreted as a void block to match the behavior * of the official block parser, however, this is a mistake and probably * the block ought to close an open block of the same name, if one is open. * * @var string */ private $type; /** * Creates a new block scanner. * * Example: * * $scanner = Block_Scanner::create( $html ); * while ( $scanner->next_delimiter() ) { * if ( $scanner->opens_block( 'core/image' ) ) { * echo "Found an image!\n"; * } * } * * This function is currently a stub so that future improvements can add configuration * options and reject creation, which cannot occur directly inside class constructors. * * @see self::next_delimiter * * @param string $source_text Input document potentially containing block content. * @return ?self Created block scanner, if successfully created. */ public static function create( string $source_text ): ?self { return new self( $source_text ); } /** * Scan to the next block delimiter in a document, indicating if one was found. * * Block comment delimiters must be valid HTML comments and may contain JSON. * This search does not determine, however, if the JSON is valid. * * Example delimiters: * * `<!-- wp:paragraph {"dropCap": true} -->` * `<!-- wp:separator /-->` * `<!-- /wp:paragraph -->` * * In the case that a block comment delimiter contains both the void indicator and * also the closing indicator, it will be treated as a void block. * * Example: * * // Find all image block opening delimiters. * $images = array(); * $scanner = Block_Scanner::create( $html ); * while ( $scanner->next_delimiter() ) { * if ( $scanner->opens_block( 'core/image' ) ) { * $images[] = $scanner->get_span(); * } * } * * Not all blocks have explicit delimiters. Non-block content at the top-level of * a document (so-called “HTML soup”) forms implicit blocks containing neither a * block name nor block attributes. Because this content often comprises only * HTML whitespace and adds undo performance burden, it is skipped by default. * To scan the implicit freeform blocks, pass the `$freeform_blocks` argument. * * Example: * * $html = '<!-- wp:void /-->\n<!-- wp:void /-->'; * $blocks = [ * [ 'blockName' => 'core/void' ], * [ 'blockName' => null ], * [ 'blockName' => 'core/void' ], * ]; * $scanner = Block_Scanner::create( $html ); * while ( $scanner->next_delimiter( freeform_blocks: 'visit' ) { * ... * } * * In some cases it may be useful to conditionally visit the implicit freeform * blocks, such as when determining if a post contains freeform content that * isn’t purely whitespace. * * Example: * * $seen_block_types = []; * $freeform_blocks = 'visit'; * $scanner = Block_Scanner::create( $html ); * while ( $scanner->next_delimiter( freeform_blocks: $freeform_blocks ) { * if ( ! $scanner->opens_block() ) { * continue; * } * * // Stop wasting time visiting freeform blocks after one has been found. * if ('visit' === $freeform_blocks ) { * if ( $scanner->is_non_whitespace_freeform() ) { * $freeform_blocks = 'skip'; * $seen_block_types['core/freeform'] = true; * } * continue; * } * * $seen_block_types[ $scanner->get_block_type() ] = true; * } * * @param string $freeform_blocks Optional. Pass `visit` to match freeform HTML content * not surrounded by block delimiters. Defaults to `skip`. * @return bool Whether a block delimiter was matched. */ public function next_delimiter( string $freeform_blocks = 'skip' ): bool { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable if ( $this->last_error ) { return false; } $text = $this->source_text; $end = strlen( $text ); $at = $this->delimiter_at + $this->delimiter_length; $found_one = false; while ( $at < $end ) { /* * Find the next possible opening. * * This follows the behavior in the official block parser, which treats a post * as a list of blocks with nested HTML. If HTML comment syntax appears within * an HTML attribute value, SCRIPT or STYLE element, or in other select places, * which it can do inside of HTML, then the block parsing may break. * * For a more robust parse scan through the document with the HTML API. In * practice, this has not been a problem in the entire history of blocks. */ $comment_opening_at = strpos( $text, '<!--', $at ); if ( false === $comment_opening_at ) { return false; } $opening_whitespace_at = $comment_opening_at + 4; $opening_whitespace_length = strspn( $text, " \t\f\r\n", $opening_whitespace_at ); if ( 0 === $opening_whitespace_length ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $wp_prefix_at = $opening_whitespace_at + $opening_whitespace_length; if ( $wp_prefix_at >= $end ) { $this->last_error = self::INCOMPLETE_INPUT; return false; } $has_closer = false; if ( '/' === $text[ $wp_prefix_at ] ) { $has_closer = true; ++$wp_prefix_at; } if ( 0 !== substr_compare( $text, 'wp:', $wp_prefix_at, 3 ) ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $namespace_at = $wp_prefix_at + 3; if ( $namespace_at >= $end ) { $this->last_error = self::INCOMPLETE_INPUT; return false; } $start_of_namespace = $text[ $namespace_at ]; // The namespace must start with a-z. if ( 'a' > $start_of_namespace || 'z' < $start_of_namespace ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $namespace_length = 1 + strspn( $text, 'abcdefghijklmnopqrstuvwxyz0123456789-_', $namespace_at + 1 ); $separator_at = $namespace_at + $namespace_length; if ( $separator_at >= $end ) { $this->last_error = self::INCOMPLETE_INPUT; return false; } $has_separator = '/' === $text[ $separator_at ]; if ( $has_separator ) { $name_at = $separator_at + 1; $start_of_name = $text[ $name_at ]; if ( 'a' > $start_of_name || 'z' < $start_of_name ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $name_length = 1 + strspn( $text, 'abcdefghijklmnopqrstuvwxyz0123456789-_', $name_at + 1 ); } else { $name_at = $namespace_at; $name_length = $namespace_length; $namespace_length = 0; } $after_name_whitespace_at = $name_at + $name_length; $after_name_whitespace_length = strspn( $text, " \t\f\r\n", $after_name_whitespace_at ); if ( 0 === $after_name_whitespace_length ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $json_at = $after_name_whitespace_at + $after_name_whitespace_length; if ( $json_at >= $end ) { $this->last_error = self::INCOMPLETE_INPUT; return false; } $has_json = '{' === $text[ $json_at ]; $json_length = 0; /* * For the final span of the delimiter it's most efficient to find the end * of the HTML comment and work backwards. This prevents complicated parsing * inside the JSON span, which cannot contain the HTML comment terminator. * * This also matches the behavior in the official block parser, though it * allows for matching invalid JSON content. */ $comment_closing_at = strpos( $text, '-->', $json_at ); if ( false === $comment_closing_at ) { $this->last_error = self::INCOMPLETE_INPUT; return false; } /* * It looks like this logic leaves an error in here, when the position * overlaps the JSON or block name. However, for neither of those is it * possible to parse a valid block if that last overlapping character * is the void flag. This, therefore, will be valid regardless of how * the rest of the comment delimiter is written. */ if ( '/' === $text[ $comment_closing_at - 1 ] ) { $has_void_flag = true; $void_flag_length = 1; } else { $has_void_flag = false; $void_flag_length = 0; } /* * If there's no JSON, then the span of text after the name * until the comment closing must be completely whitespace. */ if ( ! $has_json ) { $max_whitespace_length = $comment_closing_at - $json_at - $void_flag_length; // This shouldn't be possible, but it can't be allowed regardless. if ( $max_whitespace_length < 0 ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } $closing_whitespace_length = strspn( $text, " \t\f\r\n", $json_at, $comment_closing_at - $json_at - $void_flag_length ); if ( 0 === $after_name_whitespace_length + $closing_whitespace_length ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } // This must be a block delimiter! $found_one = true; break; } // There's no JSON, so attempt to find its boundary. $after_json_whitespace_length = 0; for ( $char_at = $comment_closing_at - $void_flag_length - 1; $char_at > $json_at; $char_at-- ) { $char = $text[ $char_at ]; switch ( $char ) { case ' ': case "\t": case "\f": case "\r": case "\n": ++$after_json_whitespace_length; continue 2; case '}': $json_length = $char_at - $json_at + 1; break 2; default: ++$at; continue 3; } } if ( 0 === $json_length || 0 === $after_json_whitespace_length ) { $at = $this->find_html_comment_end( $comment_opening_at, $end ); continue; } // This must be a block delimiter! $found_one = true; break; } if ( ! $found_one ) { return false; } $this->delimiter_at = $comment_opening_at; $this->delimiter_length = $comment_closing_at + 3 - $comment_opening_at; $this->namespace_at = $namespace_at; $this->namespace_length = $namespace_length; $this->name_at = $name_at; $this->name_length = $name_length; $this->json_at = $json_at; $this->json_length = $json_length; $this->type = $has_closer ? static::CLOSER : ( $has_void_flag ? static::VOID : static::OPENER ); $this->has_void_flag = $has_void_flag; return true; } /** * Constructor function. * * @param string $source_text Input document potentially containing block content. */ private function __construct( string $source_text ) { $this->source_text = $source_text; } /** * Returns the byte-offset after the ending character of an HTML comment, * assuming the proper starting byte offset. * * @param int $comment_starting_at Where the HTML comment started, the leading `<`. * @param int $search_end Last offset in which to search, for limiting search span. * @return int Offset after the current HTML comment ends, or `$end` if no end was found. */ private function find_html_comment_end( int $comment_starting_at, int $search_end ): int { $text = $this->source_text; // Find span-of-dashes comments which look like `<!----->`. $span_of_dashes = strspn( $text, '-', $comment_starting_at + 2 ); if ( $comment_starting_at + 2 + $span_of_dashes < $search_end && '>' === $text[ $comment_starting_at + 2 + $span_of_dashes ] ) { return $comment_starting_at + $span_of_dashes + 1; } // Otherwise, there are other characters inside the comment, find the first `-->` or `--!>`. $now_at = $comment_starting_at + 4; while ( $now_at < $search_end ) { $dashes_at = strpos( $text, '--', $now_at ); if ( false === $dashes_at ) { $this->last_error = self::INCOMPLETE_INPUT; return $search_end; } $closer_must_be_at = $dashes_at + 2 + strspn( $text, '-', $dashes_at + 2 ); if ( $closer_must_be_at < $search_end && '!' === $text[ $closer_must_be_at ] ) { $closer_must_be_at++; } if ( $closer_must_be_at < $search_end && '>' === $text[ $closer_must_be_at ] ) { return $closer_must_be_at + 1; } $now_at++; } return $search_end; } /** * Indicates if the last attempt to parse a block comment delimiter * failed, if set, otherwise `null` if the last attempt succeeded. * * @return string|null */ public function get_last_error() { return $this->last_error; } /** * Indicates if the last attempt to parse a block’s JSON attributes failed. * * @see JSON_ERROR_NONE, JSON_ERROR_DEPTH, etc… * * @return int JSON_ERROR_ code from last attempt to parse block JSON attributes. */ public function get_last_json_error(): int { return $this->last_json_error; } /** * Returns the type of the block comment delimiter. * * One of: * * - `static::OPENER` * - `static::CLOSER` * - `static::VOID` * * @return string type of the block comment delimiter. */ public function get_delimiter_type(): string { return $this->type; } /** * Returns whether the delimiter contains the void flag. * * This should be avoided except in cases of handling errors with * block closers containing the void flag. For normative use, * {@see self::get_delimiter_type}. * * @return bool */ public function has_void_flag(): bool { return $this->has_void_flag; } /** * Indicates if the block delimiter represents a block of the given type. * * Since the "core" namespace may be implicit, it's allowable to pass * either the fully-qualified block type with namespace and block name * as well as the shorthand version only containing the block name, if * the desired block is in the "core" namespace. * * Example: * * $is_core_paragraph = $scanner->is_block_type( 'paragraph' ); * $is_core_paragraph = $scanner->is_block_type( 'core/paragraph' ); * $is_formula = $scanner->is_block_type( 'math-block/formula' ); * * @param string $block_type Block type name for the desired block. * E.g. "paragraph", "core/paragraph", "math-blocks/formula". * @return bool Whether this delimiter represents a block of the given type. */ public function is_block_type( string $block_type ): bool { // This is a core/freeform text block, it’s special. if ( 0 === $this->name_length ) { return 'core/freeform' === $block_type || 'freeform' === $block_type; } $slash_at = strpos( $block_type, '/' ); if ( false === $slash_at ) { $namespace = 'core'; $block_name = $block_type; } else { // @todo Get lengths but avoid the allocation, use substr_compare below. $namespace = substr( $block_type, 0, $slash_at ); $block_name = substr( $block_type, $slash_at + 1 ); } // Only the 'core' namespace is allowed to be omitted. if ( 0 === $this->namespace_length && 'core' !== $namespace ) { return false; } // If given an explicit namespace, they must match. if ( 0 !== $this->namespace_length && ( strlen( $namespace ) !== $this->namespace_length || 0 !== substr_compare( $this->source_text, $namespace, $this->namespace_at, $this->namespace_length ) ) ) { return false; } // The block name must match. return ( strlen( $block_name ) === $this->name_length && 0 === substr_compare( $this->source_text, $block_name, $this->name_at, $this->name_length ) ); } /** * Indicates if the matched delimiter is an opening or void delimiter * (i.e. it opens the block) of the given type, if a type is provided. * * This is a helper method to ease handling of code inspecting where * blocks start, and of checking if the blocks are of a given type. * The function is variadic to allow for checking if the delimiter * opens one of many possible block types. * * Example: * * $scanner = Block_Scanner::create( $html ); * while ( $scanner->next_delimiter() ) { * if ( $scanner->opens_block( 'core/code', 'syntaxhighlighter/code' ) ) { * echo "Found code!"; * continue; * } * * if ( $scanner->opens_block( 'core/image' ) ) { * echo "Found an image!"; * continue; * } * * if ( $scanner->opens_block() ) { * echo "Found a new block!"; * } * } * * @see self::is_block_type * * @param string|null ...$block_type Optional. Is the matched block type one of these? * If none are provided, will not test block type. * @return bool Whether the matched block delimiter opens a block, and whether it * opens a block of one of the given block types, if provided. */ public function opens_block( ...$block_type ): bool { if ( static::CLOSER === $this->type ) { return false; } if ( count( $block_type ) === 0 ) { return true; } foreach ( $block_type as $block ) { if ( $this->is_block_type( $block ) ) { return true; } } return false; } /** * Indicates if the matched delimiter is implied due to top-level * non-block content in the post. * * @see self::is_non_whitespace_freeform * * @return bool Whether or not the matched delimiter is implied as `core/freeform`. */ public function is_freeform(): bool { return 0 === $this->name_length; } /** * Indicates if the matched delimiter is implicit and surrounding * top-level non-block content that contains non-whitespace text. * * Many block serializers introduce newlines between block delimiters, * so the presence of top-level non-block content does not imply that * there are “real” freeform HTML blocks. Checking if there is content * beyond whitespace is a more certain check, such as for determining * whether to load CSS for the freeform or fallback block type. * * @see self::is_freeform * * @return bool */ public function is_non_whitespace_freeform(): bool { if ( 0 !== $this->name_length ) { return false; } // For now, return false as this method is not yet fully implemented. // @todo Implement logic to check if freeform content contains non-whitespace text. return false; } /** * Allocates a substring for the block type and returns the * fully-qualified name, including the namespace. * * This function allocates a substring for the given block type. This * allocation will be small and likely fine in most cases, but it's * preferable to call {@link self::is_block_type} if only needing * to know whether the delimiter is for a given block type, as that * function is more efficient for this purpose and avoids the allocation. * * Example: * * // Avoid. * 'core/paragraph' = $scanner->get_block_type(); * * // Prefer. * $scanner->is_block_type( 'core/paragraph' ); * $scanner->is_block_type( 'paragraph' ); * * @return string Fully-qualified block namespace and type, e.g. "core/paragraph". */ public function get_block_type(): string { // This is a core/freeform text block, it’s special. if ( 0 === $this->name_length ) { return 'core/freeform'; } // This is implicitly in the "core" namespace. if ( 0 === $this->namespace_length ) { $block_name = substr( $this->source_text, $this->name_at, $this->name_length ); return "core/{$block_name}"; } return substr( $this->source_text, $this->namespace_at, $this->namespace_length + $this->name_length + 1 ); } /** * Returns a lazy wrapper around the block attributes, which can be used * for efficiently interacting with the JSON attributes. * * @throws Exception This function is not yet implemented. * * @todo Create a lazy JSON wrapper so specific attributes can be * efficiently queried without parsing everything and loading * the entire object into memory. * @todo After realistic benchmarking, see if JsonStreamingParser\Parser * could be used — it would need to be fast enough for the reduction * in memory use to be worth it, compared to {@see \json_decode}. * * @see \JsonStreamingParser\Parser * * @return never */ public function get_attributes(): void { throw new Exception( 'Lazy attribute parsing not yet supported' ); } /** * Attempts to parse and return the entire JSON attributes from the delimiter, * allocating memory and processing the JSON span in the process. * * This does not return any parsed attributes for a closing block delimiter * even if there is a span of JSON content; this JSON is a parsing error. * * Consider calling {@link self::get_attributes} instead if it's not * necessary to read all the attributes at the same time, as that provides * a more efficient mechanism for typical use cases. * * Since the JSON span inside the comment delimiter may not be valid JSON, * this function will return `null` if it cannot parse the span and set the * {@see self::get_last_json_error} to the appropriate JSON_ERROR_ constant. * * If the delimiter contains no JSON span, it will also return `null`, * but the last error will be set to {@see JSON_ERROR_NONE}. * * Example: * * $scanner = Block_Scanner::create( '<!-- wp:image {"url": "https://wordpress.org/favicon.ico"} -->' ); * $scanner->next_delimiter(); * $memory_hungry_and_slow_attributes = $scanner->allocate_and_return_parsed_attributes(); * $memory_hungry_and_slow_attributes === array( 'url' => 'https://wordpress.org/favicon.ico' ); * * $scanner = Block_Scanner::create( '<!-- /wp:image {"url": "https://wordpress.org/favicon.ico"} -->' ); * $scanner->next_delimiter(); * null = $scanner->allocate_and_return_parsed_attributes(); * JSON_ERROR_NONE = $scanner->get_last_json_error(); * * $scanner = Block_Scanner::create( '<!-- wp:separator {} /-->' ); * $scanner->next_delimiter(); * array() === $scanner->allocate_and_return_parsed_attributes(); * * $scanner = Block_Scanner::create( '<!-- wp:separator /-->' ); * $scanner->next_delimiter(); * null = $scanner->allocate_and_return_parsed_attributes(); * * $scanner = Block_Scanner::create( '<!-- wp:image {"url} -->' ); * $scanner->next_delimiter(); * null = $scanner->allocate_and_return_parsed_attributes(); * JSON_ERROR_CTRL_CHAR = $scanner->get_last_json_error(); * * @return array|null Parsed JSON attributes, if present and valid, otherwise `null`. */ public function allocate_and_return_parsed_attributes(): ?array { $this->last_json_error = JSON_ERROR_NONE; if ( static::CLOSER === $this->type ) { return null; } if ( 0 === $this->json_length ) { return null; } $json_span = substr( $this->source_text, $this->json_at, $this->json_length ); $parsed = json_decode( $json_span, null, 512, JSON_OBJECT_AS_ARRAY | JSON_INVALID_UTF8_SUBSTITUTE ); $last_error = json_last_error(); $this->last_json_error = $last_error; return ( JSON_ERROR_NONE === $last_error && is_array( $parsed ) ) ? $parsed : null; } /** * Returns the span representing the currently-matched delimiter, * if matched, otherwise `null`. * * Note that for freeform blocks this will return a span of length * zero, since there is no explicit block delimiter. * * Example: * * $scanner = Block_Scanner::create( '<!-- wp:void /-->' ); * null === $scanner->get_span(); * * $scanner->next_delimiter(); * WP_HTML_Span( 0, 17 ) === $scanner->get_span(); * * @return WP_HTML_Span|null Span of text in source text spanning matched delimiter. */ public function get_span(): ?WP_HTML_Span { return new WP_HTML_Span( $this->delimiter_at, $this->delimiter_length ); } // Debugging methods not meant for production use. /** * Prints a debugging message showing the structure of the parsed delimiter. * * This is not meant to be used in production! * * @access private */ public function debug_print_structure(): void { $c = ( ! defined( 'STDOUT' ) || posix_isatty( STDOUT ) ) ? function ( $color = null ) { return $color; } // phpcs:ignore : function ( $color ) { return ''; }; // phpcs:ignore if ( $this->is_block_type( 'core/freeform' ) ) { $closer = static::CLOSER === $this->get_delimiter_type() ? '/' : ''; echo "{$c( "\e[90m" )}<!-- "; // phpcs:ignore echo "{$c( "\e[0;31m" )}{$closer}"; // phpcs:ignore echo "{$c("\e[90m" )}wp:"; // phpcs:ignore echo "{$c( "\e[0;34m" )}freeform"; // phpcs:ignore echo "{$c( "\e[0;36m" )} {$c("\e[90m")}-->\n"; // phpcs:ignore return; } $namespace = substr( $this->source_text, $this->namespace_at, $this->namespace_length ); $slash = 0 === $this->namespace_length ? '' : '/'; $block_name = substr( $this->source_text, $this->name_at, $this->name_length ); $closer = static::CLOSER === $this->type ? '/' : ''; $json = substr( $this->source_text, $this->json_at, $this->json_length ); $opener_whitespace_at = $this->delimiter_at + 4; $opener_whitespace_length = $this->namespace_at - 3 - $opener_whitespace_at - ( static::CLOSER === $this->type ? 1 : 0 ); $after_name_whitespace_at = $this->name_at + $this->name_length; $after_name_whitespace_length = $this->json_at - $after_name_whitespace_at; $closing_whitespace_at = $this->json_at + $this->json_length; $closing_whitespace_length = $this->delimiter_at + $this->delimiter_length - 3 - $closing_whitespace_at; if ( '/' === $this->source_text[ $this->delimiter_at + $this->delimiter_length - 4 ] ) { $void_flag = '/'; --$closing_whitespace_length; } else { $void_flag = ''; } $w = function ( $whitespace ) use ( $c ) { return $c( "\e[2;90m" ) . str_replace( array( ' ', "\t", "\f", "\r", "\n" ), array( '␣', '␉', '␌', '␍', '' ), $whitespace ); }; echo "{$c( "\e[90m" )}<!--"; // phpcs:ignore echo $w( substr( $this->source_text, $opener_whitespace_at, $opener_whitespace_length ) ); // phpcs:ignore echo "{$c( "\e[0;31m" )}{$closer}"; // phpcs:ignore echo "{$c("\e[90m" )}wp:{$c( "\e[2;34m" )}{$namespace}"; // phpcs:ignore echo "{$c( "\e[2;90m" )}{$slash}"; // phpcs:ignore echo "{$c( "\e[0;34m" )}{$block_name}"; // phpcs:ignore echo $w( substr( $this->source_text, $after_name_whitespace_at, $after_name_whitespace_length ) ); // phpcs:ignore echo "{$c("\e[0;2;32m" )}{$json}"; // phpcs:ignore echo $w( substr( $this->source_text, $closing_whitespace_at, $closing_whitespace_length ) ); // phpcs:ignore echo "{$c( "\e[0;36m" )}{$void_flag}{$c("\e[90m")}-->\n"; // phpcs:ignore } // Constant declarations that would otherwise pollute the top of the class. /** * Indicates that the block comment delimiter closes an open block. */ const CLOSER = 'closer'; /** * Indicates that the parser started parsing a block comment delimiter, but * the input document ended before it could finish. The document was likely truncated. */ const INCOMPLETE_INPUT = 'incomplete-input'; /** * Indicates that the block comment delimiter opens a block. */ const OPENER = 'opener'; /** * Indicates that the parser has not yet attempted to parse a block comment delimiter. */ const UNINITIALIZED = 'uninitialized'; /** * Indicates that the block comment delimiter represents a void block * with no inner content of any kind. */ const VOID = 'void'; } PK E/]��J�G �G jetpack-status/LICENSE.txtnu ��� This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. PK E/]���JU2 U2 # jetpack-status/src/class-status.phpnu ��� <?php /** * A status class for Jetpack. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack; use Automattic\Jetpack\Status\Cache; use Automattic\Jetpack\Status\Host; use WPCOM_Masterbar; /** * Class Automattic\Jetpack\Status * * Used to retrieve information about the current status of Jetpack and the site overall. */ class Status { /** * Is Jetpack in offline mode? * * This was formerly called "Development Mode", but sites "in development" aren't always offline/localhost. * * @since 1.3.0 * * @return bool Whether Jetpack's offline mode is active. */ public function is_offline_mode() { $cached = Cache::get( 'is_offline_mode' ); if ( null !== $cached ) { return $cached; } $offline_mode = false; if ( defined( '\\JETPACK_DEV_DEBUG' ) ) { $offline_mode = constant( '\\JETPACK_DEV_DEBUG' ); } elseif ( defined( '\\WP_LOCAL_DEV' ) ) { $offline_mode = constant( '\\WP_LOCAL_DEV' ); } elseif ( $this->is_local_site() ) { $offline_mode = true; } /** * Filters Jetpack's offline mode. * * @see https://jetpack.com/support/offline-mode/ * * @since 1.3.0 * * @param bool $offline_mode Is Jetpack's offline mode active. */ $offline_mode = (bool) apply_filters( 'jetpack_offline_mode', $offline_mode ); if ( ! $offline_mode ) { $offline_mode = (bool) get_option( 'jetpack_offline_mode' ); } Cache::set( 'is_offline_mode', $offline_mode ); return $offline_mode; } /** * Whether this is a system with a multiple networks. * Implemented since there is no core is_multi_network function. * Right now there is no way to tell which network is the dominant network on the system. * * @return boolean */ public function is_multi_network() { global $wpdb; $cached = Cache::get( 'is_multi_network' ); if ( null !== $cached ) { return $cached; } // If we don't have a multi site setup no need to do any more. if ( ! is_multisite() ) { Cache::set( 'is_multi_network', false ); return false; } $num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" ); if ( $num_sites > 1 ) { Cache::set( 'is_multi_network', true ); return true; } Cache::set( 'is_multi_network', false ); return false; } /** * Whether the current site is single user site. * * @return bool */ public function is_single_user_site() { global $wpdb; $ret = Cache::get( 'is_single_user_site' ); if ( null === $ret ) { $some_users = get_transient( 'jetpack_is_single_user' ); if ( false === $some_users ) { $some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" ); set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS ); } $ret = 1 === (int) $some_users; Cache::set( 'is_single_user_site', $ret ); } return $ret; } /** * If the site is a local site. * * @since 1.3.0 * * @return bool */ public function is_local_site() { $cached = Cache::get( 'is_local_site' ); if ( null !== $cached ) { return $cached; } $site_url = site_url(); // Check for localhost and sites using an IP only first. // Note: str_contains() is not used here, as wp-includes/compat.php is not loaded in this file. $is_local = $site_url && false === strpos( $site_url, '.' ); // Use Core's environment check, if available. if ( 'local' === wp_get_environment_type() ) { $is_local = true; } // Then check for usual usual domains used by local dev tools. $known_local = array( '#\.local$#i', '#\.localhost$#i', '#\.test$#i', '#\.docksal$#i', // Docksal. '#\.docksal\.site$#i', // Docksal. '#\.dev\.cc$#i', // ServerPress. '#\.lndo\.site$#i', // Lando. '#\.ddev\.site$#i', // DDEV. '#^https?://127\.0\.0\.1$#', ); if ( ! $is_local ) { foreach ( $known_local as $url ) { if ( preg_match( $url, $site_url ) ) { $is_local = true; break; } } } /** * Filters is_local_site check. * * @since 1.3.0 * * @param bool $is_local If the current site is a local site. */ $is_local = apply_filters( 'jetpack_is_local_site', $is_local ); Cache::set( 'is_local_site', $is_local ); return $is_local; } /** * If is a staging site. * * @deprecated since 3.3.0 * * @return bool */ public function is_staging_site() { _deprecated_function( __FUNCTION__, '3.3.0', 'in_safe_mode' ); $cached = Cache::get( 'is_staging_site' ); if ( null !== $cached ) { return $cached; } /* * Core's wp_get_environment_type allows for a few specific options. * We should default to bowing out gracefully for anything other than production or local. */ $is_staging = ! in_array( wp_get_environment_type(), array( 'production', 'local' ), true ); $known_staging = array( 'urls' => array( '#\.staging\.wpengine\.com$#i', // WP Engine. This is their legacy staging URL structure. Their new platform does not have a common URL. https://github.com/Automattic/jetpack/issues/21504 '#\.staging\.kinsta\.com$#i', // Kinsta.com. '#\.kinsta\.cloud$#i', // Kinsta.com. '#\.stage\.site$#i', // DreamPress. '#\.newspackstaging\.com$#i', // Newspack. '#^(?!live-)([a-zA-Z0-9-]+)\.pantheonsite\.io$#i', // Pantheon. '#\.flywheelsites\.com$#i', // Flywheel. '#\.flywheelstaging\.com$#i', // Flywheel. '#\.cloudwaysapps\.com$#i', // Cloudways. '#\.azurewebsites\.net$#i', // Azure. '#\.wpserveur\.net$#i', // WPServeur. '#\-liquidwebsites\.com$#i', // Liquidweb. ), 'constants' => array( 'IS_WPE_SNAPSHOT', // WP Engine. This is used on their legacy staging environment. Their new platform does not have a constant. https://github.com/Automattic/jetpack/issues/21504 'KINSTA_DEV_ENV', // Kinsta.com. 'WPSTAGECOACH_STAGING', // WP Stagecoach. 'JETPACK_STAGING_MODE', // Generic. 'WP_LOCAL_DEV', // Generic. ), ); /** * Filters the flags of known staging sites. * * @since 1.1.1 * @since-jetpack 3.9.0 * * @param array $known_staging { * An array of arrays that each are used to check if the current site is staging. * @type array $urls URLs of staging sites in regex to check against site_url. * @type array $constants PHP constants of known staging/developement environments. * } */ $known_staging = apply_filters( 'jetpack_known_staging', $known_staging ); if ( isset( $known_staging['urls'] ) ) { $site_url = site_url(); foreach ( $known_staging['urls'] as $url ) { if ( preg_match( $url, wp_parse_url( $site_url, PHP_URL_HOST ) ) ) { $is_staging = true; break; } } } if ( isset( $known_staging['constants'] ) ) { foreach ( $known_staging['constants'] as $constant ) { if ( defined( $constant ) && constant( $constant ) ) { $is_staging = true; } } } // Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode. if ( ! $is_staging && method_exists( 'Automattic\\Jetpack\\Identity_Crisis', 'validate_sync_error_idc_option' ) && \Automattic\Jetpack\Identity_Crisis::validate_sync_error_idc_option() ) { $is_staging = true; } /** * Filters is_staging_site check. * * @since 1.1.1 * @since-jetpack 3.9.0 * * @param bool $is_staging If the current site is a staging site. */ $is_staging = apply_filters( 'jetpack_is_staging_site', $is_staging ); Cache::set( 'is_staging_site', $is_staging ); return $is_staging; } /** * If the site is in safe mode. * * @since 3.3.0 * * @return bool */ public function in_safe_mode() { $cached = Cache::get( 'in_safe_mode' ); if ( null !== $cached ) { return $cached; } $in_safe_mode = false; if ( method_exists( 'Automattic\\Jetpack\\Identity_Crisis', 'validate_sync_error_idc_option' ) && \Automattic\Jetpack\Identity_Crisis::validate_sync_error_idc_option() ) { $in_safe_mode = true; } /** * Filters in_safe_mode check. * * @since 3.3.0 * * @param bool $in_safe_mode If the current site is in safe mode. */ $in_safe_mode = apply_filters( 'jetpack_is_in_safe_mode', $in_safe_mode ); Cache::set( 'in_safe_mode', $in_safe_mode ); return $in_safe_mode; } /** * If the site is a development/staging site. * This is a new version of is_staging_site added to separate safe mode from the legacy staging mode. * This method checks for core WP_ENVIRONMENT_TYPE setting * Using the jetpack_is_development_site filter. * * @since 3.3.0 * * @return bool */ public static function is_development_site() { $cached = Cache::get( 'is_development_site' ); if ( null !== $cached ) { return $cached; } $is_dev_site = ! in_array( wp_get_environment_type(), array( 'production', 'local' ), true ); /** * Filters is_development_site check. * * @since 3.3.0 * * @param bool $is_dev_site If the current site is a staging or dev site. */ $is_dev_site = apply_filters( 'jetpack_is_development_site', $is_dev_site ); Cache::set( 'is_development_site', $is_dev_site ); return $is_dev_site; } /** * Whether the site is currently onboarding or not. * A site is considered as being onboarded if it currently has an onboarding token. * * @since-jetpack 5.8 * * @deprecated since 4.0.0 * * @access public * @static * * @return bool True if the site is currently onboarding, false otherwise */ public function is_onboarding() { return \Jetpack_Options::get_option( 'onboarding' ) !== false; } /** * Whether the site is currently private or not. * On WordPress.com and WoA, sites can be marked as private * * @since 1.16.0 * * @return bool True if the site is private. */ public function is_private_site() { $ret = Cache::get( 'is_private_site' ); if ( null === $ret ) { $is_private_site = '-1' === get_option( 'blog_public' ); /** * Filters the is_private_site check. * * @since 1.16.1 * * @param bool $is_private_site True if the site is private. */ $is_private_site = apply_filters( 'jetpack_is_private_site', $is_private_site ); Cache::set( 'is_private_site', $is_private_site ); return $is_private_site; } return $ret; } /** * Whether the site is currently unlaunched or not. * On WordPress.com and WoA, sites can be marked as "coming soon", aka unlaunched * * @since 1.16.0 * * @return bool True if the site is not launched. */ public function is_coming_soon() { $ret = Cache::get( 'is_coming_soon' ); if ( null === $ret ) { $is_coming_soon = ( function_exists( 'site_is_coming_soon' ) && \site_is_coming_soon() ) || get_option( 'wpcom_public_coming_soon' ); /** * Filters the is_coming_soon check. * * @since 1.16.1 * * @param bool $is_coming_soon True if the site is coming soon (i.e. unlaunched). */ $is_coming_soon = apply_filters( 'jetpack_is_coming_soon', $is_coming_soon ); Cache::set( 'is_coming_soon', $is_coming_soon ); return $is_coming_soon; } return $ret; } /** * Returns the site slug suffix to be used as part of Calypso URLs. * * Strips http:// or https:// from a url, replaces forward slash with ::. * * @since 1.6.0 * * @param string $url Optional. URL to build the site suffix from. Default: Home URL. * * @return string */ public function get_site_suffix( $url = '' ) { // On WordPress.com, site suffixes are a bit different. if ( method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) { return WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() ); } // Grab the 'site_url' option for WoA sites to avoid plugins to interfere with the site // identifier (e.g. i18n plugins may change the main url to '<DOMAIN>/<LOCALE>', but we // want to exclude the locale since it's not part of the site suffix). if ( ( new Host() )->is_woa_site() ) { $url = \site_url(); } if ( empty( $url ) ) { // WordPress can be installed in subdirectories (e.g. make.wordpress.org/plugins) // where the 'site_url' option points to the root domain (e.g. make.wordpress.org) // which could collide with another site in the same domain but with WordPress // installed in a different subdirectory (e.g. make.wordpress.org/core). To avoid // such collision, we identify the site with the 'home_url' option. $url = \home_url(); } $url = preg_replace( '#^.*?://#', '', $url ); $url = str_replace( '/', '::', $url ); return rtrim( $url, ':' ); } } PK E/]���z z ( jetpack-status/src/class-cookiestate.phpnu ��� <?php /** * Pass state to subsequent requests via cookies. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack; /** * Class Automattic\Jetpack\Status * * Used to retrieve information about the current status of Jetpack and the site overall. */ class CookieState { /** * State is passed via cookies from one request to the next, but never to subsequent requests. * SET: state( $key, $value ); * GET: $value = state( $key ); * * @param string $key State key. * @param string $value Value. * @param bool $restate Reset the cookie (private). */ public function state( $key = null, $value = null, $restate = false ) { static $state = array(); static $path, $domain; if ( ! isset( $path ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; $admin_url = ( new Paths() )->admin_url(); $bits = wp_parse_url( $admin_url ); if ( is_array( $bits ) ) { $path = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null; $domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null; } else { $path = null; $domain = null; } } // Extract state from cookies and delete cookies. if ( isset( $_COOKIE['jetpackState'] ) && is_array( $_COOKIE['jetpackState'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- User should sanitize if necessary. $yum = wp_unslash( $_COOKIE['jetpackState'] ); unset( $_COOKIE['jetpackState'] ); foreach ( $yum as $k => $v ) { if ( strlen( $v ) ) { $state[ $k ] = $v; } // @phan-suppress-next-line PhanTypeMismatchArgumentInternal -- Setting cookie to false is valid and documented for deletion. setcookie( "jetpackState[$k]", false, 0, $path, $domain, is_ssl(), true ); } } if ( $restate ) { foreach ( $state as $k => $v ) { setcookie( "jetpackState[$k]", $v, 0, $path, $domain, is_ssl(), true ); } return; } // Get a state variable. if ( isset( $key ) && ! isset( $value ) ) { if ( array_key_exists( $key, $state ) ) { return $state[ $key ]; } return null; } // Set a state variable. if ( isset( $key ) && isset( $value ) ) { if ( is_array( $value ) && isset( $value[0] ) ) { $value = $value[0]; } $state[ $key ] = $value; if ( ! headers_sent() ) { if ( $this->should_set_cookie( $key ) ) { setcookie( "jetpackState[$key]", $value, 0, $path, $domain, is_ssl(), true ); } } } } /** * Determines whether the jetpackState[$key] value should be added to the * cookie. * * @param string $key The state key. * * @return boolean Whether the value should be added to the cookie. */ public function should_set_cookie( $key ) { global $current_screen; $page = isset( $current_screen->base ) ? $current_screen->base : null; if ( 'toplevel_page_jetpack' === $page && 'display_update_modal' === $key ) { return false; } return true; } } PK E/]?DrS S ! jetpack-status/src/class-host.phpnu ��� <?php /** * A hosting provide class for Jetpack. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack\Status; use Automattic\Jetpack\Constants; /** * Hosting provider class. */ class Host { /** * Determine if this site is an WordPress.com on Atomic site or not by looking for presence of the wpcomsh plugin. * * @since 1.9.0 * * @return bool */ public function is_woa_site() { $ret = Cache::get( 'is_woa_site' ); if ( null === $ret ) { $ret = $this->is_atomic_platform() && Constants::is_true( 'WPCOMSH__PLUGIN_FILE' ); Cache::set( 'is_woa_site', $ret ); } return $ret; } /** * Determine if the site is hosted on the Atomic hosting platform. * * @since 1.9.0 * * @return bool */ public function is_atomic_platform() { return Constants::is_true( 'ATOMIC_SITE_ID' ) && Constants::is_true( 'ATOMIC_CLIENT_ID' ); } /** * Determine if this is a Newspack site. * * @return bool */ public function is_newspack_site() { return Constants::is_defined( 'NEWSPACK_PLUGIN_FILE' ); } /** * Determine if this is a VIP-hosted site. * * @return bool */ public function is_vip_site() { return Constants::is_defined( 'WPCOM_IS_VIP_ENV' ) && true === Constants::get_constant( 'WPCOM_IS_VIP_ENV' ); } /** * Determine if this is a Simple platform site. * * @return bool */ public function is_wpcom_simple() { return Constants::is_defined( 'IS_WPCOM' ) && true === Constants::get_constant( 'IS_WPCOM' ); } /** * Determine if this is a WordPress.com site. * * Includes both Simple and WoA platforms. * * @return bool */ public function is_wpcom_platform() { return $this->is_wpcom_simple() || $this->is_woa_site(); } /** * Determine if this is a P2 site. * This covers both P2 and P2020 themes. * * @return bool */ public function is_p2_site() { $site_id = $this->get_wpcom_site_id(); if ( ! $site_id ) { return false; } return str_contains( get_stylesheet(), 'pub/p2' ) || ( function_exists( '\WPForTeams\is_wpforteams_site' ) && \WPForTeams\is_wpforteams_site( $site_id ) ); } /** * Get the current site's WordPress.com ID. * * @return mixed The site's WordPress.com ID. */ public function get_wpcom_site_id() { if ( $this->is_wpcom_simple() ) { return get_current_blog_id(); } elseif ( class_exists( 'Jetpack' ) && \Jetpack::is_connection_ready() ) { return \Jetpack_Options::get_option( 'id' ); } return false; } /** * Add all wordpress.com environments to the safe redirect allowed list. * * To be used with a filter of allowed domains for a redirect. * * @param array $domains Allowed WP.com Environments. */ public static function allow_wpcom_environments( $domains ) { $domains[] = 'wordpress.com'; $domains[] = 'jetpack.wordpress.com'; $domains[] = 'wpcalypso.wordpress.com'; $domains[] = 'horizon.wordpress.com'; $domains[] = 'calypso.localhost'; return $domains; } /** * Return Calypso environment value; used for developing Jetpack and pairing * it with different Calypso environments, such as localhost. * * @since 1.18.0 * * @return string Calypso environment */ public function get_calypso_env() { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce is not required; only used for changing environments. if ( isset( $_GET['calypso_env'] ) ) { return sanitize_key( $_GET['calypso_env'] ); } // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( getenv( 'CALYPSO_ENV' ) ) { return sanitize_key( getenv( 'CALYPSO_ENV' ) ); } if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) { return sanitize_key( CALYPSO_ENV ); } return ''; } /** * Return source query param value from the URL if exists in the allowed sources list. * * @return string "source" query param value */ public function get_source_query() { // phpcs:disable WordPress.Security.NonceVerification.Recommended $allowed_sources = array( 'jetpack-manage', 'a8c-for-agencies' ); if ( isset( $_GET['source'] ) && in_array( $_GET['source'], $allowed_sources, true ) ) { return sanitize_key( $_GET['source'] ); } return ''; } /** * Returns a guess of the hosting provider for the current site based on various checks. * * @since 5.0.4 Added $guess parameter. * @since 6.0.0 Removed $guess parameter. * * @return string */ public function get_known_host_guess() { // First, let's check if we can recognize provider manually: switch ( true ) { case $this->is_woa_site(): $provider = 'woa'; break; case $this->is_atomic_platform(): $provider = 'atomic'; break; case $this->is_newspack_site(): $provider = 'newspack'; break; case $this->is_vip_site(): $provider = 'vip'; break; case $this->is_wpcom_simple(): case $this->is_wpcom_platform(): $provider = 'wpcom'; break; default: $provider = 'unknown'; break; } return $provider; } /** * Add public-api.wordpress.com to the safe redirect allowed list - only added when someone allows API access. * * @since 3.0.2 Ported from Jetpack to the Status package. * * To be used with a filter of allowed domains for a redirect. * * @param array $domains Allowed WP.com Environments. * * @return array */ public static function allow_wpcom_public_api_domain( $domains ) { $domains[] = 'public-api.wordpress.com'; return $domains; } } PK E/] =�� � # jetpack-status/src/class-errors.phpnu ��� <?php /** * An errors utility class for Jetpack. * * @package automattic/jetpack-status */ // phpcs:disable WordPress.PHP.IniSet.display_errors_Disallowed // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged // phpcs:disable WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting namespace Automattic\Jetpack; /** * Erros class. * * @deprecated since 3.2.0 */ class Errors { /** * Catches PHP errors. Must be used in conjunction with output buffering. * * @deprecated since 3.2.0 * @param bool $catch True to start catching, False to stop. * * @static */ public function catch_errors( $catch ) { _deprecated_function( __METHOD__, '3.2.0' ); static $display_errors, $error_reporting; if ( $catch ) { // Force error reporting and output, store original values. $display_errors = @ini_set( 'display_errors', 1 ); $error_reporting = @error_reporting( E_ALL ); if ( class_exists( 'Jetpack' ) ) { add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 ); } } else { // Restore the original values for error reporting and output. @ini_set( 'display_errors', $display_errors ); if ( $error_reporting !== null ) { @error_reporting( $error_reporting ); } if ( class_exists( 'Jetpack' ) ) { remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 ); } } } } PK E/]��I�1 1 $ jetpack-status/src/class-visitor.phpnu ��� <?php /** * Status and information regarding the site visitor. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack\Status; /** * Visitor class. */ class Visitor { /** * Gets current user IP address. * * @param bool $check_all_headers Check all headers? Default is `false`. * * @return string Current user IP address. */ public function get_ip( $check_all_headers = false ) { if ( $check_all_headers ) { foreach ( array( 'HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'HTTP_VIA', ) as $key ) { if ( ! empty( $_SERVER[ $key ] ) ) { // @todo Some of these might actually be lists of IPs (e.g. HTTP_X_FORWARDED_FOR) or something else entirely (HTTP_VIA). return filter_var( wp_unslash( $_SERVER[ $key ] ) ); } } } return ! empty( $_SERVER['REMOTE_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; } /** * Simple gate check for a11n feature testing purposes using AT_PROXIED_REQUEST constant. * IMPORTANT: Only use it for internal feature test purposes, not authorization. * * The goal of this function is to help us gate features by using a similar function name * we find on simple sites: is_automattician(). * * @return bool True if the current request is PROXIED, false otherwise. */ public function is_automattician_feature_flags_only() { return ( defined( 'AT_PROXIED_REQUEST' ) && AT_PROXIED_REQUEST ); } } PK E/]��R� � $ jetpack-status/src/class-request.phpnu ��� <?php /** * Get information about the current request. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack\Status; use Automattic\Jetpack\Constants; /** * Get information about the current request. */ class Request { /** * Determine whether the current request is for accessing the frontend. * Also update Vary headers to indicate that the response may vary by Accept header. * * @since 6.0.3 Added $send_vary_headers argument. * * @param bool $send_vary_headers Whether to send Vary headers. * * @return bool True if it's a frontend request, false otherwise. */ public static function is_frontend( $send_vary_headers = true ) { $is_frontend = true; $is_varying_request = true; if ( is_admin() || wp_doing_ajax() || wp_is_jsonp_request() || is_feed() || Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'REST_API_REQUEST' ) || Constants::is_true( 'WP_CLI' ) || Constants::is_true( 'WPCOM_CLI_SCRIPT' ) // Special case for CLI scripts on WP.com that aren't using WP CLI. ) { $is_frontend = false; $is_varying_request = false; } elseif ( wp_is_json_request() || wp_is_xml_request() ) { $is_frontend = false; } /* * Check existing headers for the request. * If there is no existing Vary Accept header, add one. */ if ( $send_vary_headers && $is_varying_request && ! headers_sent() ) { $headers = headers_list(); $vary_header_parts = self::get_vary_headers( $headers ); header( 'Vary: ' . implode( ', ', $vary_header_parts ) ); } /** * Filter whether the current request is for accessing the frontend. * * @since jetpack-9.0.0 * @since 6.0.3 Added $send_vary_headers argument. * * @param bool $is_frontend Whether the current request is for accessing the frontend. * @param bool $send_vary_headers Whether to send Vary headers. */ return (bool) apply_filters( 'jetpack_is_frontend', $is_frontend, $send_vary_headers ); } /** * Go through headers and get a list of Vary headers to add, * including Vary on Accept and Content-Type if necessary. * * @since jetpack-12.2 * * @param array $headers The headers to be sent. * * @return array $vary_header_parts Vary Headers to be sent. */ public static function get_vary_headers( $headers = array() ) { $vary_header_parts = array( 'accept', 'content-type' ); foreach ( $headers as $header ) { // Check for a Vary header. if ( ! str_starts_with( strtolower( $header ), 'vary:' ) ) { continue; } // If the header is a wildcard, we'll return that. if ( str_contains( $header, '*' ) ) { $vary_header_parts = array( '*' ); break; } // Remove the Vary: part of the header. $header = preg_replace( '/^vary\:\s?/i', '', $header ); // Remove spaces from the header. $header = str_replace( ' ', '', $header ); // Break the header into parts. $header_parts = explode( ',', strtolower( $header ) ); // Build an array with the Accept header and what was already there. $vary_header_parts = array_values( array_unique( array_merge( $vary_header_parts, $header_parts ) ) ); } return $vary_header_parts; } } PK E/]��%�N �N $ jetpack-status/src/class-modules.phpnu ��� <?php /** * A modules class for Jetpack. * * @package automattic/jetpack-status */ namespace Automattic\Jetpack; use Automattic\Jetpack\Current_Plan as Jetpack_Plan; use Automattic\Jetpack\IP\Utils as IP_Utils; use Automattic\Jetpack\Status\Host; /** * Class Automattic\Jetpack\Modules * * Used to retrieve information about the current status of Jetpack modules. */ class Modules { /** * Check whether or not a Jetpack module is active. * * @param string $module The slug of a Jetpack module. * @param bool $available_only Whether to only check among available modules. * * @return bool */ public function is_active( $module, $available_only = true ) { if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { return true; } return in_array( $module, self::get_active( $available_only ), true ); } /** * Load module data from module file. Headers differ from WordPress * plugin headers to avoid them being identified as standalone * plugins on the WordPress plugins page. * * @param string $module The module slug. */ public function get( $module ) { static $modules_details; // This method relies heavy on auto-generated file found in Jetpack only: module-headings.php // If it doesn't exist, it's safe to assume none of this will be helpful. if ( ! function_exists( 'jetpack_has_no_module_info' ) ) { return false; } if ( jetpack_has_no_module_info( $module ) ) { return false; } $file = $this->get_path( $this->get_slug( $module ) ); if ( isset( $modules_details[ $module ] ) ) { $mod = $modules_details[ $module ]; } else { $mod = jetpack_get_module_info( $module ); if ( null === $mod ) { // Try to get the module info from the file as a fallback. $mod = $this->get_file_data( $file, jetpack_get_all_module_header_names() ); if ( empty( $mod['name'] ) ) { // No info for this module. return false; } } $mod['sort'] = empty( $mod['sort'] ) ? 10 : (int) $mod['sort']; $mod['recommendation_order'] = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order']; $mod['deactivate'] = empty( $mod['deactivate'] ); $mod['free'] = empty( $mod['free'] ); $mod['requires_connection'] = ( ! empty( $mod['requires_connection'] ) && 'No' === $mod['requires_connection'] ) ? false : true; $mod['requires_user_connection'] = ( empty( $mod['requires_user_connection'] ) || 'No' === $mod['requires_user_connection'] ) ? false : true; if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ), true ) ) { $mod['auto_activate'] = 'No'; } else { $mod['auto_activate'] = (string) $mod['auto_activate']; } if ( $mod['module_tags'] ) { $mod['module_tags'] = explode( ',', $mod['module_tags'] ); $mod['module_tags'] = array_map( 'trim', $mod['module_tags'] ); } else { $mod['module_tags'] = array( 'Other' ); } if ( $mod['plan_classes'] ) { $mod['plan_classes'] = explode( ',', $mod['plan_classes'] ); $mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) ); } else { $mod['plan_classes'] = array( 'free' ); } if ( $mod['feature'] ) { $mod['feature'] = explode( ',', $mod['feature'] ); $mod['feature'] = array_map( 'trim', $mod['feature'] ); } else { $mod['feature'] = array( 'Other' ); } $modules_details[ $module ] = $mod; } /** * Filters the feature array on a module. * * This filter allows you to control where each module is filtered: Recommended, * and the default "Other" listing. * * @since-jetpack 3.5.0 * * @param array $mod['feature'] The areas to feature this module: * 'Recommended' shows on the main Jetpack admin screen. * 'Other' should be the default if no other value is in the array. * @param string $module The slug of the module, e.g. sharedaddy. * @param array $mod All the currently assembled module data. */ $mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod ); /** * Filter the returned data about a module. * * This filter allows overriding any info about Jetpack modules. It is dangerous, * so please be careful. * * @since-jetpack 3.6.0 * * @param array $mod The details of the requested module. * @param string $module The slug of the module, e.g. sharedaddy * @param string $file The path to the module source file. */ return apply_filters( 'jetpack_get_module', $mod, $module, $file ); } /** * Like core's get_file_data implementation, but caches the result. * * @param string $file Absolute path to the file. * @param array $headers List of headers, in the format array( 'HeaderKey' => 'Header Name' ). */ public function get_file_data( $file, $headers ) { // Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated. $file_name = basename( $file ); if ( ! Constants::is_defined( 'JETPACK__VERSION' ) ) { return get_file_data( $file, $headers ); } $cache_key = 'jetpack_file_data_' . JETPACK__VERSION; $file_data_option = get_transient( $cache_key ); if ( ! is_array( $file_data_option ) ) { delete_transient( $cache_key ); $file_data_option = false; } if ( false === $file_data_option ) { $file_data_option = array(); } $key = md5( $file_name . maybe_serialize( $headers ) ); $refresh_cache = is_admin() && isset( $_GET['page'] ) && is_string( $_GET['page'] ) && str_starts_with( $_GET['page'], 'jetpack' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput // If we don't need to refresh the cache, and already have the value, short-circuit! if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) { return $file_data_option[ $key ]; } $data = get_file_data( $file, $headers ); $file_data_option[ $key ] = $data; set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS ); return $data; } /** * Get a list of activated modules as an array of module slugs. * * @param bool $available_only Filter out the unavailable (deleted) modules. * * @return array */ public function get_active( $available_only = true ) { $active = \Jetpack_Options::get_option( 'active_modules' ); if ( ! is_array( $active ) ) { $active = array(); } if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) { $active[] = 'vaultpress'; } else { $active = array_diff( $active, array( 'vaultpress' ) ); } // If protect is active on the main site of a multisite, it should be active on all sites. Doesn't apply to WP.com. if ( ! in_array( 'protect', $active, true ) && ! ( new Host() )->is_wpcom_simple() && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) { $active[] = 'protect'; } if ( $available_only ) { // If it's not available, it shouldn't be active. // We don't delete it from the options though, as it will be active again when a plugin gets reactivated. $active = array_intersect( $active, $this->get_available() ); } /** * Allow filtering of the active modules. * * Gives theme and plugin developers the power to alter the modules that * are activated on the fly. * * @since-jetpack 5.8.0 * * @param array $active Array of active module slugs. */ $active = apply_filters( 'jetpack_active_modules', $active ); return array_unique( $active ); } /** * Extract a module's slug from its full path. * * @param string $file Full path to a file. * * @return string Module slug. */ public function get_slug( $file ) { return str_replace( '.php', '', basename( $file ) ); } /** * List available Jetpack modules. Simply lists .php files in /modules/. * Make sure to tuck away module "library" files in a sub-directory. * * @param bool|string $min_version Only return modules introduced in this version or later. Default is false, do not filter. * @param bool|string $max_version Only return modules introduced before this version. Default is false, do not filter. * @param bool|null $requires_connection Pass a boolean value to only return modules that require (or do not require) a connection. * @param bool|null $requires_user_connection Pass a boolean value to only return modules that require (or do not require) a user connection. * * @return array $modules Array of module slugs */ public function get_available( $min_version = false, $max_version = false, $requires_connection = null, $requires_user_connection = null ) { static $modules = null; if ( ! class_exists( 'Jetpack' ) || ! Constants::is_defined( 'JETPACK__VERSION' ) || ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) { return array_unique( /** * Stand alone plugins need to use this filter to register the modules they interact with. * This will allow them to activate and deactivate these modules even when Jetpack is not present. * Note: Standalone plugins can only interact with modules that also exist in the Jetpack plugin, otherwise they'll lose the ability to control it if Jetpack is activated. * * @since 1.13.6 * * @param array $modules The list of available modules as an array of slugs. * @param bool $requires_connection Whether to list only modules that require a connection to work. * @param bool $requires_user_connection Whether to list only modules that require a user connection to work. */ apply_filters( 'jetpack_get_available_standalone_modules', array(), $requires_connection, $requires_user_connection ) ); } if ( ! isset( $modules ) ) { $available_modules_option = \Jetpack_Options::get_option( 'available_modules', array() ); // Use the cache if we're on the front-end and it's available... if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) { $modules = $available_modules_option[ JETPACK__VERSION ]; } else { $files = ( new Files() )->glob_php( JETPACK__PLUGIN_DIR . 'modules' ); $modules = array(); foreach ( $files as $file ) { $slug = $this->get_slug( $file ); $headers = $this->get( $slug ); if ( ! $headers ) { continue; } $modules[ $slug ] = $headers['introduced']; } \Jetpack_Options::update_option( 'available_modules', array( JETPACK__VERSION => $modules, ) ); } } /** * Filters the array of modules available to be activated. * * @since 2.4.0 * * @param array $modules Array of available modules. * @param string $min_version Minimum version number required to use modules. * @param string $max_version Maximum version number required to use modules. * @param bool|null $requires_connection Value of the Requires Connection filter. * @param bool|null $requires_user_connection Value of the Requires User Connection filter. */ $mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version, $requires_connection, $requires_user_connection ); if ( ! $min_version && ! $max_version && $requires_connection === null && $requires_user_connection === null ) { return array_keys( $mods ); } $r = array(); foreach ( $mods as $slug => $introduced ) { if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) { continue; } if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) { continue; } $mod_details = $this->get( $slug ); if ( null !== $requires_connection && (bool) $requires_connection !== $mod_details['requires_connection'] ) { continue; } if ( null !== $requires_user_connection && (bool) $requires_user_connection !== $mod_details['requires_user_connection'] ) { continue; } $r[] = $slug; } return $r; } /** * Is slug a valid module. * * @param string $module Module slug. * * @return bool */ public function is_module( $module ) { return ! empty( $module ) && ! validate_file( $module, $this->get_available() ); } /** * Update module status. * * @param string $module - module slug. * @param boolean $active - true to activate, false to deactivate. * @param bool $exit Should exit be called after deactivation. * @param bool $redirect Should there be a redirection after activation. */ public function update_status( $module, $active, $exit = true, $redirect = true ) { return $active ? $this->activate( $module, $exit, $redirect ) : $this->deactivate( $module ); } /** * Activate a module. * * @param string $module Module slug. * @param bool $exit Should exit be called after deactivation. * @param bool $redirect Should there be a redirection after activation. * * @return bool|void */ public function activate( $module, $exit = true, $redirect = true ) { /** * Fires before a module is activated. * * @since 2.6.0 * * @param string $module Module slug. * @param bool $exit Should we exit after the module has been activated. Default to true. * @param bool $redirect Should the user be redirected after module activation? Default to true. */ do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect ); if ( ! strlen( $module ) ) { return false; } // If it's already active, then don't do it again. $active = $this->get_active(); foreach ( $active as $act ) { if ( $act === $module ) { return true; } } if ( ! $this->is_module( $module ) ) { return false; } // Jetpack plugin only if ( class_exists( 'Jetpack' ) ) { $module_data = $this->get( $module ); $status = new Status(); $state = new CookieState(); if ( ! \Jetpack::is_connection_ready() ) { if ( ! $status->is_offline_mode() ) { return false; } // If we're not connected but in offline mode, make sure the module doesn't require a connection. if ( $status->is_offline_mode() && $module_data['requires_connection'] ) { return false; } } if ( class_exists( 'Jetpack_Client_Server' ) ) { $jetpack = \Jetpack::init(); // Check and see if the old plugin is active. if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) { // Deactivate the old plugins. $deactivated = array(); foreach ( $jetpack->plugins_to_deactivate[ $module ] as $idx => $deactivate_me ) { if ( \Jetpack_Client_Server::deactivate_plugin( $deactivate_me[0], $deactivate_me[1] ) ) { // If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module // We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load. $deactivated[] = "$module:$idx"; } } if ( $deactivated ) { $state->state( 'deactivated_plugins', implode( ',', $deactivated ) ); wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) ); exit( 0 ); } } } // Protect won't work with mis-configured IPs. if ( 'protect' === $module ) { if ( ! IP_Utils::get_ip() ) { $state->state( 'message', 'protect_misconfigured_ip' ); return false; } } if ( ! Jetpack_Plan::supports( $module ) ) { return false; } // Check the file for fatal errors, a la wp-admin/plugins.php::activate. $state->state( 'module', $module ); $state->state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error. ob_start(); $module_path = $this->get_path( $module ); if ( file_exists( $module_path ) ) { require_once $this->get_path( $module ); // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.NotAbsolutePath } $active[] = $module; $this->update_active( $active ); $state->state( 'error', false ); // the override. ob_end_clean(); } else { // Not a Jetpack plugin. $active[] = $module; $this->update_active( $active ); } if ( $redirect ) { wp_safe_redirect( ( new Paths() )->admin_url( 'page=jetpack' ) ); } if ( $exit ) { exit( 0 ); } return true; } /** * Deactivate module. * * @param string $module Module slug. * * @return bool */ public function deactivate( $module ) { /** * Fires when a module is deactivated. * * @since 1.9.0 * * @param string $module Module slug. */ do_action( 'jetpack_pre_deactivate_module', $module ); $active = $this->get_active(); $new = array_filter( array_diff( $active, (array) $module ) ); return $this->update_active( $new ); } /** * Generate a module's path from its slug. * * @param string $slug Module slug. */ public function get_path( $slug ) { if ( ! Constants::is_defined( 'JETPACK__PLUGIN_DIR' ) ) { return ''; } /** * Filters the path of a modules. * * @since 7.4.0 * * @param array $return The absolute path to a module's root php file * @param string $slug The module slug */ return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug ); } /** * Saves all the currently active modules to options. * Also fires Action hooks for each newly activated and deactivated module. * * @param array $modules Array of active modules to be saved in options. * * @return bool $success true for success, false for failure. */ public function update_active( $modules ) { $current_modules = \Jetpack_Options::get_option( 'active_modules', array() ); $active_modules = $this->get_active(); $new_active_modules = array_diff( $modules, $current_modules ); $new_inactive_modules = array_diff( $active_modules, $modules ); $new_current_modules = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules ); $reindexed_modules = array_values( $new_current_modules ); $success = \Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) ); // Let's take `pre_update_option_jetpack_active_modules` filter into account // and actually decide for which modules we need to fire hooks by comparing // the 'active_modules' option before and after the update. $current_modules_post_update = \Jetpack_Options::get_option( 'active_modules', array() ); $new_inactive_modules = array_diff( $current_modules, $current_modules_post_update ); $new_inactive_modules = array_unique( $new_inactive_modules ); $new_inactive_modules = array_values( $new_inactive_modules ); $new_active_modules = array_diff( $current_modules_post_update, $current_modules ); $new_active_modules = array_unique( $new_active_modules ); $new_active_modules = array_values( $new_active_modules ); foreach ( $new_active_modules as $module ) { /** * Fires when a specific module is activated. * * @since 1.9.0 * * @param string $module Module slug. * @param boolean $success whether the module was activated. @since 4.2 */ do_action( 'jetpack_activate_module', $module, $success ); /** * Fires when a module is activated. * The dynamic part of the filter, $module, is the module slug. * * @since 1.9.0 * * @param string $module Module slug. */ do_action( "jetpack_activate_module_$module", $module ); } foreach ( $new_inactive_modules as $module ) { /** * Fired after a module has been deactivated. * * @since 4.2.0 * * @param string $module Module slug. * @param boolean $success whether the module was deactivated. */ do_action( 'jetpack_deactivate_module', $module, $success ); /** * Fires when a module is deactivated. * The dynamic part of the filter, $module, is the module slug. * * @since 1.9.0 * * @param string $module Module slug. */ do_action( "jetpack_deactivate_module_$module", $module ); } return $success; } } PK E/] �f�� � "