updated plugin Jetpack Protect
version 2.0.0
This commit is contained in:
@ -0,0 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.2.0] - 2024-01-04
|
||||
### Fixed
|
||||
- Backup: Add namespace versioning to Helper_Script_Manager and other classes. [#34739]
|
||||
|
||||
## 0.1.0 - 2023-12-13
|
||||
### Fixed
|
||||
- Initial release (improved helper script installer logging). [#34297]
|
||||
|
||||
[0.2.0]: https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v0.1.0...v0.2.0
|
@ -0,0 +1,357 @@
|
||||
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.
|
@ -0,0 +1,47 @@
|
||||
# Security Policy
|
||||
|
||||
Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Generally, only the latest version of Jetpack and its associated plugins have continued support. If a critical vulnerability is found in the current version of a plugin, we may opt to backport any patches to previous versions.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Our HackerOne program covers the below plugin software, as well as a variety of related projects and infrastructure:
|
||||
|
||||
* [Jetpack](https://jetpack.com/)
|
||||
* Jetpack Backup
|
||||
* Jetpack Boost
|
||||
* Jetpack CRM
|
||||
* Jetpack Protect
|
||||
* Jetpack Search
|
||||
* Jetpack Social
|
||||
* Jetpack VideoPress
|
||||
|
||||
**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
|
||||
|
||||
Our most critical targets are:
|
||||
|
||||
* Jetpack and the Jetpack composer packages (all within this repo)
|
||||
* Jetpack.com -- the primary marketing site.
|
||||
* cloud.jetpack.com -- a management site.
|
||||
* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
|
||||
|
||||
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
|
||||
|
||||
_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
|
||||
|
||||
## Guidelines
|
||||
|
||||
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
|
||||
|
||||
* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
|
||||
* Pen-testing Production:
|
||||
* Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
|
||||
* If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
|
||||
* **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
|
||||
* To be eligible for a bounty, all of these guidelines must be followed.
|
||||
* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
|
||||
|
||||
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "automattic/jetpack-backup-helper-script-manager",
|
||||
"description": "Install / delete helper script for backup and transport server. Not visible to site owners.",
|
||||
"type": "jetpack-library",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"yoast/phpunit-polyfills": "1.1.0",
|
||||
"automattic/jetpack-changelogger": "^4.0.5",
|
||||
"automattic/wordbless": "@dev"
|
||||
},
|
||||
"suggest": {
|
||||
"automattic/jetpack-autoloader": "Allow for better interoperability with other plugins that use this package."
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"phpunit": [
|
||||
"./vendor/phpunit/phpunit/phpunit --colors=always"
|
||||
],
|
||||
"test-php": [
|
||||
"@composer phpunit"
|
||||
],
|
||||
"post-install-cmd": "WorDBless\\Composer\\InstallDropin::copy",
|
||||
"post-update-cmd": "WorDBless\\Composer\\InstallDropin::copy"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
"extra": {
|
||||
"autotagger": true,
|
||||
"mirror-repo": "Automattic/jetpack-backup-helper-script-manager",
|
||||
"changelogger": {
|
||||
"link-template": "https://github.com/Automattic/jetpack-backup-helper-script-manager/compare/v${old}...v${new}"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-trunk": "0.2.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"roots/wordpress-core-installer": true,
|
||||
"automattic/jetpack-autoloader": true,
|
||||
"automattic/jetpack-composer-plugin": true
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,621 @@
|
||||
<?php
|
||||
/**
|
||||
* The Jetpack Backup Helper Script Manager class (implementation).
|
||||
*
|
||||
* @package automattic/jetpack-backup
|
||||
*/
|
||||
|
||||
// After changing this file, consider increasing the version number ("VXXX") in all the files using this namespace, in
|
||||
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
|
||||
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
|
||||
// are installed, or in some other cases).
|
||||
namespace Automattic\Jetpack\Backup\V0001;
|
||||
|
||||
use Exception;
|
||||
use WP_Error;
|
||||
use function content_url;
|
||||
use function get_site_url;
|
||||
use function is_wp_error;
|
||||
use function set_url_scheme;
|
||||
use function trailingslashit;
|
||||
use function wp_generate_password;
|
||||
use function wp_http_validate_url;
|
||||
use function wp_schedule_single_event;
|
||||
use function wp_upload_dir;
|
||||
use const ABSPATH;
|
||||
use const WP_CONTENT_DIR;
|
||||
|
||||
/**
|
||||
* Manage installation, deletion and cleanup of Helper Scripts to assist with backing up Jetpack Sites.
|
||||
*
|
||||
* Does *not* use WP_Filesystem, because if there are permissions issues between the webserver's user and the FTP/SSH
|
||||
* user, then we'll just install the helper script and do a backup/restore using FTP/SSH credentials (that we collect
|
||||
* ourselves), without using WP_Filesystem in any way.
|
||||
*
|
||||
* Also, if we can't write that helper script somewhere (due to writes being inaccessible to the webserver's user, or
|
||||
* for other reasons), we want to know about it (in the form of an error response), instead of having that helper
|
||||
* script silently uploaded via FTP/SFTP, so that we could fall back to a backup/restore using credentials.
|
||||
*
|
||||
* Lastly, PHP provides us with better error reporting than WP_Filesystem.
|
||||
*/
|
||||
class Helper_Script_Manager_Impl {
|
||||
|
||||
/**
|
||||
* Name of a directory that will be created for storing the helper script.
|
||||
*/
|
||||
const TEMP_DIRECTORY = 'jetpack-temp';
|
||||
|
||||
/**
|
||||
* How long until the helper script will "expire" and refuse taking requests, in seconds.
|
||||
*/
|
||||
const EXPIRY_TIME = 60 * 60 * 8;
|
||||
|
||||
/**
|
||||
* Maximum size of the helper script, in bytes.
|
||||
*/
|
||||
const MAX_FILESIZE = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Associative array of possible places to install a jetpack-temp directory, along with the URL to access each.
|
||||
*
|
||||
* Keys specify the full path of install locations, and values point to the equivalent URL.
|
||||
*
|
||||
* If null, then install locations will be determined dynamically at the point of an install.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $custom_install_locations;
|
||||
|
||||
/**
|
||||
* Filenames to ignore in scandir()'s return value.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $scandir_ignored_names = array( '.', '..' );
|
||||
|
||||
/**
|
||||
* Header that the helper script is expected to start with.
|
||||
*/
|
||||
const HELPER_HEADER = "<?php /* Jetpack Backup Helper Script */\n";
|
||||
|
||||
/**
|
||||
* Lines that will be written to README in the helper directory.
|
||||
*/
|
||||
const README_LINES = array(
|
||||
'These files have been put on your server by Jetpack to assist with backups, restores, and scans of your ' .
|
||||
'site content. They are cleaned up automatically when we no longer need them.',
|
||||
'If you no longer have Jetpack connected to your site, you can delete them manually.',
|
||||
'If you have questions or need assistance, please contact Jetpack Support at https://jetpack.com/support/',
|
||||
'If you like to build amazing things with WordPress, you should visit automattic.com/jobs and apply to join ' .
|
||||
'the fun – mention this file when you apply!',
|
||||
);
|
||||
|
||||
/**
|
||||
* Data that will be written to index.php in the helper directory.
|
||||
*/
|
||||
const INDEX_FILE = '<?php // Silence is golden';
|
||||
|
||||
/**
|
||||
* Create Helper Script Manager.
|
||||
*
|
||||
* @param array|null $custom_install_locations Associative array of possible places to install a jetpack-temp
|
||||
* directory, along with the URL to access each.
|
||||
*/
|
||||
public function __construct( $custom_install_locations = null ) {
|
||||
$this->custom_install_locations = $custom_install_locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get either the default install locations, or the ones configured in the constructor.
|
||||
*
|
||||
* Has to be done late, i.e. can't be done in constructor, because in __construct() not all constants / functions
|
||||
* might be available.
|
||||
*
|
||||
* @return array<string, string|WP_Error> Array with keys specifying the full path of install locations, and values
|
||||
* either pointing to the equivalent URL, or being WP_Error if a specific path is not accessible.
|
||||
*/
|
||||
public function install_locations() {
|
||||
if ( $this->custom_install_locations !== null ) {
|
||||
return $this->custom_install_locations;
|
||||
}
|
||||
|
||||
$abspath_url = get_site_url();
|
||||
|
||||
$locations = array();
|
||||
|
||||
// Prioritize trying to write to "wp-content/" and "wp-content/uploads/" first, because those locations are
|
||||
// expected to be writable more often (unlike ABSPATH), and ABSPATH on some setups might have a weird value
|
||||
// which doesn't point to document root.
|
||||
|
||||
try {
|
||||
if ( Throw_On_Errors::t_is_dir( WP_CONTENT_DIR ) ) {
|
||||
$wp_content_dir = Throw_On_Errors::t_realpath( WP_CONTENT_DIR );
|
||||
|
||||
// Using content_url() instead of WP_CONTENT_URL as it tests for whether we're using SSL.
|
||||
$wp_content_url = content_url();
|
||||
|
||||
// I think we mess up the order in which we load things somewhere in a test, so "wp-content" and
|
||||
// "wp-content/uploads/" URLs don't actually have the scheme+host part in them.
|
||||
if ( ! wp_http_validate_url( $wp_content_url ) ) {
|
||||
$wp_content_url = $abspath_url . $wp_content_url;
|
||||
}
|
||||
|
||||
$locations[ $wp_content_dir ] = $wp_content_url;
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$locations[ WP_CONTENT_DIR ] = new WP_Error(
|
||||
'content_path_missing',
|
||||
'Unable to access content path "' . WP_CONTENT_DIR . '"' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
$upload_dir_info = wp_upload_dir();
|
||||
$wp_uploads_dir = $upload_dir_info['basedir'];
|
||||
|
||||
try {
|
||||
if ( Throw_On_Errors::t_is_dir( $wp_uploads_dir ) ) {
|
||||
|
||||
$wp_uploads_dir = Throw_On_Errors::t_realpath( $wp_uploads_dir );
|
||||
$wp_uploads_url = $upload_dir_info['baseurl'];
|
||||
|
||||
// wp_upload_dir() doesn't check for whether we're using SSL:
|
||||
//
|
||||
// https://core.trac.wordpress.org/ticket/25449
|
||||
//
|
||||
// so set the scheme manually.
|
||||
$wp_uploads_url = set_url_scheme( $wp_uploads_url );
|
||||
|
||||
if ( ! wp_http_validate_url( $wp_uploads_url ) ) {
|
||||
$wp_uploads_url = $abspath_url . $wp_uploads_url;
|
||||
}
|
||||
|
||||
$locations[ $wp_uploads_dir ] = $wp_uploads_url;
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$locations[ $wp_uploads_dir ] = new WP_Error(
|
||||
'uploads_path_missing',
|
||||
'Unable to access uploads path "' . $wp_uploads_dir . '"' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if ( Throw_On_Errors::t_is_dir( ABSPATH ) ) {
|
||||
$abspath_dir = Throw_On_Errors::t_realpath( ABSPATH );
|
||||
$locations[ $abspath_dir ] = $abspath_url;
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$locations[ ABSPATH ] = new WP_Error(
|
||||
'abspath_missing',
|
||||
'Unable to access WordPress root "' . ABSPATH . '": ' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return $locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a Helper Script, and returns its filesystem path and access url.
|
||||
*
|
||||
* @param string $script_body Helper Script file contents.
|
||||
*
|
||||
* @return array|WP_Error Either an array containing the filesystem path ("path"), the URL ("url") of the helper
|
||||
* script, and the WordPress root ("abspath"), or an instance of WP_Error.
|
||||
*/
|
||||
public function install_helper_script( $script_body ) {
|
||||
// Check that the script body contains the correct header.
|
||||
$actual_header = static::string_starts_with_substring( $script_body, static::HELPER_HEADER );
|
||||
if ( true !== $actual_header ) {
|
||||
return new WP_Error(
|
||||
'bad_header',
|
||||
'Bad helper script header: 0x' . bin2hex( $actual_header ),
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
// Refuse to install a Helper Script that is too large.
|
||||
$helper_script_size = strlen( $script_body );
|
||||
if ( $helper_script_size > static::MAX_FILESIZE ) {
|
||||
return new WP_Error(
|
||||
'too_big',
|
||||
"Helper script is bigger ($helper_script_size bytes) " .
|
||||
'than the max. size (' . static::MAX_FILESIZE . ' bytes)',
|
||||
array( 'status' => 413 )
|
||||
);
|
||||
}
|
||||
|
||||
// Replace '[wp_path]' in the Helper Script with the WordPress installation location. Allows the Helper Script
|
||||
// to find WordPress.
|
||||
$wp_path_marker = '[wp_path]';
|
||||
try {
|
||||
$normalized_abspath = addslashes( Throw_On_Errors::t_realpath( ABSPATH ) );
|
||||
} catch ( Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'abspath_missing',
|
||||
'Error while resolving ABSPATH "' . ABSPATH . '": ' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
$script_body = str_replace(
|
||||
$wp_path_marker,
|
||||
$normalized_abspath,
|
||||
$script_body,
|
||||
$wp_path_marker_replacement_count
|
||||
);
|
||||
if ( 0 === $wp_path_marker_replacement_count ) {
|
||||
return new WP_Error(
|
||||
'no_wp_path_marker',
|
||||
"Helper script does not have the '$wp_path_marker' marker",
|
||||
array( 'status' => 400 )
|
||||
);
|
||||
}
|
||||
|
||||
$failure_paths_and_reasons = array();
|
||||
|
||||
foreach ( $this->install_locations() as $directory => $url ) {
|
||||
|
||||
if ( is_wp_error( $url ) ) {
|
||||
$failure_paths_and_reasons[] = "directory '$directory': " . $url->get_error_message();
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$installed = $this->install_to_location_or_throw( $script_body, $directory, $url );
|
||||
|
||||
// Always schedule a cleanup run shortly after EXPIRY_TIME.
|
||||
wp_schedule_single_event(
|
||||
time() + static::EXPIRY_TIME + 60,
|
||||
'jetpack_backup_cleanup_helper_scripts'
|
||||
);
|
||||
|
||||
return array(
|
||||
'path' => $installed['path'],
|
||||
'url' => $installed['url'],
|
||||
'abspath' => Throw_On_Errors::t_realpath( ABSPATH ),
|
||||
);
|
||||
|
||||
} catch ( Exception $exception ) {
|
||||
$failure_paths_and_reasons[] = "directory '$directory' (URL '$url'): " . $exception->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'all_locations_failed',
|
||||
'Unable to write the helper script to any install locations; ' .
|
||||
'tried: ' . implode( ';', $failure_paths_and_reasons ),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install helper script to a directory, or throw an exception.
|
||||
*
|
||||
* @param string $script_body Helper script's body.
|
||||
* @param string $directory Candidate directory to create "jetpack-temp" in and write the helper script.
|
||||
* @param string $url Base URL that the files in a directory are expected to be available at.
|
||||
*
|
||||
* @return string[] Array with "path" (location to the installed helper script) and "url"
|
||||
* (URL of the installed helper script) keys.
|
||||
* @throws Exception On I/O errors.
|
||||
*/
|
||||
protected function install_to_location_or_throw( $script_body, $directory, $url ) {
|
||||
if ( ! Throw_On_Errors::t_is_writable( $directory ) ) {
|
||||
throw new Exception( "Directory '$directory' is not writable" );
|
||||
}
|
||||
|
||||
$temp_dir = trailingslashit( $directory ) . static::TEMP_DIRECTORY;
|
||||
|
||||
if ( ! Throw_On_Errors::t_is_dir( $temp_dir ) ) {
|
||||
Throw_On_Errors::t_mkdir( $temp_dir );
|
||||
}
|
||||
|
||||
$readme_path = trailingslashit( $temp_dir ) . 'README';
|
||||
Throw_On_Errors::t_file_put_contents( $readme_path, implode( "\n\n", static::README_LINES ) );
|
||||
|
||||
$index_path = trailingslashit( $temp_dir ) . 'index.php';
|
||||
Throw_On_Errors::t_file_put_contents( $index_path, static::INDEX_FILE );
|
||||
|
||||
$file_key = wp_generate_password( 10, false );
|
||||
$file_name = 'jp-helper-' . $file_key . '.php';
|
||||
$file_path = trailingslashit( $temp_dir ) . $file_name;
|
||||
|
||||
// Very unlikely, but check nonetheless.
|
||||
if ( Throw_On_Errors::t_file_exists( $file_path ) ) {
|
||||
throw new Exception( "Helper script at '$file_path' already exists" );
|
||||
}
|
||||
|
||||
Throw_On_Errors::t_file_put_contents( $file_path, $script_body );
|
||||
|
||||
return array(
|
||||
'path' => $file_path,
|
||||
'url' => trailingslashit( $url ) . trailingslashit( static::TEMP_DIRECTORY ) . $file_name,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the helper script is gone (by deleting it, if needed).
|
||||
*
|
||||
* @param string $path Path to the helper script to delete.
|
||||
*
|
||||
* @return true|WP_Error True if the file helper script is gone (either it got deleted, or it was never there), or
|
||||
* WP_Error instance on deletion failures.
|
||||
*/
|
||||
public function delete_helper_script( $path ) {
|
||||
try {
|
||||
$this->delete_helper_script_or_throw( $path );
|
||||
} catch ( Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'deletion_failure',
|
||||
"Unable to delete helper script at '$path': " . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the helper script is gone (by deleting it, if needed), throw an exception on errors.
|
||||
*
|
||||
* @param string $path Path to the helper script to delete.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception On deletion failures.
|
||||
*/
|
||||
protected function delete_helper_script_or_throw( $path ) {
|
||||
|
||||
if ( ! Throw_On_Errors::t_file_exists( $path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! Throw_On_Errors::t_is_readable( $path ) ) {
|
||||
throw new Exception( "File '$path' is not readable" );
|
||||
}
|
||||
|
||||
if ( ! Throw_On_Errors::t_is_writable( $path ) ) {
|
||||
throw new Exception( "File '$path' is not writable" );
|
||||
}
|
||||
|
||||
$helper_script_size = Throw_On_Errors::t_filesize( $path );
|
||||
|
||||
// Check this file looks like a JPR helper script.
|
||||
$helper_header_size = strlen( static::HELPER_HEADER );
|
||||
if ( $helper_script_size < $helper_header_size ) {
|
||||
throw new Exception(
|
||||
"Helper script is smaller ($helper_script_size bytes) " .
|
||||
"than the expected header ($helper_header_size bytes)"
|
||||
);
|
||||
}
|
||||
if ( $helper_script_size > static::MAX_FILESIZE ) {
|
||||
throw new Exception(
|
||||
"Helper script is bigger ($helper_script_size bytes) " .
|
||||
'than the max. size (' . static::MAX_FILESIZE . ' bytes)'
|
||||
);
|
||||
}
|
||||
|
||||
$actual_header = static::verify_file_header( $path, static::HELPER_HEADER );
|
||||
if ( true !== $actual_header ) {
|
||||
throw new Exception( 'Bad helper script header: 0x' . bin2hex( $actual_header ) );
|
||||
}
|
||||
|
||||
Throw_On_Errors::t_unlink( $path );
|
||||
|
||||
$this->delete_helper_directory_if_empty( dirname( $path ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for Helper Scripts that are suspiciously old, and clean them out.
|
||||
*
|
||||
* @return true|WP_Error True if all expired helper scripts got cleaned up successfully, or an instance of
|
||||
* WP_Error if one or more expired helper scripts didn't manage to get cleaned up.
|
||||
*/
|
||||
public function cleanup_expired_helper_scripts() {
|
||||
try {
|
||||
$this->cleanup_helper_scripts( time() - static::EXPIRY_TIME );
|
||||
} catch ( Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'cleanup_failed',
|
||||
'Unable to clean up expired helper scripts: ' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for and delete all Helper Scripts. Used during uninstallation.
|
||||
*
|
||||
* @return true|WP_Error True if all helper scripts got deleted successfully, or an instance of WP_Error if one or
|
||||
* more helper scripts didn't manage to get deleted.
|
||||
*/
|
||||
public function delete_all_helper_scripts() {
|
||||
try {
|
||||
$this->cleanup_helper_scripts();
|
||||
} catch ( Exception $exception ) {
|
||||
return new WP_Error(
|
||||
'cleanup_failed',
|
||||
'Unable to clean up all helper scripts: ' . $exception->getMessage(),
|
||||
array( 'status' => 500 )
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for and delete Helper Scripts. If an $expiry_time is specified, only delete Helper Scripts
|
||||
* with a mtime older than $expiry_time. Otherwise, delete them all.
|
||||
*
|
||||
* @param int|null $expiry_time If specified, only delete scripts older than this UNIX timestamp.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If one or more helper scripts doesn't manage to get cleaned up.
|
||||
*/
|
||||
protected function cleanup_helper_scripts( $expiry_time = null ) {
|
||||
|
||||
$error_messages = array();
|
||||
|
||||
foreach ( $this->install_locations() as $directory => $url ) {
|
||||
|
||||
if ( is_wp_error( $url ) ) {
|
||||
$error_messages[] = $url->get_error_message();
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp_dir = trailingslashit( trailingslashit( $directory ) . static::TEMP_DIRECTORY );
|
||||
|
||||
if ( Throw_On_Errors::t_is_dir( $temp_dir ) ) {
|
||||
|
||||
// Find expired helper scripts and delete them.
|
||||
$temp_dir_contents = Throw_On_Errors::t_scandir( $temp_dir );
|
||||
|
||||
foreach ( $temp_dir_contents as $name ) {
|
||||
|
||||
if ( in_array( $name, $this->scandir_ignored_names, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$full_path = $temp_dir . $name;
|
||||
|
||||
$last_modified = Throw_On_Errors::t_filemtime( $full_path );
|
||||
|
||||
if ( preg_match( '/^jp-helper-.*\.php$/', $name ) ) {
|
||||
if ( null === $expiry_time || $last_modified < $expiry_time ) {
|
||||
try {
|
||||
$this->delete_helper_script_or_throw( $full_path );
|
||||
} catch ( Exception $exception ) {
|
||||
$error_messages[] = $exception->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the directory if it's empty now.
|
||||
$this->delete_helper_directory_if_empty( $temp_dir );
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $error_messages ) > 0 ) {
|
||||
throw new Exception(
|
||||
'Unable to clean up one or more helper scripts: ' . implode( ';', $error_messages )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a helper script directory if it's empty.
|
||||
*
|
||||
* @param string $dir Path to the helper script directory.
|
||||
*
|
||||
* @return bool True if the directory is missing, or was empty and got deleted; false if directory still contains
|
||||
* something and wasn't deleted.
|
||||
* @throws Exception On I/O errors.
|
||||
*/
|
||||
protected function delete_helper_directory_if_empty( $dir ) {
|
||||
|
||||
if ( ! Throw_On_Errors::t_is_dir( $dir ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check that the only remaining files are a README and index.php generated by this system.
|
||||
$allowed_files_and_headers = array(
|
||||
'README' => static::README_LINES[0],
|
||||
'index.php' => static::INDEX_FILE,
|
||||
);
|
||||
|
||||
$dir_contents = Throw_On_Errors::t_scandir( $dir );
|
||||
|
||||
if ( count( $dir_contents ) > count( $allowed_files_and_headers ) + count( $this->scandir_ignored_names ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $dir_contents as $name ) {
|
||||
|
||||
if ( in_array( $name, $this->scandir_ignored_names, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$full_path = trailingslashit( $dir ) . $name;
|
||||
if ( ! isset( $allowed_files_and_headers[ $name ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the file starts with the expected contents.
|
||||
$actual_header = static::verify_file_header( $full_path, $allowed_files_and_headers[ $name ] );
|
||||
if ( true !== $actual_header ) {
|
||||
throw new Exception( "Bad header for file '$full_path': 0x" . bin2hex( $actual_header ) );
|
||||
}
|
||||
|
||||
Throw_On_Errors::t_unlink( $full_path );
|
||||
}
|
||||
|
||||
// If the directory is now empty, delete it.
|
||||
$dir_contents_after_cleanup = Throw_On_Errors::t_scandir( $dir );
|
||||
|
||||
if ( count( $dir_contents_after_cleanup ) <= count( $this->scandir_ignored_names ) ) {
|
||||
Throw_On_Errors::t_rmdir( $dir );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if string starts with a substring, and if it doesn't, return the actual prefix.
|
||||
*
|
||||
* @param string $string String to search in.
|
||||
* @param string $expected_prefix Expected prefix.
|
||||
*
|
||||
* @return bool|string True if string starts with a substring, or the actual prefix that was found instead of the
|
||||
* expected prefix.
|
||||
*/
|
||||
protected static function string_starts_with_substring( $string, $expected_prefix ) {
|
||||
$actual_prefix = substr( $string, 0, strlen( $expected_prefix ) );
|
||||
if ( $actual_prefix !== $expected_prefix ) {
|
||||
return $actual_prefix;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a file exists, is readable, and has the expected header.
|
||||
*
|
||||
* @param string $path File to verify.
|
||||
* @param string $expected_header Header that the file should have.
|
||||
*
|
||||
* @return bool|string True if header matches, or an actual header if it doesn't match.
|
||||
* @throws Exception If the file doesn't exist, isn't readable, or is of the wrong size.
|
||||
*/
|
||||
protected static function verify_file_header( $path, $expected_header ) {
|
||||
if ( ! Throw_On_Errors::t_file_exists( $path ) ) {
|
||||
throw new Exception( "File '$path' does not exist" );
|
||||
}
|
||||
|
||||
if ( ! Throw_On_Errors::t_is_readable( $path ) ) {
|
||||
throw new Exception( "File '$path' is not readable" );
|
||||
}
|
||||
|
||||
$file_size = Throw_On_Errors::t_filesize( $path );
|
||||
|
||||
// Check this file looks like a JPR helper script.
|
||||
$expected_header_size = strlen( $expected_header );
|
||||
if ( $file_size < $expected_header_size ) {
|
||||
throw new Exception(
|
||||
"File is smaller ($file_size bytes) " .
|
||||
"than the expected header ($expected_header_size bytes)"
|
||||
);
|
||||
}
|
||||
if ( $file_size > static::MAX_FILESIZE ) {
|
||||
throw new Exception(
|
||||
"File is bigger ($file_size bytes) " .
|
||||
'than the max. size (' . static::MAX_FILESIZE . ' bytes)'
|
||||
);
|
||||
}
|
||||
|
||||
$file_contents = Throw_On_Errors::t_file_get_contents( $path );
|
||||
return static::string_starts_with_substring( $file_contents, $expected_header );
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Jetpack Backup Helper Script Manager class (static wrapper).
|
||||
*
|
||||
* @package automattic/jetpack-backup
|
||||
*/
|
||||
|
||||
// After changing this file, consider increasing the version number ("VXXX") in all the files using this namespace, in
|
||||
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
|
||||
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
|
||||
// are installed, or in some other cases).
|
||||
namespace Automattic\Jetpack\Backup\V0001;
|
||||
|
||||
/**
|
||||
* Manage installation, deletion and cleanup of Helper Scripts to assist with backing up Jetpack Sites.
|
||||
*
|
||||
* A static wrapper around an "implementation" class so that it gets autoloaded late, and we always get the latest
|
||||
* version of the class instead of a random version of it:
|
||||
*
|
||||
* https://github.com/Automattic/jetpack/pull/34297#discussion_r1424227489
|
||||
*/
|
||||
class Helper_Script_Manager {
|
||||
|
||||
/**
|
||||
* Instance of helper script manager implementation, or null if not initialized yet.
|
||||
*
|
||||
* @var Helper_Script_Manager_Impl|null
|
||||
*/
|
||||
protected static $impl = null;
|
||||
|
||||
/**
|
||||
* Initialize an instance of helper script manager implementation (if needed).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function initialize_impl_if_needed() {
|
||||
if ( null === static::$impl ) {
|
||||
static::$impl = new Helper_Script_Manager_Impl();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a Helper Script, and returns its filesystem path and access url.
|
||||
*
|
||||
* @param string $script_body Helper Script file contents.
|
||||
*
|
||||
* @return array|\WP_Error Either an array containing the filesystem path ("path"), the URL ("url") of the helper
|
||||
* script, and the WordPress root ("abspath"), or an instance of WP_Error.
|
||||
*/
|
||||
public static function install_helper_script( $script_body ) {
|
||||
static::initialize_impl_if_needed();
|
||||
return static::$impl->install_helper_script( $script_body );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the helper script is gone (by deleting it, if needed).
|
||||
*
|
||||
* @param string $path Path to the helper script to delete.
|
||||
*
|
||||
* @return true|\WP_Error True if the file helper script is gone (either it got deleted, or it was never there), or
|
||||
* WP_Error instance on deletion failures.
|
||||
*/
|
||||
public static function delete_helper_script( $path ) {
|
||||
static::initialize_impl_if_needed();
|
||||
return static::$impl->delete_helper_script( $path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for Helper Scripts that are suspiciously old, and clean them out.
|
||||
*
|
||||
* @return true|\WP_Error True if all expired helper scripts got cleaned up successfully, or an instance of
|
||||
* WP_Error if one or more expired helper scripts didn't manage to get cleaned up.
|
||||
*/
|
||||
public static function cleanup_expired_helper_scripts() {
|
||||
static::initialize_impl_if_needed();
|
||||
return static::$impl->cleanup_expired_helper_scripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for and delete all Helper Scripts. Used during uninstallation.
|
||||
*
|
||||
* @return true|\WP_Error True if all helper scripts got deleted successfully, or an instance of WP_Error if one or
|
||||
* more helper scripts didn't manage to get deleted.
|
||||
*/
|
||||
public static function delete_all_helper_scripts() {
|
||||
static::initialize_impl_if_needed();
|
||||
return static::$impl->delete_all_helper_scripts();
|
||||
}
|
||||
}
|
@ -0,0 +1,496 @@
|
||||
<?php // phpcs:disable Squiz.Commenting.FileComment.Missing
|
||||
|
||||
// phpcs:disable WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
|
||||
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
|
||||
// phpcs:disable WordPress.PHP.IniSet.display_errors_Disallowed
|
||||
|
||||
// After changing this file, consider increasing the version number ("VXXX") in all the files using this namespace, in
|
||||
// order to ensure that the specific version of this file always get loaded. Otherwise, Jetpack autoloader might decide
|
||||
// to load an older/newer version of the class (if, for example, both the standalone and bundled versions of the plugin
|
||||
// are installed, or in some other cases).
|
||||
namespace Automattic\Jetpack\Backup\V0001;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Wrappers for functions which throw an exception on errors and warnings instead of silently continuing operation.
|
||||
*
|
||||
* PHP is pretty lax with error reporting when doing I/O operations, e.g. a typical I/O helper function returns false,
|
||||
* null, and/or emits a warning, but the whole PHP application continues operation. It's for the caller of the I/O
|
||||
* helper function to test the returned result of the function, and notice + act upon I/O errors.
|
||||
*
|
||||
* We really want to know about each and every I/O error. Therefore, this class provides wrappers for some common
|
||||
* (mostly) I/O operations that throw an exception on errors instead of just returning false, null, or something else.
|
||||
* This wrapper class treats warnings as errors too.
|
||||
*
|
||||
* Given that static method names are similar to the ones used by PHP, they're prefixed with "t_" to not erroneously
|
||||
* trigger various security scanners.
|
||||
*/
|
||||
class Throw_On_Errors {
|
||||
|
||||
/**
|
||||
* Execute a callable, throw an exception (together with a descriptive label) on PHP warnings / errors.
|
||||
*
|
||||
* @param callable $callable Callable to execute.
|
||||
* @param string $label Label to add to the thrown exception to clarify what was attempted.
|
||||
*
|
||||
* @return mixed Callable's return value, if any.
|
||||
* @throws Exception On warnings thrown by the callable.
|
||||
* @noinspection PhpUnusedParameterInspection
|
||||
*/
|
||||
private static function throw_on_warnings( $callable, $label ) {
|
||||
$old_error_reporting = error_reporting( - 1 );
|
||||
$old_display_errors = ini_set( 'display_errors', 'stderr' );
|
||||
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
|
||||
set_error_handler(
|
||||
/**
|
||||
* Temporary error handler.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.set-error-handler
|
||||
* @see https://www.php.net/manual/en/function.set-error-handler.php
|
||||
*
|
||||
* @param int $errno Level of the error raised.
|
||||
* @param string $errstr Error message.
|
||||
* @param string|null $errfile Filename that the error was raised in.
|
||||
* @param int|null $errline Line number where the error was raised.
|
||||
* @param array|null $errcontext Deprecated, unused.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
function ( $errno, $errstr, $errfile = null, $errline = null, $errcontext = null ) {
|
||||
throw new Exception( "$errstr (file: $errfile; line: $errline)" );
|
||||
}
|
||||
);
|
||||
|
||||
$result = null;
|
||||
$error_message = null;
|
||||
try {
|
||||
$result = $callable();
|
||||
} catch ( Throwable $throwable ) {
|
||||
$error_message = $throwable->getMessage();
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
ini_set( 'display_errors', $old_display_errors );
|
||||
error_reporting( $old_error_reporting );
|
||||
|
||||
if ( $error_message !== null ) {
|
||||
throw new Exception( "$label failed: $error_message" );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return canonicalized absolute pathname, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.realpath
|
||||
* @see https://www.php.net/manual/en/function.realpath.php
|
||||
*
|
||||
* @param string $path Path being checked.
|
||||
*
|
||||
* @return string Canonicalized absolute pathname
|
||||
* @throws Exception On invalid parameters, or if realpath() has returned false or thrown warnings.
|
||||
*/
|
||||
public static function t_realpath( $path ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $path ) {
|
||||
throw new Exception( 'Filename for realpath() is unset' );
|
||||
}
|
||||
|
||||
$label = "realpath( '$path' )";
|
||||
|
||||
$realpath_result = static::throw_on_warnings(
|
||||
function () use ( $path ) {
|
||||
return realpath( $path );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $realpath_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
return $realpath_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file or directory exists, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-exists
|
||||
* @see https://www.php.net/manual/en/function.file-exists.php
|
||||
*
|
||||
* @param string $filename Path to the file or directory.
|
||||
*
|
||||
* @return bool True if the file or directory specified by filename exists; false otherwise.
|
||||
* @throws Exception On invalid parameters, or if file_exists() has thrown warnings.
|
||||
*/
|
||||
public static function t_file_exists( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for file_exists() is unset' );
|
||||
}
|
||||
|
||||
return static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
return file_exists( $filename );
|
||||
},
|
||||
"file_exists( '$filename' )"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the filename (or a directory) is readable, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-readable
|
||||
* @see https://www.php.net/manual/en/function.is-readable.php
|
||||
*
|
||||
* @param string $filename Filename (or directory) to check.
|
||||
*
|
||||
* @return bool True if the filename (or a directory) exists and is readable, false otherwise.
|
||||
* @throws Exception On invalid parameters, or if is_readable() has thrown warnings.
|
||||
*/
|
||||
public static function t_is_readable( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for is_readable() is unset' );
|
||||
}
|
||||
|
||||
return static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
return is_readable( $filename );
|
||||
},
|
||||
"is_readable( '$filename' )"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the filename (or a directory) is writable, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-writable
|
||||
* @see https://www.php.net/manual/en/function.is-writable.php
|
||||
*
|
||||
* @param string $filename Filename (or directory) to check.
|
||||
*
|
||||
* @return bool True if the filename (or a directory) exists and is writable, false otherwise.
|
||||
* @throws Exception On invalid parameters, or if is_writable() has thrown warnings.
|
||||
*/
|
||||
public static function t_is_writable( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for is_writable() is unset' );
|
||||
}
|
||||
|
||||
return static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
|
||||
return is_writable( $filename );
|
||||
},
|
||||
"is_writable( '$filename' )"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file size, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.filesize
|
||||
* @see https://www.php.net/manual/en/function.filesize.php
|
||||
*
|
||||
* @param string $filename Path to the file.
|
||||
*
|
||||
* @return int Size of the file in bytes
|
||||
* @throws Exception On invalid parameters, or if filesize() has thrown warnings.
|
||||
*/
|
||||
public static function t_filesize( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for filesize() is unset' );
|
||||
}
|
||||
|
||||
$label = "filesize( '$filename' )";
|
||||
|
||||
$filesize_result = static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
return filesize( $filename );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $filesize_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
return $filesize_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file modification time, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.filemtime
|
||||
* @see https://www.php.net/manual/en/function.filemtime.php
|
||||
*
|
||||
* @param string $filename Path to the file.
|
||||
*
|
||||
* @return int The time the file was last modified
|
||||
* @throws Exception On invalid parameters, or if filemtime() has thrown warnings.
|
||||
*/
|
||||
public static function t_filemtime( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for filemtime() is unset' );
|
||||
}
|
||||
|
||||
$label = "filemtime( '$filename' )";
|
||||
|
||||
$filemtime_result = static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
return filemtime( $filename );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $filemtime_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
return $filemtime_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether the filename is a directory (follow symlinks), throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.is-dir
|
||||
* @see https://www.php.net/manual/en/function.is-dir.php
|
||||
*
|
||||
* @param string $filename Path to the file.
|
||||
*
|
||||
* @return bool True if the filename (or the symlink's target) exists and is a directory, false otherwise.
|
||||
* @throws Exception On invalid parameters, if is_dir() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_is_dir( $filename ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for is_dir() is unset' );
|
||||
}
|
||||
|
||||
return static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
return is_dir( $filename );
|
||||
},
|
||||
"is_dir( '$filename' )"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a directory, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.mkdir
|
||||
* @see https://www.php.net/manual/en/function.mkdir.php
|
||||
*
|
||||
* @param string $directory Directory path.
|
||||
* @param int $permissions Permissions of the newly created directory.
|
||||
* @param bool $recursive If true, then any parent directories to the directory specified will also be created,
|
||||
* with the same permissions.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception On invalid parameters, if mkdir() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_mkdir( $directory, $permissions = 0777, $recursive = false ) {
|
||||
// PHP 5.x won't complain about permissions being null, so let's do it ourselves.
|
||||
if ( $permissions === null ) {
|
||||
throw new Exception( 'Permissions for mkdir() are unset' );
|
||||
}
|
||||
|
||||
$label = "mkdir( '$directory', 0" . decoct( $permissions ) . ', ' . ( $recursive ? 'true' : 'false' ) . ' )';
|
||||
|
||||
$mkdir_result = static::throw_on_warnings(
|
||||
function () use ( $directory, $permissions, $recursive ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
|
||||
return mkdir( $directory, $permissions, $recursive );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $mkdir_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List files and directories inside the specified path, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.scandir
|
||||
* @see https://www.php.net/manual/en/function.scandir.php
|
||||
*
|
||||
* @param string $directory Directory that will be scanned.
|
||||
*
|
||||
* @return string An array of filenames.
|
||||
* @throws Exception If scandir() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_scandir( $directory ) {
|
||||
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $directory ) {
|
||||
throw new Exception( 'Directory for scandir() is unset' );
|
||||
}
|
||||
|
||||
$label = "scandir( '$directory' )";
|
||||
|
||||
$scandir_result = static::throw_on_warnings(
|
||||
function () use ( $directory ) {
|
||||
return scandir( $directory );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $scandir_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
return $scandir_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a directory, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.rmdir
|
||||
* @see https://www.php.net/manual/en/function.rmdir.php
|
||||
*
|
||||
* @param string $directory Directory path.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception On invalid parameters, if rmdir() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_rmdir( $directory ) {
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $directory ) {
|
||||
throw new Exception( 'Directory for mkdir() is unset' );
|
||||
}
|
||||
|
||||
$label = "rmdir( '$directory' )";
|
||||
|
||||
$rmdir_result = static::throw_on_warnings(
|
||||
function () use ( $directory ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
|
||||
return rmdir( $directory );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $rmdir_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.unlink
|
||||
* @see https://www.php.net/manual/en/function.unlink.php
|
||||
*
|
||||
* @param string $filename Path to the file.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If unlink() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_unlink( $filename ) {
|
||||
|
||||
$label = "unlink( '$filename' )";
|
||||
|
||||
$unlink_result = static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
|
||||
return unlink( $filename );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $unlink_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to a file, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-put-contents
|
||||
* @see https://www.php.net/manual/en/function.file-put-contents.php
|
||||
*
|
||||
* @param string $filename Path to the file where to write the data.
|
||||
* @param string $data The data to write.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If file_put_contents() has thrown warnings, has failed, or if it didn't write all the bytes.
|
||||
*/
|
||||
public static function t_file_put_contents( $filename, $data ) {
|
||||
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for file_put_contents() is unset' );
|
||||
}
|
||||
if ( $data === null ) {
|
||||
throw new Exception( 'Data to write is null' );
|
||||
}
|
||||
|
||||
$data_length = strlen( $data );
|
||||
|
||||
$label = "file_put_contents( '$filename', $data_length bytes of data )";
|
||||
|
||||
$number_of_bytes_written = static::throw_on_warnings(
|
||||
function () use ( $filename, $data ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
|
||||
return file_put_contents( $filename, $data );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $number_of_bytes_written ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
if ( $number_of_bytes_written !== $data_length ) {
|
||||
throw new Exception(
|
||||
"$label was expected to write $data_length bytes, but wrote $number_of_bytes_written bytes"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read entire file into a string, throw on warnings / errors.
|
||||
*
|
||||
* @see https://php-legacy-docs.zend.com/manual/php5/en/function.file-get-contents
|
||||
* @see https://www.php.net/manual/en/function.file-get-contents.php
|
||||
*
|
||||
* @param string $filename Name of the file to read.
|
||||
*
|
||||
* @return string The read data.
|
||||
* @throws Exception If file_get_contents() has thrown warnings, or has failed.
|
||||
*/
|
||||
public static function t_file_get_contents( $filename ) {
|
||||
|
||||
// PHP 5.x won't complain about parameter being unset, so let's do it ourselves.
|
||||
if ( ! $filename ) {
|
||||
throw new Exception( 'Filename for file_get_contents() is unset' );
|
||||
}
|
||||
|
||||
$label = "file_get_contents( '$filename' )";
|
||||
|
||||
$file_get_contents_result = static::throw_on_warnings(
|
||||
function () use ( $filename ) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
|
||||
return file_get_contents( $filename );
|
||||
},
|
||||
$label
|
||||
);
|
||||
|
||||
if ( false === $file_get_contents_result ) {
|
||||
throw new Exception( "Unable to $label" );
|
||||
}
|
||||
|
||||
return $file_get_contents_result;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user