Total Upkeep – WordPress Backup Plugin plus Restore & Migrate by BoldGrid - Version 1.15.0

Version Description

Release date: March 15th, 2022

  • New feature: REST API calls for backup and settings management.
  • Bug fix: posix_getpgid availability check.
  • Update: Updated dependencies.
Download this release

Release Info

Developer boldgrid
Plugin Icon 128x128 Total Upkeep – WordPress Backup Plugin plus Restore & Migrate by BoldGrid
Version 1.15.0
Comparing to
See all releases

Code changes from version 1.14.14 to 1.15.0

Files changed (72) hide show
  1. admin/class-boldgrid-backup-admin-archive.php +72 -1
  2. admin/class-boldgrid-backup-admin-core.php +55 -14
  3. admin/class-boldgrid-backup-admin-cron.php +47 -9
  4. admin/class-boldgrid-backup-admin-go-pro.php +10 -0
  5. admin/class-boldgrid-backup-admin-log.php +2 -1
  6. admin/class-boldgrid-backup-admin-nopriv.php +138 -0
  7. admin/class-boldgrid-backup-admin-restore-helper.php +1 -1
  8. admin/class-boldgrid-backup-admin-task-helper.php +124 -0
  9. admin/class-boldgrid-backup-admin-task.md +129 -0
  10. admin/class-boldgrid-backup-admin-task.php +297 -0
  11. admin/class-boldgrid-backup-admin-test.php +6 -0
  12. admin/class-boldgrid-backup-admin-upload.php +6 -116
  13. admin/class-boldgrid-backup-admin-utility.php +158 -74
  14. admin/class-boldgrid-backup-admin-wp-cron.php +2 -1
  15. admin/compressor/class-boldgrid-backup-admin-compressor-system-zip.php +59 -11
  16. admin/js/boldgrid-backup-admin-in-progress.js +9 -7
  17. admin/partials/settings/backup-security.php +1 -1
  18. boldgrid-backup.php +4 -2
  19. cli/class-site-check.php +0 -31
  20. cli/verify-dd66c7edb75333982c82a15ab5b733f0.php +1 -0
  21. includes/archive/class-factory.php +92 -0
  22. includes/archive/class-option.php +126 -0
  23. includes/class-boldgrid-backup-archive-fetcher.php +290 -0
  24. includes/class-boldgrid-backup-archiver.php +117 -0
  25. includes/class-boldgrid-backup-restorer.php +251 -0
  26. includes/class-boldgrid-backup.php +51 -0
  27. readme.txt +9 -1
  28. rest/README.MD +141 -0
  29. rest/class-boldgrid-backup-rest-archive.php +277 -0
  30. rest/class-boldgrid-backup-rest-controller.php +84 -0
  31. rest/class-boldgrid-backup-rest-job.php +146 -0
  32. rest/class-boldgrid-backup-rest-setting.php +195 -0
  33. rest/class-boldgrid-backup-rest-siteurl.php +178 -0
  34. rest/class-boldgrid-backup-rest-test.php +107 -0
  35. rest/class-boldgrid-backup-rest-utility.php +77 -0
  36. vendor/autoload.php +1 -1
  37. vendor/boldgrid/library/.travis.yml +32 -10
  38. vendor/boldgrid/library/README.md +20 -0
  39. vendor/boldgrid/library/bin/install-wp-tests.sh +8 -5
  40. vendor/boldgrid/library/composer.json +3 -0
  41. vendor/boldgrid/library/composer.lock +2039 -0
  42. vendor/boldgrid/library/src/Library/Configs.php +8 -0
  43. vendor/boldgrid/library/src/Library/Dashboard/SortWidgets.php +40 -1
  44. vendor/boldgrid/library/src/Library/RatingPrompt.php +3 -1
  45. vendor/boldgrid/library/src/Util/Load.php +9 -1
  46. vendor/boldgrid/library/src/assets/js/rating-prompt.js +4 -1
  47. vendor/boldgrid/library/src/library.global.php +1 -1
  48. vendor/boldgrid/library/tests/Library/Library/test-activity.php +1 -1
  49. vendor/boldgrid/library/tests/Library/Library/test-rating-prompt.php +1 -1
  50. vendor/boldgrid/library/tests/Library/Plugin/test-factory.php +1 -1
  51. vendor/boldgrid/library/tests/Library/Plugin/test-notice.php +1 -1
  52. vendor/boldgrid/library/tests/Library/Plugin/test-page.php +1 -1
  53. vendor/boldgrid/library/tests/Library/Plugin/test-plugin.php +1 -1
  54. vendor/boldgrid/library/tests/Library/Plugin/test-plugins.php +1 -1
  55. vendor/boldgrid/library/tests/Library/Theme/test-theme.php +1 -1
  56. vendor/boldgrid/library/tests/Library/Theme/test-themes.php +1 -1
  57. vendor/boldgrid/library/tests/Library/Theme/test-update-data.php +1 -1
  58. vendor/boldgrid/library/tests/bootstrap.php +10 -1
  59. vendor/boldgrid/library/yarn.lock +12 -12
  60. vendor/composer/autoload_real.php +7 -7
  61. vendor/composer/autoload_static.php +4 -4
  62. vendor/composer/installed.json +15 -13
  63. vendor/phpseclib/phpseclib/BACKERS.md +5 -1
  64. vendor/phpseclib/phpseclib/README.md +8 -2
  65. vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php +113 -5
  66. vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +6 -6
  67. vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php +12 -0
  68. vendor/phpseclib/phpseclib/phpseclib/File/X509.php +2 -2
  69. vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +1 -1
  70. vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +670 -110
  71. vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +328 -80
  72. vendor/phpseclib/phpseclib/phpseclib/bootstrap.php +2 -1
admin/class-boldgrid-backup-admin-archive.php CHANGED
@@ -106,6 +106,36 @@ class Boldgrid_Backup_Admin_Archive {
106
  */
107
  public $view_details_url = '';
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  /**
110
  * Constructor.
111
  *
@@ -113,7 +143,11 @@ class Boldgrid_Backup_Admin_Archive {
113
  *
114
  * @param Boldgrid_Backup_Admin_Core $core Core class object.
115
  */
116
- public function __construct( $core ) {
 
 
 
 
117
  $this->core = $core;
118
  }
119
 
@@ -234,6 +268,28 @@ class Boldgrid_Backup_Admin_Archive {
234
  return $this->core->wp_filesystem->size( $this->filepath );
235
  }
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  /**
238
  * Init.
239
  *
@@ -277,6 +333,10 @@ class Boldgrid_Backup_Admin_Archive {
277
  $this->compressor = ! empty( $this->log['compressor'] ) ? $this->log['compressor'] : 'php_zip';
278
 
279
  $this->view_details_url = admin_url( 'admin.php?page=boldgrid-backup-archive-details&filename=' . $this->filename );
 
 
 
 
280
  }
281
 
282
  /**
@@ -482,6 +542,17 @@ class Boldgrid_Backup_Admin_Archive {
482
  return $this->core->archive_log->write( $this->log );
483
  }
484
 
 
 
 
 
 
 
 
 
 
 
 
485
  /**
486
  * Update an archive's timestamp based on the time in the log.
487
  *
106
  */
107
  public $view_details_url = '';
108
 
109
+ /**
110
+ * Archive id.
111
+ *
112
+ * The archive id is the archive's id as found in the boldgrid_backup_backups option.
113
+ *
114
+ * This class includes the self::set_id() method to set the actual id, but this class doesn't actually
115
+ * call that method to set the id. The id is generally set within Boldgrid\Backup\Archive\Factory.
116
+ *
117
+ * @since SINCEVERSION
118
+ * @access private
119
+ * @var int
120
+ *
121
+ * @see Boldgrid\Backup\Archive\Option for more information about the boldgrid_backup_backups option.
122
+ */
123
+ private $id;
124
+
125
+ /**
126
+ * The archive key.
127
+ *
128
+ * When retrieving a list of archives, you'll get an array, and this is the archives location in
129
+ * the array.
130
+ *
131
+ * @since SINCEVERSION
132
+ * @access private
133
+ * @var int
134
+ *
135
+ * @see self::init() To see this property initialized.
136
+ */
137
+ private $key;
138
+
139
  /**
140
  * Constructor.
141
  *
143
  *
144
  * @param Boldgrid_Backup_Admin_Core $core Core class object.
145
  */
146
+ public function __construct( Boldgrid_Backup_Admin_Core $core = null ) {
147
+ if ( empty( $core ) ) {
148
+ $core = apply_filters( 'boldgrid_backup_get_core', null );
149
+ }
150
+
151
  $this->core = $core;
152
  }
153
 
268
  return $this->core->wp_filesystem->size( $this->filepath );
269
  }
270
 
271
+ /**
272
+ * Get the archive id.
273
+ *
274
+ * @since SINCEVERSION
275
+ *
276
+ * @return int
277
+ */
278
+ public function get_id() {
279
+ return $this->id;
280
+ }
281
+
282
+ /**
283
+ * Get the archive key.
284
+ *
285
+ * @since SINCEVERSION
286
+ *
287
+ * @return int
288
+ */
289
+ public function get_key() {
290
+ return $this->key;
291
+ }
292
+
293
  /**
294
  * Init.
295
  *
333
  $this->compressor = ! empty( $this->log['compressor'] ) ? $this->log['compressor'] : 'php_zip';
334
 
335
  $this->view_details_url = admin_url( 'admin.php?page=boldgrid-backup-archive-details&filename=' . $this->filename );
336
+
337
+ // Set our key.
338
+ $details = $this->get_by_name( $this->filename );
339
+ $this->key = isset( $details['key'] ) ? $details['key'] : null;
340
  }
341
 
342
  /**
542
  return $this->core->archive_log->write( $this->log );
543
  }
544
 
545
+ /**
546
+ * Set the archive id.
547
+ *
548
+ * @since SINCEVERSION
549
+ *
550
+ * @param int $id The archive id.
551
+ */
552
+ public function set_id( $id ) {
553
+ $this->id = (int) $id;
554
+ }
555
+
556
  /**
557
  * Update an archive's timestamp based on the time in the log.
558
  *
admin/class-boldgrid-backup-admin-core.php CHANGED
@@ -1340,7 +1340,10 @@ class Boldgrid_Backup_Admin_Core {
1340
  // If changed, then update the siteurl in the database.
1341
  if ( $restored_wp_siteurl !== $wp_siteurl ) {
1342
  $update_siteurl_success =
1343
- Boldgrid_Backup_Admin_Utility::update_siteurl( $restored_wp_siteurl, $wp_siteurl );
 
 
 
1344
 
1345
  if ( ! $update_siteurl_success ) {
1346
  // Display an error notice.
@@ -2321,10 +2324,16 @@ class Boldgrid_Backup_Admin_Core {
2321
  *
2322
  * @see https://codex.wordpress.org/Function_Reference/flush_rewrite_rules
2323
  *
2324
- * @param bool $dryrun An optional switch to perform a dry run test.
 
 
 
 
 
 
2325
  * @return array An array of archive file information.
2326
  */
2327
- public function restore_archive_file( $dryrun = false ) {
2328
  $this->restoring_archive_file = true;
2329
 
2330
  $this->logger->init( 'restore-' . time() . '.log' );
@@ -2336,6 +2345,14 @@ class Boldgrid_Backup_Admin_Core {
2336
 
2337
  $restore_ok = true;
2338
 
 
 
 
 
 
 
 
 
2339
  // If a restoration was not requested, then abort.
2340
  if ( empty( $_POST['restore_now'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
2341
  $error_message = esc_html__( 'Invalid restore_now value.', 'boldgrid-backup' );
@@ -2343,6 +2360,12 @@ class Boldgrid_Backup_Admin_Core {
2343
  return [ 'error' => $error_message ];
2344
  }
2345
 
 
 
 
 
 
 
2346
  // Check if functional.
2347
  if ( ! $this->test->run_functionality_tests() ) {
2348
  $error_message = esc_html__( 'Functionality tests fail.', 'boldgrid-backup' );
@@ -2350,12 +2373,15 @@ class Boldgrid_Backup_Admin_Core {
2350
  return [ 'error' => $error_message ];
2351
  }
2352
 
2353
- // Initialize variables.
2354
- $archive_key = null;
2355
- $archive_filename = null;
2356
-
2357
- // Validate archive_key.
2358
- if ( isset( $_POST['archive_key'] ) && is_numeric( $_POST['archive_key'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
 
 
 
2359
  $archive_key = (int) $_POST['archive_key'];
2360
  } else {
2361
  $error_message = esc_html__( 'Invalid key for the selected archive file.', 'boldgrid-backup' );
@@ -2363,8 +2389,15 @@ class Boldgrid_Backup_Admin_Core {
2363
  return [ 'error' => $error_message ];
2364
  }
2365
 
2366
- // Validate archive_filename.
2367
- if ( ! empty( $_POST['archive_filename'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
 
 
 
 
 
 
 
2368
  $archive_filename = sanitize_file_name( $_POST['archive_filename'] );
2369
  } else {
2370
  $error_message = esc_html__( 'Invalid filename for the selected archive file.', 'boldgrid-backup' );
@@ -2655,7 +2688,10 @@ class Boldgrid_Backup_Admin_Core {
2655
  $this->is_archiving_update_protection = ! empty( $_POST['is_updating'] ) &&
2656
  'true' === $_POST['is_updating'];
2657
 
2658
- $archive_info = $this->archive_files( true );
 
 
 
2659
 
2660
  /*
2661
  * If there were any errors encountered during the backup, save them to the In Progress data.
@@ -3007,7 +3043,11 @@ class Boldgrid_Backup_Admin_Core {
3007
  wp_send_json_error();
3008
  }
3009
 
3010
- $archive_info = $this->restore_archive_file();
 
 
 
 
3011
 
3012
  /*
3013
  * Generate success message and add as a user notice.
@@ -3070,7 +3110,8 @@ class Boldgrid_Backup_Admin_Core {
3070
  }
3071
 
3072
  // Perform the backup operation.
3073
- $this->archive_files( true );
 
3074
  }
3075
 
3076
  /**
1340
  // If changed, then update the siteurl in the database.
1341
  if ( $restored_wp_siteurl !== $wp_siteurl ) {
1342
  $update_siteurl_success =
1343
+ Boldgrid_Backup_Admin_Utility::update_siteurl( array(
1344
+ 'old_siteurl' => $restored_wp_siteurl,
1345
+ 'siteurl' => $wp_siteurl,
1346
+ ) );
1347
 
1348
  if ( ! $update_siteurl_success ) {
1349
  // Display an error notice.
2324
  *
2325
  * @see https://codex.wordpress.org/Function_Reference/flush_rewrite_rules
2326
  *
2327
+ * @param bool $dryrun An optional switch to perform a dry run test.
2328
+ * @param array $args {
2329
+ * An optional array of args.
2330
+ *
2331
+ * @type int $archive_key An archive key.
2332
+ * @type string $archive_filename An archive filename.
2333
+ * }
2334
  * @return array An array of archive file information.
2335
  */
2336
+ public function restore_archive_file( $dryrun = false, array $args = [] ) {
2337
  $this->restoring_archive_file = true;
2338
 
2339
  $this->logger->init( 'restore-' . time() . '.log' );
2345
 
2346
  $restore_ok = true;
2347
 
2348
+ /*
2349
+ * This is a generic method to restore an archive. Do not assume the request to restore is coming
2350
+ * from a user directly via $_POST.
2351
+ *
2352
+ * Refer to check_ajax_referer usage below to help protect ajax requests.
2353
+ */
2354
+ $is_post_restore = isset( $_POST['action'] ) && 'boldgrid_backup_restore_archive' === $_POST['action']; // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
2355
+
2356
  // If a restoration was not requested, then abort.
2357
  if ( empty( $_POST['restore_now'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
2358
  $error_message = esc_html__( 'Invalid restore_now value.', 'boldgrid-backup' );
2360
  return [ 'error' => $error_message ];
2361
  }
2362
 
2363
+ if ( $is_post_restore && ! check_ajax_referer( 'boldgrid_backup_restore_archive', 'archive_auth', false ) ) {
2364
+ $error_message = esc_html__( 'Invalid nonce.', 'boldgrid-backup' );
2365
+ $this->logger->add( $error_message );
2366
+ return [ 'error' => $error_message ];
2367
+ }
2368
+
2369
  // Check if functional.
2370
  if ( ! $this->test->run_functionality_tests() ) {
2371
  $error_message = esc_html__( 'Functionality tests fail.', 'boldgrid-backup' );
2373
  return [ 'error' => $error_message ];
2374
  }
2375
 
2376
+ /*
2377
+ * Get our archive key.
2378
+ *
2379
+ * It can be passed in via $args or $_POST.
2380
+ */
2381
+ $archive_key = false;
2382
+ if ( isset( $args['archive_key'] ) ) {
2383
+ $archive_key = (int) $args['archive_key'];
2384
+ } elseif ( isset( $_POST['archive_key'] ) && is_numeric( $_POST['archive_key'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
2385
  $archive_key = (int) $_POST['archive_key'];
2386
  } else {
2387
  $error_message = esc_html__( 'Invalid key for the selected archive file.', 'boldgrid-backup' );
2389
  return [ 'error' => $error_message ];
2390
  }
2391
 
2392
+ /*
2393
+ * Get our archive filename.
2394
+ *
2395
+ * It can be passed in via $args or $_POST.
2396
+ */
2397
+ $archive_filename = false;
2398
+ if ( ! empty( $args['archive_filename'] ) ) {
2399
+ $archive_filename = sanitize_file_name( $args['archive_filename'] );
2400
+ } elseif ( ! empty( $_POST['archive_filename'] ) ) { // phpcs:ignore WordPress.CSRF.NonceVerification.NoNonceVerification
2401
  $archive_filename = sanitize_file_name( $_POST['archive_filename'] );
2402
  } else {
2403
  $error_message = esc_html__( 'Invalid filename for the selected archive file.', 'boldgrid-backup' );
2688
  $this->is_archiving_update_protection = ! empty( $_POST['is_updating'] ) &&
2689
  'true' === $_POST['is_updating'];
2690
 
2691
+ $archiver = new Boldgrid_Backup_Archiver();
2692
+ $archiver->run();
2693
+
2694
+ $archive_info = $archiver->get_info();
2695
 
2696
  /*
2697
  * If there were any errors encountered during the backup, save them to the In Progress data.
3043
  wp_send_json_error();
3044
  }
3045
 
3046
+ // Do the actual restoration.
3047
+ $restorer = new Boldgrid_Backup_Restorer();
3048
+ $restorer->run();
3049
+
3050
+ $archive_info = $restorer->get_info();
3051
 
3052
  /*
3053
  * Generate success message and add as a user notice.
3110
  }
3111
 
3112
  // Perform the backup operation.
3113
+ $archiver = new Boldgrid_Backup_Archiver();
3114
+ $archiver->run();
3115
  }
3116
 
3117
  /**
admin/class-boldgrid-backup-admin-cron.php CHANGED
@@ -202,6 +202,7 @@ class Boldgrid_Backup_Admin_Cron {
202
  if ( 'cron' === $scheduler && $this->core->scheduler->is_available( $scheduler ) ) {
203
  $this->core->scheduler->clear_all_schedules();
204
 
 
205
  if ( ! empty( $schedule ) ) {
206
  $scheduled = $this->add_cron_entry( $settings );
207
  }
@@ -347,7 +348,7 @@ class Boldgrid_Backup_Admin_Cron {
347
  $settings = $this->core->settings->get_settings();
348
  }
349
 
350
- if ( ! $settings['site_check']['enabled'] ) {
351
  return false;
352
  }
353
 
@@ -1080,7 +1081,10 @@ class Boldgrid_Backup_Admin_Cron {
1080
  /**
1081
  * Hook into "wp_ajax_nopriv_boldgrid_backup_run_backup" and generate backup.
1082
  *
1083
- * @since 1.6.1-rc.1
 
 
 
1084
  *
1085
  * @see Boldgrid_Backup_Admin_Cron::is_valid_call()
1086
  *
@@ -1091,9 +1095,10 @@ class Boldgrid_Backup_Admin_Cron {
1091
  wp_die( esc_html__( 'Error: Invalid request.', 'boldgrid-backup' ) );
1092
  }
1093
 
1094
- $archive_info = $this->core->archive_files( true );
 
1095
 
1096
- return $archive_info;
1097
  }
1098
 
1099
  /**
@@ -1106,16 +1111,49 @@ class Boldgrid_Backup_Admin_Cron {
1106
  * @return array An array of archive file information.
1107
  */
1108
  public function restore() {
 
 
 
 
1109
  if ( ! $this->is_valid_call() ) {
1110
  wp_die( esc_html__( 'Error: Invalid request.', 'boldgrid-backup' ) );
1111
  }
1112
 
1113
- $archive_info = array(
1114
- 'error' => __( 'Could not perform restoration from cron job.', 'boldgrid-backup' ),
1115
- );
 
1116
 
1117
- if ( $this->core->restore_helper->prepare_restore() ) {
1118
- $archive_info = $this->core->restore_archive_file();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1119
  }
1120
 
1121
  return $archive_info;
202
  if ( 'cron' === $scheduler && $this->core->scheduler->is_available( $scheduler ) ) {
203
  $this->core->scheduler->clear_all_schedules();
204
 
205
+ $scheduled = false;
206
  if ( ! empty( $schedule ) ) {
207
  $scheduled = $this->add_cron_entry( $settings );
208
  }
348
  $settings = $this->core->settings->get_settings();
349
  }
350
 
351
+ if ( empty( $settings['site_check']['enabled'] ) ) {
352
  return false;
353
  }
354
 
1081
  /**
1082
  * Hook into "wp_ajax_nopriv_boldgrid_backup_run_backup" and generate backup.
1083
  *
1084
+ * A scheduled backup (via cron) will call a url which ultimately triggers this method to be ran
1085
+ * to backup the site.
1086
+ *
1087
+ * @since 1.6.1
1088
  *
1089
  * @see Boldgrid_Backup_Admin_Cron::is_valid_call()
1090
  *
1095
  wp_die( esc_html__( 'Error: Invalid request.', 'boldgrid-backup' ) );
1096
  }
1097
 
1098
+ $archiver = new Boldgrid_Backup_Archiver();
1099
+ $archiver->run();
1100
 
1101
+ return $archiver->get_info();
1102
  }
1103
 
1104
  /**
1111
  * @return array An array of archive file information.
1112
  */
1113
  public function restore() {
1114
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
1115
+ $task_id = ! empty( $_POST['task_id'] ) ? $_POST['task_id'] : null;
1116
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
1117
+
1118
  if ( ! $this->is_valid_call() ) {
1119
  wp_die( esc_html__( 'Error: Invalid request.', 'boldgrid-backup' ) );
1120
  }
1121
 
1122
+ // A default error to return if restoration is not started in conditionals below.
1123
+ $archive_info = [
1124
+ 'error' => __( 'Unknown error attempting restore.', 'boldgrid-backup' ),
1125
+ ];
1126
 
1127
+ /*
1128
+ * Restore an archive.
1129
+ *
1130
+ * As of @SINCEVERSION, archives can be restored via REST. If we have a task, we're handling
1131
+ * a REST restore. Otherwise, we're handling a standard restore request.
1132
+ */
1133
+ if ( ! empty( $task_id ) ) {
1134
+ $task = new Boldgrid_Backup_Admin_Task();
1135
+ $task_found = $task->init_by_id( $task_id );
1136
+ $restorer = new Boldgrid_Backup_Restorer();
1137
+
1138
+ if ( ! $task_found ) {
1139
+ $archive_info = [
1140
+ 'error' => __( 'Resore error: Unable to instantiate task.', 'boldgrid-backup' ),
1141
+ ];
1142
+ } elseif ( false !== $task->get_data( 'url' ) ) {
1143
+ $restorer->run_by_url( $task->get_data( 'url' ) );
1144
+ $archive_info = $restorer->get_info();
1145
+ } elseif ( false !== $task->get_data( 'backup_id' ) ) {
1146
+ $restorer->run_by_id( $task->get_data( 'backup_id' ) );
1147
+ $archive_info = $restorer->get_info();
1148
+ } else {
1149
+ $archive_info = [
1150
+ 'error' => __( 'Restore error: Missing url / id.', 'boldgrid-backup' ),
1151
+ ];
1152
+ }
1153
+ } else {
1154
+ if ( $this->core->restore_helper->prepare_restore() ) {
1155
+ $archive_info = $this->core->restore_archive_file();
1156
+ }
1157
  }
1158
 
1159
  return $archive_info;
admin/class-boldgrid-backup-admin-go-pro.php CHANGED
@@ -197,6 +197,16 @@ class Boldgrid_Backup_Admin_Go_Pro {
197
  public function get_premium_url( $source = 'bgbkup', $url = 'https://www.boldgrid.com/update-backup' ) {
198
  $url = add_query_arg( 'source', $source, $url );
199
 
 
 
 
 
 
 
 
 
 
 
200
  return $url;
201
  }
202
  }
197
  public function get_premium_url( $source = 'bgbkup', $url = 'https://www.boldgrid.com/update-backup' ) {
198
  $url = add_query_arg( 'source', $source, $url );
199
 
200
+ /**
201
+ * Allow the filtering of the premium url.
202
+ *
203
+ * @since SINCEVERISON
204
+ *
205
+ * @param string $url The url to be filtered.
206
+ * @param string $source A label (used in the url) to uniquely identify this link.
207
+ */
208
+ $url = apply_filters( 'boldgrid_backup_premium_url', $url, $source );
209
+
210
  return $url;
211
  }
212
  }
admin/class-boldgrid-backup-admin-log.php CHANGED
@@ -121,7 +121,8 @@ class Boldgrid_Backup_Admin_Log {
121
 
122
  $this->add( 'WordPress Version: ' . get_bloginfo( 'version' ) );
123
 
124
- $this->add( 'Total Upkeep version: ' . BOLDGRID_BACKUP_VERSION );
 
125
 
126
  $pgid_support = Boldgrid_Backup_Admin_Test::is_getpgid_supported();
127
  $this->add( 'getpgid support: ' . ( $pgid_support ? 'Available' : 'Unavailable' ) );
121
 
122
  $this->add( 'WordPress Version: ' . get_bloginfo( 'version' ) );
123
 
124
+ $version = defined( 'BOLDGRID_BACKUP_VERSION' ) ? BOLDGRID_BACKUP_VERSION : 'Unknown';
125
+ $this->add( 'Total Upkeep version: ' . $version );
126
 
127
  $pgid_support = Boldgrid_Backup_Admin_Test::is_getpgid_supported();
128
  $this->add( 'getpgid support: ' . ( $pgid_support ? 'Available' : 'Unavailable' ) );
admin/class-boldgrid-backup-admin-nopriv.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-admin-nopriv.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Admin_Nopriv
17
+ *
18
+ * This is a generic utility class for nopriv calls.
19
+ *
20
+ * It includes methods like making an async call to trigger a backup.
21
+ *
22
+ * @since SINCEVERSION
23
+ */
24
+ class Boldgrid_Backup_Admin_Nopriv {
25
+ /**
26
+ * Generate a backup.
27
+ *
28
+ * This makes an async call to generate a backup, so that the calling method knows a backup has
29
+ * been instantiated and can continue on to other things right away.
30
+ *
31
+ * @since SINCEVERSION
32
+ *
33
+ * @param array $args {
34
+ * Optional. An array of args.
35
+ *
36
+ * @type string $task_id A task id (if one already exists).
37
+ * }
38
+ * @return mixed The results of the wp_remote_post call. An array of data on success, or a WP_Error
39
+ * on fail.
40
+ * Example return data when creating a backup via rest: https://pastebin.com/BeACwA2k
41
+ */
42
+ public function do_backup( $args = [] ) {
43
+ $url = $this->get_backup_url();
44
+
45
+ $body = [
46
+ /*
47
+ * Sometimes a task id will already be defined before the backup is started. One example
48
+ * is when a backup is started via REST. It (1) creates a task, (2) calls this method to
49
+ * start the backup, (3) immediately returns the tasks id - which a status can be queried
50
+ * for ASAP.
51
+ */
52
+ 'task_id' => ! empty( $args['task_id'] ) ? $args['task_id'] : '',
53
+ ];
54
+
55
+ $post_args = [
56
+ 'timeout' => 1,
57
+ 'blocking' => false,
58
+ 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
59
+ 'body' => $body,
60
+ ];
61
+
62
+ return wp_remote_post( $url, $post_args );
63
+ }
64
+
65
+ /**
66
+ * Restore a backup via url.
67
+ *
68
+ * @since SINCEVERSION
69
+ *
70
+ * @param array $args An optional array of args.
71
+ * @return mixed Response from wp_remote_post.
72
+ */
73
+ public function do_restore( $args = [] ) {
74
+ $url = $this->get_restore_url();
75
+
76
+ $body = [
77
+ /*
78
+ * Sometimes a task id will already be defined before the restore is started. One example
79
+ * is when a restore is started via REST. It (1) creates a task, (2) calls this method to
80
+ * start the restore, (3) immediately returns the tasks id - which a status can be queried
81
+ * for ASAP.
82
+ */
83
+ 'task_id' => ! empty( $args['task_id'] ) ? $args['task_id'] : '',
84
+ 'restore_now' => 1,
85
+ ];
86
+
87
+ $post_args = [
88
+ 'timeout' => 1,
89
+ 'blocking' => false,
90
+ 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
91
+ 'body' => $body,
92
+ ];
93
+
94
+ return wp_remote_post( $url, $post_args );
95
+ }
96
+
97
+ /**
98
+ * Get the nopriv url for generating a backup.
99
+ *
100
+ * @since SINCEVERSION
101
+ *
102
+ * @return string
103
+ */
104
+ public function get_backup_url() {
105
+ $core = apply_filters( 'boldgrid_backup_get_core', null );
106
+
107
+ return add_query_arg(
108
+ [
109
+ 'action' => 'boldgrid_backup_run_backup',
110
+ 'id' => $core->get_backup_identifier(),
111
+ 'secret' => $core->cron->get_cron_secret(),
112
+ 'doing_wp_cron' => time(),
113
+ ],
114
+ admin_url( 'admin-ajax.php' )
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Get the nopriv url for restoring a backup via url.
120
+ *
121
+ * @since SINCEVERSION
122
+ *
123
+ * @return string
124
+ */
125
+ public function get_restore_url() {
126
+ $core = apply_filters( 'boldgrid_backup_get_core', null );
127
+
128
+ return add_query_arg(
129
+ [
130
+ 'action' => 'boldgrid_backup_run_restore',
131
+ 'id' => $core->get_backup_identifier(),
132
+ 'secret' => $core->cron->get_cron_secret(),
133
+ 'doing_wp_cron' => time(),
134
+ ],
135
+ admin_url( 'admin-ajax.php' )
136
+ );
137
+ }
138
+ }
admin/class-boldgrid-backup-admin-restore-helper.php CHANGED
@@ -92,7 +92,7 @@ class Boldgrid_Backup_Admin_Restore_Helper {
92
  * @since 1.5.1
93
  */
94
  public function post_restore_htaccess() {
95
- add_action( 'shutdown', 'flush_rewrite_rules' );
96
  }
97
 
98
  /**
92
  * @since 1.5.1
93
  */
94
  public function post_restore_htaccess() {
95
+ add_action( 'shutdown', '\Boldgrid_Backup_Admin_Utility::flush_rewrite_rules' );
96
  }
97
 
98
  /**
admin/class-boldgrid-backup-admin-task-helper.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-admin-task-helper.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Admin_Task_Helper
17
+ *
18
+ * This class is a helper class for the Boldgrid_Backup_Admin_Task class.
19
+ *
20
+ * An example of tasks can be found here: admin/class-boldgrid-backup-admin-task.md#example-tasks
21
+ *
22
+ * @since SINCEVERSION
23
+ */
24
+ class Boldgrid_Backup_Admin_Task_Helper {
25
+ /**
26
+ * Option name storing our tasks.
27
+ *
28
+ * @since SINCEVERSION
29
+ * @access private
30
+ * @var string
31
+ */
32
+ private $option = 'boldgrid_backup_tasks';
33
+
34
+ /**
35
+ * Get a task by id.
36
+ *
37
+ * @since SINCEVERSION
38
+ *
39
+ * @param string $id A task id.
40
+ * @return array
41
+ */
42
+ public function get_by_id( $id ) {
43
+ $return_task = [];
44
+
45
+ $tasks = $this->get_tasks();
46
+
47
+ foreach ( $tasks as $task ) {
48
+ if ( $task['id'] === $id ) {
49
+ $return_task = $task;
50
+ break;
51
+ }
52
+ }
53
+
54
+ return $return_task;
55
+ }
56
+
57
+ /**
58
+ * Get all tasks.
59
+ *
60
+ * @since SINCEVERSION
61
+ *
62
+ * @return array
63
+ */
64
+ public function get_tasks() {
65
+ return get_option( $this->option, [] );
66
+ }
67
+
68
+ /**
69
+ * Update a task.
70
+ *
71
+ * @since SINCEVERSION
72
+ *
73
+ * @param array $update_task The task that needs updating.
74
+ * @return bool True on success.
75
+ */
76
+ public function update( $update_task ) {
77
+ // A task id is required.
78
+ if ( empty( $update_task['id'] ) ) {
79
+ return false;
80
+ }
81
+
82
+ $tasks = $this->get_tasks();
83
+
84
+ $existing_task = $this->get_by_id( $update_task['id'] );
85
+
86
+ /*
87
+ * Add our task to $tasks.
88
+ *
89
+ * If the task already exists, find it and update it. Otherwise, add it.
90
+ */
91
+ if ( ! empty( $existing_task ) ) {
92
+ foreach ( $tasks as $key => $task ) {
93
+ // Keep looking for our task by id until it's found.
94
+ if ( $task['id'] !== $update_task['id'] ) {
95
+ continue;
96
+ }
97
+
98
+ // It's been found. Replace the task by the task passed into this method.
99
+ $tasks[ $key ] = $update_task;
100
+ break;
101
+ }
102
+ } else {
103
+ // This is a new task. Simply add it to the list.
104
+ $tasks[] = $update_task;
105
+ }
106
+
107
+ return $this->update_tasks( $tasks );
108
+ }
109
+
110
+ /**
111
+ * Update all tasks.
112
+ *
113
+ * @since SINCEVERSION
114
+ *
115
+ * @param array $tasks An array of all our tasks.
116
+ */
117
+ public function update_tasks( $tasks ) {
118
+ if ( ! is_array( $tasks ) ) {
119
+ return false;
120
+ }
121
+
122
+ update_option( $this->option, $tasks );
123
+ }
124
+ }
admin/class-boldgrid-backup-admin-task.md ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Tasks vs. Jobs
2
+
3
+ ## Jobs
4
+
5
+ Jobs is a "jobs queue". The queue is checked every 5 minutes, and if a job is found, it is ran. Each
6
+ job in the queue is really just a WordPress action/hook that is ran. When it is completed, it is marked
7
+ as complete. The next time the jobs queue is processed, it will run the next action in line.
8
+
9
+ For example, when a backup is made, 2 items may be add to the the jobs queue:
10
+ 1. Upload to Google Drive
11
+ 2. Upload to Amazon S3
12
+
13
+ Every 5 minutes the jobs queue is triggered. First, the Google Drive upload will be processed. When
14
+ it is done, it will be flagged as complete. 5 minutes later, when the queue is processed again, it will
15
+ find that the Amazon S3 job is next, and it will run the action for that.
16
+
17
+ ## Tasks
18
+
19
+ A tasks is a thing to do, like "make a backup".
20
+
21
+ Here's an example of how a task works:
22
+
23
+ 1. A Rest API call comes in to create a backup. We create a new backup task and then execute it.
24
+
25
+ ```
26
+ $task = new Boldgrid_Backup_Admin_Task();
27
+ $task->init( [ 'type' => 'backup' ] );
28
+
29
+ // Trigger our backup.
30
+ $nopriv = new Boldgrid_Backup_Admin_Nopriv();
31
+ $nopriv->do_backup( [ 'task_id' => $task->get_id() ] );
32
+ ```
33
+
34
+ This new task now has an id, status, a start time, etc.
35
+
36
+ 2. Let's say that backup takes 5 minutes. Throughout that time, other Rest API calls can request the
37
+ status of that task. They'll continue to see "in progress" until the backup is complete and then the
38
+ task status will be "complete".
39
+
40
+ ## The Difference
41
+
42
+ Jobs is a collection of jobs:
43
+
44
+ ```
45
+ jobs queue
46
+ 1. job
47
+ 2. job
48
+ 3. job
49
+ ```
50
+
51
+ Theoretically, it could look like this (but it doesn't):
52
+
53
+ ```
54
+ jobs queue
55
+ 1. task
56
+ 2. task
57
+ 3. task
58
+ ```
59
+
60
+ Tasks were written ~2 or so years after the jobs queue was written, and are completely independent.
61
+ While a task and a job could be the same, the only similarities within Total Upkeep is that they represent
62
+ an action, and have things like a start time, a status, etc.
63
+
64
+ Jobs are a wordpress action/hook and belong to the jobs queue. Tasks are independent and could potentially
65
+ float around the system. Tasks are really just a tracking system for things to do.
66
+
67
+ # Example tasks
68
+
69
+ ```
70
+ wp option get boldgrid_backup_tasks
71
+ array (
72
+ 0 => array (
73
+ 'id' => '1597861098-2e90c6',
74
+ 'type' => 'backup',
75
+ 'created_at' => 1597861098,
76
+ 'started_at' => 1597861098,
77
+ 'completed_at' => 1597861109,
78
+ 'status' => 'done',
79
+ 'data' => array (),
80
+ ),
81
+ 1 => array (
82
+ 'id' => '1597861521-1b2848',
83
+ 'type' => 'backup',
84
+ 'created_at' => 1597861521,
85
+ 'started_at' => 1597861522,
86
+ 'completed_at' => 1597861527,
87
+ 'status' => 'done',
88
+ 'data' => array(),
89
+ ),
90
+ 2 => array (
91
+ 'id' => '1598616953-986059',
92
+ 'type' => 'backup',
93
+ 'created_at' => 1598616953,
94
+ 'started_at' => 1598616954,
95
+ 'completed_at' => 1598616959,
96
+ 'status' => 'done',
97
+ 'data' => array(),
98
+ ),
99
+ 3 => array (
100
+ 'id' => '1598617517-e6f0a3',
101
+ 'type' => 'backup',
102
+ 'created_at' => 1598617517,
103
+ 'started_at' => 1598617518,
104
+ 'completed_at' => 1598617523,
105
+ 'status' => 'done',
106
+ 'data' => array(),
107
+ ),
108
+ 4 => array (
109
+ 'id' => '1598619019-8d3da6',
110
+ 'type' => 'backup',
111
+ 'created_at' => 1598619019,
112
+ 'started_at' => 1598619020,
113
+ 'completed_at' => NULL,
114
+ 'status' => 'in_progress',
115
+ 'data' => array(),
116
+ ),
117
+ 5 => array (
118
+ 'id' => '1598619948-985ee2',
119
+ 'type' => 'restore',
120
+ 'created_at' => '2020-08-28T13:05:48+00:00',
121
+ 'started_at' => 1598619949,
122
+ 'completed_at' => 1598619951,
123
+ 'status' => 'done',
124
+ 'data' => array (
125
+ 'backup_id' => '4',
126
+ ),
127
+ ),
128
+ )
129
+ ```
admin/class-boldgrid-backup-admin-task.php ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-admin-task.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/admin
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Admin_Task
17
+ *
18
+ * Tasks are similar to the jobs queue, but not quite the same. Please see class-boldgrid-backup-admin-task.md
19
+ *
20
+ * @since SINCEVERSION
21
+ *
22
+ * @todo Look into merging tasks with the jobs queue.
23
+ */
24
+ class Boldgrid_Backup_Admin_Task {
25
+ /**
26
+ * A date format used in returning times.
27
+ *
28
+ * For example, 'c' in date( 'c', time() ).
29
+ *
30
+ * @since SINCEVERSION
31
+ * @var string
32
+ */
33
+ public $date_format;
34
+
35
+ /**
36
+ * The time this task was completed.
37
+ *
38
+ * @since SINCEVERSION
39
+ * @access private
40
+ * @var int
41
+ */
42
+ private $completed_at;
43
+
44
+ /**
45
+ * The time this task was created.
46
+ *
47
+ * @since SINCEVERSION
48
+ * @access private
49
+ * @var int
50
+ */
51
+ private $created_at;
52
+
53
+ /**
54
+ * Misc data associated with this task.
55
+ *
56
+ * @since SINCEVERSION
57
+ * @access private
58
+ * @var array
59
+ */
60
+ private $data;
61
+
62
+ /**
63
+ * Our helper class.
64
+ *
65
+ * @since SINCEVERSION
66
+ * @access private
67
+ * @var Boldgrid_Backup_Admin_Task_Helper
68
+ */
69
+ private $helper;
70
+
71
+ /**
72
+ * The task id.
73
+ *
74
+ * @since SINCEVERSION
75
+ * @access private
76
+ * @var string
77
+ */
78
+ private $id;
79
+
80
+ /**
81
+ * The time this task was started.
82
+ *
83
+ * @since SINCEVERSION
84
+ * @access private
85
+ * @var int
86
+ */
87
+ private $started_at;
88
+
89
+ /**
90
+ * The task type.
91
+ *
92
+ * For example: backup
93
+ *
94
+ * @since SINCEVERSION
95
+ * @access private
96
+ * @var string
97
+ */
98
+ private $type;
99
+
100
+ /**
101
+ * Constructor.
102
+ *
103
+ * @since SINCEVERSION
104
+ */
105
+ public function __construct() {
106
+ $this->helper = new Boldgrid_Backup_Admin_Task_Helper();
107
+ }
108
+
109
+ /**
110
+ * Mark this task as being complete.
111
+ *
112
+ * @since SINCEVERSION
113
+ */
114
+ public function end() {
115
+ $this->completed_at = time();
116
+
117
+ $this->update();
118
+ }
119
+
120
+ /**
121
+ * Get the properties of this class.
122
+ *
123
+ * @since SINCEVERSION
124
+ *
125
+ * @return array
126
+ */
127
+ public function get() {
128
+ return [
129
+ 'id' => $this->id,
130
+ 'type' => $this->type,
131
+ 'created_at' => empty( $this->date_format ) ? $this->created_at : date( $this->date_format, $this->created_at ),
132
+ 'started_at' => empty( $this->started_at ) ?
133
+ null :
134
+ ( empty( $this->date_format ) ? $this->started_at : date( $this->date_format, $this->started_at ) ),
135
+ 'completed_at' => empty( $this->completed_at ) ?
136
+ null :
137
+ ( empty( $this->date_format ) ? $this->completed_at : date( $this->date_format, $this->completed_at ) ),
138
+ 'status' => $this->get_status(),
139
+ 'data' => $this->data,
140
+ ];
141
+ }
142
+
143
+ /**
144
+ * Get a specific value from the data.
145
+ *
146
+ * @since SINCEVERSION
147
+ *
148
+ * @param string $key The key to get.
149
+ * @param mixed $default The default value to return.
150
+ * @return mixed
151
+ */
152
+ public function get_data( $key, $default = false ) {
153
+ return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default;
154
+ }
155
+
156
+ /**
157
+ * Get our task id.
158
+ *
159
+ * @since SINCEVERSION
160
+ *
161
+ * @return string
162
+ */
163
+ public function get_id() {
164
+ return $this->id;
165
+ }
166
+
167
+ /**
168
+ * Get the status of this task.
169
+ *
170
+ * @since SINCEVERSION
171
+ *
172
+ * @return string
173
+ */
174
+ public function get_status() {
175
+ if ( empty( $this->started_at ) ) {
176
+ $status = 'pending';
177
+ } elseif ( empty( $this->completed_at ) ) {
178
+ $status = 'in_progress';
179
+ } else {
180
+ $status = 'done';
181
+ }
182
+
183
+ return $status;
184
+ }
185
+
186
+ /**
187
+ * Initialize a new task.
188
+ *
189
+ * @since SINCEVERSION
190
+ *
191
+ * @param array $args An array of args.
192
+ * @return bool True if this task was initialized.
193
+ */
194
+ public function init( $args ) {
195
+ $this->id = ! empty( $args['id'] ) ? $args['id'] : $this->set_id();
196
+ $this->created_at = ! empty( $args['created_at'] ) ? $args['created_at'] : time();
197
+ $this->started_at = ! empty( $args['started_at'] ) ? $args['started_at'] : null;
198
+ $this->completed_at = ! empty( $args['completed_at'] ) ? $args['completed_at'] : null;
199
+ $this->data = ! empty( $args['data'] ) ? $args['data'] : [];
200
+
201
+ // You must supply a type.
202
+ $type = ! empty( $args['type'] ) ? $args['type'] : null;
203
+ if ( empty( $type ) ) {
204
+ return false;
205
+ } elseif ( ! $this->set_type( $type ) ) {
206
+ return false;
207
+ }
208
+
209
+ return true;
210
+ }
211
+
212
+ /**
213
+ * Init this task by a task id.
214
+ *
215
+ * @since SINCEVERSION
216
+ *
217
+ * @param string $id A task id.
218
+ * @return bool True if this task was initialized.
219
+ */
220
+ public function init_by_id( $id ) {
221
+ $task = $this->helper->get_by_id( $id );
222
+
223
+ return empty( $task ) ? false : $this->init( $task );
224
+ }
225
+
226
+
227
+ /**
228
+ * Create a new task id.
229
+ *
230
+ * @since SINCEVERSION
231
+ *
232
+ * @return string
233
+ */
234
+ private function set_id() {
235
+ // A task id is the current time + 6 random chars.
236
+ $this->id = time() . '-' . substr( md5( time() ), -6 );
237
+
238
+ return $this->id;
239
+ }
240
+
241
+ /**
242
+ * Set our task type.
243
+ *
244
+ * @since SINCEVERSION
245
+ *
246
+ * @param string $type Our task type.
247
+ * @return bool True if the type was set.
248
+ */
249
+ private function set_type( $type ) {
250
+ $valid_types = [
251
+ 'backup',
252
+ 'restore',
253
+ ];
254
+
255
+ if ( ! in_array( $type, $valid_types, true ) ) {
256
+ return false;
257
+ }
258
+
259
+ $this->type = $type;
260
+
261
+ return true;
262
+ }
263
+
264
+ /**
265
+ * Start this task.
266
+ *
267
+ * @since SINCEVERSION
268
+ */
269
+ public function start() {
270
+ $this->started_at = time();
271
+
272
+ $this->update();
273
+ }
274
+
275
+ /**
276
+ * Update data for a task.
277
+ *
278
+ * @since SINCEVERSION
279
+ *
280
+ * @param string key The key to update.
281
+ * @param mixed $value The value to assign to the key.
282
+ */
283
+ public function update_data( $key, $value ) {
284
+ $this->data[ $key ] = $value;
285
+
286
+ $this->update();
287
+ }
288
+
289
+ /**
290
+ * Update / save this task.
291
+ *
292
+ * @since SINCEVERSION
293
+ */
294
+ public function update() {
295
+ return $this->helper->update( $this->get() );
296
+ }
297
+ }
admin/class-boldgrid-backup-admin-test.php CHANGED
@@ -526,6 +526,12 @@ class Boldgrid_Backup_Admin_Test {
526
  return false;
527
  }
528
 
 
 
 
 
 
 
529
  self::$is_getpgid_supported = false !== posix_getpgid( $pid );
530
 
531
  return self::$is_getpgid_supported;
526
  return false;
527
  }
528
 
529
+ // posix_getpgid() may not be available in all environments. Win 10 user running xampp for example.
530
+ if ( ! function_exists( 'posix_getpgid' ) ) {
531
+ self::$is_getpgid_supported = false;
532
+ return false;
533
+ }
534
+
535
  self::$is_getpgid_supported = false !== posix_getpgid( $pid );
536
 
537
  return self::$is_getpgid_supported;
admin/class-boldgrid-backup-admin-upload.php CHANGED
@@ -424,126 +424,16 @@ class Boldgrid_Backup_Admin_Upload {
424
  );
425
  }
426
 
427
- $url = ! empty( $_POST['url'] ) ? esc_url_raw( $_POST['url'] ) : null;
428
- $url_regex = '/' . $this->core->configs['url_regex'] . '/i';
429
 
430
- if ( ! preg_match( $url_regex, $url ) ) {
431
- wp_send_json_error(
432
- array(
433
- 'error' => __( 'Invalid URL address.', 'boldgrid-backup' ),
434
- )
435
- );
436
- }
437
-
438
- $backup_directory = $this->core->backup_dir->get();
439
-
440
- if ( ! $this->core->backup_dir->is_valid( $backup_directory ) &&
441
- ! empty( $this->core->backup_dir->errors ) ) {
442
- wp_send_json_error(
443
- array(
444
- 'error' => implode( '<br />', $this->core->backup_dir->errors ),
445
- )
446
- );
447
- }
448
-
449
- $filepath = $this->get_save_path( basename( $url ) );
450
-
451
- $allowed_content_types = array(
452
- 'application/octet-stream',
453
- 'binary/octet-stream',
454
- 'application/zip',
455
- );
456
-
457
- $response = wp_remote_get(
458
- $url, array(
459
- 'filename' => $filepath,
460
- 'headers' => 'Accept: ' . implode( ', ', $allowed_content_types ),
461
- 'sslverify' => false,
462
- 'stream' => true,
463
- 'timeout' => MINUTE_IN_SECONDS * 20,
464
- )
465
- );
466
-
467
- if ( is_array( $response ) && ! is_wp_error( $response ) &&
468
- in_array( $response['headers']['content-type'], $allowed_content_types, true ) ) {
469
- $logger->add( 'Archive downloaded successfully.' );
470
- $logger->add( 'Headers: ' . ( empty( $response['headers'] ) ? 'Empty' : print_r( $response['headers'], 1 ) ) ); // phpcs:ignore
471
-
472
- // Determine the archive log file path.
473
- $log_filepath = $filepath;
474
-
475
- if ( ! empty( $response['headers']['content-disposition'] ) ) {
476
- $log_filepath = trim(
477
- str_replace(
478
- 'attachment; filename=', '', $response['headers']['content-disposition']
479
- ), '"'
480
- );
481
-
482
- $log_filepath = $this->core->backup_dir->get_path_to( $log_filepath );
483
- }
484
-
485
- $log_filepath = $this->core->archive_log->path_from_zip( $log_filepath );
486
- $filename = basename( $filepath );
487
-
488
- // Restore the log file from the archive.
489
- $restored = $this->core->archive_log->restore_by_zip( $filepath, basename( $log_filepath ) );
490
- $logger->add( 'Log restored from zip: ' . ( $restored ? 'Success' : 'Fail' ) );
491
-
492
- // Update the archive file modification time, based on the log file contents.
493
- $this->core->remote->post_download( $filepath );
494
 
495
- // Get the archive details.
496
- $archive = $this->core->archive->get_by_name( $filename );
497
-
498
- wp_send_json_success(
499
- [
500
- 'filepath' => $filepath,
501
- 'detailsUrl' => admin_url(
502
- 'admin.php?page=boldgrid-backup-archive-details&filename=' .
503
- basename( $filepath )
504
- ),
505
- 'archiveFilename' => $filename,
506
- 'archiveKey' => $archive['key'],
507
- ]
508
- );
509
- } else {
510
- // Get the data from the $response that we want to print to the log.
511
- // @todo simply the below.
512
- if ( is_wp_error( $response ) ) {
513
- $log_data = $response;
514
- } elseif ( is_array( $response ) ) {
515
- $log_data = array();
516
-
517
- if ( ! empty( $response['body'] ) ) {
518
- $log_data['body'] = $response['body'];
519
- }
520
- if ( ! empty( $response['response'] ) ) {
521
- $log_data['response'] = $response['response'];
522
- }
523
- }
524
-
525
- $logger->add( 'Failed to download archive. Additional info: ' . print_r( $log_data, 1 ) ); // phpcs:ignore
526
- $this->core->wp_filesystem->delete( $filepath );
527
- }
528
-
529
- // Determine the error message the user will see and return it.
530
- $error_message = __( 'Could not retrieve the remote file.', 'boldgrid-backup' );
531
- if ( is_wp_error( $response ) ) {
532
- // Example: cURL error 28: Connection timed out after 100001 milliseconds.
533
- $error_message .= ' ' . $response->get_error_message();
534
- } elseif ( ! empty( $response['response']['code'] && ! empty( $response['response']['message'] ) ) ) {
535
- // Example: 403 Forbidden
536
- $error_message .= ' ' . $response['response']['code'] . ' ' . $response['response']['message'];
537
  } else {
538
- // Unkown error.
539
- $error_message .= ' ' . __( 'Unknown error. It may not be a ZIP file, or the link is no longer valid.', 'boldgrid-backup' );
540
  }
541
-
542
- wp_send_json_error(
543
- [
544
- 'error' => $error_message,
545
- ]
546
- );
547
  }
548
 
549
  /**
424
  );
425
  }
426
 
427
+ $url = ! empty( $_POST['url'] ) ? esc_url_raw( $_POST['url'] ) : null;
 
428
 
429
+ $archive_fetcher = new Boldgrid_Backup_Archive_Fetcher( $url );
430
+ $archive_fetcher->download();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
+ if ( $archive_fetcher->has_error() ) {
433
+ wp_send_json_error( [ 'error' => $archive_fetcher->get_error() ] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  } else {
435
+ wp_send_json_success( $archive_fetcher->get_info() );
 
436
  }
 
 
 
 
 
 
437
  }
438
 
439
  /**
admin/class-boldgrid-backup-admin-utility.php CHANGED
@@ -94,6 +94,40 @@ class Boldgrid_Backup_Admin_Utility {
94
  return $site_id;
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  /**
98
  * Custom error handler.
99
  *
@@ -323,6 +357,42 @@ class Boldgrid_Backup_Admin_Utility {
323
  return true;
324
  }
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  /**
327
  * Increase the PHP max execution time.
328
  *
@@ -723,6 +793,27 @@ class Boldgrid_Backup_Admin_Utility {
723
  return true;
724
  }
725
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
726
  /**
727
  * Replace the siteurl in the WordPress database.
728
  *
@@ -733,103 +824,96 @@ class Boldgrid_Backup_Admin_Utility {
733
  *
734
  * @static
735
  *
736
- * @param string $old_siteurl The old/restored siteurl to find and be replaced.
737
- * @param string $new_siteurl The siteurl to replace the old siteurl.
 
 
 
 
 
738
  * @return bool
739
  */
740
- public static function update_siteurl( $old_siteurl, $new_siteurl ) {
741
- // Define filter options.
742
- $filter_options = FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED;
 
743
 
744
- // Validate the old siteurl.
745
- if ( false === filter_var( $old_siteurl, FILTER_VALIDATE_URL, $filter_options ) ) {
 
 
 
746
  return false;
747
  }
748
-
749
- // Validate the new siteurl.
750
- if ( false === filter_var( $new_siteurl, FILTER_VALIDATE_URL, $filter_options ) ) {
751
  return false;
752
  }
753
 
754
- // Ensure there are no trailing slashes in siteurl.
 
 
 
 
 
755
  $old_siteurl = untrailingslashit( $old_siteurl );
756
  $new_siteurl = untrailingslashit( $new_siteurl );
757
 
758
- // There may be a filter, so remove it.
 
 
 
 
 
 
 
 
 
 
759
  remove_all_filters( 'pre_update_option_siteurl' );
760
 
761
- // Update the WP otion "siteurl".
762
  update_option( 'siteurl', $new_siteurl );
763
-
764
- // Connect to the WordPress database via $wpdb.
765
- global $wpdb;
766
-
767
- // Get the database prefix (blog id 1 or 0 gets the base prefix).
768
- $db_prefix = $wpdb->get_blog_prefix( 1 );
769
-
770
- // phpcs:disable WordPress.DB.PreparedSQLPlaceholders
771
-
772
- // Replace the URL in wp_posts.
773
- $wpdb->query(
774
- $wpdb->prepare(
775
- 'UPDATE `%1$sposts` SET `post_content` = REPLACE( `post_content`, \'%2$s\', \'%3$s\' ) WHERE `post_content` LIKE \'%%%2$s%%\';',
776
- array(
777
- $db_prefix,
778
- $old_siteurl,
779
- $new_siteurl,
780
- )
781
- )
782
  );
783
 
 
 
 
 
784
  // Check if the upload_url_path needs to be updated.
785
  $upload_url_path = get_option( 'upload_url_path' );
786
-
787
  if ( ! empty( $upload_url_path ) ) {
788
  $upload_url_path = str_replace( $old_siteurl, $new_siteurl, $upload_url_path );
789
-
790
  update_option( 'upload_url_path', $upload_url_path );
791
  }
792
 
793
- // Find old siteurl references in WP options.
794
- // Match old_siteurl with and without escaped URL, for example, JSON data escapes slashes.
795
- $matched_options = $wpdb->get_results(
796
- $wpdb->prepare(
797
- 'SELECT `option_name` FROM `%1$soptions` WHERE `option_value` LIKE \'%%%2$s%%\' OR `option_value` LIKE \'%%%3$s%%\';',
798
- array(
799
- $db_prefix,
800
- $old_siteurl,
801
- addslashes( $old_siteurl ),
802
- )
803
- ),
804
- ARRAY_N
805
- );
806
-
807
- // phpcs:enable WordPress.DB.PreparedSQLPlaceholders
808
-
809
- // If there are no matches options, then return.
810
- if ( ! $matched_options ) {
811
- return true;
812
- }
813
-
814
- // Replace the siteurl in matched options.
815
- foreach ( $matched_options as $option_name ) {
816
- $option_value = get_option( $option_name[0] );
817
-
818
- // Replace siteurl.
819
- $option_value = self::str_replace_recursive(
820
- $old_siteurl,
821
- $new_siteurl,
822
- $option_value
823
- );
824
-
825
- // Replace siteurl escaped with slashes.
826
- $option_value = self::str_replace_recursive(
827
- addslashes( $old_siteurl ),
828
- addslashes( $new_siteurl ),
829
- $option_value
830
- );
831
-
832
- update_option( $option_name[0], $option_value );
833
  }
834
 
835
  return true;
94
  return $site_id;
95
  }
96
 
97
+ /**
98
+ * Database find and replace.
99
+ *
100
+ * Take note we also have self::option_find_replace that does a find and replace specific to option
101
+ * values because they are serialized. It uses self::str_replace_recursive.
102
+ *
103
+ * @since SINCEVERSION
104
+ *
105
+ * @param string $table
106
+ * @param string $column
107
+ * @param string $find
108
+ * @param string $replace
109
+ */
110
+ public static function db_find_replace( $table, $column, $find, $replace ) {
111
+ global $wpdb;
112
+
113
+ // phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnquotedComplexPlaceholder, WordPress.DB.PreparedSQLPlaceholders.LikeWildcardsInQuery
114
+ $wpdb->query(
115
+ $wpdb->prepare(
116
+ 'UPDATE `' . $wpdb->prefix . '%1$s`
117
+ SET `%2$s` = REPLACE( `%3$s`, "%4$s", "%5$s" )
118
+ WHERE `%6$s` LIKE "%%%7$s%%";',
119
+ $table,
120
+ $column,
121
+ $column,
122
+ $find,
123
+ $replace,
124
+ $column,
125
+ $wpdb->esc_like( $find )
126
+ )
127
+ );
128
+ // phpcs:enable
129
+ }
130
+
131
  /**
132
  * Custom error handler.
133
  *
357
  return true;
358
  }
359
 
360
+ /**
361
+ * Find and replace for option values.
362
+ *
363
+ * Similar to self::db_find_replace. This one is special however because it ends up using
364
+ * self::str_replace_recursive for the replacement mechanism.
365
+ *
366
+ * @since SINCEVERSION
367
+ *
368
+ * @param string $find
369
+ * @param string $replace
370
+ */
371
+ public static function option_find_replace( $find, $replace ) {
372
+ global $wpdb;
373
+
374
+ $matched_options = $wpdb->get_results(
375
+ $wpdb->prepare(
376
+ 'SELECT `option_name`
377
+ FROM `' . $wpdb->prefix . 'options`
378
+ WHERE `option_value` LIKE %s;',
379
+ '%' . $wpdb->esc_like( $find ) . '%'
380
+ ),
381
+ ARRAY_N
382
+ );
383
+
384
+ if ( empty( $matched_options ) ) {
385
+ return;
386
+ }
387
+
388
+ foreach ( $matched_options as $option_name ) {
389
+ $option_value = get_option( $option_name[0] );
390
+ $option_value = self::str_replace_recursive( $find, $replace, $option_value );
391
+
392
+ update_option( $option_name[0], $option_value );
393
+ }
394
+ }
395
+
396
  /**
397
  * Increase the PHP max execution time.
398
  *
793
  return true;
794
  }
795
 
796
+ /**
797
+ * A wrapper for WordPress' flush_rewrite_rules.
798
+ *
799
+ * Wrapper function is necessary because rewriting the .htaccess only works if the
800
+ * save_mod_rewrite_rules() function exists, which only does in admin. This method ensures it's
801
+ * there.
802
+ *
803
+ * @link https://core.trac.wordpress.org/ticket/51805
804
+ *
805
+ * @param bool $hard Whether to update .htaccess (hard flush) or just update rewrite_rules option
806
+ * (soft flush).
807
+ */
808
+ public static function flush_rewrite_rules( $hard = true ) {
809
+ // A requirement for rewriting the .htaccess file.
810
+ if ( $hard && ! function_exists( 'save_mod_rewrite_rules' ) ) {
811
+ require_once ABSPATH . 'wp-admin/includes/misc.php';
812
+ }
813
+
814
+ flush_rewrite_rules( $hard );
815
+ }
816
+
817
  /**
818
  * Replace the siteurl in the WordPress database.
819
  *
824
  *
825
  * @static
826
  *
827
+ * @param array $args {
828
+ * An array of arguments.
829
+ *
830
+ * @type string $old_siteurl The old/restored siteurl to find and be replaced.
831
+ * @type string $siteurl The siteurl to replace the old siteurl.
832
+ * @type bool $flush Whether or not to flush the rewrite rules.
833
+ * }
834
  * @return bool
835
  */
836
+ public static function update_siteurl( $args = array() ) {
837
+ wp_parse_args( $args, array(
838
+ 'flush' => false,
839
+ ) );
840
 
841
+ $old_siteurl = $args['old_siteurl'];
842
+ $new_siteurl = $args['siteurl'];
843
+
844
+ // Validate.
845
+ if ( false === filter_var( $old_siteurl, FILTER_VALIDATE_URL ) ) {
846
  return false;
847
  }
848
+ if ( false === filter_var( $new_siteurl, FILTER_VALIDATE_URL ) ) {
 
 
849
  return false;
850
  }
851
 
852
+ /*
853
+ * Ensure the site url does not end in a trailing slash.
854
+ *
855
+ * This is best practice. IE when you manually edit your home / siteurl in the dashboard on
856
+ * the Settings > General page, WordPress will automatically untrailingslahsit.
857
+ */
858
  $old_siteurl = untrailingslashit( $old_siteurl );
859
  $new_siteurl = untrailingslashit( $new_siteurl );
860
 
861
+ /*
862
+ * Find and replace option values.
863
+ *
864
+ * Do this before updating the "siteurl" and "home" via the update_option calls below. Otherwise,
865
+ * we'll runing into:
866
+ * # Old url: domain.com
867
+ * # Requested new url: domain.com/514/514
868
+ * # Resulting url: domain.com/514/514/514/514
869
+ */
870
+ self::option_find_replace( $old_siteurl, $new_siteurl );
871
+
872
  remove_all_filters( 'pre_update_option_siteurl' );
873
 
 
874
  update_option( 'siteurl', $new_siteurl );
875
+ update_option( 'home', $new_siteurl );
876
+
877
+ $replacers = array(
878
+ // Post content.
879
+ array(
880
+ 'table' => 'posts',
881
+ 'column' => 'post_content',
882
+ 'find' => $old_siteurl,
883
+ 'replace' => $new_siteurl,
884
+ ),
885
+ // Custom urls in menus.
886
+ array(
887
+ 'table' => 'postmeta',
888
+ 'column' => 'meta_value',
889
+ 'find' => $old_siteurl,
890
+ 'replace' => $new_siteurl,
891
+ ),
 
 
892
  );
893
 
894
+ foreach ( $replacers as $replacer ) {
895
+ self::db_find_replace( $replacer['table'], $replacer['column'], $replacer['find'], $replacer['replace'] );
896
+ }
897
+
898
  // Check if the upload_url_path needs to be updated.
899
  $upload_url_path = get_option( 'upload_url_path' );
 
900
  if ( ! empty( $upload_url_path ) ) {
901
  $upload_url_path = str_replace( $old_siteurl, $new_siteurl, $upload_url_path );
 
902
  update_option( 'upload_url_path', $upload_url_path );
903
  }
904
 
905
+ /*
906
+ * If requested (false by default), flush the rewrite rules.
907
+ *
908
+ * Why not make this a standard operation? We would except for the fact that the
909
+ * Boldgrid_Backup_Admin_Restore_Helper class adds the "flush_rewrite_rules" function to the
910
+ * "shutdown" hook. Does it need to be done at shutdown? Not entirely sure.
911
+ *
912
+ * This optional action is helpful when updating the site url outside of a restoration process,
913
+ * IE a stand alone REST call to update the site url.
914
+ */
915
+ if ( ! empty( $args['flush'] ) ) {
916
+ self::flush_rewrite_rules();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  }
918
 
919
  return true;
admin/class-boldgrid-backup-admin-wp-cron.php CHANGED
@@ -349,6 +349,7 @@ class Boldgrid_Backup_Admin_WP_Cron {
349
  * @since 1.5.1
350
  */
351
  public function backup() {
352
- $archive_info = $this->core->archive_files( true );
 
353
  }
354
  }
349
  * @since 1.5.1
350
  */
351
  public function backup() {
352
+ $archiver = new Boldgrid_Backup_Archiver();
353
+ $archiver->run();
354
  }
355
  }
admin/compressor/class-boldgrid-backup-admin-compressor-system-zip.php CHANGED
@@ -20,6 +20,19 @@
20
  * @since 1.13.0
21
  */
22
  class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_Compressor {
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  /**
24
  * An array of files that should be archived.
25
  *
@@ -125,6 +138,8 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
125
  * @type bool save 1
126
  * @type int total_size 0
127
  * }
 
 
128
  */
129
  public function archive_files( $filelist, &$info ) {
130
  if ( $info['dryrun'] ) {
@@ -143,8 +158,9 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
143
 
144
  Boldgrid_Backup_Admin_In_Progress_Data::set_arg( 'step', 3 );
145
 
146
- $this->zip();
147
 
 
148
  $this->zip_sql();
149
 
150
  Boldgrid_Backup_Admin_In_Progress_Data::delete_arg( 'step' );
@@ -152,7 +168,9 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
152
  // Actions to take when we're all done / cleanup.
153
  $this->core->wp_filesystem->delete( $this->filelist_path );
154
 
155
- return true;
 
 
156
  }
157
 
158
  /**
@@ -207,6 +225,9 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
207
  * Run the command to actually zip the files.
208
  *
209
  * @since 1.13.0
 
 
 
210
  */
211
  private function zip() {
212
  $this->core->logger->add( 'Starting to close the zip file.' );
@@ -214,12 +235,14 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
214
 
215
  $this->temp_folder->create();
216
 
217
- $this->close();
218
 
219
  $this->temp_folder->delete();
220
 
221
  $this->core->logger->add( 'Finished closing the zip file.' );
222
  $this->core->logger->add_memory();
 
 
223
  }
224
 
225
  /**
@@ -264,13 +287,21 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
264
 
265
  foreach ( $filelist_chunks as $filelist_chunk ) {
266
  $chunk_start_time = microtime( true );
267
- $add_file_string = implode( ' ', $filelist_chunk );
268
- $this->zip_proc( $filelist_chunk );
 
 
 
 
 
269
  $chunks_closed++;
270
  $percent_complete = round( $chunks_closed / $total_chunks, 2 );
271
  $chunk_end_time = microtime( true );
272
  $close_duration = $chunk_end_time - $chunk_start_time;
 
273
  Boldgrid_Backup_Admin_In_Progress_Data::set_arg( 'percent_closed', $percent_complete );
 
 
274
  $this->core->logger->add(
275
  'Chunk closed in ' .
276
  $close_duration .
@@ -279,6 +310,8 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
279
  );
280
  $this->core->logger->add_memory();
281
  }
 
 
282
  }
283
 
284
  /**
@@ -299,12 +332,19 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
299
  * @since 1.14.0
300
  *
301
  * @param array $filelist_chunk Array of Files to be added.
 
302
  */
303
  private function zip_proc( $filelist_chunk ) {
 
 
304
  $descriptorspec = array(
305
  0 => array( 'pipe', 'r' ), // stdin is a pipe that the child will read from.
306
  1 => array( 'pipe', 'w' ), // stdout is a pipe that the child will write to.
307
- 2 => array( 'file', '/tmp/error-output.txt', 'a' ), // stderr is a file to write to.
 
 
 
 
308
  );
309
 
310
  $cwd = ABSPATH;
@@ -319,11 +359,6 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
319
  );
320
 
321
  if ( is_resource( $process ) ) {
322
- /* $pipes now looks like this:
323
- * 0 => writeable handle connected to child stdin
324
- * 1 => readable handle connected to child stdout
325
- * Any error output will be appended to /tmp/error-output.txt
326
- */
327
  foreach ( $filelist_chunk as $file ) {
328
  fwrite( $pipes[0], $file . "\n" ); //phpcs:ignore WordPress.WP.AlternativeFunctions
329
  }
@@ -332,10 +367,23 @@ class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_
332
 
333
  fclose( $pipes[1] ); //phpcs:ignore WordPress.WP.AlternativeFunctions
334
 
 
 
 
 
 
 
 
 
 
335
  // It is important that you close any pipes before calling.
336
  // proc_close in order to avoid a deadlock.
337
  proc_close( $process );
 
 
338
  }
 
 
339
  }
340
 
341
  /**
20
  * @since 1.13.0
21
  */
22
  class Boldgrid_Backup_Admin_Compressor_System_Zip extends Boldgrid_Backup_Admin_Compressor {
23
+ /**
24
+ * An error message.
25
+ *
26
+ * If we encounter an error while zipping, the error may be placed here - key phrase "may be placed
27
+ * here". At the introduction of this class property, it is being used in only one place. The entire
28
+ * class was not checked and tested to ensure all methods add any errors they run into here.
29
+ *
30
+ * @since SINCEVERSION
31
+ * @access private
32
+ * @var string
33
+ */
34
+ private $error;
35
+
36
  /**
37
  * An array of files that should be archived.
38
  *
138
  * @type bool save 1
139
  * @type int total_size 0
140
  * }
141
+ * @return mixed True on success, an array on failure. This approach has been taken to follow the
142
+ * standards already set by the pcl-zip and php-zip classes.
143
  */
144
  public function archive_files( $filelist, &$info ) {
145
  if ( $info['dryrun'] ) {
158
 
159
  Boldgrid_Backup_Admin_In_Progress_Data::set_arg( 'step', 3 );
160
 
161
+ $zip_success = $this->zip();
162
 
163
+ // @todo Simliar to the zip call above which returns a success status, so should zip_sql.
164
  $this->zip_sql();
165
 
166
  Boldgrid_Backup_Admin_In_Progress_Data::delete_arg( 'step' );
168
  // Actions to take when we're all done / cleanup.
169
  $this->core->wp_filesystem->delete( $this->filelist_path );
170
 
171
+ return true === $zip_success ? true : array(
172
+ 'error' => $this->error,
173
+ );
174
  }
175
 
176
  /**
225
  * Run the command to actually zip the files.
226
  *
227
  * @since 1.13.0
228
+ *
229
+ * @return bool True on success. Do note that $this->close() calls $this->zip_proc(), which will
230
+ * store any error messages in $this->error.
231
  */
232
  private function zip() {
233
  $this->core->logger->add( 'Starting to close the zip file.' );
235
 
236
  $this->temp_folder->create();
237
 
238
+ $success = $this->close();
239
 
240
  $this->temp_folder->delete();
241
 
242
  $this->core->logger->add( 'Finished closing the zip file.' );
243
  $this->core->logger->add_memory();
244
+
245
+ return $success;
246
  }
247
 
248
  /**
287
 
288
  foreach ( $filelist_chunks as $filelist_chunk ) {
289
  $chunk_start_time = microtime( true );
290
+
291
+ $success = $this->zip_proc( $filelist_chunk );
292
+ if ( ! $success ) {
293
+ return false;
294
+ }
295
+
296
+ // Process some stats.
297
  $chunks_closed++;
298
  $percent_complete = round( $chunks_closed / $total_chunks, 2 );
299
  $chunk_end_time = microtime( true );
300
  $close_duration = $chunk_end_time - $chunk_start_time;
301
+
302
  Boldgrid_Backup_Admin_In_Progress_Data::set_arg( 'percent_closed', $percent_complete );
303
+
304
+ // Add messages to the log.
305
  $this->core->logger->add(
306
  'Chunk closed in ' .
307
  $close_duration .
310
  );
311
  $this->core->logger->add_memory();
312
  }
313
+
314
+ return true;
315
  }
316
 
317
  /**
332
  * @since 1.14.0
333
  *
334
  * @param array $filelist_chunk Array of Files to be added.
335
+ * @return bool True on success.
336
  */
337
  private function zip_proc( $filelist_chunk ) {
338
+ $has_error = false;
339
+
340
  $descriptorspec = array(
341
  0 => array( 'pipe', 'r' ), // stdin is a pipe that the child will read from.
342
  1 => array( 'pipe', 'w' ), // stdout is a pipe that the child will write to.
343
+ /**
344
+ * Initially we sent errors to /tmp/error-output.txt. This caused warnings when the file
345
+ * was not writable. For any error messages, see $pipes[2] further down this method.
346
+ */
347
+ 2 => array( 'pipe', 'w' ),
348
  );
349
 
350
  $cwd = ABSPATH;
359
  );
360
 
361
  if ( is_resource( $process ) ) {
 
 
 
 
 
362
  foreach ( $filelist_chunk as $file ) {
363
  fwrite( $pipes[0], $file . "\n" ); //phpcs:ignore WordPress.WP.AlternativeFunctions
364
  }
367
 
368
  fclose( $pipes[1] ); //phpcs:ignore WordPress.WP.AlternativeFunctions
369
 
370
+ // Check for any errors.
371
+ $stderr = stream_get_contents( $pipes[2] );
372
+ fclose( $pipes[2] ); //phpcs:ignore WordPress.WP.AlternativeFunctions
373
+ if ( ! empty( $stderr ) ) {
374
+ $this->error = $stderr;
375
+ $this->core->logger->add( 'Error zipping files with system zip: ' . $stderr );
376
+ $has_error = true;
377
+ }
378
+
379
  // It is important that you close any pipes before calling.
380
  // proc_close in order to avoid a deadlock.
381
  proc_close( $process );
382
+ } else {
383
+ $has_error = true;
384
  }
385
+
386
+ return ! $has_error;
387
  }
388
 
389
  /**
admin/js/boldgrid-backup-admin-in-progress.js CHANGED
@@ -191,12 +191,13 @@ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
191
  * @since 1.14.13
192
  */
193
  onClickCancel: function() {
 
194
  /*
195
- * Make the ajax call to cancel the backup.
196
- *
197
- * No success, error, or complete callback is passed to the ajax call. Status updates will
198
- * be handled naturally by the in progress system.
199
- */
200
  $.ajax( {
201
  url: ajaxurl,
202
  data: {
@@ -206,8 +207,9 @@ BOLDGRID.BACKUP = BOLDGRID.BACKUP || {};
206
  type: 'post'
207
  } );
208
 
209
- $( '#bgbkup_progress_actions' ).html( wp.i18n.__( 'Canceling backup', 'boldgrid-backup' ) +
210
- ' <span class="spinner inline"></span>' );
 
211
  },
212
 
213
  /**
191
  * @since 1.14.13
192
  */
193
  onClickCancel: function() {
194
+
195
  /*
196
+ * Make the ajax call to cancel the backup.
197
+ *
198
+ * No success, error, or complete callback is passed to the ajax call. Status updates will
199
+ * be handled naturally by the in progress system.
200
+ */
201
  $.ajax( {
202
  url: ajaxurl,
203
  data: {
207
  type: 'post'
208
  } );
209
 
210
+ $( '#bgbkup_progress_actions' ).html(
211
+ wp.i18n.__( 'Canceling backup', 'boldgrid-backup' ) + ' <span class="spinner inline"></span>'
212
+ );
213
  },
214
 
215
  /**
admin/partials/settings/backup-security.php CHANGED
@@ -69,7 +69,7 @@ if ( ! $is_premium ) {
69
  <tr><td colspan="2">
70
  <div class="bg-box-bottom premium wp-clearfix">
71
  <?php
72
- $get_premium_url = 'https://www.boldgrid.com/update-backup?source=bgbkup-settings-security';
73
  printf(
74
  // translators: 1: Get premium button/link, 2: Opening <a> tag, 3: Closing </a> tag.
75
  esc_html__(
69
  <tr><td colspan="2">
70
  <div class="bg-box-bottom premium wp-clearfix">
71
  <?php
72
+ $get_premium_url = $this->core->go_pro->get_premium_url( 'bgbkup-settings-security' );
73
  printf(
74
  // translators: 1: Get premium button/link, 2: Opening <a> tag, 3: Closing </a> tag.
75
  esc_html__(
boldgrid-backup.php CHANGED
@@ -16,7 +16,7 @@
16
  * Plugin Name: Total Upkeep
17
  * Plugin URI: https://www.boldgrid.com/boldgrid-backup/
18
  * Description: Automated backups, remote backup to Amazon S3 and Google Drive, stop website crashes before they happen and more. Total Upkeep is the backup solution you need.
19
- * Version: 1.14.14
20
  * Author: BoldGrid
21
  * Author URI: https://www.boldgrid.com/
22
  * License: GPL-2.0+
@@ -50,6 +50,8 @@ if ( ! defined( 'BOLDGRID_BACKUP_TITLE' ) ) {
50
  define( 'BOLDGRID_BACKUP_TITLE', 'Total Upkeep' );
51
  }
52
 
 
 
53
  /**
54
  * The code that runs during plugin activation.
55
  * This action is documented in includes/class-boldgrid-backup-activator.php
@@ -140,7 +142,7 @@ function load_boldgrid_backup() {
140
  *
141
  * Run the plugin only if on a wp-admin page or when DOING_CRON.
142
  */
143
- if ( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || defined( 'WP_CLI' ) && WP_CLI ) {
144
  // If we could not load boldgrid_backup (missing system requirements), abort.
145
  if ( load_boldgrid_backup() ) {
146
  require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup.php';
16
  * Plugin Name: Total Upkeep
17
  * Plugin URI: https://www.boldgrid.com/boldgrid-backup/
18
  * Description: Automated backups, remote backup to Amazon S3 and Google Drive, stop website crashes before they happen and more. Total Upkeep is the backup solution you need.
19
+ * Version: 1.15.0
20
  * Author: BoldGrid
21
  * Author URI: https://www.boldgrid.com/
22
  * License: GPL-2.0+
50
  define( 'BOLDGRID_BACKUP_TITLE', 'Total Upkeep' );
51
  }
52
 
53
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-utility.php';
54
+
55
  /**
56
  * The code that runs during plugin activation.
57
  * This action is documented in includes/class-boldgrid-backup-activator.php
142
  *
143
  * Run the plugin only if on a wp-admin page or when DOING_CRON.
144
  */
145
+ if ( is_admin() || ( defined( 'DOING_CRON' ) && DOING_CRON ) || defined( 'WP_CLI' ) && WP_CLI || Boldgrid_Backup_Rest_Utility::is_rest() ) {
146
  // If we could not load boldgrid_backup (missing system requirements), abort.
147
  if ( load_boldgrid_backup() ) {
148
  require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup.php';
cli/class-site-check.php CHANGED
@@ -158,37 +158,6 @@ class Site_Check {
158
  return $success;
159
  }
160
 
161
- /**
162
- * Check if a port is open.
163
- *
164
- * @since 1.10.0
165
- * @static
166
- *
167
- * @link https://www.php.net/manual/en/function.fsockopen.php
168
- *
169
- * @param int $port Port number (1-65535).
170
- * @param string $host Optional hostname; defaults to "localhost".
171
- * @param int $timeout Connect timeout, in seconds; defaults to 5.
172
- * @param int $errno If provided, holds the system level error number that occurred in the system-level connect() call.
173
- * @param string $errstr The error message as a string.
174
- * @return bool
175
- */
176
- public static function check_port( $port, $host = 'localhost', $timeout = 5, &$errno, &$errstr ) {
177
- // Check for valid port reange.
178
- if ( 0 > $port || 65535 < $port ) {
179
- return false;
180
- }
181
-
182
- $res = @fsockopen( $host, $port, $errno, $errstr, $timeout ); // phpcs:ignore Generic.PHP.NoSilencedErrors
183
-
184
- if ( is_resource( $res ) ) {
185
- fclose( $res );
186
- return true;
187
- }
188
-
189
- return false;
190
- }
191
-
192
  /**
193
  * Perform a site check.
194
  *
158
  return $success;
159
  }
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  /**
162
  * Perform a site check.
163
  *
cli/verify-dd66c7edb75333982c82a15ab5b733f0.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // phpcs:disable
includes/archive/class-factory.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Factory class.
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid\Backup
9
+ * @subpackage Boldgrid\Backup\Archive
10
+ * @copyright BoldGrid
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ namespace Boldgrid\Backup\Archive;
15
+
16
+ /**
17
+ * Class: Factory
18
+ *
19
+ * A factory for getting an archive of type Boldgrid_Backup_Admin_Archive.
20
+ *
21
+ * @since SINCEVERSION
22
+ */
23
+ class Factory {
24
+ /**
25
+ * Get an archive by filename.
26
+ *
27
+ * @since SINCEVERSION
28
+ *
29
+ * @param string $filename The filename of a backup.
30
+ * @return Boldgrid_Backup_Admin_Archive
31
+ */
32
+ public static function get_by_filename( $filename ) {
33
+ $archive = new \Boldgrid_Backup_Admin_Archive();
34
+
35
+ $archive->init_by_filename( $filename );
36
+
37
+ $archive = self::set_id( $archive );
38
+
39
+ return $archive;
40
+ }
41
+
42
+ /**
43
+ * Get a backup by id.
44
+ *
45
+ * @since SINCEVERSION
46
+ *
47
+ * @param string $id The backup id.
48
+ * @return Boldgrid_Backup_Admin_Archive
49
+ */
50
+ public static function get_by_id( $id ) {
51
+ $archive = new \Boldgrid_Backup_Admin_Archive();
52
+
53
+ // Get the filename of our backup based on id.
54
+ $option = new Option();
55
+ $option_row = $option->get_by_key( 'id', (int) $id );
56
+ $filename = ! empty( $option_row['filename'] ) ? $option_row['filename'] : null;
57
+
58
+ if ( ! empty( $filename ) ) {
59
+ $archive->init_by_filename( $filename );
60
+ }
61
+
62
+ return $archive;
63
+ }
64
+
65
+ /**
66
+ * Give a backup an id.
67
+ *
68
+ * @since SINCEVERSION
69
+ *
70
+ * @param Boldgrid_Backup_Admin_Archive $archive An archive.
71
+ * @return Boldgrid_Backup_Admin_Archive
72
+ */
73
+ private static function set_id( $archive ) {
74
+ $option = new Option();
75
+
76
+ $option_row = $option->get_by_key( 'filename', $archive->filename );
77
+
78
+ if ( empty( $option_row ) ) {
79
+ $option_row = [ 'filename' => $archive->filename ];
80
+ }
81
+
82
+ if ( isset( $option_row['id'] ) ) {
83
+ $archive->set_id( $option_row['id'] );
84
+ } else {
85
+ $archive->set_id( $option->get_next_id() );
86
+
87
+ $option->update_by_filename( $archive->filename, 'id', $archive->get_id() );
88
+ }
89
+
90
+ return $archive;
91
+ }
92
+ }
includes/archive/class-option.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Option class.
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid\Backup
9
+ * @subpackage Boldgrid\Backup\Archive
10
+ * @copyright BoldGrid
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ namespace Boldgrid\Backup\Archive;
15
+
16
+ /**
17
+ * Class: Option
18
+ *
19
+ * This class is used to manage the boldgrid_backup_backups option.
20
+ *
21
+ * In it's first implementation, each entry in the array represents a single backup, and has an id and
22
+ * a filename. For examples, please see: https://pastebin.com/Wuey2zvP
23
+ *
24
+ * @since SINCEVERSION
25
+ */
26
+ class Option {
27
+ /**
28
+ * The option name storing backups.
29
+ *
30
+ * @since SINCEVERSION
31
+ * @access private
32
+ * @var string
33
+ */
34
+ private $option = 'boldgrid_backup_backups';
35
+
36
+ /**
37
+ * Get all our backups.
38
+ *
39
+ * @since SINCEVERSION
40
+ *
41
+ * @return array
42
+ */
43
+ public function get_all() {
44
+ return get_option( $this->option, [] );
45
+ }
46
+
47
+ /**
48
+ * Get one backup.
49
+ *
50
+ * @since SINCEVERSION
51
+ *
52
+ * @param string $filename The filename to look for.
53
+ * @return array
54
+ */
55
+ public function get_by_key( $key, $value ) {
56
+ $found_backup = [];
57
+
58
+ $backups = $this->get_all();
59
+
60
+ foreach ( $backups as $backup ) {
61
+ if ( isset( $backup[ $key ] ) && $backup[ $key ] === $value ) {
62
+ $found_backup = $backup;
63
+ break;
64
+ }
65
+ }
66
+
67
+ return $found_backup;
68
+ }
69
+
70
+ /**
71
+ * Get a new id for a new backup being added to the list.
72
+ *
73
+ * @since SINCEVERSION
74
+ *
75
+ * @return int
76
+ */
77
+ public function get_next_id() {
78
+ $next_id = 1;
79
+
80
+ $backups = $this->get_all();
81
+
82
+ foreach ( $backups as $backup ) {
83
+ $id = isset( $backup['id'] ) ? $backup['id'] : 1;
84
+
85
+ $next_id = $id >= $next_id ? ( $id + 1 ) : $next_id;
86
+ }
87
+
88
+ return $next_id;
89
+ }
90
+
91
+ /**
92
+ * Update a backup entry based on the filename.
93
+ *
94
+ * @since SINCEVERSION
95
+ *
96
+ * @param string $filename The filename to update attributes of.
97
+ * @param string $key The key to update.
98
+ * @param string $value The value for the key.
99
+ */
100
+ public function update_by_filename( $filename, $key, $value ) {
101
+ $found = false;
102
+
103
+ $backups = $this->get_all();
104
+
105
+ // Find our backup by filename, and update the key.
106
+ foreach ( $backups as $k => $backup ) {
107
+ if ( ! empty( $backup['filename'] ) && $backup['filename'] === $filename ) {
108
+ $found = true;
109
+
110
+ $backups[ $k ][ $key ] = $value;
111
+
112
+ break;
113
+ }
114
+ }
115
+
116
+ // If the backup was not found in the array, add it.
117
+ if ( ! $found ) {
118
+ $backups[] = [
119
+ 'filename' => $filename,
120
+ $key => $value,
121
+ ];
122
+ }
123
+
124
+ update_option( $this->option, $backups );
125
+ }
126
+ }
includes/class-boldgrid-backup-archive-fetcher.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-archive-fetcher.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Archive_Fetcher
17
+ *
18
+ * This class is used to download archives from a url.
19
+ *
20
+ * The contents of this class were originally in the Boldgrid_Backup_Admin_Upload class, and have been
21
+ * moved here for reusability.
22
+ *
23
+ * @since SINCEVERSION
24
+ */
25
+ class Boldgrid_Backup_Archive_Fetcher {
26
+ /**
27
+ * The url to download.
28
+ *
29
+ * @since SINCEVERSION
30
+ * @param string
31
+ */
32
+ public $url;
33
+
34
+ /**
35
+ * Allowed content types.
36
+ *
37
+ * @since SINCVERSION
38
+ * @access private
39
+ * @var array
40
+ */
41
+ private $allowed_content_types = [
42
+ 'application/octet-stream',
43
+ 'binary/octet-stream',
44
+ 'application/zip',
45
+ ];
46
+
47
+ /**
48
+ * The core class object.
49
+ *
50
+ * @since SINCEVERSION
51
+ * @access private
52
+ * @var Boldgrid_Backup_Admin_Core
53
+ */
54
+ private $core;
55
+
56
+ /**
57
+ * An error message.
58
+ *
59
+ * @since SINCEVERSION
60
+ * @access private
61
+ * @var string
62
+ */
63
+ private $error;
64
+
65
+ /**
66
+ * Filepath to our archive.
67
+ *
68
+ * @since SINCEVERSION
69
+ * @access private
70
+ * @var string
71
+ */
72
+ private $filepath;
73
+
74
+ /**
75
+ * Fetcher info.
76
+ *
77
+ * After a successful fetch, this array will have info about our new backup.
78
+ *
79
+ * @since SINCEVERSION
80
+ * @access private
81
+ * @var array {
82
+ * @type string $filepath The filepath to the archive.
83
+ * @type string $detailsUrl The admin url to the details page for this archive.
84
+ * @type string $archiveFilename The filename of the archive.
85
+ * @type int $archiveKey The archive key.
86
+ * }
87
+ */
88
+ private $info = [];
89
+
90
+ /**
91
+ * The path to the archive's log.
92
+ *
93
+ * @since SINCEVERSION
94
+ * @access private
95
+ * @var string
96
+ */
97
+ private $log_filepath;
98
+
99
+ /**
100
+ * The response received when trying to download the file.
101
+ *
102
+ * @since SINCEVERSION
103
+ * @access private
104
+ * @var mixed
105
+ */
106
+ private $response;
107
+
108
+ /**
109
+ * Constructor.
110
+ *
111
+ * @since SINCEVERSION
112
+ *
113
+ * @param string $url The url we will be downloading.
114
+ */
115
+ public function __construct( $url ) {
116
+ $this->core = apply_filters( 'boldgrid_backup_get_core', null );
117
+
118
+ $this->url = $url;
119
+ }
120
+
121
+ /**
122
+ * Download a backup file from a remote server.
123
+ *
124
+ * @since SINCEVERSION
125
+ *
126
+ * @return bool True on success.
127
+ */
128
+ public function download() {
129
+ if ( ! $this->is_valid_url() ) {
130
+ $this->error = __( 'Invalid URL address.', 'boldgrid-backup' );
131
+ return false;
132
+ }
133
+
134
+ if ( ! $this->is_valid_backupdir() ) {
135
+ $this->error = implode( '<br />', $this->core->backup_dir->errors );
136
+ return false;
137
+ }
138
+
139
+ $this->filepath = $this->core->upload->get_save_path( basename( $this->url ) );
140
+
141
+ $this->response = wp_remote_get(
142
+ $this->url,
143
+ [
144
+ 'filename' => $this->filepath,
145
+ 'headers' => 'Accept: ' . implode( ', ', $this->allowed_content_types ),
146
+ 'sslverify' => false,
147
+ 'stream' => true,
148
+ 'timeout' => MINUTE_IN_SECONDS * 20,
149
+ ]
150
+ );
151
+
152
+ if ( $this->is_call_successful() ) {
153
+ $this->post_successful_download();
154
+
155
+ return true;
156
+ } else {
157
+ $this->core->wp_filesystem->delete( $this->filepath );
158
+
159
+ $this->error = __(
160
+ 'Could not retrieve the remote file. It may not be a ZIP file, or the link is no longer valid.',
161
+ 'boldgrid-backup'
162
+ );
163
+
164
+ return false;
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Get our error message.
170
+ *
171
+ * @since SINCEVERSION
172
+ *
173
+ * @return string
174
+ */
175
+ public function get_error() {
176
+ return $this->error;
177
+ }
178
+
179
+ /**
180
+ * Get our array of info after a successful download.
181
+ *
182
+ * @since SINCEVERSION
183
+ *
184
+ * @return array
185
+ */
186
+ public function get_info() {
187
+ return $this->info;
188
+ }
189
+
190
+ /**
191
+ * Whether or not we encountered an error during the download process.
192
+ *
193
+ * @since SINCEVERSION
194
+ *
195
+ * @return bool
196
+ */
197
+ public function has_error() {
198
+ return ! empty( $this->error );
199
+ }
200
+
201
+ /**
202
+ * Whether or not the call to download the file was successful.
203
+ *
204
+ * This does not represent the success of the download() method, but instead the state of the
205
+ * wp_remote_get call.
206
+ *
207
+ * @since SINCEVERSION
208
+ *
209
+ * @return bool True on success.
210
+ */
211
+ private function is_call_successful() {
212
+ return is_array( $this->response ) &&
213
+ ! is_wp_error( $this->response ) &&
214
+ in_array( $this->response['headers']['content-type'], $this->allowed_content_types, true );
215
+ }
216
+
217
+ /**
218
+ * Validate our backup directory.
219
+ *
220
+ * @since SINCEVERSION
221
+ *
222
+ * @return bool True if valid.
223
+ */
224
+ private function is_valid_backupdir() {
225
+ $backup_directory = $this->core->backup_dir->get();
226
+
227
+ return $this->core->backup_dir->is_valid( $backup_directory ) && empty( $this->core->backup_dir->errors );
228
+ }
229
+
230
+ /**
231
+ * Validate our download url.
232
+ *
233
+ * @since SINCEVERSION
234
+ *
235
+ * @return bool True if valid.
236
+ */
237
+ private function is_valid_url() {
238
+ $url_regex = '/' . $this->core->configs['url_regex'] . '/i';
239
+
240
+ return preg_match( $url_regex, $this->url );
241
+ }
242
+
243
+ /**
244
+ * Steps to take if our raw wp_remote_get() call to download the backup was successful.
245
+ *
246
+ * @since SINCEVERSION
247
+ */
248
+ private function post_successful_download() {
249
+ $this->set_logfilepath();
250
+
251
+ $filename = basename( $this->filepath );
252
+
253
+ // Restore the log file from the archive.
254
+ $this->core->archive_log->restore_by_zip( $this->filepath, basename( $this->log_filepath ) );
255
+
256
+ // Update the archive file modification time, based on the log file contents.
257
+ $this->core->remote->post_download( $this->filepath );
258
+
259
+ // Get the archive details.
260
+ $archive = $this->core->archive->get_by_name( $filename );
261
+
262
+ $this->info = [
263
+ 'filepath' => $this->filepath,
264
+ 'detailsUrl' => admin_url( 'admin.php?page=boldgrid-backup-archive-details&filename=' . basename( $this->filepath ) ),
265
+ 'archiveFilename' => $filename,
266
+ 'archiveKey' => $archive['key'],
267
+ ];
268
+ }
269
+
270
+ /**
271
+ * Set the archive log filepath.
272
+ *
273
+ * @since SINCEVERSION
274
+ */
275
+ private function set_logfilepath() {
276
+ $this->log_filepath = $this->filepath;
277
+
278
+ if ( ! empty( $this->response['headers']['content-disposition'] ) ) {
279
+ $this->log_filepath = trim(
280
+ str_replace(
281
+ 'attachment; filename=', '', $this->response['headers']['content-disposition']
282
+ ), '"'
283
+ );
284
+
285
+ $this->log_filepath = $this->core->backup_dir->get_path_to( $this->log_filepath );
286
+ }
287
+
288
+ $this->log_filepath = $this->core->archive_log->path_from_zip( $this->log_filepath );
289
+ }
290
+ }
includes/class-boldgrid-backup-archiver.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-archiver.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Archiver
17
+ *
18
+ * Originally, all methods for archiving a file have lived in Boldgrid_Backup_Admin_Core. This class,
19
+ * over time, will absorb those methods.
20
+ *
21
+ * @since SINCEVERSION
22
+ */
23
+ class Boldgrid_Backup_Archiver {
24
+ /**
25
+ * Admin core.
26
+ *
27
+ * @since SINCEVERSION
28
+ * @access private
29
+ * @var Boldgrid_Backup_Admin_Core
30
+ */
31
+ private $core;
32
+
33
+ /**
34
+ * An array of info about our archive.
35
+ *
36
+ * @since SINCEVERSION
37
+ * @access private
38
+ * @var array
39
+ */
40
+ private $info;
41
+
42
+ /**
43
+ * An instance of Boldgrid_Backup_Admin_Task.
44
+ *
45
+ * @since SINCEVERSION
46
+ * @access private
47
+ * @var Boldgrid_Backup_Admin_Task
48
+ */
49
+ private $task;
50
+
51
+ /**
52
+ * Constructor.
53
+ *
54
+ * @since SINCEVERSION
55
+ */
56
+ public function __construct() {
57
+ $this->core = apply_filters( 'boldgrid_backup_get_core', null );
58
+ }
59
+
60
+ /**
61
+ * Steps to take when archiving is complete.
62
+ *
63
+ * @since SINCEVERSION
64
+ */
65
+ public function complete() {
66
+ $this->core->logger->add( 'Backup complete!' );
67
+ $this->core->logger->add_memory();
68
+
69
+ $this->task->end();
70
+ }
71
+
72
+ /**
73
+ * Get our archive info.
74
+ *
75
+ * @since SINCEVERSION
76
+ *
77
+ * @return array
78
+ */
79
+ public function get_info() {
80
+ return $this->info;
81
+ }
82
+
83
+ /**
84
+ * Steps to take before an archive is started.
85
+ *
86
+ * @since SINCEVERSION
87
+ */
88
+ public function init() {
89
+ // Init our logger.
90
+ $this->core->logger->init( 'archive-' . time() . '.log' );
91
+ $this->core->logger->add( 'Backup process initialized.' );
92
+
93
+ // Init our task.
94
+ $this->task = new Boldgrid_Backup_Admin_Task();
95
+ if ( ! empty( $_POST['task_id'] ) ) { // phpcs:ignore
96
+ $this->task->init_by_id( $_POST['task_id'] ); // phpcs:ignore
97
+ } else {
98
+ $this->task->init( [ 'type' => 'backup' ] );
99
+ }
100
+ $this->task->start();
101
+ }
102
+
103
+ /**
104
+ * Create an archive.
105
+ *
106
+ * Do everything.
107
+ *
108
+ * @since SINCEVERSION
109
+ */
110
+ public function run() {
111
+ $this->init();
112
+
113
+ $this->info = $this->core->archive_files( true );
114
+
115
+ $this->complete();
116
+ }
117
+ }
includes/class-boldgrid-backup-restorer.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-restorer.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @subpackage Boldgrid_Backup/includes
10
+ * @copyright BoldGrid
11
+ * @version $Id$
12
+ * @author BoldGrid <support@boldgrid.com>
13
+ */
14
+
15
+ /**
16
+ * Class: Boldgrid_Backup_Restorer
17
+ *
18
+ * Originally, all methods for restoring a file have lived in Boldgrid_Backup_Admin_Core. This class,
19
+ * over time, will absorb those methods.
20
+ *
21
+ * @since SINCEVERSION
22
+ */
23
+ class Boldgrid_Backup_Restorer {
24
+ /**
25
+ * Admin core.
26
+ *
27
+ * @since SINCEVERSION
28
+ * @access private
29
+ * @var Boldgrid_Backup_Admin_Core
30
+ */
31
+ private $core;
32
+
33
+ /**
34
+ * An array of info about our archive.
35
+ *
36
+ * @since SINCEVERSION
37
+ * @access private
38
+ * @var array
39
+ */
40
+ private $info;
41
+
42
+ /**
43
+ * An instance of Boldgrid_Backup_Admin_Task.
44
+ *
45
+ * @since SINCEVERSION
46
+ * @access private
47
+ * @var Boldgrid_Backup_Admin_Task
48
+ */
49
+ private $task;
50
+
51
+ /**
52
+ * Constructor.
53
+ *
54
+ * @since SINCEVERSION
55
+ */
56
+ public function __construct() {
57
+ $this->core = apply_filters( 'boldgrid_backup_get_core', null );
58
+ }
59
+
60
+ /**
61
+ * Steps to take when archiving is complete.
62
+ *
63
+ * @since SINCEVERSION
64
+ */
65
+ public function complete() {
66
+ // Update the log.
67
+ $this->core->logger->add( 'Restoration complete!' );
68
+ $this->core->logger->add_memory();
69
+
70
+ if ( $this->has_error() ) {
71
+ $this->core->logger->add( 'Error during restoration: ' . $this->get_error() );
72
+ }
73
+
74
+ // End the task.
75
+ $this->task->end();
76
+ }
77
+
78
+ /**
79
+ * Get our error message.
80
+ *
81
+ * @since SINCEVERSION
82
+ *
83
+ * @return string
84
+ */
85
+ private function get_error() {
86
+ return ! empty( $this->info['error'] ) ? $this->info['error'] : __( 'Unknown error', 'boldgrid-backup' );
87
+ }
88
+
89
+ /**
90
+ * Get our archive info.
91
+ *
92
+ * @since SINCEVERSION
93
+ *
94
+ * @return array
95
+ */
96
+ public function get_info() {
97
+ return $this->info;
98
+ }
99
+
100
+ /**
101
+ * Whether or not we have an error.
102
+ *
103
+ * @since SINCEVERSION
104
+ *
105
+ * @return bool
106
+ */
107
+ private function has_error() {
108
+ return ! empty( $this->info['error'] );
109
+ }
110
+
111
+ /**
112
+ * Steps to take before an archive is started.
113
+ *
114
+ * @since SINCEVERSION
115
+ */
116
+ public function init() {
117
+ // Init our logger.
118
+ $this->core->logger->init( 'restore-' . time() . '.log' );
119
+ $this->core->logger->add( 'Restore process initialized.' );
120
+ $this->core->logger->add_memory();
121
+
122
+ /*
123
+ * Setup our task.
124
+ *
125
+ * We're either starting a new task, or continuing on an existing task.
126
+ */
127
+ $this->task = new Boldgrid_Backup_Admin_Task();
128
+ if ( ! empty( $_POST['task_id'] ) ) { // phpcs:ignore
129
+ $this->task->init_by_id( $_POST['task_id'] ); // phpcs:ignore
130
+ } else {
131
+ $this->task->init( [ 'type' => 'restore' ] );
132
+ }
133
+ $this->task->start();
134
+ }
135
+
136
+ /**
137
+ * Restore a backup by id.
138
+ *
139
+ * @since SINCEVERSION
140
+ *
141
+ * @param string $id The backup id.
142
+ */
143
+ public function restore_by_id( $id ) {
144
+ $archive = Boldgrid\Backup\Archive\Factory::get_by_id( $id );
145
+
146
+ $this->restore_by_key( $archive->get_key(), $archive->filename );
147
+ }
148
+
149
+ /**
150
+ * Restore an archive by key (and filename).
151
+ *
152
+ * @since SINCEVERSION
153
+ *
154
+ * @param int $key The archive key to restore.
155
+ * @param string $filename The archive filename to restore.
156
+ */
157
+ public function restore_by_key( $key, $filename ) {
158
+ $restore_args = [
159
+ 'archive_key' => $key,
160
+ 'archive_filename' => $filename,
161
+ ];
162
+
163
+ $this->info = $this->core->restore_archive_file( false, $restore_args );
164
+ }
165
+
166
+ /**
167
+ * Restore a site by url.
168
+ *
169
+ * @since SINCEVERSION
170
+ *
171
+ * @param string $url A url to a zip file.
172
+ */
173
+ public function restore_by_url( $url ) {
174
+ // Download the backup file.
175
+ $archive_fetcher = new Boldgrid_Backup_Archive_Fetcher( $url );
176
+ $archive_fetcher->download();
177
+
178
+ // If we have errors, abort.
179
+ if ( $archive_fetcher->has_error() ) {
180
+ $this->set_error( $archive_fetcher->get_error() );
181
+ return;
182
+ }
183
+
184
+ // Restore the new archive just downloaded.
185
+ $info = $archive_fetcher->get_info();
186
+ $this->restore_by_key( $info['archiveKey'], $info['archiveFilename'] );
187
+ }
188
+
189
+ /**
190
+ * Run a basic restore.
191
+ *
192
+ * This mimics a standard call to core->restore_archive_file, except it adds additional things
193
+ * like tasks and logs.
194
+ *
195
+ * @since SINCEVERSION
196
+ */
197
+ public function run() {
198
+ $this->init();
199
+
200
+ $this->info = $this->core->restore_archive_file();
201
+
202
+ $this->complete();
203
+ }
204
+
205
+ /**
206
+ * Run a restoration by backup id.
207
+ *
208
+ * Method run_by_id: This method, does more than just restoration. Handles logging, etc.
209
+ * Method restore_by_id: Handles just the restoration, nothing more.
210
+ *
211
+ * @since SINCEVERSION
212
+ *
213
+ * @param int $id The backup id.
214
+ */
215
+ public function run_by_id( $id ) {
216
+ $this->init();
217
+
218
+ $this->restore_by_id( $id );
219
+
220
+ $this->complete();
221
+ }
222
+
223
+ /**
224
+ * Run a restoration by a url.
225
+ *
226
+ * Method run_by_url: This method, does more than just restoration. Handles logging, etc.
227
+ * Method restore_by_url: Handles just the restoration, nothing more.
228
+ *
229
+ * @since SINCEVERSION
230
+ *
231
+ * @param string $url A url to a zip file.
232
+ */
233
+ public function run_by_url( $url ) {
234
+ $this->init();
235
+
236
+ $this->restore_by_url( $url );
237
+
238
+ $this->complete();
239
+ }
240
+
241
+ /**
242
+ * Set an error message.
243
+ *
244
+ * @since SINCEVERSION
245
+ *
246
+ * @param string $message The error message to set.
247
+ */
248
+ private function set_error( $message ) {
249
+ $this->info['error'] = $message;
250
+ }
251
+ }
includes/class-boldgrid-backup.php CHANGED
@@ -294,12 +294,36 @@ class Boldgrid_Backup {
294
 
295
  require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-activator.php';
296
 
 
 
 
 
 
 
 
 
297
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-usage.php';
298
 
299
  // Logs system.
300
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-log.php';
301
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-log-page.php';
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-plugin-notices.php';
304
 
305
  // Orphaned files.
@@ -551,6 +575,24 @@ class Boldgrid_Backup {
551
  $this->loader->add_action( 'admin_notices', $plugin_admin_core->notice, 'plugin_renamed_notice' );
552
  $this->loader->add_action( 'wp_ajax_dismissBoldgridNotice', 'Boldgrid\Library\Library\Notice', 'dismiss' );
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  $usage = new Boldgrid_Backup_Admin_Usage();
555
 
556
  $this->loader->add_action( 'admin_init', $usage, 'admin_init' );
@@ -583,6 +625,15 @@ class Boldgrid_Backup {
583
  $this->loader->add_filter( 'Boldgrid\Library\Plugin\Notices\admin_enqueue_scripts', $plugin_notices, 'filter' );
584
 
585
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin_core, 'add_thickbox' );
 
 
 
 
 
 
 
 
 
586
  }
587
 
588
  /**
294
 
295
  require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-activator.php';
296
 
297
+ // REST API support.
298
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-controller.php';
299
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-job.php';
300
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-setting.php';
301
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-archive.php';
302
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-test.php';
303
+ require_once BOLDGRID_BACKUP_PATH . '/rest/class-boldgrid-backup-rest-siteurl.php';
304
+
305
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-usage.php';
306
 
307
  // Logs system.
308
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-log.php';
309
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-log-page.php';
310
 
311
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-nopriv.php';
312
+
313
+ // Task system.
314
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-task.php';
315
+ require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-task-helper.php';
316
+
317
+ // Archiver and Restorer classes.
318
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-archiver.php';
319
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-restorer.php';
320
+
321
+ require_once BOLDGRID_BACKUP_PATH . '/includes/class-boldgrid-backup-archive-fetcher.php';
322
+
323
+ // Archive namespace.
324
+ require_once BOLDGRID_BACKUP_PATH . '/includes/archive/class-factory.php';
325
+ require_once BOLDGRID_BACKUP_PATH . '/includes/archive/class-option.php';
326
+
327
  require_once BOLDGRID_BACKUP_PATH . '/admin/class-boldgrid-backup-admin-plugin-notices.php';
328
 
329
  // Orphaned files.
575
  $this->loader->add_action( 'admin_notices', $plugin_admin_core->notice, 'plugin_renamed_notice' );
576
  $this->loader->add_action( 'wp_ajax_dismissBoldgridNotice', 'Boldgrid\Library\Library\Notice', 'dismiss' );
577
 
578
+ // Register REST endpoints.
579
+ add_action( 'rest_api_init', function() use ( $plugin_admin_core ) {
580
+ $rest_job = new Boldgrid_Backup_Rest_Job( $plugin_admin_core );
581
+ $rest_job->register_routes();
582
+
583
+ $rest_archive = new Boldgrid_Backup_Rest_Archive( $plugin_admin_core );
584
+ $rest_archive->register_routes();
585
+
586
+ $rest_setting = new Boldgrid_Backup_Rest_Setting( $plugin_admin_core );
587
+ $rest_setting->register_routes();
588
+
589
+ $rest_test = new Boldgrid_Backup_Rest_Test( $plugin_admin_core );
590
+ $rest_test->register_routes();
591
+
592
+ $rest_siteurl = new Boldgrid_Backup_Rest_Siteurl( $plugin_admin_core );
593
+ $rest_siteurl->register_routes();
594
+ } );
595
+
596
  $usage = new Boldgrid_Backup_Admin_Usage();
597
 
598
  $this->loader->add_action( 'admin_init', $usage, 'admin_init' );
625
  $this->loader->add_filter( 'Boldgrid\Library\Plugin\Notices\admin_enqueue_scripts', $plugin_notices, 'filter' );
626
 
627
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin_core, 'add_thickbox' );
628
+
629
+ /*
630
+ * Things to do in a dev environment.
631
+ *
632
+ * @link https://make.wordpress.org/core/2020/07/24/new-wp_get_environment_type-function-in-wordpress-5-5/
633
+ */
634
+ if ( defined( 'WP_ENVIRONMENT_TYPE' ) && 'development' === WP_ENVIRONMENT_TYPE ) {
635
+ $this->loader->add_action( 'admin_footer', 'Boldgrid_Backup_Rest_Utility', 'insert_nonce' );
636
+ }
637
  }
638
 
639
  /**
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: backup, cloud backup, database backup, restore, wordpress backup
4
  Requires at least: 4.4
5
  Tested up to: 5.9
6
  Requires PHP: 5.4
7
- Stable tag: 1.14.14
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -132,6 +132,14 @@ Have a problem? First, take a look at our [Getting Started](https://www.boldgrid
132
 
133
  == Changelog ==
134
 
 
 
 
 
 
 
 
 
135
  = 1.14.14 =
136
 
137
  Release date: February 24th, 2022
4
  Requires at least: 4.4
5
  Tested up to: 5.9
6
  Requires PHP: 5.4
7
+ Stable tag: 1.15.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
132
 
133
  == Changelog ==
134
 
135
+ = 1.15.0 =
136
+
137
+ Release date: March 15th, 2022
138
+
139
+ * New feature: REST API calls for backup and settings management.
140
+ * Bug fix: posix_getpgid availability check.
141
+ * Update: Updated dependencies.
142
+
143
  = 1.14.14 =
144
 
145
  Release date: February 24th, 2022
rest/README.MD ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Rest API
2
+
3
+ ## Authentication
4
+
5
+ ### How are Total Upkeep's Rest API calls authenticated?
6
+
7
+ REST API calls are authenticated with a [permissions callback](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#permissions-callback).
8
+
9
+ Each registered route includes a permission_callback:
10
+
11
+ ```php
12
+ register_rest_route( $this->namespace, '/' . $this->resource, [
13
+ [
14
+ 'methods' => WP_REST_Server::READABLE,
15
+ 'callback' => [ $this, 'get_items' ],
16
+ 'permission_callback' => [ $this, 'permission_check' ],
17
+ ],
18
+ 'schema' => [ $this, 'get_schema' ],
19
+ ] );
20
+ ```
21
+
22
+ Our rest classes extend `Boldgrid_Backup_Rest_Controller`, which includes the `permission_check` method.
23
+
24
+ ### Are there any public Rest calls?
25
+
26
+ No.
27
+
28
+ ### How can I make a Rest call if no routes are public?
29
+
30
+ All calls will need to be authenticated. Please see [Using the REST API / Authentication](https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/).
31
+
32
+ > For developers making manual Ajax requests, the nonce will need to be passed with each request. The API uses nonces with the
33
+ > action set to wp_rest. These can then be passed to the API via the _wpnonce data parameter (either POST data or in the query for
34
+ > GET requests), or via the X-WP-Nonce header. If no nonce is provided the API will set the current user to 0, turning the request
35
+ > into an unauthenticated request, even if you’re logged into WordPress.
36
+
37
+ The examples on this page use the `X-WP-Nonce` method.
38
+
39
+ # Manual Testing
40
+
41
+ ## Setup
42
+
43
+ Before testing the jQuery calls on this page, be sure to add the following to your wp-config.php:
44
+
45
+ `define( 'WP_ENVIRONMENT_TYPE', 'development' );`
46
+
47
+ This will:
48
+
49
+ 1. Add the `wp_rest` nonce to all your admin pages
50
+ 1. Add your site url as `bgbkup_site_url`. This is a hidden input, who's value is used in the example calls below.
51
+
52
+ Failure to do the above, and you will have unauthenticated calls, resulting in a 403.
53
+
54
+ ## Archives
55
+
56
+ ### Get a list of archives
57
+
58
+ ```
59
+ jQuery.ajax( {
60
+ url: jQuery( '#bgbkup_site_url' ).val() + '/wp-json/bgbkup/v1/archives/',
61
+ method: 'GET',
62
+ beforeSend: function ( xhr ) {
63
+ xhr.setRequestHeader( 'X-WP-Nonce', jQuery( '#wp_rest' ).val() );
64
+ }
65
+ } ).done( function ( response ) {
66
+ console.log( response );
67
+ } );
68
+ ```
69
+
70
+ ### Create
71
+
72
+ ```
73
+ jQuery.ajax( {
74
+ url: jQuery( '#bgbkup_site_url' ).val() + '/wp-json/bgbkup/v1/archives',
75
+ method: 'POST',
76
+ beforeSend: function ( xhr ) {
77
+ xhr.setRequestHeader( 'X-WP-Nonce', jQuery( '#wp_rest' ).val() );
78
+ }
79
+ } ).done( function ( response ) {
80
+ console.log( response );
81
+ } );
82
+ ```
83
+
84
+ ### Restore
85
+
86
+ #### Restore via id
87
+
88
+ Take note of the `id=4` in the url.
89
+
90
+ ```
91
+ jQuery.ajax( {
92
+ url: jQuery( '#bgbkup_site_url' ).val() + '/wp-json/bgbkup/v1/archives/?id=4',
93
+ method: 'PUT',
94
+ beforeSend: function ( xhr ) {
95
+ xhr.setRequestHeader( 'X-WP-Nonce', jQuery( '#wp_rest' ).val() );
96
+ }
97
+ } ).done( function ( response ) {
98
+ console.log( response );
99
+ } );
100
+ ```
101
+
102
+ #### Restore via url
103
+
104
+ This needs to be fleshed out more.
105
+
106
+ ```
107
+ jQuery.ajax({
108
+ url: 'https://domain.com/wp-json/bgbkup/v1/archives/?url=' + encodeURIComponent( <URL> ),
109
+ type: 'put'
110
+ });
111
+ ```
112
+
113
+ ## Site URL ##
114
+
115
+ ### Get ###
116
+
117
+ ```
118
+ jQuery.ajax( {
119
+ url: jQuery( '#bgbkup_site_url' ).val() + '/wp-json/bgbkup/v1/siteurl',
120
+ method: 'GET',
121
+ beforeSend: function ( xhr ) {
122
+ xhr.setRequestHeader( 'X-WP-Nonce', jQuery( '#wp_rest' ).val() );
123
+ }
124
+ } ).done( function ( response ) {
125
+ console.log( response );
126
+ } );
127
+ ```
128
+
129
+ ### Set ###
130
+
131
+ ```
132
+ jQuery.ajax( {
133
+ url: jQuery( '#bgbkup_site_url' ).val() + '/wp-json/bgbkup/v1/siteurl/?siteurl=https://[NEW_SITE_URL]',
134
+ method: 'POST',
135
+ beforeSend: function ( xhr ) {
136
+ xhr.setRequestHeader( 'X-WP-Nonce', jQuery( '#wp_rest' ).val() );
137
+ }
138
+ } ).done( function ( response ) {
139
+ console.log( response );
140
+ } );
141
+ ```
rest/class-boldgrid-backup-rest-archive.php ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-archive.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Archive
16
+ *
17
+ * REST endpoints to perform simple archive manipulation.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Archive extends Boldgrid_Backup_Rest_Controller {
22
+
23
+ /**
24
+ * Resource name.
25
+ *
26
+ * @since SINCEVERSION
27
+ * @access private
28
+ * @var string
29
+ */
30
+ protected $resource = 'archives';
31
+
32
+ /**
33
+ * Register all routes.
34
+ *
35
+ * @since SINCEVERSION
36
+ */
37
+ public function register_routes() {
38
+ $this->register_creation();
39
+ $this->register_restore();
40
+ $this->register_list();
41
+ }
42
+
43
+ /**
44
+ * Register the route for creating an archive.
45
+ *
46
+ * @since SINCEVERSION
47
+ */
48
+ public function register_creation() {
49
+ register_rest_route( $this->namespace, '/' . $this->resource, [
50
+ [
51
+ 'methods' => WP_REST_Server::CREATABLE,
52
+ 'callback' => [ $this, 'create_item' ],
53
+ 'permission_callback' => [ $this, 'permission_check' ],
54
+ ],
55
+ 'schema' => [ $this, 'get_schema' ],
56
+ ] );
57
+ }
58
+
59
+ /**
60
+ * Register the route for restoring a backup.
61
+ *
62
+ * @since SINCEVERSION
63
+ */
64
+ public function register_restore() {
65
+ register_rest_route( $this->namespace, '/' . $this->resource, [
66
+ [
67
+ 'methods' => 'PUT',
68
+ 'callback' => [ $this, 'restore' ],
69
+ 'permission_callback' => [ $this, 'permission_check' ],
70
+ 'args' => [
71
+ 'url' => [
72
+ 'required' => false,
73
+ 'description' => esc_html__( 'Route URL to restore.', 'boldgrid-backup' ),
74
+ 'type' => 'string',
75
+ 'sanitation_callback' => function ( $field ) {
76
+ return esc_url_raw( $field );
77
+ },
78
+ ],
79
+ 'id' => [
80
+ 'required' => false,
81
+ 'description' => esc_html__( 'Backup id to restore.', 'boldgrid-backup' ),
82
+ 'type' => 'integer',
83
+ 'sanitation_callback' => function ( $field ) {
84
+ return (int) $field;
85
+ },
86
+ ],
87
+ ],
88
+ ],
89
+ 'schema' => [ $this, 'get_schema' ],
90
+ ] );
91
+ }
92
+
93
+ /**
94
+ * Register the route for viewing a list of backups.
95
+ *
96
+ * @since SINCEVERSION
97
+ */
98
+ public function register_list() {
99
+ register_rest_route( $this->namespace, '/' . $this->resource, [
100
+ [
101
+ 'methods' => WP_REST_Server::READABLE,
102
+ 'callback' => [ $this, 'get_items' ],
103
+ 'permission_callback' => [ $this, 'permission_check' ],
104
+ ],
105
+ 'schema' => [ $this, 'get_schema' ],
106
+ ] );
107
+ }
108
+
109
+ /**
110
+ * Get our sample schema for an archive.
111
+ *
112
+ * @since SINCEVERSION
113
+ *
114
+ * @return array Schema Format.
115
+ */
116
+ public function get_schema() {
117
+ $schema = [
118
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
119
+ 'title' => $this->resource,
120
+ 'type' => 'object',
121
+ 'properties' => [
122
+ 'id' => [
123
+ 'context' => [ 'view' ],
124
+ 'description' => esc_html__( 'Unique identifier for the object.', 'boldgrid-backup' ),
125
+ 'type' => 'string',
126
+ ],
127
+ 'title' => [
128
+ 'context' => [ 'view' ],
129
+ 'description' => esc_html__( 'Name of the archive.', 'boldgrid-backup' ),
130
+ 'type' => 'string',
131
+ ],
132
+ 'description' => [
133
+ 'context' => [ 'view' ],
134
+ 'description' => esc_html__( 'Description of the archive.', 'boldgrid-backup' ),
135
+ 'type' => 'array',
136
+ ],
137
+ 'filename' => [
138
+ 'context' => [ 'view' ],
139
+ 'description' => esc_html__( 'Filename of backup.', 'boldgrid-backup' ),
140
+ 'type' => 'string',
141
+ ],
142
+ 'url' => [
143
+ 'context' => [ 'view' ],
144
+ 'description' => esc_html__( 'Download link for the backup.', 'boldgrid-backup' ),
145
+ 'type' => 'string',
146
+ ],
147
+ 'creation_date' => [
148
+ 'context' => [ 'view' ],
149
+ 'description' => esc_html__( 'Date the archive was created.', 'boldgrid-backup' ),
150
+ 'type' => 'string',
151
+ ],
152
+ ],
153
+ ];
154
+
155
+ return $schema;
156
+ }
157
+
158
+ /**
159
+ * Prepare the item for the REST response.
160
+ *
161
+ * @since SINCEVERSION
162
+ *
163
+ * @param mixed $item WordPress representation of the item.
164
+ * @param WP_REST_Request $request Request object.
165
+ * @return mixed
166
+ */
167
+ public function prepare_item_for_response( $item, $request ) {
168
+ return $this->filter_schema_properties( $item );
169
+ }
170
+
171
+ /**
172
+ * Create a new archive.
173
+ *
174
+ * For examples, please see rest/README.MD
175
+ *
176
+ * @since SINCEVERSION
177
+ *
178
+ * @see Boldgrid_Backup_Rest_Controller::permission_check for more information if you are getting
179
+ * a 401 unauthorized error.
180
+ *
181
+ * @param WP_REST_Request $request Full data about the request.
182
+ * @return array Job Resource.
183
+ */
184
+ public function create_item( $request ) {
185
+ // Initialize a new task.
186
+ $task = new Boldgrid_Backup_Admin_Task();
187
+ $task->init( [ 'type' => 'backup' ] );
188
+ $task->update();
189
+ $task->date_format = 'c';
190
+
191
+ // Trigger our backup.
192
+ $nopriv = new Boldgrid_Backup_Admin_Nopriv();
193
+ $nopriv->do_backup( [ 'task_id' => $task->get_id() ] );
194
+
195
+ return new WP_REST_Response( $task->get(), 200 );
196
+ }
197
+
198
+ /**
199
+ * Get all archives for a WordPress.
200
+ *
201
+ * For examples, please see rest/README.MD
202
+ *
203
+ * @since SINCEVERSION
204
+ *
205
+ * @param WP_REST_Request $request Full data about the request.
206
+ * @return array A collection of archive resources.
207
+ */
208
+ public function get_items( $request ) {
209
+ // Init our list of raw backup data.
210
+ $core = apply_filters( 'boldgrid_backup_get_core', null );
211
+ $core->archives_all->init();
212
+
213
+ // Init our list of backups to return.
214
+ $backups = [];
215
+
216
+ foreach ( $core->archives_all->all as $backup ) {
217
+ $archive = Boldgrid\Backup\Archive\Factory::get_by_filename( $backup['filename'] );
218
+ $url = $archive->generate_download_link( $backup['filename'] );
219
+
220
+ $backups[] = [
221
+ 'id' => $archive->get_id(),
222
+ 'title' => $archive->get_attribute( 'title' ),
223
+ 'description' => $archive->get_attribute( 'description' ),
224
+ 'url' => $url['download_url'],
225
+ 'filename' => $backup['filename'],
226
+ 'creation_date' => date( 'c', $archive->timestamp ),
227
+ ];
228
+ }
229
+
230
+ foreach ( $backups as &$backup ) {
231
+ $backup = $this->prepare_item_for_response( $backup, $request );
232
+ }
233
+
234
+ return $backups;
235
+ }
236
+
237
+ /**
238
+ * Restore an archive.
239
+ *
240
+ * For examples, please see rest/README.MD
241
+ *
242
+ * @since SINCEVERSION
243
+ *
244
+ * @param WP_REST_Request $request Full data about the request.
245
+ * @return array Job Resource.
246
+ */
247
+ public function restore( $request ) {
248
+ $id = $request->get_param( 'id' );
249
+ $url = $request->get_param( 'url' );
250
+
251
+ // Initialize a new task.
252
+ $task = new Boldgrid_Backup_Admin_Task();
253
+ $task->init( [ 'type' => 'restore' ] );
254
+ $task->date_format = 'c';
255
+
256
+ if ( ! empty( $url ) || ! empty( $id ) ) {
257
+ // Update our task with either url or backup id.
258
+ if ( ! empty( $url ) ) {
259
+ $task->update_data( 'url', $url );
260
+ } else {
261
+ $task->update_data( 'backup_id', $id );
262
+ }
263
+
264
+ // Trigger our backup.
265
+ $nopriv = new Boldgrid_Backup_Admin_Nopriv();
266
+ $nopriv->do_restore( [ 'task_id' => $task->get_id() ] );
267
+ } else { // phpcs:ignore
268
+ return new WP_Error(
269
+ 'bgbkup_rest_missing_param',
270
+ __( 'Unable to restore. Missing required parameters.', 'boldgrid-backup' ),
271
+ [ 'status' => 400 ]
272
+ );
273
+ }
274
+
275
+ return new WP_REST_Response( $task->get(), 200 );
276
+ }
277
+ }
rest/class-boldgrid-backup-rest-controller.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-controller.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Controller
16
+ *
17
+ * A base class for the wp rest controller.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Controller extends WP_REST_Controller {
22
+
23
+ /**
24
+ * The core class object.
25
+ *
26
+ * @since SINCEVERSION
27
+ * @access private
28
+ * @var Boldgrid_Backup_Admin_Core
29
+ */
30
+ protected $core;
31
+
32
+ /**
33
+ * Namespace of the class.
34
+ *
35
+ * @since SINCEVERSION
36
+ * @access private
37
+ * @var string
38
+ */
39
+ protected $namespace = 'bgbkup/v1';
40
+
41
+ /**
42
+ * Setup the core backup class.
43
+ *
44
+ * @since SINCEVERSION
45
+ * @param Boldgrid_Backup_Admin_Core $core Core Backup class.
46
+ */
47
+ public function __construct( $core ) {
48
+ $this->core = $core;
49
+ }
50
+
51
+ /**
52
+ * Ensure user has access to any of the archive endpoints.
53
+ *
54
+ * @since SINCEVERSION
55
+ *
56
+ * @return boolean Has Access?
57
+ */
58
+ public function permission_check() {
59
+ return current_user_can( 'activate_plugins' );
60
+ }
61
+
62
+ /**
63
+ * Make sure that an item only has the items present in the schema.
64
+ *
65
+ * @since SINCEVERSION
66
+ *
67
+ * @param array $item Resource Item.
68
+ * @return array Updated resource item.
69
+ */
70
+ protected function filter_schema_properties( $item ) {
71
+ $resource = [];
72
+ $schema = $this->get_schema();
73
+ foreach ( $schema['properties'] as $name => $property ) {
74
+ if ( isset( $item[ $name ] ) ) {
75
+ $resource[ $name ] = $item[ $name ];
76
+ } else {
77
+ $resource[ $name ] = 'array' === $property['type'] ? [] : null;
78
+ }
79
+ }
80
+
81
+ return $resource;
82
+ }
83
+
84
+ }
rest/class-boldgrid-backup-rest-job.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-job.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Job
16
+ *
17
+ * REST endpoints to perform get jobs.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Job extends Boldgrid_Backup_Rest_Controller {
22
+
23
+ /**
24
+ * Resource name.
25
+ *
26
+ * @since SINCEVERSION
27
+ * @access private
28
+ * @var string
29
+ */
30
+ protected $resource = 'jobs';
31
+
32
+ /**
33
+ * Register the routes for the objects of the controller.
34
+ *
35
+ * @since SINCEVERSION
36
+ */
37
+ public function register_routes() {
38
+ register_rest_route( $this->namespace, '/' . $this->resource . '/(?P<id>[\w-]+)', [
39
+ [
40
+ 'methods' => WP_REST_Server::READABLE,
41
+ 'callback' => [ $this, 'get_item' ],
42
+ 'permission_callback' => [ $this, 'permission_check' ],
43
+ 'args' => [
44
+ 'id' => [
45
+ 'required' => true,
46
+ 'context' => [ 'view' ],
47
+ 'description' => esc_html__( 'Unique identifier for the object.', 'boldgrid-backup' ),
48
+ 'type' => 'string',
49
+ ],
50
+ ],
51
+ ],
52
+ 'schema' => [ $this, 'get_schema' ],
53
+ ] );
54
+ }
55
+
56
+ /**
57
+ * Get our sample schema for comments.
58
+ *
59
+ * @since SINCEVERSION
60
+ *
61
+ * @return array Schema Format.
62
+ */
63
+ public function get_schema() {
64
+ $schema = [
65
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
66
+ 'title' => 'job',
67
+ 'type' => 'object',
68
+ 'properties' => [
69
+ 'id' => [
70
+ 'context' => [ 'view' ],
71
+ 'description' => esc_html__( 'Unique identifier for the object.', 'boldgrid-backup' ),
72
+ 'type' => 'string',
73
+ ],
74
+ 'type' => [
75
+ 'context' => [ 'view' ],
76
+ 'description' => esc_html__( 'Type of job.', 'boldgrid-backup' ),
77
+ 'type' => 'string',
78
+ 'enum' => [
79
+ 'backup',
80
+ 'restoration',
81
+ ],
82
+ ],
83
+ 'status' => [
84
+ 'context' => [ 'view' ],
85
+ 'description' => esc_html__( 'Currently status of the job.', 'boldgrid-backup' ),
86
+ 'type' => 'string',
87
+ ],
88
+ 'started_at' => [
89
+ 'context' => [ 'view' ],
90
+ 'description' => esc_html__( 'Information attached to the start of the prcoess.', 'boldgrid-backup' ),
91
+ 'type' => 'string',
92
+ ],
93
+ 'completed_at' => [
94
+ 'context' => [ 'view' ],
95
+ 'description' => esc_html__( 'Results of the process.', 'boldgrid-backup' ),
96
+ 'type' => 'string',
97
+ ],
98
+ ],
99
+ ];
100
+
101
+ return $schema;
102
+ }
103
+
104
+ /**
105
+ * Prepare the item for the REST response.
106
+ *
107
+ * @since SINCEVERSION
108
+ *
109
+ * @param mixed $item WordPress representation of the item.
110
+ * @param WP_REST_Request $request Request object.
111
+ * @return mixed
112
+ */
113
+ public function prepare_item_for_response( $item, $request ) {
114
+ return $this->filter_schema_properties( $item );
115
+ }
116
+
117
+ /**
118
+ * Get one item from the collection.
119
+ *
120
+ * Example call:
121
+ * jQuery.get( 'https://domain/wp-json/bgbkup/v1/jobs/<JOB-ID>' );
122
+ *
123
+ * @since SINCEVERSION
124
+ *
125
+ * @param WP_REST_Request $request Full data about the request.
126
+ * @return WP_Error|WP_REST_Response
127
+ */
128
+ public function get_item( $request ) {
129
+ $id = $request->get_param( 'id' );
130
+
131
+ $task = new Boldgrid_Backup_Admin_Task();
132
+ $found = $task->init_by_id( $id );
133
+
134
+ $task->date_format = 'c';
135
+
136
+ $item = $found ? $task->get() : null;
137
+ if ( ! empty( $item ) ) {
138
+ $data = $this->prepare_item_for_response( $item, $request );
139
+ return new WP_REST_Response( $data, 200 );
140
+ } else {
141
+ return new WP_Error( 'no_job', esc_html__( 'Job Not found', 'boldgrid-backup' ), [
142
+ 'status' => 404,
143
+ ] );
144
+ }
145
+ }
146
+ }
rest/class-boldgrid-backup-rest-setting.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-setting.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Setting
16
+ *
17
+ * REST endpoints to access the backup settings.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Setting extends Boldgrid_Backup_Rest_Controller {
22
+
23
+ /**
24
+ * Resource name.
25
+ *
26
+ * @since SINCEVERSION
27
+ * @access private
28
+ * @var string
29
+ */
30
+ protected $resource = 'settings';
31
+
32
+ /**
33
+ * Register the routes for the objects of the controller.
34
+ *
35
+ * @since SINCEVERSION
36
+ */
37
+ public function register_routes() {
38
+ $this->register_get();
39
+ $this->register_update();
40
+ }
41
+
42
+ /**
43
+ * Register the route for creating a backup.
44
+ *
45
+ * @since SINCEVERSION
46
+ */
47
+ public function register_get() {
48
+ register_rest_route( $this->namespace, '/' . $this->resource, [
49
+ [
50
+ 'methods' => WP_REST_Server::READABLE,
51
+ 'callback' => [ $this, 'get_item' ],
52
+ 'permission_callback' => [ $this, 'permission_check' ],
53
+ ],
54
+ 'schema' => [ $this, 'get_schema' ],
55
+ ] );
56
+ }
57
+
58
+ /**
59
+ * Register router for updating settings.
60
+ *
61
+ * @since SINCEVERSION
62
+ */
63
+ public function register_update() {
64
+ register_rest_route( $this->namespace, '/' . $this->resource, [
65
+ [
66
+ 'methods' => WP_REST_Server::EDITABLE,
67
+ 'callback' => [ $this, 'update_item' ],
68
+ 'permission_callback' => [ $this, 'permission_check' ],
69
+ ],
70
+ 'schema' => [ $this, 'get_schema' ],
71
+ ] );
72
+ }
73
+
74
+ /**
75
+ * Prepare the item for the REST response.
76
+ *
77
+ * @since SINCEVERSION
78
+ *
79
+ * @param mixed $item WordPress representation of the item.
80
+ * @param WP_REST_Request $request Request object.
81
+ * @return mixed
82
+ */
83
+ public function prepare_item_for_response( $item, $request ) {
84
+ return $this->filter_schema_properties( $item );
85
+ }
86
+
87
+ /**
88
+ * Get schema for settings.
89
+ *
90
+ * @since SINCEVERSION
91
+ *
92
+ * @return array Schema Format.
93
+ */
94
+ public function get_schema() {
95
+ $schema = [
96
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
97
+ 'title' => $this->resource,
98
+ 'type' => 'object',
99
+ 'properties' => [
100
+ 'schedule' => [
101
+ 'context' => [ 'view' ],
102
+ 'description' => esc_html__( 'Date the backup was created.', 'boldgrid-backup' ),
103
+ 'type' => 'array',
104
+ ],
105
+ 'autoupdate' => [
106
+ 'context' => [ 'view' ],
107
+ 'description' => esc_html__( 'Automatic Update.', 'boldgrid-backup' ),
108
+ 'type' => 'array',
109
+ ],
110
+ 'notification_email' => [
111
+ 'context' => [ 'view' ],
112
+ 'description' => esc_html__( 'Email to notify got backups.', 'boldgrid-backup' ),
113
+ 'type' => 'string',
114
+ ],
115
+ 'auto_backup' => [
116
+ 'context' => [ 'view' ],
117
+ 'description' => esc_html__( 'Does a site have auto backups enabled?', 'boldgrid-backup' ),
118
+ 'type' => 'integer',
119
+ ],
120
+ 'auto_backup' => [
121
+ 'context' => [ 'view' ],
122
+ 'description' => esc_html__( 'Does a site have auto rollback enabled?', 'boldgrid-backup' ),
123
+ 'type' => 'integer',
124
+ ],
125
+ 'folder_exclusion_include' => [
126
+ 'context' => [ 'view' ],
127
+ 'description' => esc_html__( 'Directories and files to include.', 'boldgrid-backup' ),
128
+ 'type' => 'string',
129
+ ],
130
+ 'folder_exclusion_exclude' => [
131
+ 'context' => [ 'view' ],
132
+ 'description' => esc_html__( 'Directories and files to exclude.', 'boldgrid-backup' ),
133
+ 'type' => 'string',
134
+ ],
135
+ ],
136
+ ];
137
+
138
+ return $schema;
139
+ }
140
+
141
+ /**
142
+ * Get the users plugin settings.
143
+ *
144
+ * @since SINCEVERSION
145
+ *
146
+ * @param WP_REST_Request $request Request object.
147
+ * @return array Plugin settings.
148
+ */
149
+ public function get_item( $request ) {
150
+ $settings = get_option( 'boldgrid_backup_settings', [] );
151
+ $boldgrid_settings = get_option( 'boldgrid_settings' );
152
+
153
+ $settings['autoupdate'] = isset( $boldgrid_settings['autoupdate'] ) ? $boldgrid_settings['autoupdate'] : null;
154
+
155
+ return $this->prepare_item_for_response( $settings, $request );
156
+ }
157
+
158
+ /**
159
+ * Update settings through API.
160
+ *
161
+ * @since SINCEVERSION
162
+ *
163
+ * @param WP_REST_Request $request Request object.
164
+ * @return array Updated Settings.
165
+ */
166
+ public function update_item( $request ) {
167
+ $schema = $this->get_schema();
168
+ $settings = get_option( 'boldgrid_backup_settings', [] );
169
+ $requested_settings = $request->get_param( 'settings' );
170
+
171
+ foreach ( $schema['properties'] as $name => $value ) {
172
+ if ( isset( $requested_settings[ $name ] ) ) {
173
+ $settings[ $name ] = $requested_settings[ $name ];
174
+ }
175
+ }
176
+
177
+ $scheduler = new Boldgrid_Backup_Admin_Scheduler( $this->core );
178
+ $settings['scheduler'] = $scheduler->get();
179
+
180
+ $admin_settings = new Boldgrid_Backup_Admin_Settings( $this->core );
181
+ $settings = $admin_settings->update_cron( $settings );
182
+
183
+ // Update Settings.
184
+ update_option( 'boldgrid_backup_settings', $settings );
185
+
186
+ // Update the auto update setting.
187
+ if ( ! empty( $requested_settings['autoupdate'] ) ) {
188
+ $boldgrid_settings = get_option( 'boldgrid_settings' );
189
+ $boldgrid_settings['autoupdate'] = $requested_settings['autoupdate'];
190
+ update_option( 'boldgrid_settings', $boldgrid_settings );
191
+ }
192
+
193
+ return $this->get_item( $request );
194
+ }
195
+ }
rest/class-boldgrid-backup-rest-siteurl.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-siteurl.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Siteurl
16
+ *
17
+ * REST endpoint for the siteurl.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Siteurl extends Boldgrid_Backup_Rest_Controller {
22
+ /**
23
+ * Resource name.
24
+ *
25
+ * @since SINCEVERSION
26
+ * @access private
27
+ * @var string
28
+ */
29
+ protected $resource = 'siteurl';
30
+
31
+ /**
32
+ * Register the routes for the objects of the controller.
33
+ *
34
+ * @since SINCEVERSION
35
+ */
36
+ public function register_routes() {
37
+ $this->register_get();
38
+ $this->register_update();
39
+ }
40
+
41
+ /**
42
+ * Register the route for creating a backup.
43
+ *
44
+ * @since SINCEVERSION
45
+ */
46
+ public function register_get() {
47
+ register_rest_route( $this->namespace, '/' . $this->resource, [
48
+ [
49
+ 'methods' => WP_REST_Server::READABLE,
50
+ 'callback' => [ $this, 'get_item' ],
51
+ 'permission_callback' => [ $this, 'permission_check' ],
52
+ ],
53
+ 'schema' => [ $this, 'get_schema' ],
54
+ ] );
55
+ }
56
+
57
+ /**
58
+ * Register router for updating settings.
59
+ *
60
+ * @since SINCEVERSION
61
+ */
62
+ public function register_update() {
63
+ register_rest_route( $this->namespace, '/' . $this->resource, [
64
+ [
65
+ 'methods' => WP_REST_Server::EDITABLE,
66
+ 'callback' => [ $this, 'update_item' ],
67
+ 'permission_callback' => [ $this, 'permission_check' ],
68
+ 'args' => array(
69
+ 'siteurl' => array(
70
+ 'required' => true,
71
+ 'description' => esc_html__( 'New site url.', 'boldgrid-backup' ),
72
+ 'type' => 'string',
73
+ 'sanitation_callback' => function ( $field ) {
74
+ return esc_url_raw( $field );
75
+ },
76
+ ),
77
+ ),
78
+ ],
79
+ 'schema' => [ $this, 'get_schema' ],
80
+ ] );
81
+ }
82
+
83
+ /**
84
+ * Prepare the item for the REST response.
85
+ *
86
+ * @since SINCEVERSION
87
+ *
88
+ * @param mixed $item WordPress representation of the item.
89
+ * @param WP_REST_Request $request Request object.
90
+ * @return mixed
91
+ */
92
+ public function prepare_item_for_response( $item, $request ) {
93
+ return $this->filter_schema_properties( $item );
94
+ }
95
+
96
+ /**
97
+ * Get schema for settings.
98
+ *
99
+ * @since SINCEVERSION
100
+ *
101
+ * @return array Schema Format.
102
+ */
103
+ public function get_schema() {
104
+ $schema = [
105
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
106
+ 'title' => $this->resource,
107
+ 'type' => 'object',
108
+ 'properties' => [
109
+ 'home' => [
110
+ 'context' => [ 'view' ],
111
+ 'description' => esc_html__( 'Home.', 'boldgrid-backup' ),
112
+ 'type' => 'string',
113
+ ],
114
+ 'siteurl' => [
115
+ 'context' => [ 'view' ],
116
+ 'description' => esc_html__( 'Siteurl.', 'boldgrid-backup' ),
117
+ 'type' => 'string',
118
+ ],
119
+ 'old_home' => [
120
+ 'context' => [ 'view' ],
121
+ 'description' => esc_html__( 'Old home (before changing).', 'boldgrid-backup' ),
122
+ 'type' => 'string',
123
+ ],
124
+ 'old_siteurl' => [
125
+ 'context' => [ 'view' ],
126
+ 'description' => esc_html__( 'Old siteurl (before changing).', 'boldgrid-backup' ),
127
+ 'type' => 'string',
128
+ ],
129
+ ],
130
+ ];
131
+
132
+ return $schema;
133
+ }
134
+
135
+ /**
136
+ * Get the users plugin settings.
137
+ *
138
+ * @since SINCEVERSION
139
+ *
140
+ * @param WP_REST_Request $request Request object.
141
+ * @return array Plugin settings.
142
+ */
143
+ public function get_item( $request ) {
144
+ return $this->prepare_item_for_response( array(
145
+ 'home' => get_option( 'home' ),
146
+ 'siteurl' => get_option( 'siteurl' ),
147
+ ), $request );
148
+ }
149
+
150
+ /**
151
+ * Update a site url via a REST call.
152
+ *
153
+ * @since SINCEVERSION
154
+ *
155
+ * @param WP_REST_Request $request Request object.
156
+ * @return array Updated Settings.
157
+ */
158
+ public function update_item( $request ) {
159
+ $old_home = get_option( 'home' );
160
+ $old_siteurl = get_option( 'siteurl' );
161
+
162
+ // Get the new site url.
163
+ $siteurl = $request->get_param( 'siteurl' );
164
+
165
+ Boldgrid_Backup_Admin_Utility::update_siteurl( array(
166
+ 'old_siteurl' => $old_siteurl,
167
+ 'siteurl' => $siteurl,
168
+ 'flush' => true,
169
+ ) );
170
+
171
+ return $this->prepare_item_for_response( array(
172
+ 'old_home' => $old_home,
173
+ 'old_siteurl' => $old_siteurl,
174
+ 'home' => get_option( 'home' ),
175
+ 'siteurl' => get_option( 'siteurl' ),
176
+ ), $request );
177
+ }
178
+ }
rest/class-boldgrid-backup-rest-test.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-test.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Test
16
+ *
17
+ * REST endpoints to access the backup preflight check results.
18
+ *
19
+ * @since SINCEVERSION
20
+ */
21
+ class Boldgrid_Backup_Rest_Test extends Boldgrid_Backup_Rest_Controller {
22
+
23
+ /**
24
+ * Resource name.
25
+ *
26
+ * @since SINCEVERSION
27
+ * @access private
28
+ * @var string
29
+ */
30
+ protected $resource = 'test';
31
+
32
+ /**
33
+ * Register the routes for the objects of the controller.
34
+ *
35
+ * @since SINCEVERSION
36
+ */
37
+ public function register_routes() {
38
+ $this->register_get();
39
+ }
40
+
41
+ /**
42
+ * Register the route for getting test results.
43
+ *
44
+ * @since SINCEVERSION
45
+ */
46
+ public function register_get() {
47
+ register_rest_route( $this->namespace, '/' . $this->resource, [
48
+ [
49
+ 'methods' => WP_REST_Server::READABLE,
50
+ 'callback' => [ $this, 'get_item' ],
51
+ 'permission_callback' => [ $this, 'permission_check' ],
52
+ ],
53
+ 'schema' => [ $this, 'get_schema' ],
54
+ ] );
55
+ }
56
+
57
+ /**
58
+ * Prepare the item for the REST response.
59
+ *
60
+ * @since SINCEVERSION
61
+ *
62
+ * @param mixed $item WordPress representation of the item.
63
+ * @param WP_REST_Request $request Request object.
64
+ * @return mixed
65
+ */
66
+ public function prepare_item_for_response( $item, $request ) {
67
+ return $this->filter_schema_properties( $item );
68
+ }
69
+
70
+ /**
71
+ * Get schema for results.
72
+ *
73
+ * @since SINCEVERSION
74
+ *
75
+ * @return array Schema Format.
76
+ */
77
+ public function get_schema() {
78
+ $schema = [
79
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
80
+ 'title' => $this->resource,
81
+ 'type' => 'object',
82
+ 'properties' => [
83
+ 'passed' => [
84
+ 'context' => [ 'view' ],
85
+ 'description' => esc_html__( 'Whether or not the site passed the preflight check.', 'boldgrid-backup' ),
86
+ 'type' => 'bool',
87
+ ],
88
+ ],
89
+ ];
90
+
91
+ return $schema;
92
+ }
93
+
94
+ /**
95
+ * Get the the preflight check results.
96
+ *
97
+ * @since SINCEVERSION
98
+ *
99
+ * @param WP_REST_Request $request Request object.
100
+ * @return array Preflight check results.
101
+ */
102
+ public function get_item( $request ) {
103
+ $preflight_test = new Boldgrid_Backup_Admin_Test( $this->core );
104
+ $settings['passed'] = $preflight_test->run_functionality_tests();
105
+ return $this->prepare_item_for_response( $settings, $request );
106
+ }
107
+ }
rest/class-boldgrid-backup-rest-utility.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File: class-boldgrid-backup-rest-utility.php
4
+ *
5
+ * @link https://www.boldgrid.com
6
+ * @since SINCEVERSION
7
+ *
8
+ * @package Boldgrid_Backup
9
+ * @copyright BoldGrid
10
+ * @version $Id$
11
+ * @author BoldGrid <support@boldgrid.com>
12
+ */
13
+
14
+ /**
15
+ * Class: Boldgrid_Backup_Rest_Utility
16
+ *
17
+ * @since SINCEVERSION
18
+ */
19
+ class Boldgrid_Backup_Rest_Utility {
20
+ /**
21
+ * Get the current url.
22
+ *
23
+ * @since SINCEVERSION
24
+ *
25
+ * @return string
26
+ */
27
+ public static function get_current_url() {
28
+ $url = '';
29
+
30
+ if ( ! empty( $_SERVER['HTTP_HOST'] ) ) {
31
+ $url = ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
32
+ }
33
+
34
+ return $url;
35
+ }
36
+
37
+ /**
38
+ * Echo a wp_rest nonce to the screen.
39
+ *
40
+ * This method is only using for testing, and is only called in a dev environment.
41
+ *
42
+ * @since SINCEVERSION
43
+ *
44
+ * @see Boldgrid_Backup::define_admin_hooks() For more info on how this method is used within a
45
+ * dev environment.
46
+ * @see rest/README.MD For a list of example rest calls that depend on this
47
+ * method.
48
+ */
49
+ public static function insert_nonce() {
50
+ wp_nonce_field( 'wp_rest', 'wp_rest' );
51
+ echo '<input type="hidden" id="bgbkup_site_url" value="' . esc_attr( get_site_url() ) . '" />';
52
+ }
53
+
54
+ /**
55
+ * Whether or not we're in a REST call.
56
+ *
57
+ * @since SINCEVERSION
58
+ *
59
+ * @return bool
60
+ */
61
+ public static function is_rest() {
62
+ $current_url = self::get_current_url();
63
+
64
+ // True when the current url begins with http://domain.com/wp-json/.
65
+ $rest_prefix = get_site_url( null, 'wp-json/' );
66
+ $in_pretty_permalink = substr( $current_url, 0, strlen( $rest_prefix ) ) === $rest_prefix;
67
+
68
+ // True when the current url begins with http://domain.com/index.php/wp-json/
69
+ $rest_prefix = get_site_url( null, 'index.php/wp-json/' );
70
+ $in_index_url = substr( $current_url, 0, strlen( $rest_prefix ) ) === $rest_prefix;
71
+
72
+ // True when url includes the rest_route parameter.
73
+ $in_get = ! empty( $_GET['rest_route'] ); // phpcs:ignore
74
+
75
+ return $in_pretty_permalink || $in_index_url || $in_get;
76
+ }
77
+ }
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit21ce95d681fa0cbccf157df200b627c0::getLoader();
vendor/boldgrid/library/.travis.yml CHANGED
@@ -1,26 +1,48 @@
1
  language: php
2
 
3
- dist: trusty
 
 
 
 
 
 
4
 
5
  notifications:
6
  email:
7
  on_success: never
8
  on_failure: change
9
 
10
- php:
11
- - 5.6
12
 
13
- language: node_js
14
  node_js:
15
- - "8.12.0"
16
 
17
  env:
18
  - WP_VERSION=latest WP_MULTISITE=0
19
 
20
- before_script:
21
- - yarn
22
- - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  script:
25
- - phpunit --debug
26
- - yarn run js-lint
 
 
1
  language: php
2
 
3
+ # Distrubution / environments: https://docs.travis-ci.com/user/reference/overview/
4
+ # Ubuntu Trusty does not support php8: https://docs.travis-ci.com/user/languages/php/
5
+ dist: focal
6
+
7
+ services:
8
+ # Specifically including MySQL was not needed with Trusty, but other environments do need it.
9
+ - mysql
10
 
11
  notifications:
12
  email:
13
  on_success: never
14
  on_failure: change
15
 
16
+ language: php, node_js
 
17
 
 
18
  node_js:
19
+ - lts/*
20
 
21
  env:
22
  - WP_VERSION=latest WP_MULTISITE=0
23
 
24
+ matrix:
25
+ include:
26
+ - php: 7.4
27
+ before_script:
28
+ - yarn install
29
+ # Remove phpunit and install best phpunit version based on this environment.
30
+ - composer update phpunit/phpunit
31
+ # A note on our composer / yarn usage:
32
+ # Our composer post-autoload-dump script will copy files from within node_modules/* to our plugin
33
+ # and then delete the node_modules folder. If node_modules are needed after this point, such
34
+ # as for "yarn run js-lint", another "yarn install" will be needed.
35
+ - composer install -o
36
+ - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
37
+ - php: 8.0
38
+ before_script:
39
+ - yarn install
40
+ - composer update phpunit/phpunit
41
+ - composer install -o
42
+ - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
43
 
44
  script:
45
+ - vendor/phpunit/phpunit/phpunit --debug
46
+ # Temporarily disabling js-lint. @todo This needs to be fixed.
47
+ # The current config used, eslint-config-wordpress, is deprecated. That packages names replacements.
48
+ # - yarn run js-lint
vendor/boldgrid/library/README.md CHANGED
@@ -11,6 +11,26 @@ composer require boldgrid/library
11
 
12
  ## Changelog ##
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  ### 2.13.3 ###
15
 
16
  Release date: October 13th, 2020
11
 
12
  ## Changelog ##
13
 
14
+ ### 2.13.6 ###
15
+
16
+ Release date: March 15th, 2022
17
+
18
+ * Update: Allow configs to be updated via an option.
19
+ * Update: Misc updates to automated tests.
20
+ * Bug fix: Avoid issues on the ftp filesystem.
21
+
22
+ ### 2.13.5 ###
23
+
24
+ Release date: September 8, 2021
25
+
26
+ * Bug Fix: Review prompt prevents admins from deleting other user accounts [#192](https://github.com/BoldGrid/post-and-page-builder/issues/192)
27
+
28
+ ### 2.13.4 ###
29
+
30
+ Release date: June 10th, 2021
31
+
32
+ * Bug Fix: Avoid PHP Error in Dashboard/SortWidgets.php by validating widget containers.
33
+
34
  ### 2.13.3 ###
35
 
36
  Release date: October 13th, 2020
vendor/boldgrid/library/bin/install-wp-tests.sh CHANGED
@@ -19,14 +19,16 @@ set -ex
19
  install_wp() {
20
  mkdir -p $WP_CORE_DIR
21
 
22
- if [ $WP_VERSION == 'latest' ]; then
23
  local ARCHIVE_NAME='latest'
24
  else
25
  local ARCHIVE_NAME="wordpress-$WP_VERSION"
26
  fi
27
 
28
- wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/${ARCHIVE_NAME}.tar.gz
29
- tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
 
 
30
 
31
  wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
32
  }
@@ -42,9 +44,10 @@ install_test_suite() {
42
  # set up testing suite
43
  mkdir -p $WP_TESTS_DIR
44
  cd $WP_TESTS_DIR
45
- svn co --quiet https://develop.svn.wordpress.org/branches/5.2/tests/phpunit/includes/
46
 
47
- wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/branches/5.2/wp-tests-config-sample.php
 
 
48
  sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
49
  sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
50
  sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
19
  install_wp() {
20
  mkdir -p $WP_CORE_DIR
21
 
22
+ if [ $WP_VERSION == 'latest' ]; then
23
  local ARCHIVE_NAME='latest'
24
  else
25
  local ARCHIVE_NAME="wordpress-$WP_VERSION"
26
  fi
27
 
28
+ # Install the WordPress files.
29
+ # Unzip quietly (-qq) so the automated tests are not flooded with the unzip output.
30
+ wget -nv -O /tmp/wordpress-latest.zip https://wordpress.org/nightly-builds/wordpress-latest.zip
31
+ unzip -qq /tmp/wordpress-latest.zip -d /tmp
32
 
33
  wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php
34
  }
44
  # set up testing suite
45
  mkdir -p $WP_TESTS_DIR
46
  cd $WP_TESTS_DIR
 
47
 
48
+ # As each new version of WP is released, the branch should be updated in the 2 lines below.
49
+ svn co --quiet https://develop.svn.wordpress.org/branches/5.9/tests/phpunit/includes/
50
+ wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/branches/5.9/wp-tests-config-sample.php
51
  sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
52
  sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
53
  sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
vendor/boldgrid/library/composer.json CHANGED
@@ -35,5 +35,8 @@
35
  "post-autoload-dump": [
36
  "yarn && yarn run cpx 'node_modules/jquery-toggles/toggles.min.js' 'build/' && yarn run cpx 'node_modules/jquery-toggles/css/toggles-full.css' 'build/' && yarn run rimraf node_modules"
37
  ]
 
 
 
38
  }
39
  }
35
  "post-autoload-dump": [
36
  "yarn && yarn run cpx 'node_modules/jquery-toggles/toggles.min.js' 'build/' && yarn run cpx 'node_modules/jquery-toggles/css/toggles-full.css' 'build/' && yarn run rimraf node_modules"
37
  ]
38
+ },
39
+ "require-dev": {
40
+ "yoast/phpunit-polyfills": "^1.0"
41
  }
42
  }
vendor/boldgrid/library/composer.lock ADDED
@@ -0,0 +1,2039 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "81680ded536dd0cad4416e4e04092729",
8
+ "packages": [],
9
+ "packages-dev": [
10
+ {
11
+ "name": "doctrine/instantiator",
12
+ "version": "1.4.1",
13
+ "source": {
14
+ "type": "git",
15
+ "url": "https://github.com/doctrine/instantiator.git",
16
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
17
+ },
18
+ "dist": {
19
+ "type": "zip",
20
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
21
+ "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
22
+ "shasum": ""
23
+ },
24
+ "require": {
25
+ "php": "^7.1 || ^8.0"
26
+ },
27
+ "require-dev": {
28
+ "doctrine/coding-standard": "^9",
29
+ "ext-pdo": "*",
30
+ "ext-phar": "*",
31
+ "phpbench/phpbench": "^0.16 || ^1",
32
+ "phpstan/phpstan": "^1.4",
33
+ "phpstan/phpstan-phpunit": "^1",
34
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
35
+ "vimeo/psalm": "^4.22"
36
+ },
37
+ "type": "library",
38
+ "autoload": {
39
+ "psr-4": {
40
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
41
+ }
42
+ },
43
+ "notification-url": "https://packagist.org/downloads/",
44
+ "license": [
45
+ "MIT"
46
+ ],
47
+ "authors": [
48
+ {
49
+ "name": "Marco Pivetta",
50
+ "email": "ocramius@gmail.com",
51
+ "homepage": "https://ocramius.github.io/"
52
+ }
53
+ ],
54
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
55
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
56
+ "keywords": [
57
+ "constructor",
58
+ "instantiate"
59
+ ],
60
+ "funding": [
61
+ {
62
+ "url": "https://www.doctrine-project.org/sponsorship.html",
63
+ "type": "custom"
64
+ },
65
+ {
66
+ "url": "https://www.patreon.com/phpdoctrine",
67
+ "type": "patreon"
68
+ },
69
+ {
70
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
71
+ "type": "tidelift"
72
+ }
73
+ ],
74
+ "time": "2022-03-03T08:28:38+00:00"
75
+ },
76
+ {
77
+ "name": "myclabs/deep-copy",
78
+ "version": "1.11.0",
79
+ "source": {
80
+ "type": "git",
81
+ "url": "https://github.com/myclabs/DeepCopy.git",
82
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
83
+ },
84
+ "dist": {
85
+ "type": "zip",
86
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
87
+ "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
88
+ "shasum": ""
89
+ },
90
+ "require": {
91
+ "php": "^7.1 || ^8.0"
92
+ },
93
+ "conflict": {
94
+ "doctrine/collections": "<1.6.8",
95
+ "doctrine/common": "<2.13.3 || >=3,<3.2.2"
96
+ },
97
+ "require-dev": {
98
+ "doctrine/collections": "^1.6.8",
99
+ "doctrine/common": "^2.13.3 || ^3.2.2",
100
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
101
+ },
102
+ "type": "library",
103
+ "autoload": {
104
+ "files": [
105
+ "src/DeepCopy/deep_copy.php"
106
+ ],
107
+ "psr-4": {
108
+ "DeepCopy\\": "src/DeepCopy/"
109
+ }
110
+ },
111
+ "notification-url": "https://packagist.org/downloads/",
112
+ "license": [
113
+ "MIT"
114
+ ],
115
+ "description": "Create deep copies (clones) of your objects",
116
+ "keywords": [
117
+ "clone",
118
+ "copy",
119
+ "duplicate",
120
+ "object",
121
+ "object graph"
122
+ ],
123
+ "funding": [
124
+ {
125
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
126
+ "type": "tidelift"
127
+ }
128
+ ],
129
+ "time": "2022-03-03T13:19:32+00:00"
130
+ },
131
+ {
132
+ "name": "nikic/php-parser",
133
+ "version": "v4.13.2",
134
+ "source": {
135
+ "type": "git",
136
+ "url": "https://github.com/nikic/PHP-Parser.git",
137
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077"
138
+ },
139
+ "dist": {
140
+ "type": "zip",
141
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
142
+ "reference": "210577fe3cf7badcc5814d99455df46564f3c077",
143
+ "shasum": ""
144
+ },
145
+ "require": {
146
+ "ext-tokenizer": "*",
147
+ "php": ">=7.0"
148
+ },
149
+ "require-dev": {
150
+ "ircmaxell/php-yacc": "^0.0.7",
151
+ "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
152
+ },
153
+ "bin": [
154
+ "bin/php-parse"
155
+ ],
156
+ "type": "library",
157
+ "extra": {
158
+ "branch-alias": {
159
+ "dev-master": "4.9-dev"
160
+ }
161
+ },
162
+ "autoload": {
163
+ "psr-4": {
164
+ "PhpParser\\": "lib/PhpParser"
165
+ }
166
+ },
167
+ "notification-url": "https://packagist.org/downloads/",
168
+ "license": [
169
+ "BSD-3-Clause"
170
+ ],
171
+ "authors": [
172
+ {
173
+ "name": "Nikita Popov"
174
+ }
175
+ ],
176
+ "description": "A PHP parser written in PHP",
177
+ "keywords": [
178
+ "parser",
179
+ "php"
180
+ ],
181
+ "time": "2021-11-30T19:35:32+00:00"
182
+ },
183
+ {
184
+ "name": "phar-io/manifest",
185
+ "version": "2.0.3",
186
+ "source": {
187
+ "type": "git",
188
+ "url": "https://github.com/phar-io/manifest.git",
189
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
190
+ },
191
+ "dist": {
192
+ "type": "zip",
193
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
194
+ "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
195
+ "shasum": ""
196
+ },
197
+ "require": {
198
+ "ext-dom": "*",
199
+ "ext-phar": "*",
200
+ "ext-xmlwriter": "*",
201
+ "phar-io/version": "^3.0.1",
202
+ "php": "^7.2 || ^8.0"
203
+ },
204
+ "type": "library",
205
+ "extra": {
206
+ "branch-alias": {
207
+ "dev-master": "2.0.x-dev"
208
+ }
209
+ },
210
+ "autoload": {
211
+ "classmap": [
212
+ "src/"
213
+ ]
214
+ },
215
+ "notification-url": "https://packagist.org/downloads/",
216
+ "license": [
217
+ "BSD-3-Clause"
218
+ ],
219
+ "authors": [
220
+ {
221
+ "name": "Arne Blankerts",
222
+ "email": "arne@blankerts.de",
223
+ "role": "Developer"
224
+ },
225
+ {
226
+ "name": "Sebastian Heuer",
227
+ "email": "sebastian@phpeople.de",
228
+ "role": "Developer"
229
+ },
230
+ {
231
+ "name": "Sebastian Bergmann",
232
+ "email": "sebastian@phpunit.de",
233
+ "role": "Developer"
234
+ }
235
+ ],
236
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
237
+ "time": "2021-07-20T11:28:43+00:00"
238
+ },
239
+ {
240
+ "name": "phar-io/version",
241
+ "version": "3.2.1",
242
+ "source": {
243
+ "type": "git",
244
+ "url": "https://github.com/phar-io/version.git",
245
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
246
+ },
247
+ "dist": {
248
+ "type": "zip",
249
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
250
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
251
+ "shasum": ""
252
+ },
253
+ "require": {
254
+ "php": "^7.2 || ^8.0"
255
+ },
256
+ "type": "library",
257
+ "autoload": {
258
+ "classmap": [
259
+ "src/"
260
+ ]
261
+ },
262
+ "notification-url": "https://packagist.org/downloads/",
263
+ "license": [
264
+ "BSD-3-Clause"
265
+ ],
266
+ "authors": [
267
+ {
268
+ "name": "Arne Blankerts",
269
+ "email": "arne@blankerts.de",
270
+ "role": "Developer"
271
+ },
272
+ {
273
+ "name": "Sebastian Heuer",
274
+ "email": "sebastian@phpeople.de",
275
+ "role": "Developer"
276
+ },
277
+ {
278
+ "name": "Sebastian Bergmann",
279
+ "email": "sebastian@phpunit.de",
280
+ "role": "Developer"
281
+ }
282
+ ],
283
+ "description": "Library for handling version information and constraints",
284
+ "time": "2022-02-21T01:04:05+00:00"
285
+ },
286
+ {
287
+ "name": "phpdocumentor/reflection-common",
288
+ "version": "2.2.0",
289
+ "source": {
290
+ "type": "git",
291
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
292
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
293
+ },
294
+ "dist": {
295
+ "type": "zip",
296
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
297
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
298
+ "shasum": ""
299
+ },
300
+ "require": {
301
+ "php": "^7.2 || ^8.0"
302
+ },
303
+ "type": "library",
304
+ "extra": {
305
+ "branch-alias": {
306
+ "dev-2.x": "2.x-dev"
307
+ }
308
+ },
309
+ "autoload": {
310
+ "psr-4": {
311
+ "phpDocumentor\\Reflection\\": "src/"
312
+ }
313
+ },
314
+ "notification-url": "https://packagist.org/downloads/",
315
+ "license": [
316
+ "MIT"
317
+ ],
318
+ "authors": [
319
+ {
320
+ "name": "Jaap van Otterdijk",
321
+ "email": "opensource@ijaap.nl"
322
+ }
323
+ ],
324
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
325
+ "homepage": "http://www.phpdoc.org",
326
+ "keywords": [
327
+ "FQSEN",
328
+ "phpDocumentor",
329
+ "phpdoc",
330
+ "reflection",
331
+ "static analysis"
332
+ ],
333
+ "time": "2020-06-27T09:03:43+00:00"
334
+ },
335
+ {
336
+ "name": "phpdocumentor/reflection-docblock",
337
+ "version": "5.3.0",
338
+ "source": {
339
+ "type": "git",
340
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
341
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
342
+ },
343
+ "dist": {
344
+ "type": "zip",
345
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
346
+ "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
347
+ "shasum": ""
348
+ },
349
+ "require": {
350
+ "ext-filter": "*",
351
+ "php": "^7.2 || ^8.0",
352
+ "phpdocumentor/reflection-common": "^2.2",
353
+ "phpdocumentor/type-resolver": "^1.3",
354
+ "webmozart/assert": "^1.9.1"
355
+ },
356
+ "require-dev": {
357
+ "mockery/mockery": "~1.3.2",
358
+ "psalm/phar": "^4.8"
359
+ },
360
+ "type": "library",
361
+ "extra": {
362
+ "branch-alias": {
363
+ "dev-master": "5.x-dev"
364
+ }
365
+ },
366
+ "autoload": {
367
+ "psr-4": {
368
+ "phpDocumentor\\Reflection\\": "src"
369
+ }
370
+ },
371
+ "notification-url": "https://packagist.org/downloads/",
372
+ "license": [
373
+ "MIT"
374
+ ],
375
+ "authors": [
376
+ {
377
+ "name": "Mike van Riel",
378
+ "email": "me@mikevanriel.com"
379
+ },
380
+ {
381
+ "name": "Jaap van Otterdijk",
382
+ "email": "account@ijaap.nl"
383
+ }
384
+ ],
385
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
386
+ "time": "2021-10-19T17:43:47+00:00"
387
+ },
388
+ {
389
+ "name": "phpdocumentor/type-resolver",
390
+ "version": "1.6.0",
391
+ "source": {
392
+ "type": "git",
393
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
394
+ "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706"
395
+ },
396
+ "dist": {
397
+ "type": "zip",
398
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706",
399
+ "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706",
400
+ "shasum": ""
401
+ },
402
+ "require": {
403
+ "php": "^7.2 || ^8.0",
404
+ "phpdocumentor/reflection-common": "^2.0"
405
+ },
406
+ "require-dev": {
407
+ "ext-tokenizer": "*",
408
+ "psalm/phar": "^4.8"
409
+ },
410
+ "type": "library",
411
+ "extra": {
412
+ "branch-alias": {
413
+ "dev-1.x": "1.x-dev"
414
+ }
415
+ },
416
+ "autoload": {
417
+ "psr-4": {
418
+ "phpDocumentor\\Reflection\\": "src"
419
+ }
420
+ },
421
+ "notification-url": "https://packagist.org/downloads/",
422
+ "license": [
423
+ "MIT"
424
+ ],
425
+ "authors": [
426
+ {
427
+ "name": "Mike van Riel",
428
+ "email": "me@mikevanriel.com"
429
+ }
430
+ ],
431
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
432
+ "time": "2022-01-04T19:58:01+00:00"
433
+ },
434
+ {
435
+ "name": "phpspec/prophecy",
436
+ "version": "v1.15.0",
437
+ "source": {
438
+ "type": "git",
439
+ "url": "https://github.com/phpspec/prophecy.git",
440
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
441
+ },
442
+ "dist": {
443
+ "type": "zip",
444
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
445
+ "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
446
+ "shasum": ""
447
+ },
448
+ "require": {
449
+ "doctrine/instantiator": "^1.2",
450
+ "php": "^7.2 || ~8.0, <8.2",
451
+ "phpdocumentor/reflection-docblock": "^5.2",
452
+ "sebastian/comparator": "^3.0 || ^4.0",
453
+ "sebastian/recursion-context": "^3.0 || ^4.0"
454
+ },
455
+ "require-dev": {
456
+ "phpspec/phpspec": "^6.0 || ^7.0",
457
+ "phpunit/phpunit": "^8.0 || ^9.0"
458
+ },
459
+ "type": "library",
460
+ "extra": {
461
+ "branch-alias": {
462
+ "dev-master": "1.x-dev"
463
+ }
464
+ },
465
+ "autoload": {
466
+ "psr-4": {
467
+ "Prophecy\\": "src/Prophecy"
468
+ }
469
+ },
470
+ "notification-url": "https://packagist.org/downloads/",
471
+ "license": [
472
+ "MIT"
473
+ ],
474
+ "authors": [
475
+ {
476
+ "name": "Konstantin Kudryashov",
477
+ "email": "ever.zet@gmail.com",
478
+ "homepage": "http://everzet.com"
479
+ },
480
+ {
481
+ "name": "Marcello Duarte",
482
+ "email": "marcello.duarte@gmail.com"
483
+ }
484
+ ],
485
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
486
+ "homepage": "https://github.com/phpspec/prophecy",
487
+ "keywords": [
488
+ "Double",
489
+ "Dummy",
490
+ "fake",
491
+ "mock",
492
+ "spy",
493
+ "stub"
494
+ ],
495
+ "time": "2021-12-08T12:19:24+00:00"
496
+ },
497
+ {
498
+ "name": "phpunit/php-code-coverage",
499
+ "version": "9.2.15",
500
+ "source": {
501
+ "type": "git",
502
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
503
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
504
+ },
505
+ "dist": {
506
+ "type": "zip",
507
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
508
+ "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
509
+ "shasum": ""
510
+ },
511
+ "require": {
512
+ "ext-dom": "*",
513
+ "ext-libxml": "*",
514
+ "ext-xmlwriter": "*",
515
+ "nikic/php-parser": "^4.13.0",
516
+ "php": ">=7.3",
517
+ "phpunit/php-file-iterator": "^3.0.3",
518
+ "phpunit/php-text-template": "^2.0.2",
519
+ "sebastian/code-unit-reverse-lookup": "^2.0.2",
520
+ "sebastian/complexity": "^2.0",
521
+ "sebastian/environment": "^5.1.2",
522
+ "sebastian/lines-of-code": "^1.0.3",
523
+ "sebastian/version": "^3.0.1",
524
+ "theseer/tokenizer": "^1.2.0"
525
+ },
526
+ "require-dev": {
527
+ "phpunit/phpunit": "^9.3"
528
+ },
529
+ "suggest": {
530
+ "ext-pcov": "*",
531
+ "ext-xdebug": "*"
532
+ },
533
+ "type": "library",
534
+ "extra": {
535
+ "branch-alias": {
536
+ "dev-master": "9.2-dev"
537
+ }
538
+ },
539
+ "autoload": {
540
+ "classmap": [
541
+ "src/"
542
+ ]
543
+ },
544
+ "notification-url": "https://packagist.org/downloads/",
545
+ "license": [
546
+ "BSD-3-Clause"
547
+ ],
548
+ "authors": [
549
+ {
550
+ "name": "Sebastian Bergmann",
551
+ "email": "sebastian@phpunit.de",
552
+ "role": "lead"
553
+ }
554
+ ],
555
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
556
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
557
+ "keywords": [
558
+ "coverage",
559
+ "testing",
560
+ "xunit"
561
+ ],
562
+ "funding": [
563
+ {
564
+ "url": "https://github.com/sebastianbergmann",
565
+ "type": "github"
566
+ }
567
+ ],
568
+ "time": "2022-03-07T09:28:20+00:00"
569
+ },
570
+ {
571
+ "name": "phpunit/php-file-iterator",
572
+ "version": "3.0.6",
573
+ "source": {
574
+ "type": "git",
575
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
576
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
577
+ },
578
+ "dist": {
579
+ "type": "zip",
580
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
581
+ "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
582
+ "shasum": ""
583
+ },
584
+ "require": {
585
+ "php": ">=7.3"
586
+ },
587
+ "require-dev": {
588
+ "phpunit/phpunit": "^9.3"
589
+ },
590
+ "type": "library",
591
+ "extra": {
592
+ "branch-alias": {
593
+ "dev-master": "3.0-dev"
594
+ }
595
+ },
596
+ "autoload": {
597
+ "classmap": [
598
+ "src/"
599
+ ]
600
+ },
601
+ "notification-url": "https://packagist.org/downloads/",
602
+ "license": [
603
+ "BSD-3-Clause"
604
+ ],
605
+ "authors": [
606
+ {
607
+ "name": "Sebastian Bergmann",
608
+ "email": "sebastian@phpunit.de",
609
+ "role": "lead"
610
+ }
611
+ ],
612
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
613
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
614
+ "keywords": [
615
+ "filesystem",
616
+ "iterator"
617
+ ],
618
+ "funding": [
619
+ {
620
+ "url": "https://github.com/sebastianbergmann",
621
+ "type": "github"
622
+ }
623
+ ],
624
+ "time": "2021-12-02T12:48:52+00:00"
625
+ },
626
+ {
627
+ "name": "phpunit/php-invoker",
628
+ "version": "3.1.1",
629
+ "source": {
630
+ "type": "git",
631
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
632
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
633
+ },
634
+ "dist": {
635
+ "type": "zip",
636
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
637
+ "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
638
+ "shasum": ""
639
+ },
640
+ "require": {
641
+ "php": ">=7.3"
642
+ },
643
+ "require-dev": {
644
+ "ext-pcntl": "*",
645
+ "phpunit/phpunit": "^9.3"
646
+ },
647
+ "suggest": {
648
+ "ext-pcntl": "*"
649
+ },
650
+ "type": "library",
651
+ "extra": {
652
+ "branch-alias": {
653
+ "dev-master": "3.1-dev"
654
+ }
655
+ },
656
+ "autoload": {
657
+ "classmap": [
658
+ "src/"
659
+ ]
660
+ },
661
+ "notification-url": "https://packagist.org/downloads/",
662
+ "license": [
663
+ "BSD-3-Clause"
664
+ ],
665
+ "authors": [
666
+ {
667
+ "name": "Sebastian Bergmann",
668
+ "email": "sebastian@phpunit.de",
669
+ "role": "lead"
670
+ }
671
+ ],
672
+ "description": "Invoke callables with a timeout",
673
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
674
+ "keywords": [
675
+ "process"
676
+ ],
677
+ "funding": [
678
+ {
679
+ "url": "https://github.com/sebastianbergmann",
680
+ "type": "github"
681
+ }
682
+ ],
683
+ "time": "2020-09-28T05:58:55+00:00"
684
+ },
685
+ {
686
+ "name": "phpunit/php-text-template",
687
+ "version": "2.0.4",
688
+ "source": {
689
+ "type": "git",
690
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
691
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
692
+ },
693
+ "dist": {
694
+ "type": "zip",
695
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
696
+ "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
697
+ "shasum": ""
698
+ },
699
+ "require": {
700
+ "php": ">=7.3"
701
+ },
702
+ "require-dev": {
703
+ "phpunit/phpunit": "^9.3"
704
+ },
705
+ "type": "library",
706
+ "extra": {
707
+ "branch-alias": {
708
+ "dev-master": "2.0-dev"
709
+ }
710
+ },
711
+ "autoload": {
712
+ "classmap": [
713
+ "src/"
714
+ ]
715
+ },
716
+ "notification-url": "https://packagist.org/downloads/",
717
+ "license": [
718
+ "BSD-3-Clause"
719
+ ],
720
+ "authors": [
721
+ {
722
+ "name": "Sebastian Bergmann",
723
+ "email": "sebastian@phpunit.de",
724
+ "role": "lead"
725
+ }
726
+ ],
727
+ "description": "Simple template engine.",
728
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
729
+ "keywords": [
730
+ "template"
731
+ ],
732
+ "funding": [
733
+ {
734
+ "url": "https://github.com/sebastianbergmann",
735
+ "type": "github"
736
+ }
737
+ ],
738
+ "time": "2020-10-26T05:33:50+00:00"
739
+ },
740
+ {
741
+ "name": "phpunit/php-timer",
742
+ "version": "5.0.3",
743
+ "source": {
744
+ "type": "git",
745
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
746
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
747
+ },
748
+ "dist": {
749
+ "type": "zip",
750
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
751
+ "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
752
+ "shasum": ""
753
+ },
754
+ "require": {
755
+ "php": ">=7.3"
756
+ },
757
+ "require-dev": {
758
+ "phpunit/phpunit": "^9.3"
759
+ },
760
+ "type": "library",
761
+ "extra": {
762
+ "branch-alias": {
763
+ "dev-master": "5.0-dev"
764
+ }
765
+ },
766
+ "autoload": {
767
+ "classmap": [
768
+ "src/"
769
+ ]
770
+ },
771
+ "notification-url": "https://packagist.org/downloads/",
772
+ "license": [
773
+ "BSD-3-Clause"
774
+ ],
775
+ "authors": [
776
+ {
777
+ "name": "Sebastian Bergmann",
778
+ "email": "sebastian@phpunit.de",
779
+ "role": "lead"
780
+ }
781
+ ],
782
+ "description": "Utility class for timing",
783
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
784
+ "keywords": [
785
+ "timer"
786
+ ],
787
+ "funding": [
788
+ {
789
+ "url": "https://github.com/sebastianbergmann",
790
+ "type": "github"
791
+ }
792
+ ],
793
+ "time": "2020-10-26T13:16:10+00:00"
794
+ },
795
+ {
796
+ "name": "phpunit/phpunit",
797
+ "version": "9.5.18",
798
+ "source": {
799
+ "type": "git",
800
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
801
+ "reference": "1b5856028273bfd855e60a887278857d872ec67a"
802
+ },
803
+ "dist": {
804
+ "type": "zip",
805
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b5856028273bfd855e60a887278857d872ec67a",
806
+ "reference": "1b5856028273bfd855e60a887278857d872ec67a",
807
+ "shasum": ""
808
+ },
809
+ "require": {
810
+ "doctrine/instantiator": "^1.3.1",
811
+ "ext-dom": "*",
812
+ "ext-json": "*",
813
+ "ext-libxml": "*",
814
+ "ext-mbstring": "*",
815
+ "ext-xml": "*",
816
+ "ext-xmlwriter": "*",
817
+ "myclabs/deep-copy": "^1.10.1",
818
+ "phar-io/manifest": "^2.0.3",
819
+ "phar-io/version": "^3.0.2",
820
+ "php": ">=7.3",
821
+ "phpspec/prophecy": "^1.12.1",
822
+ "phpunit/php-code-coverage": "^9.2.13",
823
+ "phpunit/php-file-iterator": "^3.0.5",
824
+ "phpunit/php-invoker": "^3.1.1",
825
+ "phpunit/php-text-template": "^2.0.3",
826
+ "phpunit/php-timer": "^5.0.2",
827
+ "sebastian/cli-parser": "^1.0.1",
828
+ "sebastian/code-unit": "^1.0.6",
829
+ "sebastian/comparator": "^4.0.5",
830
+ "sebastian/diff": "^4.0.3",
831
+ "sebastian/environment": "^5.1.3",
832
+ "sebastian/exporter": "^4.0.3",
833
+ "sebastian/global-state": "^5.0.1",
834
+ "sebastian/object-enumerator": "^4.0.3",
835
+ "sebastian/resource-operations": "^3.0.3",
836
+ "sebastian/type": "^2.3.4",
837
+ "sebastian/version": "^3.0.2"
838
+ },
839
+ "require-dev": {
840
+ "ext-pdo": "*",
841
+ "phpspec/prophecy-phpunit": "^2.0.1"
842
+ },
843
+ "suggest": {
844
+ "ext-soap": "*",
845
+ "ext-xdebug": "*"
846
+ },
847
+ "bin": [
848
+ "phpunit"
849
+ ],
850
+ "type": "library",
851
+ "extra": {
852
+ "branch-alias": {
853
+ "dev-master": "9.5-dev"
854
+ }
855
+ },
856
+ "autoload": {
857
+ "files": [
858
+ "src/Framework/Assert/Functions.php"
859
+ ],
860
+ "classmap": [
861
+ "src/"
862
+ ]
863
+ },
864
+ "notification-url": "https://packagist.org/downloads/",
865
+ "license": [
866
+ "BSD-3-Clause"
867
+ ],
868
+ "authors": [
869
+ {
870
+ "name": "Sebastian Bergmann",
871
+ "email": "sebastian@phpunit.de",
872
+ "role": "lead"
873
+ }
874
+ ],
875
+ "description": "The PHP Unit Testing framework.",
876
+ "homepage": "https://phpunit.de/",
877
+ "keywords": [
878
+ "phpunit",
879
+ "testing",
880
+ "xunit"
881
+ ],
882
+ "funding": [
883
+ {
884
+ "url": "https://phpunit.de/sponsors.html",
885
+ "type": "custom"
886
+ },
887
+ {
888
+ "url": "https://github.com/sebastianbergmann",
889
+ "type": "github"
890
+ }
891
+ ],
892
+ "time": "2022-03-08T06:52:28+00:00"
893
+ },
894
+ {
895
+ "name": "sebastian/cli-parser",
896
+ "version": "1.0.1",
897
+ "source": {
898
+ "type": "git",
899
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
900
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
901
+ },
902
+ "dist": {
903
+ "type": "zip",
904
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
905
+ "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
906
+ "shasum": ""
907
+ },
908
+ "require": {
909
+ "php": ">=7.3"
910
+ },
911
+ "require-dev": {
912
+ "phpunit/phpunit": "^9.3"
913
+ },
914
+ "type": "library",
915
+ "extra": {
916
+ "branch-alias": {
917
+ "dev-master": "1.0-dev"
918
+ }
919
+ },
920
+ "autoload": {
921
+ "classmap": [
922
+ "src/"
923
+ ]
924
+ },
925
+ "notification-url": "https://packagist.org/downloads/",
926
+ "license": [
927
+ "BSD-3-Clause"
928
+ ],
929
+ "authors": [
930
+ {
931
+ "name": "Sebastian Bergmann",
932
+ "email": "sebastian@phpunit.de",
933
+ "role": "lead"
934
+ }
935
+ ],
936
+ "description": "Library for parsing CLI options",
937
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
938
+ "funding": [
939
+ {
940
+ "url": "https://github.com/sebastianbergmann",
941
+ "type": "github"
942
+ }
943
+ ],
944
+ "time": "2020-09-28T06:08:49+00:00"
945
+ },
946
+ {
947
+ "name": "sebastian/code-unit",
948
+ "version": "1.0.8",
949
+ "source": {
950
+ "type": "git",
951
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
952
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
953
+ },
954
+ "dist": {
955
+ "type": "zip",
956
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
957
+ "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
958
+ "shasum": ""
959
+ },
960
+ "require": {
961
+ "php": ">=7.3"
962
+ },
963
+ "require-dev": {
964
+ "phpunit/phpunit": "^9.3"
965
+ },
966
+ "type": "library",
967
+ "extra": {
968
+ "branch-alias": {
969
+ "dev-master": "1.0-dev"
970
+ }
971
+ },
972
+ "autoload": {
973
+ "classmap": [
974
+ "src/"
975
+ ]
976
+ },
977
+ "notification-url": "https://packagist.org/downloads/",
978
+ "license": [
979
+ "BSD-3-Clause"
980
+ ],
981
+ "authors": [
982
+ {
983
+ "name": "Sebastian Bergmann",
984
+ "email": "sebastian@phpunit.de",
985
+ "role": "lead"
986
+ }
987
+ ],
988
+ "description": "Collection of value objects that represent the PHP code units",
989
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
990
+ "funding": [
991
+ {
992
+ "url": "https://github.com/sebastianbergmann",
993
+ "type": "github"
994
+ }
995
+ ],
996
+ "time": "2020-10-26T13:08:54+00:00"
997
+ },
998
+ {
999
+ "name": "sebastian/code-unit-reverse-lookup",
1000
+ "version": "2.0.3",
1001
+ "source": {
1002
+ "type": "git",
1003
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
1004
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
1005
+ },
1006
+ "dist": {
1007
+ "type": "zip",
1008
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1009
+ "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1010
+ "shasum": ""
1011
+ },
1012
+ "require": {
1013
+ "php": ">=7.3"
1014
+ },
1015
+ "require-dev": {
1016
+ "phpunit/phpunit": "^9.3"
1017
+ },
1018
+ "type": "library",
1019
+ "extra": {
1020
+ "branch-alias": {
1021
+ "dev-master": "2.0-dev"
1022
+ }
1023
+ },
1024
+ "autoload": {
1025
+ "classmap": [
1026
+ "src/"
1027
+ ]
1028
+ },
1029
+ "notification-url": "https://packagist.org/downloads/",
1030
+ "license": [
1031
+ "BSD-3-Clause"
1032
+ ],
1033
+ "authors": [
1034
+ {
1035
+ "name": "Sebastian Bergmann",
1036
+ "email": "sebastian@phpunit.de"
1037
+ }
1038
+ ],
1039
+ "description": "Looks up which function or method a line of code belongs to",
1040
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
1041
+ "funding": [
1042
+ {
1043
+ "url": "https://github.com/sebastianbergmann",
1044
+ "type": "github"
1045
+ }
1046
+ ],
1047
+ "time": "2020-09-28T05:30:19+00:00"
1048
+ },
1049
+ {
1050
+ "name": "sebastian/comparator",
1051
+ "version": "4.0.6",
1052
+ "source": {
1053
+ "type": "git",
1054
+ "url": "https://github.com/sebastianbergmann/comparator.git",
1055
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382"
1056
+ },
1057
+ "dist": {
1058
+ "type": "zip",
1059
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382",
1060
+ "reference": "55f4261989e546dc112258c7a75935a81a7ce382",
1061
+ "shasum": ""
1062
+ },
1063
+ "require": {
1064
+ "php": ">=7.3",
1065
+ "sebastian/diff": "^4.0",
1066
+ "sebastian/exporter": "^4.0"
1067
+ },
1068
+ "require-dev": {
1069
+ "phpunit/phpunit": "^9.3"
1070
+ },
1071
+ "type": "library",
1072
+ "extra": {
1073
+ "branch-alias": {
1074
+ "dev-master": "4.0-dev"
1075
+ }
1076
+ },
1077
+ "autoload": {
1078
+ "classmap": [
1079
+ "src/"
1080
+ ]
1081
+ },
1082
+ "notification-url": "https://packagist.org/downloads/",
1083
+ "license": [
1084
+ "BSD-3-Clause"
1085
+ ],
1086
+ "authors": [
1087
+ {
1088
+ "name": "Sebastian Bergmann",
1089
+ "email": "sebastian@phpunit.de"
1090
+ },
1091
+ {
1092
+ "name": "Jeff Welch",
1093
+ "email": "whatthejeff@gmail.com"
1094
+ },
1095
+ {
1096
+ "name": "Volker Dusch",
1097
+ "email": "github@wallbash.com"
1098
+ },
1099
+ {
1100
+ "name": "Bernhard Schussek",
1101
+ "email": "bschussek@2bepublished.at"
1102
+ }
1103
+ ],
1104
+ "description": "Provides the functionality to compare PHP values for equality",
1105
+ "homepage": "https://github.com/sebastianbergmann/comparator",
1106
+ "keywords": [
1107
+ "comparator",
1108
+ "compare",
1109
+ "equality"
1110
+ ],
1111
+ "funding": [
1112
+ {
1113
+ "url": "https://github.com/sebastianbergmann",
1114
+ "type": "github"
1115
+ }
1116
+ ],
1117
+ "time": "2020-10-26T15:49:45+00:00"
1118
+ },
1119
+ {
1120
+ "name": "sebastian/complexity",
1121
+ "version": "2.0.2",
1122
+ "source": {
1123
+ "type": "git",
1124
+ "url": "https://github.com/sebastianbergmann/complexity.git",
1125
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
1126
+ },
1127
+ "dist": {
1128
+ "type": "zip",
1129
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
1130
+ "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
1131
+ "shasum": ""
1132
+ },
1133
+ "require": {
1134
+ "nikic/php-parser": "^4.7",
1135
+ "php": ">=7.3"
1136
+ },
1137
+ "require-dev": {
1138
+ "phpunit/phpunit": "^9.3"
1139
+ },
1140
+ "type": "library",
1141
+ "extra": {
1142
+ "branch-alias": {
1143
+ "dev-master": "2.0-dev"
1144
+ }
1145
+ },
1146
+ "autoload": {
1147
+ "classmap": [
1148
+ "src/"
1149
+ ]
1150
+ },
1151
+ "notification-url": "https://packagist.org/downloads/",
1152
+ "license": [
1153
+ "BSD-3-Clause"
1154
+ ],
1155
+ "authors": [
1156
+ {
1157
+ "name": "Sebastian Bergmann",
1158
+ "email": "sebastian@phpunit.de",
1159
+ "role": "lead"
1160
+ }
1161
+ ],
1162
+ "description": "Library for calculating the complexity of PHP code units",
1163
+ "homepage": "https://github.com/sebastianbergmann/complexity",
1164
+ "funding": [
1165
+ {
1166
+ "url": "https://github.com/sebastianbergmann",
1167
+ "type": "github"
1168
+ }
1169
+ ],
1170
+ "time": "2020-10-26T15:52:27+00:00"
1171
+ },
1172
+ {
1173
+ "name": "sebastian/diff",
1174
+ "version": "4.0.4",
1175
+ "source": {
1176
+ "type": "git",
1177
+ "url": "https://github.com/sebastianbergmann/diff.git",
1178
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
1179
+ },
1180
+ "dist": {
1181
+ "type": "zip",
1182
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
1183
+ "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
1184
+ "shasum": ""
1185
+ },
1186
+ "require": {
1187
+ "php": ">=7.3"
1188
+ },
1189
+ "require-dev": {
1190
+ "phpunit/phpunit": "^9.3",
1191
+ "symfony/process": "^4.2 || ^5"
1192
+ },
1193
+ "type": "library",
1194
+ "extra": {
1195
+ "branch-alias": {
1196
+ "dev-master": "4.0-dev"
1197
+ }
1198
+ },
1199
+ "autoload": {
1200
+ "classmap": [
1201
+ "src/"
1202
+ ]
1203
+ },
1204
+ "notification-url": "https://packagist.org/downloads/",
1205
+ "license": [
1206
+ "BSD-3-Clause"
1207
+ ],
1208
+ "authors": [
1209
+ {
1210
+ "name": "Sebastian Bergmann",
1211
+ "email": "sebastian@phpunit.de"
1212
+ },
1213
+ {
1214
+ "name": "Kore Nordmann",
1215
+ "email": "mail@kore-nordmann.de"
1216
+ }
1217
+ ],
1218
+ "description": "Diff implementation",
1219
+ "homepage": "https://github.com/sebastianbergmann/diff",
1220
+ "keywords": [
1221
+ "diff",
1222
+ "udiff",
1223
+ "unidiff",
1224
+ "unified diff"
1225
+ ],
1226
+ "funding": [
1227
+ {
1228
+ "url": "https://github.com/sebastianbergmann",
1229
+ "type": "github"
1230
+ }
1231
+ ],
1232
+ "time": "2020-10-26T13:10:38+00:00"
1233
+ },
1234
+ {
1235
+ "name": "sebastian/environment",
1236
+ "version": "5.1.3",
1237
+ "source": {
1238
+ "type": "git",
1239
+ "url": "https://github.com/sebastianbergmann/environment.git",
1240
+ "reference": "388b6ced16caa751030f6a69e588299fa09200ac"
1241
+ },
1242
+ "dist": {
1243
+ "type": "zip",
1244
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
1245
+ "reference": "388b6ced16caa751030f6a69e588299fa09200ac",
1246
+ "shasum": ""
1247
+ },
1248
+ "require": {
1249
+ "php": ">=7.3"
1250
+ },
1251
+ "require-dev": {
1252
+ "phpunit/phpunit": "^9.3"
1253
+ },
1254
+ "suggest": {
1255
+ "ext-posix": "*"
1256
+ },
1257
+ "type": "library",
1258
+ "extra": {
1259
+ "branch-alias": {
1260
+ "dev-master": "5.1-dev"
1261
+ }
1262
+ },
1263
+ "autoload": {
1264
+ "classmap": [
1265
+ "src/"
1266
+ ]
1267
+ },
1268
+ "notification-url": "https://packagist.org/downloads/",
1269
+ "license": [
1270
+ "BSD-3-Clause"
1271
+ ],
1272
+ "authors": [
1273
+ {
1274
+ "name": "Sebastian Bergmann",
1275
+ "email": "sebastian@phpunit.de"
1276
+ }
1277
+ ],
1278
+ "description": "Provides functionality to handle HHVM/PHP environments",
1279
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
1280
+ "keywords": [
1281
+ "Xdebug",
1282
+ "environment",
1283
+ "hhvm"
1284
+ ],
1285
+ "funding": [
1286
+ {
1287
+ "url": "https://github.com/sebastianbergmann",
1288
+ "type": "github"
1289
+ }
1290
+ ],
1291
+ "time": "2020-09-28T05:52:38+00:00"
1292
+ },
1293
+ {
1294
+ "name": "sebastian/exporter",
1295
+ "version": "4.0.4",
1296
+ "source": {
1297
+ "type": "git",
1298
+ "url": "https://github.com/sebastianbergmann/exporter.git",
1299
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9"
1300
+ },
1301
+ "dist": {
1302
+ "type": "zip",
1303
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9",
1304
+ "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9",
1305
+ "shasum": ""
1306
+ },
1307
+ "require": {
1308
+ "php": ">=7.3",
1309
+ "sebastian/recursion-context": "^4.0"
1310
+ },
1311
+ "require-dev": {
1312
+ "ext-mbstring": "*",
1313
+ "phpunit/phpunit": "^9.3"
1314
+ },
1315
+ "type": "library",
1316
+ "extra": {
1317
+ "branch-alias": {
1318
+ "dev-master": "4.0-dev"
1319
+ }
1320
+ },
1321
+ "autoload": {
1322
+ "classmap": [
1323
+ "src/"
1324
+ ]
1325
+ },
1326
+ "notification-url": "https://packagist.org/downloads/",
1327
+ "license": [
1328
+ "BSD-3-Clause"
1329
+ ],
1330
+ "authors": [
1331
+ {
1332
+ "name": "Sebastian Bergmann",
1333
+ "email": "sebastian@phpunit.de"
1334
+ },
1335
+ {
1336
+ "name": "Jeff Welch",
1337
+ "email": "whatthejeff@gmail.com"
1338
+ },
1339
+ {
1340
+ "name": "Volker Dusch",
1341
+ "email": "github@wallbash.com"
1342
+ },
1343
+ {
1344
+ "name": "Adam Harvey",
1345
+ "email": "aharvey@php.net"
1346
+ },
1347
+ {
1348
+ "name": "Bernhard Schussek",
1349
+ "email": "bschussek@gmail.com"
1350
+ }
1351
+ ],
1352
+ "description": "Provides the functionality to export PHP variables for visualization",
1353
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
1354
+ "keywords": [
1355
+ "export",
1356
+ "exporter"
1357
+ ],
1358
+ "funding": [
1359
+ {
1360
+ "url": "https://github.com/sebastianbergmann",
1361
+ "type": "github"
1362
+ }
1363
+ ],
1364
+ "time": "2021-11-11T14:18:36+00:00"
1365
+ },
1366
+ {
1367
+ "name": "sebastian/global-state",
1368
+ "version": "5.0.5",
1369
+ "source": {
1370
+ "type": "git",
1371
+ "url": "https://github.com/sebastianbergmann/global-state.git",
1372
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
1373
+ },
1374
+ "dist": {
1375
+ "type": "zip",
1376
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
1377
+ "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
1378
+ "shasum": ""
1379
+ },
1380
+ "require": {
1381
+ "php": ">=7.3",
1382
+ "sebastian/object-reflector": "^2.0",
1383
+ "sebastian/recursion-context": "^4.0"
1384
+ },
1385
+ "require-dev": {
1386
+ "ext-dom": "*",
1387
+ "phpunit/phpunit": "^9.3"
1388
+ },
1389
+ "suggest": {
1390
+ "ext-uopz": "*"
1391
+ },
1392
+ "type": "library",
1393
+ "extra": {
1394
+ "branch-alias": {
1395
+ "dev-master": "5.0-dev"
1396
+ }
1397
+ },
1398
+ "autoload": {
1399
+ "classmap": [
1400
+ "src/"
1401
+ ]
1402
+ },
1403
+ "notification-url": "https://packagist.org/downloads/",
1404
+ "license": [
1405
+ "BSD-3-Clause"
1406
+ ],
1407
+ "authors": [
1408
+ {
1409
+ "name": "Sebastian Bergmann",
1410
+ "email": "sebastian@phpunit.de"
1411
+ }
1412
+ ],
1413
+ "description": "Snapshotting of global state",
1414
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
1415
+ "keywords": [
1416
+ "global state"
1417
+ ],
1418
+ "funding": [
1419
+ {
1420
+ "url": "https://github.com/sebastianbergmann",
1421
+ "type": "github"
1422
+ }
1423
+ ],
1424
+ "time": "2022-02-14T08:28:10+00:00"
1425
+ },
1426
+ {
1427
+ "name": "sebastian/lines-of-code",
1428
+ "version": "1.0.3",
1429
+ "source": {
1430
+ "type": "git",
1431
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
1432
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
1433
+ },
1434
+ "dist": {
1435
+ "type": "zip",
1436
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
1437
+ "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
1438
+ "shasum": ""
1439
+ },
1440
+ "require": {
1441
+ "nikic/php-parser": "^4.6",
1442
+ "php": ">=7.3"
1443
+ },
1444
+ "require-dev": {
1445
+ "phpunit/phpunit": "^9.3"
1446
+ },
1447
+ "type": "library",
1448
+ "extra": {
1449
+ "branch-alias": {
1450
+ "dev-master": "1.0-dev"
1451
+ }
1452
+ },
1453
+ "autoload": {
1454
+ "classmap": [
1455
+ "src/"
1456
+ ]
1457
+ },
1458
+ "notification-url": "https://packagist.org/downloads/",
1459
+ "license": [
1460
+ "BSD-3-Clause"
1461
+ ],
1462
+ "authors": [
1463
+ {
1464
+ "name": "Sebastian Bergmann",
1465
+ "email": "sebastian@phpunit.de",
1466
+ "role": "lead"
1467
+ }
1468
+ ],
1469
+ "description": "Library for counting the lines of code in PHP source code",
1470
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
1471
+ "funding": [
1472
+ {
1473
+ "url": "https://github.com/sebastianbergmann",
1474
+ "type": "github"
1475
+ }
1476
+ ],
1477
+ "time": "2020-11-28T06:42:11+00:00"
1478
+ },
1479
+ {
1480
+ "name": "sebastian/object-enumerator",
1481
+ "version": "4.0.4",
1482
+ "source": {
1483
+ "type": "git",
1484
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1485
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
1486
+ },
1487
+ "dist": {
1488
+ "type": "zip",
1489
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
1490
+ "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
1491
+ "shasum": ""
1492
+ },
1493
+ "require": {
1494
+ "php": ">=7.3",
1495
+ "sebastian/object-reflector": "^2.0",
1496
+ "sebastian/recursion-context": "^4.0"
1497
+ },
1498
+ "require-dev": {
1499
+ "phpunit/phpunit": "^9.3"
1500
+ },
1501
+ "type": "library",
1502
+ "extra": {
1503
+ "branch-alias": {
1504
+ "dev-master": "4.0-dev"
1505
+ }
1506
+ },
1507
+ "autoload": {
1508
+ "classmap": [
1509
+ "src/"
1510
+ ]
1511
+ },
1512
+ "notification-url": "https://packagist.org/downloads/",
1513
+ "license": [
1514
+ "BSD-3-Clause"
1515
+ ],
1516
+ "authors": [
1517
+ {
1518
+ "name": "Sebastian Bergmann",
1519
+ "email": "sebastian@phpunit.de"
1520
+ }
1521
+ ],
1522
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1523
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1524
+ "funding": [
1525
+ {
1526
+ "url": "https://github.com/sebastianbergmann",
1527
+ "type": "github"
1528
+ }
1529
+ ],
1530
+ "time": "2020-10-26T13:12:34+00:00"
1531
+ },
1532
+ {
1533
+ "name": "sebastian/object-reflector",
1534
+ "version": "2.0.4",
1535
+ "source": {
1536
+ "type": "git",
1537
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
1538
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
1539
+ },
1540
+ "dist": {
1541
+ "type": "zip",
1542
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
1543
+ "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
1544
+ "shasum": ""
1545
+ },
1546
+ "require": {
1547
+ "php": ">=7.3"
1548
+ },
1549
+ "require-dev": {
1550
+ "phpunit/phpunit": "^9.3"
1551
+ },
1552
+ "type": "library",
1553
+ "extra": {
1554
+ "branch-alias": {
1555
+ "dev-master": "2.0-dev"
1556
+ }
1557
+ },
1558
+ "autoload": {
1559
+ "classmap": [
1560
+ "src/"
1561
+ ]
1562
+ },
1563
+ "notification-url": "https://packagist.org/downloads/",
1564
+ "license": [
1565
+ "BSD-3-Clause"
1566
+ ],
1567
+ "authors": [
1568
+ {
1569
+ "name": "Sebastian Bergmann",
1570
+ "email": "sebastian@phpunit.de"
1571
+ }
1572
+ ],
1573
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
1574
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1575
+ "funding": [
1576
+ {
1577
+ "url": "https://github.com/sebastianbergmann",
1578
+ "type": "github"
1579
+ }
1580
+ ],
1581
+ "time": "2020-10-26T13:14:26+00:00"
1582
+ },
1583
+ {
1584
+ "name": "sebastian/recursion-context",
1585
+ "version": "4.0.4",
1586
+ "source": {
1587
+ "type": "git",
1588
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
1589
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172"
1590
+ },
1591
+ "dist": {
1592
+ "type": "zip",
1593
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172",
1594
+ "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172",
1595
+ "shasum": ""
1596
+ },
1597
+ "require": {
1598
+ "php": ">=7.3"
1599
+ },
1600
+ "require-dev": {
1601
+ "phpunit/phpunit": "^9.3"
1602
+ },
1603
+ "type": "library",
1604
+ "extra": {
1605
+ "branch-alias": {
1606
+ "dev-master": "4.0-dev"
1607
+ }
1608
+ },
1609
+ "autoload": {
1610
+ "classmap": [
1611
+ "src/"
1612
+ ]
1613
+ },
1614
+ "notification-url": "https://packagist.org/downloads/",
1615
+ "license": [
1616
+ "BSD-3-Clause"
1617
+ ],
1618
+ "authors": [
1619
+ {
1620
+ "name": "Sebastian Bergmann",
1621
+ "email": "sebastian@phpunit.de"
1622
+ },
1623
+ {
1624
+ "name": "Jeff Welch",
1625
+ "email": "whatthejeff@gmail.com"
1626
+ },
1627
+ {
1628
+ "name": "Adam Harvey",
1629
+ "email": "aharvey@php.net"
1630
+ }
1631
+ ],
1632
+ "description": "Provides functionality to recursively process PHP variables",
1633
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1634
+ "funding": [
1635
+ {
1636
+ "url": "https://github.com/sebastianbergmann",
1637
+ "type": "github"
1638
+ }
1639
+ ],
1640
+ "time": "2020-10-26T13:17:30+00:00"
1641
+ },
1642
+ {
1643
+ "name": "sebastian/resource-operations",
1644
+ "version": "3.0.3",
1645
+ "source": {
1646
+ "type": "git",
1647
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
1648
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
1649
+ },
1650
+ "dist": {
1651
+ "type": "zip",
1652
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
1653
+ "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
1654
+ "shasum": ""
1655
+ },
1656
+ "require": {
1657
+ "php": ">=7.3"
1658
+ },
1659
+ "require-dev": {
1660
+ "phpunit/phpunit": "^9.0"
1661
+ },
1662
+ "type": "library",
1663
+ "extra": {
1664
+ "branch-alias": {
1665
+ "dev-master": "3.0-dev"
1666
+ }
1667
+ },
1668
+ "autoload": {
1669
+ "classmap": [
1670
+ "src/"
1671
+ ]
1672
+ },
1673
+ "notification-url": "https://packagist.org/downloads/",
1674
+ "license": [
1675
+ "BSD-3-Clause"
1676
+ ],
1677
+ "authors": [
1678
+ {
1679
+ "name": "Sebastian Bergmann",
1680
+ "email": "sebastian@phpunit.de"
1681
+ }
1682
+ ],
1683
+ "description": "Provides a list of PHP built-in functions that operate on resources",
1684
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1685
+ "funding": [
1686
+ {
1687
+ "url": "https://github.com/sebastianbergmann",
1688
+ "type": "github"
1689
+ }
1690
+ ],
1691
+ "time": "2020-09-28T06:45:17+00:00"
1692
+ },
1693
+ {
1694
+ "name": "sebastian/type",
1695
+ "version": "2.3.4",
1696
+ "source": {
1697
+ "type": "git",
1698
+ "url": "https://github.com/sebastianbergmann/type.git",
1699
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
1700
+ },
1701
+ "dist": {
1702
+ "type": "zip",
1703
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
1704
+ "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
1705
+ "shasum": ""
1706
+ },
1707
+ "require": {
1708
+ "php": ">=7.3"
1709
+ },
1710
+ "require-dev": {
1711
+ "phpunit/phpunit": "^9.3"
1712
+ },
1713
+ "type": "library",
1714
+ "extra": {
1715
+ "branch-alias": {
1716
+ "dev-master": "2.3-dev"
1717
+ }
1718
+ },
1719
+ "autoload": {
1720
+ "classmap": [
1721
+ "src/"
1722
+ ]
1723
+ },
1724
+ "notification-url": "https://packagist.org/downloads/",
1725
+ "license": [
1726
+ "BSD-3-Clause"
1727
+ ],
1728
+ "authors": [
1729
+ {
1730
+ "name": "Sebastian Bergmann",
1731
+ "email": "sebastian@phpunit.de",
1732
+ "role": "lead"
1733
+ }
1734
+ ],
1735
+ "description": "Collection of value objects that represent the types of the PHP type system",
1736
+ "homepage": "https://github.com/sebastianbergmann/type",
1737
+ "funding": [
1738
+ {
1739
+ "url": "https://github.com/sebastianbergmann",
1740
+ "type": "github"
1741
+ }
1742
+ ],
1743
+ "time": "2021-06-15T12:49:02+00:00"
1744
+ },
1745
+ {
1746
+ "name": "sebastian/version",
1747
+ "version": "3.0.2",
1748
+ "source": {
1749
+ "type": "git",
1750
+ "url": "https://github.com/sebastianbergmann/version.git",
1751
+ "reference": "c6c1022351a901512170118436c764e473f6de8c"
1752
+ },
1753
+ "dist": {
1754
+ "type": "zip",
1755
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
1756
+ "reference": "c6c1022351a901512170118436c764e473f6de8c",
1757
+ "shasum": ""
1758
+ },
1759
+ "require": {
1760
+ "php": ">=7.3"
1761
+ },
1762
+ "type": "library",
1763
+ "extra": {
1764
+ "branch-alias": {
1765
+ "dev-master": "3.0-dev"
1766
+ }
1767
+ },
1768
+ "autoload": {
1769
+ "classmap": [
1770
+ "src/"
1771
+ ]
1772
+ },
1773
+ "notification-url": "https://packagist.org/downloads/",
1774
+ "license": [
1775
+ "BSD-3-Clause"
1776
+ ],
1777
+ "authors": [
1778
+ {
1779
+ "name": "Sebastian Bergmann",
1780
+ "email": "sebastian@phpunit.de",
1781
+ "role": "lead"
1782
+ }
1783
+ ],
1784
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1785
+ "homepage": "https://github.com/sebastianbergmann/version",
1786
+ "funding": [
1787
+ {
1788
+ "url": "https://github.com/sebastianbergmann",
1789
+ "type": "github"
1790
+ }
1791
+ ],
1792
+ "time": "2020-09-28T06:39:44+00:00"
1793
+ },
1794
+ {
1795
+ "name": "symfony/polyfill-ctype",
1796
+ "version": "v1.25.0",
1797
+ "source": {
1798
+ "type": "git",
1799
+ "url": "https://github.com/symfony/polyfill-ctype.git",
1800
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab"
1801
+ },
1802
+ "dist": {
1803
+ "type": "zip",
1804
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
1805
+ "reference": "30885182c981ab175d4d034db0f6f469898070ab",
1806
+ "shasum": ""
1807
+ },
1808
+ "require": {
1809
+ "php": ">=7.1"
1810
+ },
1811
+ "provide": {
1812
+ "ext-ctype": "*"
1813
+ },
1814
+ "suggest": {
1815
+ "ext-ctype": "For best performance"
1816
+ },
1817
+ "type": "library",
1818
+ "extra": {
1819
+ "branch-alias": {
1820
+ "dev-main": "1.23-dev"
1821
+ },
1822
+ "thanks": {
1823
+ "name": "symfony/polyfill",
1824
+ "url": "https://github.com/symfony/polyfill"
1825
+ }
1826
+ },
1827
+ "autoload": {
1828
+ "files": [
1829
+ "bootstrap.php"
1830
+ ],
1831
+ "psr-4": {
1832
+ "Symfony\\Polyfill\\Ctype\\": ""
1833
+ }
1834
+ },
1835
+ "notification-url": "https://packagist.org/downloads/",
1836
+ "license": [
1837
+ "MIT"
1838
+ ],
1839
+ "authors": [
1840
+ {
1841
+ "name": "Gert de Pagter",
1842
+ "email": "BackEndTea@gmail.com"
1843
+ },
1844
+ {
1845
+ "name": "Symfony Community",
1846
+ "homepage": "https://symfony.com/contributors"
1847
+ }
1848
+ ],
1849
+ "description": "Symfony polyfill for ctype functions",
1850
+ "homepage": "https://symfony.com",
1851
+ "keywords": [
1852
+ "compatibility",
1853
+ "ctype",
1854
+ "polyfill",
1855
+ "portable"
1856
+ ],
1857
+ "funding": [
1858
+ {
1859
+ "url": "https://symfony.com/sponsor",
1860
+ "type": "custom"
1861
+ },
1862
+ {
1863
+ "url": "https://github.com/fabpot",
1864
+ "type": "github"
1865
+ },
1866
+ {
1867
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
1868
+ "type": "tidelift"
1869
+ }
1870
+ ],
1871
+ "time": "2021-10-20T20:35:02+00:00"
1872
+ },
1873
+ {
1874
+ "name": "theseer/tokenizer",
1875
+ "version": "1.2.1",
1876
+ "source": {
1877
+ "type": "git",
1878
+ "url": "https://github.com/theseer/tokenizer.git",
1879
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
1880
+ },
1881
+ "dist": {
1882
+ "type": "zip",
1883
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
1884
+ "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
1885
+ "shasum": ""
1886
+ },
1887
+ "require": {
1888
+ "ext-dom": "*",
1889
+ "ext-tokenizer": "*",
1890
+ "ext-xmlwriter": "*",
1891
+ "php": "^7.2 || ^8.0"
1892
+ },
1893
+ "type": "library",
1894
+ "autoload": {
1895
+ "classmap": [
1896
+ "src/"
1897
+ ]
1898
+ },
1899
+ "notification-url": "https://packagist.org/downloads/",
1900
+ "license": [
1901
+ "BSD-3-Clause"
1902
+ ],
1903
+ "authors": [
1904
+ {
1905
+ "name": "Arne Blankerts",
1906
+ "email": "arne@blankerts.de",
1907
+ "role": "Developer"
1908
+ }
1909
+ ],
1910
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1911
+ "funding": [
1912
+ {
1913
+ "url": "https://github.com/theseer",
1914
+ "type": "github"
1915
+ }
1916
+ ],
1917
+ "time": "2021-07-28T10:34:58+00:00"
1918
+ },
1919
+ {
1920
+ "name": "webmozart/assert",
1921
+ "version": "1.10.0",
1922
+ "source": {
1923
+ "type": "git",
1924
+ "url": "https://github.com/webmozarts/assert.git",
1925
+ "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
1926
+ },
1927
+ "dist": {
1928
+ "type": "zip",
1929
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
1930
+ "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
1931
+ "shasum": ""
1932
+ },
1933
+ "require": {
1934
+ "php": "^7.2 || ^8.0",
1935
+ "symfony/polyfill-ctype": "^1.8"
1936
+ },
1937
+ "conflict": {
1938
+ "phpstan/phpstan": "<0.12.20",
1939
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
1940
+ },
1941
+ "require-dev": {
1942
+ "phpunit/phpunit": "^8.5.13"
1943
+ },
1944
+ "type": "library",
1945
+ "extra": {
1946
+ "branch-alias": {
1947
+ "dev-master": "1.10-dev"
1948
+ }
1949
+ },
1950
+ "autoload": {
1951
+ "psr-4": {
1952
+ "Webmozart\\Assert\\": "src/"
1953
+ }
1954
+ },
1955
+ "notification-url": "https://packagist.org/downloads/",
1956
+ "license": [
1957
+ "MIT"
1958
+ ],
1959
+ "authors": [
1960
+ {
1961
+ "name": "Bernhard Schussek",
1962
+ "email": "bschussek@gmail.com"
1963
+ }
1964
+ ],
1965
+ "description": "Assertions to validate method input/output with nice error messages.",
1966
+ "keywords": [
1967
+ "assert",
1968
+ "check",
1969
+ "validate"
1970
+ ],
1971
+ "time": "2021-03-09T10:59:23+00:00"
1972
+ },
1973
+ {
1974
+ "name": "yoast/phpunit-polyfills",
1975
+ "version": "1.0.3",
1976
+ "source": {
1977
+ "type": "git",
1978
+ "url": "https://github.com/Yoast/PHPUnit-Polyfills.git",
1979
+ "reference": "5ea3536428944955f969bc764bbe09738e151ada"
1980
+ },
1981
+ "dist": {
1982
+ "type": "zip",
1983
+ "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/5ea3536428944955f969bc764bbe09738e151ada",
1984
+ "reference": "5ea3536428944955f969bc764bbe09738e151ada",
1985
+ "shasum": ""
1986
+ },
1987
+ "require": {
1988
+ "php": ">=5.4",
1989
+ "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
1990
+ },
1991
+ "require-dev": {
1992
+ "yoast/yoastcs": "^2.2.0"
1993
+ },
1994
+ "type": "library",
1995
+ "extra": {
1996
+ "branch-alias": {
1997
+ "dev-main": "1.x-dev",
1998
+ "dev-develop": "1.x-dev"
1999
+ }
2000
+ },
2001
+ "autoload": {
2002
+ "files": [
2003
+ "phpunitpolyfills-autoload.php"
2004
+ ]
2005
+ },
2006
+ "notification-url": "https://packagist.org/downloads/",
2007
+ "license": [
2008
+ "BSD-3-Clause"
2009
+ ],
2010
+ "authors": [
2011
+ {
2012
+ "name": "Team Yoast",
2013
+ "email": "support@yoast.com",
2014
+ "homepage": "https://yoast.com"
2015
+ },
2016
+ {
2017
+ "name": "Contributors",
2018
+ "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors"
2019
+ }
2020
+ ],
2021
+ "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests",
2022
+ "homepage": "https://github.com/Yoast/PHPUnit-Polyfills",
2023
+ "keywords": [
2024
+ "phpunit",
2025
+ "polyfill",
2026
+ "testing"
2027
+ ],
2028
+ "time": "2021-11-23T01:37:03+00:00"
2029
+ }
2030
+ ],
2031
+ "aliases": [],
2032
+ "minimum-stability": "stable",
2033
+ "stability-flags": [],
2034
+ "prefer-stable": false,
2035
+ "prefer-lowest": false,
2036
+ "platform": [],
2037
+ "platform-dev": [],
2038
+ "plugin-api-version": "1.1.0"
2039
+ }
vendor/boldgrid/library/src/Library/Configs.php CHANGED
@@ -82,6 +82,14 @@ class Configs {
82
  }
83
  }
84
 
 
 
 
 
 
 
 
 
85
  return self::$configs = wp_parse_args( $configs, $defaults );
86
  }
87
 
82
  }
83
  }
84
 
85
+ /*
86
+ * Allow the default configs to be filtered via an option, bg_library_configs.
87
+ *
88
+ * Filtering via a actual filter (above) may be difficult due to timing / plugin load order.
89
+ */
90
+ $option_overrides = get_option( 'bglib_configs', array() );
91
+ $defaults = wp_parse_args( $option_overrides, $defaults );
92
+
93
  return self::$configs = wp_parse_args( $configs, $defaults );
94
  }
95
 
vendor/boldgrid/library/src/Library/Dashboard/SortWidgets.php CHANGED
@@ -144,6 +144,11 @@ class SortWidgets {
144
  /**
145
  * Sort the widgets as containted in the global $wp_meta_boxes.
146
  *
 
 
 
 
 
147
  * @since 2.9.0
148
  */
149
  public function sortGlobal() {
@@ -157,10 +162,44 @@ class SortWidgets {
157
  continue;
158
  }
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  // First, remove the widget from where it is now.
161
  unset( $wp_meta_boxes['dashboard'][$widget['container']][$widget['priority']][$id] );
162
 
163
- // Then, add it to the correct location.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ] =
165
  array( $id => $widget['widget'] ) +
166
  $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ];
144
  /**
145
  * Sort the widgets as containted in the global $wp_meta_boxes.
146
  *
147
+ * We're not actually "sorting". This method will look in our configs and find the BoldGrid widgets
148
+ * that we want to appear at the top of the dashboard, and put them there at the top. It does this
149
+ * by removing the widget from wherever it is and then adding it to the beginning of the array of
150
+ * widgets.
151
+ *
152
  * @since 2.9.0
153
  */
154
  public function sortGlobal() {
162
  continue;
163
  }
164
 
165
+ /*
166
+ * Avoid the following error: Fatal error: Uncaught Error: Unsupported operand types.
167
+ *
168
+ * This is a very edge case, and we cannot reproduce it, but we need to avoid the fatal.
169
+ * It is caused when we are trying move our metabox to the beginning using:
170
+ * $boxes = $our_metabox + $boxes.
171
+ *
172
+ * Validate $boxes in the above scenario.
173
+ */
174
+ if ( ! is_array( $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ] ) ) {
175
+ $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ] = array();
176
+ }
177
+
178
  // First, remove the widget from where it is now.
179
  unset( $wp_meta_boxes['dashboard'][$widget['container']][$widget['priority']][$id] );
180
 
181
+ /*
182
+ * Then, add it to the beginning of the array.
183
+ *
184
+ * We can't use array_unshift because we don't want the array keys to change.
185
+ *
186
+ * In the example below, we are setting "boldgrid-notifications" as the first metabox in
187
+ * $wp_meta_boxes['dashboard']['normal']['core']. This is how we are making it show atop
188
+ * other metaboxes.
189
+ *
190
+ * Before: Array(
191
+ * [dashboard_site_health] => Array,
192
+ * [dashboard_right_now] => Array,
193
+ * [dashboard_activity] => Array,
194
+ * )
195
+ *
196
+ * After: Array(
197
+ * [boldgrid-notifications] => Array,
198
+ * [dashboard_site_health] => Array,
199
+ * [dashboard_right_now] => Array,
200
+ * [dashboard_activity] => Array,
201
+ * )
202
+ */
203
  $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ] =
204
  array( $id => $widget['widget'] ) +
205
  $wp_meta_boxes['dashboard'][ $configs['container'] ][ $configs['priority'] ];
vendor/boldgrid/library/src/Library/RatingPrompt.php CHANGED
@@ -127,7 +127,9 @@ class RatingPrompt {
127
  foreach ( $slides as $slide ) {
128
  echo $slide;
129
  }
130
- wp_nonce_field( 'bglib-rating-prompt' );
 
 
131
  echo '</div>';
132
  }
133
  }
127
  foreach ( $slides as $slide ) {
128
  echo $slide;
129
  }
130
+
131
+ // This Nonce was causing conflicts with core nonces. Added a prefix to the nonce name.
132
+ wp_nonce_field( 'bglib-rating-prompt', 'bglib_rating_prompt_nonce' );
133
  echo '</div>';
134
  }
135
  }
vendor/boldgrid/library/src/Util/Load.php CHANGED
@@ -285,6 +285,14 @@ class Load {
285
  * @return bool
286
  */
287
  public function isValidPath( $path ) {
288
- return Version::getWpFilesystem()->exists( trailingslashit( $path ) . 'vendor/boldgrid/library' );
 
 
 
 
 
 
 
 
289
  }
290
  }
285
  * @return bool
286
  */
287
  public function isValidPath( $path ) {
288
+ $wp_filesystem = Version::getWpFilesystem();
289
+
290
+ // This is a band-aid. Avoid issues on the ftp filesystem.
291
+ $is_ftp = 'ftpext' === get_filesystem_method() && 'WP_Filesystem_FTPext' === get_class( $wp_filesystem );
292
+ if ( $is_ftp && ! empty( $wp_filesystem->errors->errors ) ) {
293
+ return false;
294
+ }
295
+
296
+ return $wp_filesystem->exists( trailingslashit( $path ) . 'vendor/boldgrid/library' );
297
  }
298
  }
vendor/boldgrid/library/src/assets/js/rating-prompt.js CHANGED
@@ -19,6 +19,9 @@ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
19
  /**
20
  * @summary Dismiss (or snooze) a rating prompt.
21
  *
 
 
 
22
  * @since 2.7.7
23
  *
24
  * @param string name
@@ -31,7 +34,7 @@ BOLDGRID.LIBRARY = BOLDGRID.LIBRARY || {};
31
  type: type,
32
  length: length,
33
  name: name,
34
- security: $( '.bglib-rating-prompt #_wpnonce' ).val()
35
  };
36
 
37
  $.ajax( {
19
  /**
20
  * @summary Dismiss (or snooze) a rating prompt.
21
  *
22
+ * As of 2.13.5, the nonce's ID has been changed from '_wpnonce' to
23
+ * bglib_rating_prompt_nonce. This is to prevent conflicts with core nonces.
24
+ *
25
  * @since 2.7.7
26
  *
27
  * @param string name
34
  type: type,
35
  length: length,
36
  name: name,
37
+ security: $( '.bglib-rating-prompt #bglib_rating_prompt_nonce' ).val()
38
  };
39
 
40
  $.ajax( {
vendor/boldgrid/library/src/library.global.php CHANGED
@@ -7,7 +7,7 @@ defined( 'WPFORMS_SHAREASALE_ID' ) || define( 'WPFORMS_SHAREASALE_ID', '1581233'
7
 
8
  return array(
9
  // libraryVersion is used to put the version number on js/css files.
10
- 'libraryVersion' => '2.13.3',
11
  'api' => 'https://api.boldgrid.com',
12
  'option' => 'license',
13
  'key' => get_site_option( 'boldgrid_api_key', null ),
7
 
8
  return array(
9
  // libraryVersion is used to put the version number on js/css files.
10
+ 'libraryVersion' => '2.13.6',
11
  'api' => 'https://api.boldgrid.com',
12
  'option' => 'license',
13
  'key' => get_site_option( 'boldgrid_api_key', null ),
vendor/boldgrid/library/tests/Library/Library/test-activity.php CHANGED
@@ -27,7 +27,7 @@ class Test_Activty extends WP_UnitTestCase {
27
  /**
28
  *
29
  */
30
- public function setup() {
31
  $this->reset();
32
 
33
  $this->activityClass = new \Boldgrid\Library\Library\Activity( 'boldgrid-backup' );
27
  /**
28
  *
29
  */
30
+ public function set_up() {
31
  $this->reset();
32
 
33
  $this->activityClass = new \Boldgrid\Library\Library\Activity( 'boldgrid-backup' );
vendor/boldgrid/library/tests/Library/Library/test-rating-prompt.php CHANGED
@@ -178,7 +178,7 @@ class Test_Rating_Prompt extends WP_UnitTestCase {
178
  /**
179
  * Setup.
180
  */
181
- public function setup() {
182
  $this->ratingPrompt = new \Boldgrid\Library\Library\RatingPrompt();
183
 
184
  $this->reset();
178
  /**
179
  * Setup.
180
  */
181
+ public function set_up() {
182
  $this->ratingPrompt = new \Boldgrid\Library\Library\RatingPrompt();
183
 
184
  $this->reset();
vendor/boldgrid/library/tests/Library/Plugin/test-factory.php CHANGED
@@ -30,7 +30,7 @@ class Test_BoldGrid_Library_Library_Plugin_Factory extends WP_UnitTestCase {
30
  *
31
  * @since 2.12.2
32
  */
33
- public function setUp() {
34
  $plugin_data = array(
35
  'Name' => 'Total Upkeep',
36
  'PluginURI' => 'https://www.boldgrid.com/boldgrid-backup/',
30
  *
31
  * @since 2.12.2
32
  */
33
+ public function set_up() {
34
  $plugin_data = array(
35
  'Name' => 'Total Upkeep',
36
  'PluginURI' => 'https://www.boldgrid.com/boldgrid-backup/',
vendor/boldgrid/library/tests/Library/Plugin/test-notice.php CHANGED
@@ -22,7 +22,7 @@ class Test_BoldGrid_Library_Library_Plugin_Notice extends WP_UnitTestCase {
22
  *
23
  * @since 2.12.2
24
  */
25
- public function setUp() {
26
  $this->sample_plugin = Plugin\Factory::create( 'boldgrid-backup/boldgrid-backup.php' );
27
  $this->sample_notice_array = array(
28
  'id' => 'bgbkup_database_encryption',
22
  *
23
  * @since 2.12.2
24
  */
25
+ public function set_up() {
26
  $this->sample_plugin = Plugin\Factory::create( 'boldgrid-backup/boldgrid-backup.php' );
27
  $this->sample_notice_array = array(
28
  'id' => 'bgbkup_database_encryption',
vendor/boldgrid/library/tests/Library/Plugin/test-page.php CHANGED
@@ -22,7 +22,7 @@ class Test_BoldGrid_Library_Library_Plugin_Page extends WP_UnitTestCase {
22
  *
23
  * @since 2.12.2
24
  */
25
- public function setUp() {
26
  $this->config = array(
27
  'pages' => array(
28
  'boldgrid-backup-premium-features',
22
  *
23
  * @since 2.12.2
24
  */
25
+ public function set_up() {
26
  $this->config = array(
27
  'pages' => array(
28
  'boldgrid-backup-premium-features',
vendor/boldgrid/library/tests/Library/Plugin/test-plugin.php CHANGED
@@ -22,7 +22,7 @@ class Test_BoldGrid_Library_Library_Plugin_Plugin extends WP_UnitTestCase {
22
  *
23
  * @since 2.12.2
24
  */
25
- public function setUp() {
26
  $this->config = array(
27
  'pages' => array(
28
  'boldgrid-backup-premium-features',
22
  *
23
  * @since 2.12.2
24
  */
25
+ public function set_up() {
26
  $this->config = array(
27
  'pages' => array(
28
  'boldgrid-backup-premium-features',
vendor/boldgrid/library/tests/Library/Plugin/test-plugins.php CHANGED
@@ -29,7 +29,7 @@ class Test_BoldGrid_Library_Library_Plugin_Plugins extends WP_UnitTestCase {
29
  *
30
  * @since 1.7.7
31
  */
32
- public function setUp() {
33
  $plugin_dirs = scandir( ABSPATH . '/wp-content/plugins/' );
34
  $this->expected_plugins = array();
35
  $this->expected_plugin_slugs = array();
29
  *
30
  * @since 1.7.7
31
  */
32
+ public function set_up() {
33
  $plugin_dirs = scandir( ABSPATH . '/wp-content/plugins/' );
34
  $this->expected_plugins = array();
35
  $this->expected_plugin_slugs = array();
vendor/boldgrid/library/tests/Library/Theme/test-theme.php CHANGED
@@ -27,7 +27,7 @@ class Test_BoldGrid_Library_Library_Theme_Theme extends WP_UnitTestCase {
27
  *
28
  * @since 1.7.7
29
  */
30
- public function setUp() {
31
  // Setup our configs.
32
  delete_site_transient( 'update_themes' );
33
  $this->stylesheet = 'twentytwenty';
27
  *
28
  * @since 1.7.7
29
  */
30
+ public function set_up() {
31
  // Setup our configs.
32
  delete_site_transient( 'update_themes' );
33
  $this->stylesheet = 'twentytwenty';
vendor/boldgrid/library/tests/Library/Theme/test-themes.php CHANGED
@@ -29,7 +29,7 @@ class Test_BoldGrid_Library_Library_Theme_Themes extends WP_UnitTestCase {
29
  *
30
  * @since 1.7.7
31
  */
32
- public function setUp() {
33
  global $wpdb;
34
 
35
  $this->themes = new Theme\Themes();
29
  *
30
  * @since 1.7.7
31
  */
32
+ public function set_up() {
33
  global $wpdb;
34
 
35
  $this->themes = new Theme\Themes();
vendor/boldgrid/library/tests/Library/Theme/test-update-data.php CHANGED
@@ -29,7 +29,7 @@ class Test_BoldGrid_Library_Library_Theme_UpdateData extends WP_UnitTestCase {
29
  *
30
  * @since 1.7.7
31
  */
32
- public function setUp() {
33
  // Setup our configs.
34
  delete_transient( 'boldgrid_theme_information' );
35
  $this->transient_data = array(
29
  *
30
  * @since 1.7.7
31
  */
32
+ public function set_up() {
33
  // Setup our configs.
34
  delete_transient( 'boldgrid_theme_information' );
35
  $this->transient_data = array(
vendor/boldgrid/library/tests/bootstrap.php CHANGED
@@ -45,5 +45,14 @@ require_once dirname( dirname( __FILE__ ) ) . '/src/Library/Activity.php';
45
  require_once dirname( dirname( __FILE__ ) ) . '/src/Util/Option.php';
46
  require_once dirname( dirname( __FILE__ ) ) . '/src/Util/Version.php';
47
 
48
- require $_tests_dir . '/includes/bootstrap.php';
 
 
 
 
 
 
 
 
49
 
 
45
  require_once dirname( dirname( __FILE__ ) ) . '/src/Util/Option.php';
46
  require_once dirname( dirname( __FILE__ ) ) . '/src/Util/Version.php';
47
 
48
+ /*
49
+ * Yoast/PHPUnit-Polyfills, required for running the WP test suite.
50
+ * Please see https://make.wordpress.org/core/2021/09/27/changes-to-the-wordpress-core-php-test-suite/
51
+ *
52
+ * The WP Core test suite can now run on all PHPUnit versions between PHPUnit 5.7.21 up to the latest
53
+ * release (at the time of writing: PHPUnit 9.5.10), which allows for running the test suite against
54
+ * all supported PHP versions using the most appropriate PHPUnit version for that PHP version.
55
+ */
56
+ require_once dirname( dirname( __FILE__ ) ) . '/vendor/yoast/phpunit-polyfills/phpunitpolyfills-autoload.php';
57
 
58
+ require $_tests_dir . '/includes/bootstrap.php';
vendor/boldgrid/library/yarn.lock CHANGED
@@ -1255,9 +1255,9 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
1255
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
1256
 
1257
  ini@~1.3.0:
1258
- version "1.3.5"
1259
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
1260
- integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
1261
 
1262
  inquirer@^6.2.2:
1263
  version "6.5.2"
@@ -1579,9 +1579,9 @@ lodash.unescape@4.0.1:
1579
  integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
1580
 
1581
  lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
1582
- version "4.17.19"
1583
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
1584
- integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
1585
 
1586
  loglevel-colored-level-prefix@^1.0.0:
1587
  version "1.0.0"
@@ -2747,9 +2747,9 @@ write@1.0.3:
2747
  mkdirp "^0.5.1"
2748
 
2749
  y18n@^4.0.0:
2750
- version "4.0.0"
2751
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
2752
- integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
2753
 
2754
  yallist@^3.0.0, yallist@^3.0.3:
2755
  version "3.1.1"
@@ -2757,9 +2757,9 @@ yallist@^3.0.0, yallist@^3.0.3:
2757
  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
2758
 
2759
  yargs-parser@^13.1.1:
2760
- version "13.1.1"
2761
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
2762
- integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
2763
  dependencies:
2764
  camelcase "^5.0.0"
2765
  decamelize "^1.2.0"
1255
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
1256
 
1257
  ini@~1.3.0:
1258
+ version "1.3.7"
1259
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
1260
+ integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
1261
 
1262
  inquirer@^6.2.2:
1263
  version "6.5.2"
1579
  integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
1580
 
1581
  lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4:
1582
+ version "4.17.21"
1583
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
1584
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
1585
 
1586
  loglevel-colored-level-prefix@^1.0.0:
1587
  version "1.0.0"
2747
  mkdirp "^0.5.1"
2748
 
2749
  y18n@^4.0.0:
2750
+ version "4.0.1"
2751
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
2752
+ integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
2753
 
2754
  yallist@^3.0.0, yallist@^3.0.3:
2755
  version "3.1.1"
2757
  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
2758
 
2759
  yargs-parser@^13.1.1:
2760
+ version "13.1.2"
2761
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
2762
+ integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
2763
  dependencies:
2764
  camelcase "^5.0.0"
2765
  decamelize "^1.2.0"
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03
6
  {
7
  private static $loader;
8
 
@@ -22,15 +22,15 @@ class ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03
22
  return self::$loader;
23
  }
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
- spl_autoload_unregister(array('ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
- call_user_func(\Composer\Autoload\ComposerStaticInit651c2267921840af4e23d4a1a286ef03::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
@@ -51,19 +51,19 @@ class ComposerAutoloaderInit651c2267921840af4e23d4a1a286ef03
51
  $loader->register(true);
52
 
53
  if ($useStaticLoader) {
54
- $includeFiles = Composer\Autoload\ComposerStaticInit651c2267921840af4e23d4a1a286ef03::$files;
55
  } else {
56
  $includeFiles = require __DIR__ . '/autoload_files.php';
57
  }
58
  foreach ($includeFiles as $fileIdentifier => $file) {
59
- composerRequire651c2267921840af4e23d4a1a286ef03($fileIdentifier, $file);
60
  }
61
 
62
  return $loader;
63
  }
64
  }
65
 
66
- function composerRequire651c2267921840af4e23d4a1a286ef03($fileIdentifier, $file)
67
  {
68
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
69
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit21ce95d681fa0cbccf157df200b627c0
6
  {
7
  private static $loader;
8
 
22
  return self::$loader;
23
  }
24
 
25
+ spl_autoload_register(array('ComposerAutoloaderInit21ce95d681fa0cbccf157df200b627c0', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit21ce95d681fa0cbccf157df200b627c0', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
+ call_user_func(\Composer\Autoload\ComposerStaticInit21ce95d681fa0cbccf157df200b627c0::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
51
  $loader->register(true);
52
 
53
  if ($useStaticLoader) {
54
+ $includeFiles = Composer\Autoload\ComposerStaticInit21ce95d681fa0cbccf157df200b627c0::$files;
55
  } else {
56
  $includeFiles = require __DIR__ . '/autoload_files.php';
57
  }
58
  foreach ($includeFiles as $fileIdentifier => $file) {
59
+ composerRequire21ce95d681fa0cbccf157df200b627c0($fileIdentifier, $file);
60
  }
61
 
62
  return $loader;
63
  }
64
  }
65
 
66
+ function composerRequire21ce95d681fa0cbccf157df200b627c0($fileIdentifier, $file)
67
  {
68
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
69
  require $file;
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit651c2267921840af4e23d4a1a286ef03
8
  {
9
  public static $files = array (
10
  'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
@@ -91,9 +91,9 @@ class ComposerStaticInit651c2267921840af4e23d4a1a286ef03
91
  public static function getInitializer(ClassLoader $loader)
92
  {
93
  return \Closure::bind(function () use ($loader) {
94
- $loader->prefixLengthsPsr4 = ComposerStaticInit651c2267921840af4e23d4a1a286ef03::$prefixLengthsPsr4;
95
- $loader->prefixDirsPsr4 = ComposerStaticInit651c2267921840af4e23d4a1a286ef03::$prefixDirsPsr4;
96
- $loader->classMap = ComposerStaticInit651c2267921840af4e23d4a1a286ef03::$classMap;
97
 
98
  }, null, ClassLoader::class);
99
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit21ce95d681fa0cbccf157df200b627c0
8
  {
9
  public static $files = array (
10
  'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
91
  public static function getInitializer(ClassLoader $loader)
92
  {
93
  return \Closure::bind(function () use ($loader) {
94
+ $loader->prefixLengthsPsr4 = ComposerStaticInit21ce95d681fa0cbccf157df200b627c0::$prefixLengthsPsr4;
95
+ $loader->prefixDirsPsr4 = ComposerStaticInit21ce95d681fa0cbccf157df200b627c0::$prefixDirsPsr4;
96
+ $loader->classMap = ComposerStaticInit21ce95d681fa0cbccf157df200b627c0::$classMap;
97
 
98
  }, null, ClassLoader::class);
99
  }
vendor/composer/installed.json CHANGED
@@ -1,20 +1,23 @@
1
  [
2
  {
3
  "name": "boldgrid/library",
4
- "version": "2.13.3",
5
- "version_normalized": "2.13.3.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/BoldGrid/library.git",
9
- "reference": "066c8b68f7ca7ecf1f8769c3904dc85cb76fd7f4"
10
  },
11
  "dist": {
12
  "type": "zip",
13
- "url": "https://api.github.com/repos/BoldGrid/library/zipball/066c8b68f7ca7ecf1f8769c3904dc85cb76fd7f4",
14
- "reference": "066c8b68f7ca7ecf1f8769c3904dc85cb76fd7f4",
15
  "shasum": ""
16
  },
17
- "time": "2020-10-13T17:55:32+00:00",
 
 
 
18
  "type": "library",
19
  "installation-source": "dist",
20
  "autoload": {
@@ -60,7 +63,6 @@
60
  "url": "https://github.com/chland/tdcron",
61
  "reference": "origin/master"
62
  },
63
- "time": "2018-01-23T14:06:12+00:00",
64
  "type": "library",
65
  "installation-source": "source"
66
  },
@@ -125,17 +127,17 @@
125
  },
126
  {
127
  "name": "phpseclib/phpseclib",
128
- "version": "2.0.31",
129
- "version_normalized": "2.0.31.0",
130
  "source": {
131
  "type": "git",
132
  "url": "https://github.com/phpseclib/phpseclib.git",
133
- "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4"
134
  },
135
  "dist": {
136
  "type": "zip",
137
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4",
138
- "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4",
139
  "shasum": ""
140
  },
141
  "require": {
@@ -152,7 +154,7 @@
152
  "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
153
  "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
154
  },
155
- "time": "2021-04-06T13:56:45+00:00",
156
  "type": "library",
157
  "installation-source": "dist",
158
  "autoload": {
1
  [
2
  {
3
  "name": "boldgrid/library",
4
+ "version": "2.13.6",
5
+ "version_normalized": "2.13.6.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/BoldGrid/library.git",
9
+ "reference": "f225b4cabbd3758b2beebf0ec890959c6bea2564"
10
  },
11
  "dist": {
12
  "type": "zip",
13
+ "url": "https://api.github.com/repos/BoldGrid/library/zipball/f225b4cabbd3758b2beebf0ec890959c6bea2564",
14
+ "reference": "f225b4cabbd3758b2beebf0ec890959c6bea2564",
15
  "shasum": ""
16
  },
17
+ "require-dev": {
18
+ "yoast/phpunit-polyfills": "^1.0"
19
+ },
20
+ "time": "2022-03-15T17:39:20+00:00",
21
  "type": "library",
22
  "installation-source": "dist",
23
  "autoload": {
63
  "url": "https://github.com/chland/tdcron",
64
  "reference": "origin/master"
65
  },
 
66
  "type": "library",
67
  "installation-source": "source"
68
  },
127
  },
128
  {
129
  "name": "phpseclib/phpseclib",
130
+ "version": "2.0.36",
131
+ "version_normalized": "2.0.36.0",
132
  "source": {
133
  "type": "git",
134
  "url": "https://github.com/phpseclib/phpseclib.git",
135
+ "reference": "a97547126396548c224703a267a30af1592be146"
136
  },
137
  "dist": {
138
  "type": "zip",
139
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/a97547126396548c224703a267a30af1592be146",
140
+ "reference": "a97547126396548c224703a267a30af1592be146",
141
  "shasum": ""
142
  },
143
  "require": {
154
  "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
155
  "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
156
  },
157
+ "time": "2022-01-30T08:48:36+00:00",
158
  "type": "library",
159
  "installation-source": "dist",
160
  "autoload": {
vendor/phpseclib/phpseclib/BACKERS.md CHANGED
@@ -4,5 +4,9 @@ phpseclib ongoing development is made possible by [Tidelift](https://tidelift.co
4
 
5
  ## Backers
6
 
 
 
7
  - Zane Hooper
8
- - [Setasign](https://www.setasign.com/)
 
 
4
 
5
  ## Backers
6
 
7
+ - Allan Simon
8
+ - Raghu Veer Dendukuri
9
  - Zane Hooper
10
+ - [Setasign](https://www.setasign.com/)
11
+ - [Charles Severance](https://github.com/csev)
12
+ - [Rachel Fish](https://github.com/itsrachelfish)
vendor/phpseclib/phpseclib/README.md CHANGED
@@ -1,6 +1,6 @@
1
  # phpseclib - PHP Secure Communications Library
2
 
3
- [![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/phpseclib/phpseclib)
4
 
5
  ## Supporting phpseclib
6
 
@@ -52,7 +52,7 @@ SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 /
52
  * Composer compatible (PSR-0 autoloading)
53
  * Install using Composer: `composer require phpseclib/phpseclib:~1.0`
54
  * Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm)
55
- * [Download 1.0.19 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download)
56
 
57
  ## Security contact information
58
 
@@ -66,6 +66,12 @@ Need Support?
66
  * [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new)
67
  * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
68
 
 
 
 
 
 
 
69
  ## Contributing
70
 
71
  1. Fork the Project
1
  # phpseclib - PHP Secure Communications Library
2
 
3
+ [![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/github/phpseclib/phpseclib)
4
 
5
  ## Supporting phpseclib
6
 
52
  * Composer compatible (PSR-0 autoloading)
53
  * Install using Composer: `composer require phpseclib/phpseclib:~1.0`
54
  * Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm)
55
+ * [Download 1.0.20 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.20.zip/download)
56
 
57
  ## Security contact information
58
 
66
  * [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new)
67
  * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
68
 
69
+ ## Special Thanks
70
+
71
+ Special Thanks to our Patreon sponsors!:
72
+
73
+ - Allan Simon
74
+
75
  ## Contributing
76
 
77
  1. Fork the Project
vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php CHANGED
@@ -79,7 +79,11 @@ abstract class Base
79
  /**
80
  * Encrypt / decrypt using the Cipher Feedback mode (8bit)
81
  */
82
- const MODE_CFB8 = 38;
 
 
 
 
83
  /**
84
  * Encrypt / decrypt using the Output Feedback mode.
85
  *
@@ -484,6 +488,7 @@ abstract class Base
484
  case self::MODE_CTR:
485
  case self::MODE_CFB:
486
  case self::MODE_CFB8:
 
487
  case self::MODE_OFB:
488
  case self::MODE_STREAM:
489
  $this->mode = $mode;
@@ -773,8 +778,25 @@ abstract class Base
773
  }
774
  }
775
  return $ciphertext;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  case self::MODE_OFB:
777
  return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
 
 
778
  }
779
  }
780
 
@@ -959,12 +981,14 @@ abstract class Base
959
  }
960
  break;
961
  case self::MODE_CFB8:
 
 
962
  $ciphertext = '';
963
  $len = strlen($plaintext);
964
  $iv = $this->encryptIV;
965
 
966
  for ($i = 0; $i < $len; ++$i) {
967
- $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
968
  $iv = substr($iv, 1) . $c;
969
  }
970
 
@@ -976,6 +1000,21 @@ abstract class Base
976
  }
977
  }
978
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
  case self::MODE_OFB:
980
  $xor = $this->encryptIV;
981
  if (strlen($buffer['xor'])) {
@@ -1116,6 +1155,21 @@ abstract class Base
1116
  }
1117
  }
1118
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1119
  case self::MODE_OFB:
1120
  $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
1121
  }
@@ -1290,7 +1344,7 @@ abstract class Base
1290
  $iv = $this->decryptIV;
1291
 
1292
  for ($i = 0; $i < $len; ++$i) {
1293
- $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv);
1294
  $iv = substr($iv, 1) . $ciphertext[$i];
1295
  }
1296
 
@@ -1302,6 +1356,21 @@ abstract class Base
1302
  }
1303
  }
1304
  break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1305
  case self::MODE_OFB:
1306
  $xor = $this->decryptIV;
1307
  if (strlen($buffer['xor'])) {
@@ -1864,6 +1933,7 @@ abstract class Base
1864
  self::MODE_CFB => 'ncfb',
1865
  self::MODE_CFB8 => MCRYPT_MODE_CFB,
1866
  self::MODE_OFB => MCRYPT_MODE_NOFB,
 
1867
  self::MODE_STREAM => MCRYPT_MODE_STREAM,
1868
  );
1869
 
@@ -2446,7 +2516,7 @@ abstract class Base
2446
  for ($_i = 0; $_i < $_len; ++$_i) {
2447
  $in = $_iv;
2448
  '.$encrypt_block.'
2449
- $_ciphertext .= ($_c = $_text[$_i] ^ $in);
2450
  $_iv = substr($_iv, 1) . $_c;
2451
  }
2452
 
@@ -2468,7 +2538,7 @@ abstract class Base
2468
  for ($_i = 0; $_i < $_len; ++$_i) {
2469
  $in = $_iv;
2470
  '.$encrypt_block.'
2471
- $_plaintext .= $_text[$_i] ^ $in;
2472
  $_iv = substr($_iv, 1) . $_text[$_i];
2473
  }
2474
 
@@ -2480,6 +2550,44 @@ abstract class Base
2480
  }
2481
  }
2482
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2483
  return $_plaintext;
2484
  ';
2485
  break;
79
  /**
80
  * Encrypt / decrypt using the Cipher Feedback mode (8bit)
81
  */
82
+ const MODE_CFB8 = 6;
83
+ /**
84
+ * Encrypt / decrypt using the Output Feedback mode (8bit)
85
+ */
86
+ const MODE_OFB8 = 7;
87
  /**
88
  * Encrypt / decrypt using the Output Feedback mode.
89
  *
488
  case self::MODE_CTR:
489
  case self::MODE_CFB:
490
  case self::MODE_CFB8:
491
+ case self::MODE_OFB8:
492
  case self::MODE_OFB:
493
  case self::MODE_STREAM:
494
  $this->mode = $mode;
778
  }
779
  }
780
  return $ciphertext;
781
+ case self::MODE_OFB8:
782
+ $ciphertext = '';
783
+ $len = strlen($plaintext);
784
+ $iv = $this->encryptIV;
785
+
786
+ for ($i = 0; $i < $len; ++$i) {
787
+ $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
788
+ $ciphertext.= $plaintext[$i] ^ $xor;
789
+ $iv = substr($iv, 1) . $xor[0];
790
+ }
791
+
792
+ if ($this->continuousBuffer) {
793
+ $this->encryptIV = $iv;
794
+ }
795
+ break;
796
  case self::MODE_OFB:
797
  return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
798
+ case self::MODE_OFB8:
799
+ // OpenSSL has built in support for cfb8 but not ofb8
800
  }
801
  }
802
 
981
  }
982
  break;
983
  case self::MODE_CFB8:
984
+ // compared to regular CFB, which encrypts a block at a time,
985
+ // here, we're encrypting a byte at a time
986
  $ciphertext = '';
987
  $len = strlen($plaintext);
988
  $iv = $this->encryptIV;
989
 
990
  for ($i = 0; $i < $len; ++$i) {
991
+ $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
992
  $iv = substr($iv, 1) . $c;
993
  }
994
 
1000
  }
1001
  }
1002
  break;
1003
+ case self::MODE_OFB8:
1004
+ $ciphertext = '';
1005
+ $len = strlen($plaintext);
1006
+ $iv = $this->encryptIV;
1007
+
1008
+ for ($i = 0; $i < $len; ++$i) {
1009
+ $xor = $this->_encryptBlock($iv);
1010
+ $ciphertext.= $plaintext[$i] ^ $xor;
1011
+ $iv = substr($iv, 1) . $xor[0];
1012
+ }
1013
+
1014
+ if ($this->continuousBuffer) {
1015
+ $this->encryptIV = $iv;
1016
+ }
1017
+ break;
1018
  case self::MODE_OFB:
1019
  $xor = $this->encryptIV;
1020
  if (strlen($buffer['xor'])) {
1155
  }
1156
  }
1157
  break;
1158
+ case self::MODE_OFB8:
1159
+ $plaintext = '';
1160
+ $len = strlen($ciphertext);
1161
+ $iv = $this->decryptIV;
1162
+
1163
+ for ($i = 0; $i < $len; ++$i) {
1164
+ $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
1165
+ $plaintext.= $ciphertext[$i] ^ $xor;
1166
+ $iv = substr($iv, 1) . $xor[0];
1167
+ }
1168
+
1169
+ if ($this->continuousBuffer) {
1170
+ $this->decryptIV = $iv;
1171
+ }
1172
+ break;
1173
  case self::MODE_OFB:
1174
  $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
1175
  }
1344
  $iv = $this->decryptIV;
1345
 
1346
  for ($i = 0; $i < $len; ++$i) {
1347
+ $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv);
1348
  $iv = substr($iv, 1) . $ciphertext[$i];
1349
  }
1350
 
1356
  }
1357
  }
1358
  break;
1359
+ case self::MODE_OFB8:
1360
+ $plaintext = '';
1361
+ $len = strlen($ciphertext);
1362
+ $iv = $this->decryptIV;
1363
+
1364
+ for ($i = 0; $i < $len; ++$i) {
1365
+ $xor = $this->_encryptBlock($iv);
1366
+ $plaintext.= $ciphertext[$i] ^ $xor;
1367
+ $iv = substr($iv, 1) . $xor[0];
1368
+ }
1369
+
1370
+ if ($this->continuousBuffer) {
1371
+ $this->decryptIV = $iv;
1372
+ }
1373
+ break;
1374
  case self::MODE_OFB:
1375
  $xor = $this->decryptIV;
1376
  if (strlen($buffer['xor'])) {
1933
  self::MODE_CFB => 'ncfb',
1934
  self::MODE_CFB8 => MCRYPT_MODE_CFB,
1935
  self::MODE_OFB => MCRYPT_MODE_NOFB,
1936
+ self::MODE_OFB8 => MCRYPT_MODE_OFB,
1937
  self::MODE_STREAM => MCRYPT_MODE_STREAM,
1938
  );
1939
 
2516
  for ($_i = 0; $_i < $_len; ++$_i) {
2517
  $in = $_iv;
2518
  '.$encrypt_block.'
2519
+ $_ciphertext.= ($_c = $_text[$_i] ^ $in);
2520
  $_iv = substr($_iv, 1) . $_c;
2521
  }
2522
 
2538
  for ($_i = 0; $_i < $_len; ++$_i) {
2539
  $in = $_iv;
2540
  '.$encrypt_block.'
2541
+ $_plaintext.= $_text[$_i] ^ $in;
2542
  $_iv = substr($_iv, 1) . $_text[$_i];
2543
  }
2544
 
2550
  }
2551
  }
2552
 
2553
+ return $_plaintext;
2554
+ ';
2555
+ break;
2556
+ case self::MODE_OFB8:
2557
+ $encrypt = $init_encrypt . '
2558
+ $_ciphertext = "";
2559
+ $_len = strlen($_text);
2560
+ $_iv = $self->encryptIV;
2561
+
2562
+ for ($_i = 0; $_i < $_len; ++$_i) {
2563
+ $in = $_iv;
2564
+ '.$encrypt_block.'
2565
+ $_ciphertext.= $_text[$_i] ^ $in;
2566
+ $_iv = substr($_iv, 1) . $in[0];
2567
+ }
2568
+
2569
+ if ($self->continuousBuffer) {
2570
+ $self->encryptIV = $_iv;
2571
+ }
2572
+
2573
+ return $_ciphertext;
2574
+ ';
2575
+ $decrypt = $init_encrypt . '
2576
+ $_plaintext = "";
2577
+ $_len = strlen($_text);
2578
+ $_iv = $self->decryptIV;
2579
+
2580
+ for ($_i = 0; $_i < $_len; ++$_i) {
2581
+ $in = $_iv;
2582
+ '.$encrypt_block.'
2583
+ $_plaintext.= $_text[$_i] ^ $in;
2584
+ $_iv = substr($_iv, 1) . $in[0];
2585
+ }
2586
+
2587
+ if ($self->continuousBuffer) {
2588
+ $self->decryptIV = $_iv;
2589
+ }
2590
+
2591
  return $_plaintext;
2592
  ';
2593
  break;
vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php CHANGED
@@ -470,7 +470,7 @@ class RSA
470
  case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
471
  define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
472
  break;
473
- case extension_loaded('openssl') && file_exists($this->configFile):
474
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
475
  $versions = array();
476
 
@@ -878,9 +878,9 @@ class RSA
878
  );
879
  $key = "openssh-key-v1\0$key";
880
 
881
- return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
882
- chunk_split(base64_encode($key), 70) .
883
- "-----END OPENSSH PRIVATE KEY-----";
884
  default: // eg. self::PRIVATE_FORMAT_PKCS1
885
  $components = array();
886
  foreach ($raw as $name => $value) {
@@ -2580,9 +2580,9 @@ class RSA
2580
  $offset+= $patternMatch ? 0 : 1;
2581
  }
2582
 
2583
- // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
2584
  // to protect against timing attacks
2585
- if (!$hashesMatch & !$patternMatch) {
2586
  user_error('Decryption error');
2587
  return false;
2588
  }
470
  case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
471
  define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
472
  break;
473
+ case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile):
474
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
475
  $versions = array();
476
 
878
  );
879
  $key = "openssh-key-v1\0$key";
880
 
881
+ return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
882
+ chunk_split(base64_encode($key), 70, "\n") .
883
+ "-----END OPENSSH PRIVATE KEY-----\n";
884
  default: // eg. self::PRIVATE_FORMAT_PKCS1
885
  $components = array();
886
  foreach ($raw as $name => $value) {
2580
  $offset+= $patternMatch ? 0 : 1;
2581
  }
2582
 
2583
+ // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
2584
  // to protect against timing attacks
2585
+ if (!$hashesMatch | !$patternMatch) {
2586
  user_error('Decryption error');
2587
  return false;
2588
  }
vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php CHANGED
@@ -234,6 +234,9 @@ class ASN1
234
  {
235
  $current = array('start' => $start);
236
 
 
 
 
237
  $type = ord($encoded[$encoded_pos++]);
238
  $startOffset = 1;
239
 
@@ -244,6 +247,9 @@ class ASN1
244
  $tag = 0;
245
  // process septets (since the eighth bit is ignored, it's not an octet)
246
  do {
 
 
 
247
  $temp = ord($encoded[$encoded_pos++]);
248
  $startOffset++;
249
  $loop = $temp >> 7;
@@ -260,6 +266,9 @@ class ASN1
260
  $start+= $startOffset;
261
 
262
  // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
 
 
 
263
  $length = ord($encoded[$encoded_pos++]);
264
  $start++;
265
  if ($length == 0x80) { // indefinite length
@@ -993,7 +1002,10 @@ class ASN1
993
  case self::TYPE_GENERALIZED_TIME:
994
  $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
995
  $format.= 'mdHis';
 
996
  $date = new DateTime($source, new DateTimeZone('GMT'));
 
 
997
  $value = $date->format($format) . 'Z';
998
  break;
999
  case self::TYPE_BIT_STRING:
234
  {
235
  $current = array('start' => $start);
236
 
237
+ if (!isset($encoded[$encoded_pos])) {
238
+ return false;
239
+ }
240
  $type = ord($encoded[$encoded_pos++]);
241
  $startOffset = 1;
242
 
247
  $tag = 0;
248
  // process septets (since the eighth bit is ignored, it's not an octet)
249
  do {
250
+ if (!isset($encoded[$encoded_pos])) {
251
+ return false;
252
+ }
253
  $temp = ord($encoded[$encoded_pos++]);
254
  $startOffset++;
255
  $loop = $temp >> 7;
266
  $start+= $startOffset;
267
 
268
  // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
269
+ if (!isset($encoded[$encoded_pos])) {
270
+ return false;
271
+ }
272
  $length = ord($encoded[$encoded_pos++]);
273
  $start++;
274
  if ($length == 0x80) { // indefinite length
1002
  case self::TYPE_GENERALIZED_TIME:
1003
  $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
1004
  $format.= 'mdHis';
1005
+ // if $source does _not_ include timezone information within it then assume that the timezone is GMT
1006
  $date = new DateTime($source, new DateTimeZone('GMT'));
1007
+ // if $source _does_ include timezone information within it then convert the time to GMT
1008
+ $date->setTimezone(new DateTimeZone('GMT'));
1009
  $value = $date->format($format) . 'Z';
1010
  break;
1011
  case self::TYPE_BIT_STRING:
vendor/phpseclib/phpseclib/phpseclib/File/X509.php CHANGED
@@ -1622,7 +1622,6 @@ class X509
1622
  $id = $extensions[$i]['extnId'];
1623
  $value = &$extensions[$i]['extnValue'];
1624
  $value = base64_decode($value);
1625
- $decoded = $asn1->decodeBER($value);
1626
  /* [extnValue] contains the DER encoding of an ASN.1 value
1627
  corresponding to the extension type identified by extnID */
1628
  $map = $this->_getMapping($id);
@@ -1630,6 +1629,7 @@ class X509
1630
  $decoder = $id == 'id-ce-nameConstraints' ?
1631
  array($this, '_decodeNameConstraintIP') :
1632
  array($this, '_decodeIP');
 
1633
  $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
1634
  $value = $mapped === false ? $decoded[0] : $mapped;
1635
 
@@ -5058,7 +5058,7 @@ class X509
5058
  $temp = $str;
5059
  } else {
5060
  $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
5061
- $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $str, 1);
5062
  }
5063
  // remove new lines
5064
  $temp = str_replace(array("\r", "\n", ' '), '', $temp);
1622
  $id = $extensions[$i]['extnId'];
1623
  $value = &$extensions[$i]['extnValue'];
1624
  $value = base64_decode($value);
 
1625
  /* [extnValue] contains the DER encoding of an ASN.1 value
1626
  corresponding to the extension type identified by extnID */
1627
  $map = $this->_getMapping($id);
1629
  $decoder = $id == 'id-ce-nameConstraints' ?
1630
  array($this, '_decodeNameConstraintIP') :
1631
  array($this, '_decodeIP');
1632
+ $decoded = $asn1->decodeBER($value);
1633
  $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
1634
  $value = $mapped === false ? $decoded[0] : $mapped;
1635
 
5058
  $temp = $str;
5059
  } else {
5060
  $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
5061
+ $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
5062
  }
5063
  // remove new lines
5064
  $temp = str_replace(array("\r", "\n", ' '), '', $temp);
vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php CHANGED
@@ -263,7 +263,7 @@ class BigInteger
263
  }
264
  }
265
 
266
- if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
267
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
268
  $versions = array();
269
 
263
  }
264
  }
265
 
266
+ if (function_exists('phpinfo') && extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
267
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
268
  $versions = array();
269
 
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php CHANGED
@@ -5,9 +5,7 @@
5
  *
6
  * PHP version 5
7
  *
8
- * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
9
- * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
10
- * to an SFTPv4/5/6 server.
11
  *
12
  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
13
  *
@@ -154,6 +152,24 @@ class SFTP extends SSH2
154
  */
155
  var $version;
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  /**
158
  * Current working directory
159
  *
@@ -269,6 +285,39 @@ class SFTP extends SSH2
269
  */
270
  var $preserveTime = false;
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  /**
273
  * Default Constructor.
274
  *
@@ -289,15 +338,13 @@ class SFTP extends SSH2
289
  $this->packet_types = array(
290
  1 => 'NET_SFTP_INIT',
291
  2 => 'NET_SFTP_VERSION',
292
- /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
293
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
294
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
295
  3 => 'NET_SFTP_OPEN',
296
  4 => 'NET_SFTP_CLOSE',
297
  5 => 'NET_SFTP_READ',
298
  6 => 'NET_SFTP_WRITE',
299
  7 => 'NET_SFTP_LSTAT',
300
  9 => 'NET_SFTP_SETSTAT',
 
301
  11 => 'NET_SFTP_OPENDIR',
302
  12 => 'NET_SFTP_READDIR',
303
  13 => 'NET_SFTP_REMOVE',
@@ -305,18 +352,13 @@ class SFTP extends SSH2
305
  15 => 'NET_SFTP_RMDIR',
306
  16 => 'NET_SFTP_REALPATH',
307
  17 => 'NET_SFTP_STAT',
308
- /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
309
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
310
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
311
  18 => 'NET_SFTP_RENAME',
312
  19 => 'NET_SFTP_READLINK',
313
  20 => 'NET_SFTP_SYMLINK',
 
314
 
315
  101=> 'NET_SFTP_STATUS',
316
  102=> 'NET_SFTP_HANDLE',
317
- /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
318
- SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
319
- pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
320
  103=> 'NET_SFTP_DATA',
321
  104=> 'NET_SFTP_NAME',
322
  105=> 'NET_SFTP_ATTRS',
@@ -361,25 +403,59 @@ class SFTP extends SSH2
361
  // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why
362
  $this->attributes = array(
363
  0x00000001 => 'NET_SFTP_ATTR_SIZE',
364
- 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
 
365
  0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
366
  0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
 
 
 
 
 
 
 
 
 
 
 
367
  // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
368
  // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
369
  // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
370
  // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
371
  (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
372
  );
373
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
374
- // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
375
- // the array for that $this->open5_flags and similarly alter the constant names.
376
  $this->open_flags = array(
377
  0x00000001 => 'NET_SFTP_OPEN_READ',
378
  0x00000002 => 'NET_SFTP_OPEN_WRITE',
379
  0x00000004 => 'NET_SFTP_OPEN_APPEND',
380
  0x00000008 => 'NET_SFTP_OPEN_CREATE',
381
  0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
382
- 0x00000020 => 'NET_SFTP_OPEN_EXCL'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  );
384
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
385
  // see \phpseclib\Net\SFTP::_parseLongname() for an explanation
@@ -401,6 +477,7 @@ class SFTP extends SSH2
401
  $this->status_codes,
402
  $this->attributes,
403
  $this->open_flags,
 
404
  $this->file_types
405
  );
406
 
@@ -413,18 +490,32 @@ class SFTP extends SSH2
413
  }
414
 
415
  /**
416
- * Login
417
  *
418
- * @param string $username
419
  * @return bool
420
  * @access public
421
  */
422
- function login($username)
423
  {
424
- if (!call_user_func_array('parent::login', func_get_args())) {
425
  return false;
426
  }
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
429
 
430
  $packet = pack(
@@ -446,6 +537,8 @@ class SFTP extends SSH2
446
  $response = $this->_get_channel_packet(self::CHANNEL, true);
447
  if ($response === false) {
448
  return false;
 
 
449
  }
450
 
451
  $packet = pack(
@@ -492,6 +585,8 @@ class SFTP extends SSH2
492
  if ($response === false) {
493
  return false;
494
  }
 
 
495
  }
496
 
497
  $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
@@ -506,11 +601,13 @@ class SFTP extends SSH2
506
  return false;
507
  }
508
 
 
 
509
  if (strlen($response) < 4) {
510
  return false;
511
  }
512
  extract(unpack('Nversion', $this->_string_shift($response, 4)));
513
- $this->version = $version;
514
  while (!empty($response)) {
515
  if (strlen($response) < 4) {
516
  return false;
@@ -525,21 +622,22 @@ class SFTP extends SSH2
525
  $this->extensions[$key] = $value;
526
  }
527
 
528
- /*
529
- SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
530
- however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
531
- not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
532
- one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
533
- 'newline@vandyke.com' would.
534
- */
535
- /*
536
- if (isset($this->extensions['newline@vandyke.com'])) {
537
- $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
538
- unset($this->extensions['newline@vandyke.com']);
539
- }
540
- */
541
 
542
- $this->use_request_id = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
543
 
544
  /*
545
  A Note on SFTPv4/5/6 support:
@@ -564,12 +662,60 @@ class SFTP extends SSH2
564
  in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the
565
  channel and reopen it with a new and updated SSH_FXP_INIT packet.
566
  */
567
- switch ($this->version) {
568
- case 2:
569
- case 3:
570
- break;
571
- default:
572
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  }
574
 
575
  $this->pwd = $this->_realpath('.');
@@ -629,6 +775,26 @@ class SFTP extends SSH2
629
  $this->canonicalize_paths = false;
630
  }
631
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  /**
633
  * Returns the current directory name
634
  *
@@ -637,6 +803,10 @@ class SFTP extends SSH2
637
  */
638
  function pwd()
639
  {
 
 
 
 
640
  return $this->pwd;
641
  }
642
 
@@ -678,6 +848,10 @@ class SFTP extends SSH2
678
  */
679
  function realpath($path)
680
  {
 
 
 
 
681
  return $this->_realpath($path);
682
  }
683
 
@@ -760,7 +934,7 @@ class SFTP extends SSH2
760
  */
761
  function chdir($dir)
762
  {
763
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
764
  return false;
765
  }
766
 
@@ -917,7 +1091,7 @@ class SFTP extends SSH2
917
  */
918
  function _list($dir, $raw = true)
919
  {
920
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
921
  return false;
922
  }
923
 
@@ -972,13 +1146,17 @@ class SFTP extends SSH2
972
  }
973
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
974
  $shortname = $this->_string_shift($response, $length);
975
- if (strlen($response) < 4) {
976
- return false;
 
 
 
 
 
 
977
  }
978
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
979
- $longname = $this->_string_shift($response, $length);
980
  $attributes = $this->_parseAttributes($response);
981
- if (!isset($attributes['type'])) {
982
  $fileType = $this->_parseLongname($longname);
983
  if ($fileType) {
984
  $attributes['type'] = $fileType;
@@ -1138,10 +1316,6 @@ class SFTP extends SSH2
1138
  */
1139
  function size($filename)
1140
  {
1141
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1142
- return false;
1143
- }
1144
-
1145
  $result = $this->stat($filename);
1146
  if ($result === false) {
1147
  return false;
@@ -1258,7 +1432,7 @@ class SFTP extends SSH2
1258
  */
1259
  function stat($filename)
1260
  {
1261
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1262
  return false;
1263
  }
1264
 
@@ -1315,7 +1489,7 @@ class SFTP extends SSH2
1315
  */
1316
  function lstat($filename)
1317
  {
1318
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1319
  return false;
1320
  }
1321
 
@@ -1429,7 +1603,7 @@ class SFTP extends SSH2
1429
  */
1430
  function touch($filename, $time = null, $atime = null)
1431
  {
1432
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1433
  return false;
1434
  }
1435
 
@@ -1445,9 +1619,25 @@ class SFTP extends SSH2
1445
  $atime = $time;
1446
  }
1447
 
1448
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL;
1449
- $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime);
1450
- $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1451
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1452
  return false;
1453
  }
@@ -1470,19 +1660,47 @@ class SFTP extends SSH2
1470
  /**
1471
  * Changes file or directory owner
1472
  *
 
 
 
 
 
1473
  * Returns true on success or false on error.
1474
  *
1475
  * @param string $filename
1476
- * @param int $uid
1477
  * @param bool $recursive
1478
  * @return bool
1479
  * @access public
1480
  */
1481
  function chown($filename, $uid, $recursive = false)
1482
  {
1483
- // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1484
- // "if the owner or group is specified as -1, then that ID is not changed"
1485
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1486
 
1487
  return $this->_setstat($filename, $attr, $recursive);
1488
  }
@@ -1490,17 +1708,24 @@ class SFTP extends SSH2
1490
  /**
1491
  * Changes file or directory group
1492
  *
 
 
 
 
 
1493
  * Returns true on success or false on error.
1494
  *
1495
  * @param string $filename
1496
- * @param int $gid
1497
  * @param bool $recursive
1498
  * @return bool
1499
  * @access public
1500
  */
1501
  function chgrp($filename, $gid, $recursive = false)
1502
  {
1503
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
 
 
1504
 
1505
  return $this->_setstat($filename, $attr, $recursive);
1506
  }
@@ -1567,7 +1792,7 @@ class SFTP extends SSH2
1567
  */
1568
  function _setstat($filename, $attr, $recursive)
1569
  {
1570
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1571
  return false;
1572
  }
1573
 
@@ -1585,9 +1810,10 @@ class SFTP extends SSH2
1585
  return $result;
1586
  }
1587
 
1588
- // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
1589
- // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
1590
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
 
1591
  return false;
1592
  }
1593
 
@@ -1657,7 +1883,10 @@ class SFTP extends SSH2
1657
  return false;
1658
  }
1659
  } else {
1660
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
 
 
 
1661
  return false;
1662
  }
1663
 
@@ -1672,7 +1901,10 @@ class SFTP extends SSH2
1672
  }
1673
  }
1674
 
1675
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
 
 
 
1676
  return false;
1677
  }
1678
 
@@ -1697,7 +1929,7 @@ class SFTP extends SSH2
1697
  */
1698
  function readlink($link)
1699
  {
1700
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1701
  return false;
1702
  }
1703
 
@@ -1747,15 +1979,44 @@ class SFTP extends SSH2
1747
  */
1748
  function symlink($target, $link)
1749
  {
1750
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1751
  return false;
1752
  }
1753
 
1754
  //$target = $this->_realpath($target);
1755
  $link = $this->_realpath($link);
1756
 
1757
- $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
1758
- if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1759
  return false;
1760
  }
1761
 
@@ -1788,7 +2049,7 @@ class SFTP extends SSH2
1788
  */
1789
  function mkdir($dir, $mode = -1, $recursive = false)
1790
  {
1791
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1792
  return false;
1793
  }
1794
 
@@ -1857,7 +2118,7 @@ class SFTP extends SSH2
1857
  */
1858
  function rmdir($dir)
1859
  {
1860
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1861
  return false;
1862
  }
1863
 
@@ -1906,7 +2167,8 @@ class SFTP extends SSH2
1906
  * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
1907
  * large $remote_file will be, as well.
1908
  *
1909
- * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data
 
1910
  *
1911
  * If $data is a resource then it'll be used as a resource instead.
1912
  *
@@ -1942,7 +2204,7 @@ class SFTP extends SSH2
1942
  */
1943
  function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
1944
  {
1945
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
1946
  return false;
1947
  }
1948
 
@@ -1953,10 +2215,14 @@ class SFTP extends SSH2
1953
 
1954
  $this->_remove_from_stat_cache($remote_file);
1955
 
1956
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
1957
- // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
1958
- // in practice, it doesn't seem to do that.
1959
- //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
 
 
 
 
1960
 
1961
  if ($start >= 0) {
1962
  $offset = $start;
@@ -1966,10 +2232,17 @@ class SFTP extends SSH2
1966
  $offset = $size !== false ? $size : 0;
1967
  } else {
1968
  $offset = 0;
1969
- $flags|= NET_SFTP_OPEN_TRUNCATE;
 
 
 
 
1970
  }
1971
 
1972
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
 
 
 
1973
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1974
  return false;
1975
  }
@@ -2078,6 +2351,8 @@ class SFTP extends SSH2
2078
  }
2079
  }
2080
 
 
 
2081
  if (!$this->_read_put_responses($i)) {
2082
  if ($mode & self::SOURCE_LOCAL_FILE) {
2083
  fclose($fp);
@@ -2086,18 +2361,33 @@ class SFTP extends SSH2
2086
  return false;
2087
  }
2088
 
2089
- if ($mode & self::SOURCE_LOCAL_FILE) {
2090
- if ($this->preserveTime) {
2091
- $stat = fstat($fp);
2092
- $this->touch($remote_file, $stat['mtime'], $stat['atime']);
2093
- }
2094
-
2095
  if (isset($fp) && is_resource($fp)) {
2096
  fclose($fp);
2097
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2098
  }
2099
 
2100
- return $this->_close_handle($handle);
2101
  }
2102
 
2103
  /**
@@ -2184,7 +2474,7 @@ class SFTP extends SSH2
2184
  */
2185
  function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2186
  {
2187
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2188
  return false;
2189
  }
2190
 
@@ -2193,7 +2483,10 @@ class SFTP extends SSH2
2193
  return false;
2194
  }
2195
 
2196
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
 
 
 
2197
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2198
  return false;
2199
  }
@@ -2293,7 +2586,14 @@ class SFTP extends SSH2
2293
  if ($fclose_check) {
2294
  fclose($fp);
2295
  }
2296
- user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
 
 
 
 
 
 
 
2297
  }
2298
  $response = null;
2299
  }
@@ -2338,7 +2638,7 @@ class SFTP extends SSH2
2338
  */
2339
  function delete($path, $recursive = true)
2340
  {
2341
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2342
  return false;
2343
  }
2344
 
@@ -2467,6 +2767,10 @@ class SFTP extends SSH2
2467
  function file_exists($path)
2468
  {
2469
  if ($this->use_stat_cache) {
 
 
 
 
2470
  $path = $this->_realpath($path);
2471
 
2472
  $result = $this->_query_stat_cache($path);
@@ -2537,6 +2841,10 @@ class SFTP extends SSH2
2537
  */
2538
  function is_readable($path)
2539
  {
 
 
 
 
2540
  $path = $this->_realpath($path);
2541
 
2542
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
@@ -2565,6 +2873,10 @@ class SFTP extends SSH2
2565
  */
2566
  function is_writable($path)
2567
  {
 
 
 
 
2568
  $path = $this->_realpath($path);
2569
 
2570
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
@@ -2745,6 +3057,10 @@ class SFTP extends SSH2
2745
  */
2746
  function _get_xstat_cache_prop($path, $prop, $type)
2747
  {
 
 
 
 
2748
  if ($this->use_stat_cache) {
2749
  $path = $this->_realpath($path);
2750
 
@@ -2765,7 +3081,9 @@ class SFTP extends SSH2
2765
  }
2766
 
2767
  /**
2768
- * Renames a file or a directory on the SFTP server
 
 
2769
  *
2770
  * @param string $oldname
2771
  * @param string $newname
@@ -2774,7 +3092,7 @@ class SFTP extends SSH2
2774
  */
2775
  function rename($oldname, $newname)
2776
  {
2777
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
2778
  return false;
2779
  }
2780
 
@@ -2786,6 +3104,18 @@ class SFTP extends SSH2
2786
 
2787
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2788
  $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
 
 
 
 
 
 
 
 
 
 
 
 
2789
  if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
2790
  return false;
2791
  }
@@ -2815,6 +3145,31 @@ class SFTP extends SSH2
2815
  return true;
2816
  }
2817
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2818
  /**
2819
  * Parse Attributes
2820
  *
@@ -2826,16 +3181,56 @@ class SFTP extends SSH2
2826
  */
2827
  function _parseAttributes(&$response)
2828
  {
 
 
 
 
 
 
 
 
2829
  $attr = array();
2830
- if (strlen($response) < 4) {
2831
  user_error('Malformed file attributes');
2832
  return array();
2833
  }
2834
- extract(unpack('Nflags', $this->_string_shift($response, 4)));
2835
- // SFTPv4+ have a type field (a byte) that follows the above flag field
 
 
2836
  foreach ($this->attributes as $key => $value) {
2837
  switch ($flags & $key) {
2838
- case NET_SFTP_ATTR_SIZE: // 0x00000001
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2839
  // The size attribute is defined as an unsigned 64-bit integer.
2840
  // The following will use floats on 32-bit platforms, if necessary.
2841
  // As can be seen in the BigInteger class, floats are generally
@@ -2844,14 +3239,14 @@ class SFTP extends SSH2
2844
  // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
2845
  $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
2846
  break;
2847
- case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
2848
  if (strlen($response) < 8) {
2849
  user_error('Malformed file attributes');
2850
  return $attr;
2851
  }
2852
  $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
2853
  break;
2854
- case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
2855
  if (strlen($response) < 4) {
2856
  user_error('Malformed file attributes');
2857
  return $attr;
@@ -2865,14 +3260,134 @@ class SFTP extends SSH2
2865
  $attr+= array('type' => $fileType);
2866
  }
2867
  break;
2868
- case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
 
 
 
 
2869
  if (strlen($response) < 8) {
2870
  user_error('Malformed file attributes');
2871
  return $attr;
2872
  }
2873
  $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
2874
  break;
2875
- case NET_SFTP_ATTR_EXTENDED: // 0x80000000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2876
  if (strlen($response) < 4) {
2877
  user_error('Malformed file attributes');
2878
  return $attr;
@@ -3055,6 +3570,8 @@ class SFTP extends SSH2
3055
  */
3056
  function _get_sftp_packet($request_id = null)
3057
  {
 
 
3058
  if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
3059
  $this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
3060
  $temp = $this->requestBuffer[$request_id]['packet'];
@@ -3071,11 +3588,17 @@ class SFTP extends SSH2
3071
  // SFTP packet length
3072
  while (strlen($this->packet_buffer) < 4) {
3073
  $temp = $this->_get_channel_packet(self::CHANNEL, true);
3074
- if (is_bool($temp)) {
 
 
 
3075
  $this->packet_type = false;
3076
  $this->packet_buffer = '';
3077
  return false;
3078
  }
 
 
 
3079
  $this->packet_buffer.= $temp;
3080
  }
3081
  if (strlen($this->packet_buffer) < 4) {
@@ -3085,9 +3608,8 @@ class SFTP extends SSH2
3085
  $tempLength = $length;
3086
  $tempLength-= strlen($this->packet_buffer);
3087
 
3088
-
3089
  // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
3090
- if ($tempLength > 256 * 1024) {
3091
  user_error('Invalid SFTP packet size');
3092
  return false;
3093
  }
@@ -3205,13 +3727,51 @@ class SFTP extends SSH2
3205
  */
3206
  function getSupportedVersions()
3207
  {
3208
- $temp = array('version' => $this->version);
 
 
 
 
 
 
 
 
3209
  if (isset($this->extensions['versions'])) {
3210
  $temp['extensions'] = $this->extensions['versions'];
3211
  }
3212
  return $temp;
3213
  }
3214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3215
  /**
3216
  * Disconnect
3217
  *
5
  *
6
  * PHP version 5
7
  *
8
+ * Supports SFTPv2/3/4/5/6. Defaults to v3.
 
 
9
  *
10
  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
11
  *
152
  */
153
  var $version;
154
 
155
+ /**
156
+ * Default Server SFTP version
157
+ *
158
+ * @var int
159
+ * @see self::_initChannel()
160
+ * @access private
161
+ */
162
+ var $defaultVersion;
163
+
164
+ /**
165
+ * Preferred SFTP version
166
+ *
167
+ * @var int
168
+ * @see self::_initChannel()
169
+ * @access private
170
+ */
171
+ var $preferredVersion = 3;
172
+
173
  /**
174
  * Current working directory
175
  *
285
  */
286
  var $preserveTime = false;
287
 
288
+ /**
289
+ * Arbitrary Length Packets Flag
290
+ *
291
+ * Determines whether or not packets of any length should be allowed,
292
+ * in cases where the server chooses the packet length (such as
293
+ * directory listings). By default, packets are only allowed to be
294
+ * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
295
+ *
296
+ * @see self::enableArbitraryLengthPackets()
297
+ * @see self::_get_sftp_packet()
298
+ * @var bool
299
+ * @access private
300
+ */
301
+ var $allow_arbitrary_length_packets = false;
302
+
303
+ /**
304
+ * Was the last packet due to the channels being closed or not?
305
+ *
306
+ * @see self::get()
307
+ * @see self::get_sftp_packet()
308
+ * @var bool
309
+ * @access private
310
+ */
311
+ var $channel_close = false;
312
+
313
+ /**
314
+ * Has the SFTP channel been partially negotiated?
315
+ *
316
+ * @var bool
317
+ * @access private
318
+ */
319
+ var $partial_init = false;
320
+
321
  /**
322
  * Default Constructor.
323
  *
338
  $this->packet_types = array(
339
  1 => 'NET_SFTP_INIT',
340
  2 => 'NET_SFTP_VERSION',
 
 
 
341
  3 => 'NET_SFTP_OPEN',
342
  4 => 'NET_SFTP_CLOSE',
343
  5 => 'NET_SFTP_READ',
344
  6 => 'NET_SFTP_WRITE',
345
  7 => 'NET_SFTP_LSTAT',
346
  9 => 'NET_SFTP_SETSTAT',
347
+ 10 => 'NET_SFTP_FSETSTAT',
348
  11 => 'NET_SFTP_OPENDIR',
349
  12 => 'NET_SFTP_READDIR',
350
  13 => 'NET_SFTP_REMOVE',
352
  15 => 'NET_SFTP_RMDIR',
353
  16 => 'NET_SFTP_REALPATH',
354
  17 => 'NET_SFTP_STAT',
 
 
 
355
  18 => 'NET_SFTP_RENAME',
356
  19 => 'NET_SFTP_READLINK',
357
  20 => 'NET_SFTP_SYMLINK',
358
+ 21 => 'NET_SFTP_LINK',
359
 
360
  101=> 'NET_SFTP_STATUS',
361
  102=> 'NET_SFTP_HANDLE',
 
 
 
362
  103=> 'NET_SFTP_DATA',
363
  104=> 'NET_SFTP_NAME',
364
  105=> 'NET_SFTP_ATTRS',
403
  // the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why
404
  $this->attributes = array(
405
  0x00000001 => 'NET_SFTP_ATTR_SIZE',
406
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
407
+ 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+
408
  0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
409
  0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
410
+ 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+
411
+ 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
412
+ 0x00000040 => 'NET_SFTP_ATTR_ACL',
413
+ 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
414
+ 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+
415
+ 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
416
+ 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
417
+ 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
418
+ 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
419
+ 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
420
+ 0x00008000 => 'NET_SFTP_ATTR_CTIME',
421
  // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
422
  // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
423
  // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
424
  // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
425
  (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
426
  );
 
 
 
427
  $this->open_flags = array(
428
  0x00000001 => 'NET_SFTP_OPEN_READ',
429
  0x00000002 => 'NET_SFTP_OPEN_WRITE',
430
  0x00000004 => 'NET_SFTP_OPEN_APPEND',
431
  0x00000008 => 'NET_SFTP_OPEN_CREATE',
432
  0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
433
+ 0x00000020 => 'NET_SFTP_OPEN_EXCL',
434
+ 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
435
+ );
436
+ // SFTPv5+ changed the flags up:
437
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
438
+ $this->open_flags5 = array(
439
+ // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
440
+ 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
441
+ 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
442
+ 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
443
+ 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
444
+ 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
445
+ // the rest of the flags are not supported
446
+ 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
447
+ 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
448
+ 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
449
+ 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
450
+ 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
451
+ 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
452
+ 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
453
+ 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
454
+ 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
455
+ 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
456
+ 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
457
+ 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
458
+ 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
459
  );
460
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
461
  // see \phpseclib\Net\SFTP::_parseLongname() for an explanation
477
  $this->status_codes,
478
  $this->attributes,
479
  $this->open_flags,
480
+ $this->open_flags5,
481
  $this->file_types
482
  );
483
 
490
  }
491
 
492
  /**
493
+ * Check a few things before SFTP functions are called
494
  *
 
495
  * @return bool
496
  * @access public
497
  */
498
+ function _precheck()
499
  {
500
+ if (!($this->bitmap & SSH2::MASK_LOGIN)) {
501
  return false;
502
  }
503
 
504
+ if ($this->pwd === false) {
505
+ return $this->_init_sftp_connection();
506
+ }
507
+
508
+ return true;
509
+ }
510
+
511
+ /**
512
+ * Partially initialize an SFTP connection
513
+ *
514
+ * @return bool
515
+ * @access public
516
+ */
517
+ function _partial_init_sftp_connection()
518
+ {
519
  $this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
520
 
521
  $packet = pack(
537
  $response = $this->_get_channel_packet(self::CHANNEL, true);
538
  if ($response === false) {
539
  return false;
540
+ } elseif ($response === true && $this->isTimeout()) {
541
+ return false;
542
  }
543
 
544
  $packet = pack(
585
  if ($response === false) {
586
  return false;
587
  }
588
+ } elseif ($response === true && $this->isTimeout()) {
589
+ return false;
590
  }
591
 
592
  $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
601
  return false;
602
  }
603
 
604
+ $this->use_request_id = true;
605
+
606
  if (strlen($response) < 4) {
607
  return false;
608
  }
609
  extract(unpack('Nversion', $this->_string_shift($response, 4)));
610
+ $this->defaultVersion = $version;
611
  while (!empty($response)) {
612
  if (strlen($response) < 4) {
613
  return false;
622
  $this->extensions[$key] = $value;
623
  }
624
 
625
+ $this->partial_init = true;
 
 
 
 
 
 
 
 
 
 
 
 
626
 
627
+ return true;
628
+ }
629
+
630
+ /**
631
+ * (Re)initializes the SFTP channel
632
+ *
633
+ * @return bool
634
+ * @access private
635
+ */
636
+ function _init_sftp_connection()
637
+ {
638
+ if (!$this->partial_init && !$this->_partial_init_sftp_connection()) {
639
+ return false;
640
+ }
641
 
642
  /*
643
  A Note on SFTPv4/5/6 support:
662
  in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the
663
  channel and reopen it with a new and updated SSH_FXP_INIT packet.
664
  */
665
+ $this->version = $this->defaultVersion;
666
+ if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
667
+ $versions = explode(',', $this->extensions['versions']);
668
+ $supported = array(6, 5, 4);
669
+ if ($this->preferredVersion) {
670
+ $supported = array_diff($supported, array($this->preferredVersion));
671
+ array_unshift($supported, $this->preferredVersion);
672
+ }
673
+ foreach ($supported as $ver) {
674
+ if (in_array($ver, $versions)) {
675
+ if ($ver === $this->version) {
676
+ break;
677
+ }
678
+ $this->version = (int) $ver;
679
+ $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver);
680
+ if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) {
681
+ return false;
682
+ }
683
+ $response = $this->_get_sftp_packet();
684
+ if ($this->packet_type != NET_SFTP_STATUS) {
685
+ user_error('Expected SSH_FXP_STATUS');
686
+ return false;
687
+ }
688
+
689
+ if (strlen($response) < 4) {
690
+ return false;
691
+ }
692
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
693
+ if ($status != NET_SFTP_STATUS_OK) {
694
+ $this->_logError($response, $status);
695
+ return false;
696
+ }
697
+
698
+ break;
699
+ }
700
+ }
701
+ }
702
+
703
+ /*
704
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
705
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
706
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
707
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
708
+ 'newline@vandyke.com' would.
709
+ */
710
+ /*
711
+ if (isset($this->extensions['newline@vandyke.com'])) {
712
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
713
+ unset($this->extensions['newline@vandyke.com']);
714
+ }
715
+ */
716
+
717
+ if ($this->version < 2 || $this->version > 6) {
718
+ return false;
719
  }
720
 
721
  $this->pwd = $this->_realpath('.');
775
  $this->canonicalize_paths = false;
776
  }
777
 
778
+ /**
779
+ * Enable arbitrary length packets
780
+ *
781
+ * @access public
782
+ */
783
+ function enableArbitraryLengthPackets()
784
+ {
785
+ $this->allow_arbitrary_length_packets = true;
786
+ }
787
+
788
+ /**
789
+ * Disable arbitrary length packets
790
+ *
791
+ * @access public
792
+ */
793
+ function disableArbitraryLengthPackets()
794
+ {
795
+ $this->allow_arbitrary_length_packets = false;
796
+ }
797
+
798
  /**
799
  * Returns the current directory name
800
  *
803
  */
804
  function pwd()
805
  {
806
+ if (!$this->_precheck()) {
807
+ return false;
808
+ }
809
+
810
  return $this->pwd;
811
  }
812
 
848
  */
849
  function realpath($path)
850
  {
851
+ if (!$this->_precheck()) {
852
+ return false;
853
+ }
854
+
855
  return $this->_realpath($path);
856
  }
857
 
934
  */
935
  function chdir($dir)
936
  {
937
+ if (!$this->_precheck()) {
938
  return false;
939
  }
940
 
1091
  */
1092
  function _list($dir, $raw = true)
1093
  {
1094
+ if (!$this->_precheck()) {
1095
  return false;
1096
  }
1097
 
1146
  }
1147
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
1148
  $shortname = $this->_string_shift($response, $length);
1149
+ // SFTPv4 "removed the long filename from the names structure-- it can now be
1150
+ // built from information available in the attrs structure."
1151
+ if ($this->version < 4) {
1152
+ if (strlen($response) < 4) {
1153
+ return false;
1154
+ }
1155
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1156
+ $longname = $this->_string_shift($response, $length);
1157
  }
 
 
1158
  $attributes = $this->_parseAttributes($response);
1159
+ if (!isset($attributes['type']) && $this->version < 4) {
1160
  $fileType = $this->_parseLongname($longname);
1161
  if ($fileType) {
1162
  $attributes['type'] = $fileType;
1316
  */
1317
  function size($filename)
1318
  {
 
 
 
 
1319
  $result = $this->stat($filename);
1320
  if ($result === false) {
1321
  return false;
1432
  */
1433
  function stat($filename)
1434
  {
1435
+ if (!$this->_precheck()) {
1436
  return false;
1437
  }
1438
 
1489
  */
1490
  function lstat($filename)
1491
  {
1492
+ if (!$this->_precheck()) {
1493
  return false;
1494
  }
1495
 
1603
  */
1604
  function touch($filename, $time = null, $atime = null)
1605
  {
1606
+ if (!$this->_precheck()) {
1607
  return false;
1608
  }
1609
 
1619
  $atime = $time;
1620
  }
1621
 
1622
+ if ($this->version < 4) {
1623
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time);
1624
+ } else {
1625
+ $attr = pack(
1626
+ 'N5',
1627
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
1628
+ $atime / 4294967296,
1629
+ $atime,
1630
+ $time / 4294967296,
1631
+ $time
1632
+ );
1633
+ }
1634
+
1635
+ $packet = pack('Na*', strlen($filename), $filename);
1636
+ $packet.= $this->version >= 5 ?
1637
+ pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
1638
+ pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
1639
+ $packet.= $attr;
1640
+
1641
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1642
  return false;
1643
  }
1660
  /**
1661
  * Changes file or directory owner
1662
  *
1663
+ * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
1664
+ * would be of the form "user@dns_domain" but it does not need to be.
1665
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
1666
+ * that's being used.
1667
+ *
1668
  * Returns true on success or false on error.
1669
  *
1670
  * @param string $filename
1671
+ * @param int|string $uid
1672
  * @param bool $recursive
1673
  * @return bool
1674
  * @access public
1675
  */
1676
  function chown($filename, $uid, $recursive = false)
1677
  {
1678
+ /*
1679
+ quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1680
+
1681
+ "To avoid a representation that is tied to a particular underlying
1682
+ implementation at the client or server, the use of UTF-8 strings has
1683
+ been chosen. The string should be of the form "user@dns_domain".
1684
+ This will allow for a client and server that do not use the same
1685
+ local representation the ability to translate to a common syntax that
1686
+ can be interpreted by both. In the case where there is no
1687
+ translation available to the client or server, the attribute value
1688
+ must be constructed without the "@"."
1689
+
1690
+ phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't
1691
+ have one? phpseclib would have no way of knowing so rather than guess phpseclib
1692
+ will just use whatever value the user provided
1693
+ */
1694
+
1695
+ $attr = $this->version < 4 ?
1696
+ // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1697
+ // "if the owner or group is specified as -1, then that ID is not changed"
1698
+ pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :
1699
+ // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1700
+ // "If either the owner or group field is zero length, the field should be
1701
+ // considered absent, and no change should be made to that specific field
1702
+ // during a modification operation"
1703
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');
1704
 
1705
  return $this->_setstat($filename, $attr, $recursive);
1706
  }
1708
  /**
1709
  * Changes file or directory group
1710
  *
1711
+ * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
1712
+ * would be of the form "user@dns_domain" but it does not need to be.
1713
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
1714
+ * that's being used.
1715
+ *
1716
  * Returns true on success or false on error.
1717
  *
1718
  * @param string $filename
1719
+ * @param int|string $gid
1720
  * @param bool $recursive
1721
  * @return bool
1722
  * @access public
1723
  */
1724
  function chgrp($filename, $gid, $recursive = false)
1725
  {
1726
+ $attr = $this->version < 4 ?
1727
+ pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) :
1728
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid);
1729
 
1730
  return $this->_setstat($filename, $attr, $recursive);
1731
  }
1792
  */
1793
  function _setstat($filename, $attr, $recursive)
1794
  {
1795
+ if (!$this->_precheck()) {
1796
  return false;
1797
  }
1798
 
1810
  return $result;
1811
  }
1812
 
1813
+ $packet = $this->version >= 4 ?
1814
+ pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
1815
+ pack('Na*a*', strlen($filename), $filename, $attr);
1816
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1817
  return false;
1818
  }
1819
 
1883
  return false;
1884
  }
1885
  } else {
1886
+ $packet = $this->version >= 4 ?
1887
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
1888
+ pack('Na*a*', strlen($temp), $temp, $attr);
1889
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1890
  return false;
1891
  }
1892
 
1901
  }
1902
  }
1903
 
1904
+ $packet = $this->version >= 4 ?
1905
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
1906
+ pack('Na*a*', strlen($temp), $temp, $attr);
1907
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1908
  return false;
1909
  }
1910
 
1929
  */
1930
  function readlink($link)
1931
  {
1932
+ if (!$this->_precheck()) {
1933
  return false;
1934
  }
1935
 
1979
  */
1980
  function symlink($target, $link)
1981
  {
1982
+ if (!$this->_precheck()) {
1983
  return false;
1984
  }
1985
 
1986
  //$target = $this->_realpath($target);
1987
  $link = $this->_realpath($link);
1988
 
1989
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 :
1990
+
1991
+ Changed the SYMLINK packet to be LINK and give it the ability to
1992
+ create hard links. Also change it's packet number because many
1993
+ implementation implemented SYMLINK with the arguments reversed.
1994
+ Hopefully the new argument names make it clear which way is which.
1995
+ */
1996
+ if ($this->version == 6) {
1997
+ $type = NET_SFTP_LINK;
1998
+ $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1);
1999
+ } else {
2000
+ $type = NET_SFTP_SYMLINK;
2001
+ /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
2002
+
2003
+ 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
2004
+
2005
+ When OpenSSH's sftp-server was implemented, the order of the arguments
2006
+ to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
2007
+ the reversal was not noticed until the server was widely deployed. Since
2008
+ fixing this to follow the specification would cause incompatibility, the
2009
+ current order was retained. For correct operation, clients should send
2010
+ SSH_FXP_SYMLINK as follows:
2011
+
2012
+ uint32 id
2013
+ string targetpath
2014
+ string linkpath */
2015
+ $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
2016
+ pack('Na*Na*', strlen($target), $target, strlen($link), $link) :
2017
+ pack('Na*Na*', strlen($link), $link, strlen($target), $target);
2018
+ }
2019
+ if (!$this->_send_sftp_packet($type, $packet)) {
2020
  return false;
2021
  }
2022
 
2049
  */
2050
  function mkdir($dir, $mode = -1, $recursive = false)
2051
  {
2052
+ if (!$this->_precheck()) {
2053
  return false;
2054
  }
2055
 
2118
  */
2119
  function rmdir($dir)
2120
  {
2121
+ if (!$this->_precheck()) {
2122
  return false;
2123
  }
2124
 
2167
  * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
2168
  * large $remote_file will be, as well.
2169
  *
2170
+ * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number
2171
+ * of bytes to return, and returns a string if there is some data or null if there is no more data
2172
  *
2173
  * If $data is a resource then it'll be used as a resource instead.
2174
  *
2204
  */
2205
  function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
2206
  {
2207
+ if (!$this->_precheck()) {
2208
  return false;
2209
  }
2210
 
2215
 
2216
  $this->_remove_from_stat_cache($remote_file);
2217
 
2218
+ if ($this->version >= 5) {
2219
+ $flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
2220
+ } else {
2221
+ $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
2222
+ // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
2223
+ // in practice, it doesn't seem to do that.
2224
+ //$flags|= ($mode & SFTP::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
2225
+ }
2226
 
2227
  if ($start >= 0) {
2228
  $offset = $start;
2232
  $offset = $size !== false ? $size : 0;
2233
  } else {
2234
  $offset = 0;
2235
+ if ($this->version >= 5) {
2236
+ $flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
2237
+ } else {
2238
+ $flags|= NET_SFTP_OPEN_TRUNCATE;
2239
+ }
2240
  }
2241
 
2242
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
2243
+ $packet.= $this->version >= 5 ?
2244
+ pack('N3', 0, $flags, 0) :
2245
+ pack('N2', $flags, 0);
2246
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2247
  return false;
2248
  }
2351
  }
2352
  }
2353
 
2354
+ $result = $this->_close_handle($handle);
2355
+
2356
  if (!$this->_read_put_responses($i)) {
2357
  if ($mode & self::SOURCE_LOCAL_FILE) {
2358
  fclose($fp);
2361
  return false;
2362
  }
2363
 
2364
+ if ($mode & SFTP::SOURCE_LOCAL_FILE) {
 
 
 
 
 
2365
  if (isset($fp) && is_resource($fp)) {
2366
  fclose($fp);
2367
  }
2368
+
2369
+ if ($this->preserveTime) {
2370
+ $stat = stat($data);
2371
+ if ($this->version < 4) {
2372
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']);
2373
+ } else {
2374
+ $attr = pack(
2375
+ 'N5',
2376
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
2377
+ $stat['atime'] / 4294967296,
2378
+ $stat['atime'],
2379
+ $stat['mtime'] / 4294967296,
2380
+ $stat['mtime']
2381
+ );
2382
+ }
2383
+
2384
+ if (!$this->_setstat($remote_file, $attr, false)) {
2385
+ user_error('Error setting file time');
2386
+ }
2387
+ }
2388
  }
2389
 
2390
+ return $result;
2391
  }
2392
 
2393
  /**
2474
  */
2475
  function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2476
  {
2477
+ if (!$this->_precheck()) {
2478
  return false;
2479
  }
2480
 
2483
  return false;
2484
  }
2485
 
2486
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
2487
+ $packet.= $this->version >= 5 ?
2488
+ pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
2489
+ pack('N2', NET_SFTP_OPEN_READ, 0);
2490
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2491
  return false;
2492
  }
2586
  if ($fclose_check) {
2587
  fclose($fp);
2588
  }
2589
+ // maybe the file was successfully transferred, maybe it wasn't
2590
+ if ($this->channel_close) {
2591
+ $this->partial_init = false;
2592
+ $this->_init_sftp_connection();
2593
+ return false;
2594
+ } else {
2595
+ user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
2596
+ }
2597
  }
2598
  $response = null;
2599
  }
2638
  */
2639
  function delete($path, $recursive = true)
2640
  {
2641
+ if (!$this->_precheck()) {
2642
  return false;
2643
  }
2644
 
2767
  function file_exists($path)
2768
  {
2769
  if ($this->use_stat_cache) {
2770
+ if (!$this->_precheck()) {
2771
+ return false;
2772
+ }
2773
+
2774
  $path = $this->_realpath($path);
2775
 
2776
  $result = $this->_query_stat_cache($path);
2841
  */
2842
  function is_readable($path)
2843
  {
2844
+ if (!$this->_precheck()) {
2845
+ return false;
2846
+ }
2847
+
2848
  $path = $this->_realpath($path);
2849
 
2850
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
2873
  */
2874
  function is_writable($path)
2875
  {
2876
+ if (!$this->_precheck()) {
2877
+ return false;
2878
+ }
2879
+
2880
  $path = $this->_realpath($path);
2881
 
2882
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
3057
  */
3058
  function _get_xstat_cache_prop($path, $prop, $type)
3059
  {
3060
+ if (!$this->_precheck()) {
3061
+ return false;
3062
+ }
3063
+
3064
  if ($this->use_stat_cache) {
3065
  $path = $this->_realpath($path);
3066
 
3081
  }
3082
 
3083
  /**
3084
+ * Renames a file or a directory on the SFTP server.
3085
+ *
3086
+ * If the file already exists this will return false
3087
  *
3088
  * @param string $oldname
3089
  * @param string $newname
3092
  */
3093
  function rename($oldname, $newname)
3094
  {
3095
+ if (!$this->_precheck()) {
3096
  return false;
3097
  }
3098
 
3104
 
3105
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
3106
  $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
3107
+ if ($this->version >= 5) {
3108
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 ,
3109
+
3110
+ 'flags' is 0 or a combination of:
3111
+
3112
+ SSH_FXP_RENAME_OVERWRITE 0x00000001
3113
+ SSH_FXP_RENAME_ATOMIC 0x00000002
3114
+ SSH_FXP_RENAME_NATIVE 0x00000004
3115
+
3116
+ (none of these are currently supported) */
3117
+ $packet.= "\0\0\0\0";
3118
+ }
3119
  if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
3120
  return false;
3121
  }
3145
  return true;
3146
  }
3147
 
3148
+ /**
3149
+ * Parse Time
3150
+ *
3151
+ * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info.
3152
+ *
3153
+ * @param string $key
3154
+ * @param int $flags
3155
+ * @param string $response
3156
+ * @return array
3157
+ * @access private
3158
+ */
3159
+ function _parseTime($key, $flags, &$response)
3160
+ {
3161
+ if (strlen($response) < 8) {
3162
+ user_error('Malformed file attributes');
3163
+ return array();
3164
+ }
3165
+ $attr = array();
3166
+ $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8)));
3167
+ if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
3168
+ $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4)));
3169
+ }
3170
+ return $attr;
3171
+ }
3172
+
3173
  /**
3174
  * Parse Attributes
3175
  *
3181
  */
3182
  function _parseAttributes(&$response)
3183
  {
3184
+ if ($this->version >= 4) {
3185
+ $length = 5;
3186
+ $format = 'Nflags/Ctype';
3187
+ } else {
3188
+ $length = 4;
3189
+ $format = 'Nflags';
3190
+ }
3191
+
3192
  $attr = array();
3193
+ if (strlen($response) < $length) {
3194
  user_error('Malformed file attributes');
3195
  return array();
3196
  }
3197
+ extract(unpack($format, $this->_string_shift($response, $length)));
3198
+ if (isset($type)) {
3199
+ $attr['type'] = $type;
3200
+ }
3201
  foreach ($this->attributes as $key => $value) {
3202
  switch ($flags & $key) {
3203
+ case NET_SFTP_ATTR_UIDGID:
3204
+ if ($this->version > 3) {
3205
+ continue 2;
3206
+ }
3207
+ break;
3208
+ case NET_SFTP_ATTR_CREATETIME:
3209
+ case NET_SFTP_ATTR_MODIFYTIME:
3210
+ case NET_SFTP_ATTR_ACL:
3211
+ case NET_SFTP_ATTR_OWNERGROUP:
3212
+ case NET_SFTP_ATTR_SUBSECOND_TIMES:
3213
+ if ($this->version < 4) {
3214
+ continue 2;
3215
+ }
3216
+ break;
3217
+ case NET_SFTP_ATTR_BITS:
3218
+ if ($this->version < 5) {
3219
+ continue 2;
3220
+ }
3221
+ break;
3222
+ case NET_SFTP_ATTR_ALLOCATION_SIZE:
3223
+ case NET_SFTP_ATTR_TEXT_HINT:
3224
+ case NET_SFTP_ATTR_MIME_TYPE:
3225
+ case NET_SFTP_ATTR_LINK_COUNT:
3226
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:
3227
+ case NET_SFTP_ATTR_CTIME:
3228
+ if ($this->version < 6) {
3229
+ continue 2;
3230
+ }
3231
+ }
3232
+ switch ($flags & $key) {
3233
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
3234
  // The size attribute is defined as an unsigned 64-bit integer.
3235
  // The following will use floats on 32-bit platforms, if necessary.
3236
  // As can be seen in the BigInteger class, floats are generally
3239
  // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
3240
  $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
3241
  break;
3242
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier)
3243
  if (strlen($response) < 8) {
3244
  user_error('Malformed file attributes');
3245
  return $attr;
3246
  }
3247
  $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
3248
  break;
3249
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
3250
  if (strlen($response) < 4) {
3251
  user_error('Malformed file attributes');
3252
  return $attr;
3260
  $attr+= array('type' => $fileType);
3261
  }
3262
  break;
3263
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
3264
+ if ($this->version >= 4) {
3265
+ $attr+= $this->_parseTime('atime', $flags, $response);
3266
+ break;
3267
+ }
3268
  if (strlen($response) < 8) {
3269
  user_error('Malformed file attributes');
3270
  return $attr;
3271
  }
3272
  $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
3273
  break;
3274
+ case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+)
3275
+ $attr+= $this->_parseTime('createtime', $flags, $response);
3276
+ break;
3277
+ case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020
3278
+ $attr+= $this->_parseTime('mtime', $flags, $response);
3279
+ break;
3280
+ case NET_SFTP_ATTR_ACL: // 0x00000040
3281
+ // access control list
3282
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
3283
+ // currently unsupported
3284
+ if (strlen($response) < 4) {
3285
+ user_error('Malformed file attributes');
3286
+ return $attr;
3287
+ }
3288
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
3289
+ for ($i = 0; $i < $count; $i++) {
3290
+ if (strlen($response) < 16) {
3291
+ user_error('Malformed file attributes');
3292
+ return $attr;
3293
+ }
3294
+ extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16)));
3295
+ if (strlen($response) < $length) {
3296
+ user_error('Malformed file attributes');
3297
+ return $attr;
3298
+ }
3299
+ $this->_string_shift($response, $length); // who
3300
+ }
3301
+ break;
3302
+ case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080
3303
+ if (strlen($response) < 4) {
3304
+ user_error('Malformed file attributes');
3305
+ return $attr;
3306
+ }
3307
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3308
+ if (strlen($response) < $length) {
3309
+ user_error('Malformed file attributes');
3310
+ return $attr;
3311
+ }
3312
+ $attr['owner'] = $this->_string_shift($response, $length);
3313
+
3314
+ if (strlen($response) < 4) {
3315
+ user_error('Malformed file attributes');
3316
+ return $attr;
3317
+ }
3318
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3319
+ if (strlen($response) < $length) {
3320
+ user_error('Malformed file attributes');
3321
+ return $attr;
3322
+ }
3323
+ $attr['group'] = $this->_string_shift($response, $length);
3324
+ break;
3325
+ case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100
3326
+ break;
3327
+ case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+)
3328
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
3329
+ // currently unsupported
3330
+ // tells if you file is:
3331
+ // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
3332
+ // append only, immutable, sync
3333
+ if (strlen($response) < 8) {
3334
+ user_error('Malformed file attributes');
3335
+ return $attr;
3336
+ }
3337
+ extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8)));
3338
+ break;
3339
+ case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+)
3340
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
3341
+ // represents the number of bytes htat the file consumes on the disk. will
3342
+ // usually be larger than the 'size' field
3343
+ $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
3344
+ break;
3345
+ case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800
3346
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
3347
+ // currently unsupported
3348
+ // tells if file is "known text", "guessed text", "known binary", "guessed binary"
3349
+ extract(unpack('Ctext-hint', $this->_string_shift($response)));
3350
+ break;
3351
+ case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000
3352
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
3353
+ if (strlen($response) < 4) {
3354
+ user_error('Malformed file attributes');
3355
+ return $attr;
3356
+ }
3357
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3358
+ if (strlen($response) < $length) {
3359
+ user_error('Malformed file attributes');
3360
+ return $attr;
3361
+ }
3362
+ $attr['mime-type'] = $this->_string_shift($response, $length);
3363
+ break;
3364
+ case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000
3365
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
3366
+ if (strlen($response) < 4) {
3367
+ user_error('Malformed file attributes');
3368
+ return $attr;
3369
+ }
3370
+ $attr+= unpack('Nlink-count', $this->_string_shift($response, 4));
3371
+ break;
3372
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000
3373
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
3374
+ if (strlen($response) < 4) {
3375
+ user_error('Malformed file attributes');
3376
+ return $attr;
3377
+ }
3378
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3379
+ if (strlen($response) < $length) {
3380
+ user_error('Malformed file attributes');
3381
+ return $attr;
3382
+ }
3383
+ $attr['untranslated-name'] = $this->_string_shift($response, $length);
3384
+ break;
3385
+ case NET_SFTP_ATTR_CTIME: // 0x00008000
3386
+ // 'ctime' contains the last time the file attributes were changed. The
3387
+ // exact meaning of this field depends on the server.
3388
+ $attr+= $this->_parseTime('ctime', $flags, $response);
3389
+ break;
3390
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
3391
  if (strlen($response) < 4) {
3392
  user_error('Malformed file attributes');
3393
  return $attr;
3570
  */
3571
  function _get_sftp_packet($request_id = null)
3572
  {
3573
+ $this->channel_close = false;
3574
+
3575
  if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
3576
  $this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
3577
  $temp = $this->requestBuffer[$request_id]['packet'];
3588
  // SFTP packet length
3589
  while (strlen($this->packet_buffer) < 4) {
3590
  $temp = $this->_get_channel_packet(self::CHANNEL, true);
3591
+ if ($temp === true) {
3592
+ if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
3593
+ $this->channel_close = true;
3594
+ }
3595
  $this->packet_type = false;
3596
  $this->packet_buffer = '';
3597
  return false;
3598
  }
3599
+ if ($temp === false) {
3600
+ return false;
3601
+ }
3602
  $this->packet_buffer.= $temp;
3603
  }
3604
  if (strlen($this->packet_buffer) < 4) {
3608
  $tempLength = $length;
3609
  $tempLength-= strlen($this->packet_buffer);
3610
 
 
3611
  // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
3612
+ if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
3613
  user_error('Invalid SFTP packet size');
3614
  return false;
3615
  }
3727
  */
3728
  function getSupportedVersions()
3729
  {
3730
+ if (!($this->bitmap & SSH2::MASK_LOGIN)) {
3731
+ return false;
3732
+ }
3733
+
3734
+ if (!$this->partial_init) {
3735
+ $this->_partial_init_sftp_connection();
3736
+ }
3737
+
3738
+ $temp = array('version' => $this->defaultVersion);
3739
  if (isset($this->extensions['versions'])) {
3740
  $temp['extensions'] = $this->extensions['versions'];
3741
  }
3742
  return $temp;
3743
  }
3744
 
3745
+ /**
3746
+ * Get supported SFTP versions
3747
+ *
3748
+ * @return array
3749
+ * @access public
3750
+ */
3751
+ function getNegotiatedVersion()
3752
+ {
3753
+ if (!$this->_precheck()) {
3754
+ return false;
3755
+ }
3756
+
3757
+ return $this->version;
3758
+ }
3759
+
3760
+ /**
3761
+ * Set preferred version
3762
+ *
3763
+ * If you're preferred version isn't supported then the highest supported
3764
+ * version of SFTP will be utilized. Set to null or false or int(0) to
3765
+ * unset the preferred version
3766
+ *
3767
+ * @param int $version
3768
+ * @access public
3769
+ */
3770
+ function setPreferredVersion($version)
3771
+ {
3772
+ $this->preferredVersion = $version;
3773
+ }
3774
+
3775
  /**
3776
  * Disconnect
3777
  *
vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php CHANGED
@@ -70,6 +70,25 @@ use phpseclib\System\SSH\Agent;
70
  */
71
  class SSH2
72
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  /**#@+
74
  * Execution Bitmap Masks
75
  *
@@ -970,6 +989,71 @@ class SSH2
970
  */
971
  var $auth = array();
972
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
973
  /**
974
  * Default Constructor.
975
  *
@@ -1210,8 +1294,8 @@ class SSH2
1210
  $read = array($this->fsock);
1211
  $write = $except = null;
1212
  $start = microtime(true);
1213
- $sec = floor($this->curTimeout);
1214
- $usec = 1000000 * ($this->curTimeout - $sec);
1215
  // on windows this returns a "Warning: Invalid CRT parameters detected" error
1216
  // the !count() is done as a workaround for <https://bugs.php.net/42682>
1217
  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
@@ -1226,6 +1310,7 @@ class SSH2
1226
  if (strlen($temp) == 255) {
1227
  continue;
1228
  }
 
1229
  if ($temp === false) {
1230
  return false;
1231
  }
@@ -1347,6 +1432,7 @@ class SSH2
1347
  function _key_exchange($kexinit_payload_server = false)
1348
  {
1349
  $preferred = $this->preferred;
 
1350
 
1351
  $kex_algorithms = isset($preferred['kex']) ?
1352
  $preferred['kex'] :
@@ -1430,7 +1516,7 @@ class SSH2
1430
  0
1431
  );
1432
 
1433
- if ($this->send_kex_first) {
1434
  if (!$this->_send_binary_packet($kexinit_payload_client)) {
1435
  return false;
1436
  }
@@ -1446,6 +1532,8 @@ class SSH2
1446
  user_error('Expected SSH_MSG_KEXINIT');
1447
  return false;
1448
  }
 
 
1449
  }
1450
 
1451
  $response = $kexinit_payload_server;
@@ -1518,7 +1606,7 @@ class SSH2
1518
  extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1519
  $first_kex_packet_follows = $first_kex_packet_follows != 0;
1520
 
1521
- if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
1522
  return false;
1523
  }
1524
 
@@ -1558,19 +1646,25 @@ class SSH2
1558
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1559
  }
1560
 
 
 
 
 
 
 
1561
  $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1562
  if ($compression_algorithm_out === false) {
1563
  user_error('No compatible client to server compression algorithms found');
1564
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1565
  }
1566
- //$this->decompress = $compression_algorithm_out == 'zlib';
1567
 
1568
- $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
1569
  if ($compression_algorithm_in === false) {
1570
  user_error('No compatible server to client compression algorithms found');
1571
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1572
  }
1573
- //$this->compress = $compression_algorithm_in == 'zlib';
1574
 
1575
  // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
1576
  $exchange_hash_rfc4419 = '';
@@ -2003,6 +2097,8 @@ class SSH2
2003
  }
2004
  $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
2005
 
 
 
2006
  return true;
2007
  }
2008
 
@@ -2131,7 +2227,7 @@ class SSH2
2131
 
2132
  // try logging with 'none' as an authentication method first since that's what
2133
  // PuTTY does
2134
- if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP') {
2135
  if ($this->_login($username)) {
2136
  return true;
2137
  }
@@ -2163,9 +2259,61 @@ class SSH2
2163
  return $this->_login_helper($username);
2164
  }
2165
 
2166
- foreach ($args as $arg) {
2167
- if ($this->_login_helper($username, $arg)) {
2168
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2169
  }
2170
  }
2171
  return false;
@@ -2275,7 +2423,9 @@ class SSH2
2275
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2276
  $this->bitmap |= self::MASK_LOGIN;
2277
  return true;
2278
- //case NET_SSH2_MSG_USERAUTH_FAILURE:
 
 
2279
  default:
2280
  return false;
2281
  }
@@ -2347,6 +2497,7 @@ class SSH2
2347
  }
2348
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2349
  $auth_methods = explode(',', $this->_string_shift($response, $length));
 
2350
  if (!strlen($response)) {
2351
  return false;
2352
  }
@@ -2519,6 +2670,8 @@ class SSH2
2519
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2520
  return true;
2521
  case NET_SSH2_MSG_USERAUTH_FAILURE:
 
 
2522
  return false;
2523
  }
2524
 
@@ -2627,8 +2780,9 @@ class SSH2
2627
  if (strlen($response) < 4) {
2628
  return false;
2629
  }
2630
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
2631
- $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
 
2632
  return false;
2633
  case NET_SSH2_MSG_USERAUTH_PK_OK:
2634
  // we'll just take it on faith that the public key blob and the public key algorithm name are as
@@ -2669,6 +2823,8 @@ class SSH2
2669
  switch ($type) {
2670
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2671
  // either the login is bad or the server employs multi-factor authentication
 
 
2672
  return false;
2673
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2674
  $this->bitmap |= self::MASK_LOGIN;
@@ -2795,26 +2951,12 @@ class SSH2
2795
  return false;
2796
  }
2797
 
2798
- $response = $this->_get_binary_packet();
2799
- if ($response === false) {
2800
- $this->bitmap = 0;
2801
- user_error('Connection closed by server');
2802
- return false;
2803
- }
2804
-
2805
- if (!strlen($response)) {
2806
- return false;
2807
  }
2808
- list(, $type) = unpack('C', $this->_string_shift($response, 1));
2809
 
2810
- switch ($type) {
2811
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
2812
- break;
2813
- case NET_SSH2_MSG_CHANNEL_FAILURE:
2814
- default:
2815
- user_error('Unable to request pseudo-terminal');
2816
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2817
- }
2818
  $this->in_request_pty_exec = true;
2819
  }
2820
 
@@ -2935,6 +3077,13 @@ class SSH2
2935
  return false;
2936
  }
2937
 
 
 
 
 
 
 
 
2938
  $packet = pack(
2939
  'CNNa*C',
2940
  NET_SSH2_MSG_CHANNEL_REQUEST,
@@ -2947,7 +3096,12 @@ class SSH2
2947
  return false;
2948
  }
2949
 
2950
- $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_IGNORE;
 
 
 
 
 
2951
 
2952
  $this->bitmap |= self::MASK_SHELL;
2953
 
@@ -3319,7 +3473,7 @@ class SSH2
3319
  $read = array($this->fsock);
3320
  $write = $except = null;
3321
 
3322
- if ($this->curTimeout <= 0) {
3323
  if ($this->keepAlive <= 0) {
3324
  @stream_select($read, $write, $except, null);
3325
  } else {
@@ -3350,8 +3504,8 @@ class SSH2
3350
  $this->curTimeout-= $elapsed;
3351
  }
3352
 
3353
- $sec = floor($this->curTimeout);
3354
- $usec = 1000000 * ($this->curTimeout - $sec);
3355
 
3356
  // on windows this returns a "Warning: Invalid CRT parameters detected" error
3357
  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
@@ -3365,7 +3519,7 @@ class SSH2
3365
 
3366
  if (!is_resource($this->fsock) || feof($this->fsock)) {
3367
  $this->bitmap = 0;
3368
- user_error('Connection closed prematurely');
3369
  return false;
3370
  }
3371
 
@@ -3373,7 +3527,8 @@ class SSH2
3373
  $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
3374
 
3375
  if (!strlen($raw)) {
3376
- return '';
 
3377
  }
3378
 
3379
  if ($this->decrypt !== false) {
@@ -3436,9 +3591,41 @@ class SSH2
3436
  }
3437
  }
3438
 
3439
- //if ($this->decompress) {
3440
- // $payload = gzinflate(substr($payload, 2));
3441
- //}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3442
 
3443
  $this->get_seq_no++;
3444
 
@@ -3513,6 +3700,10 @@ class SSH2
3513
 
3514
  // only called when we've already logged in
3515
  if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
 
 
 
 
3516
  switch (ord($payload[0])) {
3517
  case NET_SSH2_MSG_CHANNEL_REQUEST:
3518
  if (strlen($payload) == 31) {
@@ -3714,7 +3905,20 @@ class SSH2
3714
  function _get_channel_packet($client_channel, $skip_extended = false)
3715
  {
3716
  if (!empty($this->channel_buffers[$client_channel])) {
3717
- return array_shift($this->channel_buffers[$client_channel]);
 
 
 
 
 
 
 
 
 
 
 
 
 
3718
  }
3719
 
3720
  while (true) {
@@ -3788,10 +3992,7 @@ class SSH2
3788
  if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3789
  return $data;
3790
  }
3791
- if (!isset($this->channel_buffers[$channel])) {
3792
- $this->channel_buffers[$channel] = array();
3793
- }
3794
- $this->channel_buffers[$channel][] = $data;
3795
 
3796
  continue 2;
3797
  case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3870,20 +4071,15 @@ class SSH2
3870
  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3871
  $this->_on_channel_open();
3872
  return $result;
3873
- //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3874
- default:
3875
  user_error('Unable to open channel');
3876
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3877
- }
3878
- break;
3879
- case NET_SSH2_MSG_IGNORE:
3880
- switch ($type) {
3881
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
3882
- //$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA;
3883
- continue 3;
3884
- case NET_SSH2_MSG_CHANNEL_FAILURE:
3885
- user_error('Error opening channel');
3886
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3887
  }
3888
  break;
3889
  case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3892,6 +4088,14 @@ class SSH2
3892
  return true;
3893
  case NET_SSH2_MSG_CHANNEL_FAILURE:
3894
  return false;
 
 
 
 
 
 
 
 
3895
  default:
3896
  user_error('Unable to fulfill channel request');
3897
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
@@ -3905,10 +4109,6 @@ class SSH2
3905
 
3906
  switch ($type) {
3907
  case NET_SSH2_MSG_CHANNEL_DATA:
3908
- //if ($this->channel_status[$channel] == NET_SSH2_MSG_IGNORE) {
3909
- // $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA;
3910
- //}
3911
-
3912
  /*
3913
  if ($channel == self::CHANNEL_EXEC) {
3914
  // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
@@ -3935,10 +4135,7 @@ class SSH2
3935
  if ($client_channel == $channel) {
3936
  return $data;
3937
  }
3938
- if (!isset($this->channel_buffers[$channel])) {
3939
- $this->channel_buffers[$channel] = array();
3940
- }
3941
- $this->channel_buffers[$channel][] = $data;
3942
  break;
3943
  case NET_SSH2_MSG_CHANNEL_CLOSE:
3944
  $this->curTimeout = 5;
@@ -3957,7 +4154,7 @@ class SSH2
3957
  case NET_SSH2_MSG_CHANNEL_EOF:
3958
  break;
3959
  default:
3960
- user_error('Error reading channel data');
3961
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3962
  }
3963
  }
@@ -3982,11 +4179,27 @@ class SSH2
3982
  return false;
3983
  }
3984
 
3985
- //if ($this->compress) {
3986
- // // the -4 removes the checksum:
3987
- // // http://php.net/function.gzcompress#57710
3988
- // $data = substr(gzcompress($data), 0, -4);
3989
- //}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3990
 
3991
  // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3992
  $packet_length = strlen($data) + 9;
@@ -4014,10 +4227,10 @@ class SSH2
4014
 
4015
  if (defined('NET_SSH2_LOGGING')) {
4016
  $current = microtime(true);
4017
- $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
4018
  $message_number = '-> ' . $message_number .
4019
  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
4020
- $this->_append_log($message_number, isset($logged) ? $logged : $data);
4021
  $this->last_packet = $current;
4022
  }
4023
 
@@ -4712,10 +4925,12 @@ class SSH2
4712
  */
4713
  function getSupportedCompressionAlgorithms()
4714
  {
4715
- return array(
4716
- 'none' // REQUIRED no compression
4717
- //'zlib' // OPTIONAL ZLIB (LZ77) compression
4718
- );
 
 
4719
  }
4720
 
4721
  /**
@@ -4730,18 +4945,24 @@ class SSH2
4730
  {
4731
  $this->_connect();
4732
 
 
 
 
 
 
 
4733
  return array(
4734
  'kex' => $this->kex_algorithm,
4735
  'hostkey' => $this->signature_format,
4736
  'client_to_server' => array(
4737
  'crypt' => $this->encrypt->name,
4738
  'mac' => $this->hmac_create->name,
4739
- 'comp' => 'none',
4740
  ),
4741
  'server_to_client' => array(
4742
  'crypt' => $this->decrypt->name,
4743
  'mac' => $this->hmac_check->name,
4744
- 'comp' => 'none',
4745
  )
4746
  );
4747
  }
@@ -5143,4 +5364,31 @@ class SSH2
5143
  );
5144
  }
5145
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5146
  }
70
  */
71
  class SSH2
72
  {
73
+ /**#@+
74
+ * Compression Types
75
+ *
76
+ * @access private
77
+ */
78
+ /**
79
+ * No compression
80
+ */
81
+ const NET_SSH2_COMPRESSION_NONE = 1;
82
+ /**
83
+ * zlib compression
84
+ */
85
+ const NET_SSH2_COMPRESSION_ZLIB = 2;
86
+ /**
87
+ * zlib@openssh.com
88
+ */
89
+ const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;
90
+ /**#@-*/
91
+
92
  /**#@+
93
  * Execution Bitmap Masks
94
  *
989
  */
990
  var $auth = array();
991
 
992
+ /**
993
+ * The authentication methods that may productively continue authentication.
994
+ *
995
+ * @see https://tools.ietf.org/html/rfc4252#section-5.1
996
+ * @var array|null
997
+ * @access private
998
+ */
999
+ var $auth_methods_to_continue = null;
1000
+
1001
+ /**
1002
+ * Compression method
1003
+ *
1004
+ * @var int
1005
+ * @access private
1006
+ */
1007
+ var $compress = self::NET_SSH2_COMPRESSION_NONE;
1008
+
1009
+ /**
1010
+ * Decompression method
1011
+ *
1012
+ * @var resource|object
1013
+ * @access private
1014
+ */
1015
+ var $decompress = self::NET_SSH2_COMPRESSION_NONE;
1016
+
1017
+ /**
1018
+ * Compression context
1019
+ *
1020
+ * @var int
1021
+ * @access private
1022
+ */
1023
+ var $compress_context;
1024
+
1025
+ /**
1026
+ * Decompression context
1027
+ *
1028
+ * @var resource|object
1029
+ * @access private
1030
+ */
1031
+ var $decompress_context;
1032
+
1033
+ /**
1034
+ * Regenerate Compression Context
1035
+ *
1036
+ * @var bool
1037
+ * @access private
1038
+ */
1039
+ var $regenerate_compression_context = false;
1040
+
1041
+ /**
1042
+ * Regenerate Decompression Context
1043
+ *
1044
+ * @var bool
1045
+ * @access private
1046
+ */
1047
+ var $regenerate_decompression_context = false;
1048
+
1049
+ /**
1050
+ * Smart multi-factor authentication flag
1051
+ *
1052
+ * @var bool
1053
+ * @access private
1054
+ */
1055
+ var $smartMFA = true;
1056
+
1057
  /**
1058
  * Default Constructor.
1059
  *
1294
  $read = array($this->fsock);
1295
  $write = $except = null;
1296
  $start = microtime(true);
1297
+ $sec = (int) floor($this->curTimeout);
1298
+ $usec = (int) (1000000 * ($this->curTimeout - $sec));
1299
  // on windows this returns a "Warning: Invalid CRT parameters detected" error
1300
  // the !count() is done as a workaround for <https://bugs.php.net/42682>
1301
  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1310
  if (strlen($temp) == 255) {
1311
  continue;
1312
  }
1313
+
1314
  if ($temp === false) {
1315
  return false;
1316
  }
1432
  function _key_exchange($kexinit_payload_server = false)
1433
  {
1434
  $preferred = $this->preferred;
1435
+ $send_kex = true;
1436
 
1437
  $kex_algorithms = isset($preferred['kex']) ?
1438
  $preferred['kex'] :
1516
  0
1517
  );
1518
 
1519
+ if ($kexinit_payload_server === false) {
1520
  if (!$this->_send_binary_packet($kexinit_payload_client)) {
1521
  return false;
1522
  }
1532
  user_error('Expected SSH_MSG_KEXINIT');
1533
  return false;
1534
  }
1535
+
1536
+ $send_kex = false;
1537
  }
1538
 
1539
  $response = $kexinit_payload_server;
1606
  extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1607
  $first_kex_packet_follows = $first_kex_packet_follows != 0;
1608
 
1609
+ if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) {
1610
  return false;
1611
  }
1612
 
1646
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1647
  }
1648
 
1649
+ $compression_map = array(
1650
+ 'none' => self::NET_SSH2_COMPRESSION_NONE,
1651
+ 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
1652
+ 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
1653
+ );
1654
+
1655
  $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1656
  if ($compression_algorithm_out === false) {
1657
  user_error('No compatible client to server compression algorithms found');
1658
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1659
  }
1660
+ $this->compress = $compression_map[$compression_algorithm_out];
1661
 
1662
+ $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
1663
  if ($compression_algorithm_in === false) {
1664
  user_error('No compatible server to client compression algorithms found');
1665
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1666
  }
1667
+ $this->decompress = $compression_map[$compression_algorithm_in];
1668
 
1669
  // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
1670
  $exchange_hash_rfc4419 = '';
2097
  }
2098
  $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
2099
 
2100
+ $this->regenerate_compression_context = $this->regenerate_decompression_context = true;
2101
+
2102
  return true;
2103
  }
2104
 
2227
 
2228
  // try logging with 'none' as an authentication method first since that's what
2229
  // PuTTY does
2230
+ if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
2231
  if ($this->_login($username)) {
2232
  return true;
2233
  }
2259
  return $this->_login_helper($username);
2260
  }
2261
 
2262
+ while (count($args)) {
2263
+ if (!$this->auth_methods_to_continue || !$this->smartMFA) {
2264
+ $newargs = $args;
2265
+ $args = array();
2266
+ } else {
2267
+ $newargs = array();
2268
+ foreach ($this->auth_methods_to_continue as $method) {
2269
+ switch ($method) {
2270
+ case 'publickey':
2271
+ foreach ($args as $key => $arg) {
2272
+ if (is_object($arg)) {
2273
+ $newargs[] = $arg;
2274
+ unset($args[$key]);
2275
+ break;
2276
+ }
2277
+ }
2278
+ break;
2279
+ case 'keyboard-interactive':
2280
+ $hasArray = $hasString = false;
2281
+ foreach ($args as $arg) {
2282
+ if ($hasArray || is_array($arg)) {
2283
+ $hasArray = true;
2284
+ break;
2285
+ }
2286
+ if ($hasString || is_string($arg)) {
2287
+ $hasString = true;
2288
+ break;
2289
+ }
2290
+ }
2291
+ if ($hasArray && $hasString) {
2292
+ foreach ($args as $key => $arg) {
2293
+ if (is_array($arg)) {
2294
+ $newargs[] = $arg;
2295
+ break 2;
2296
+ }
2297
+ }
2298
+ }
2299
+ case 'password':
2300
+ foreach ($args as $key => $arg) {
2301
+ $newargs[] = $arg;
2302
+ unset($args[$key]);
2303
+ break;
2304
+ }
2305
+ }
2306
+ }
2307
+ }
2308
+
2309
+ if (!count($newargs)) {
2310
+ return false;
2311
+ }
2312
+
2313
+ foreach ($newargs as $arg) {
2314
+ if ($this->_login_helper($username, $arg)) {
2315
+ return true;
2316
+ }
2317
  }
2318
  }
2319
  return false;
2423
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2424
  $this->bitmap |= self::MASK_LOGIN;
2425
  return true;
2426
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
2427
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2428
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2429
  default:
2430
  return false;
2431
  }
2497
  }
2498
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2499
  $auth_methods = explode(',', $this->_string_shift($response, $length));
2500
+ $this->auth_methods_to_continue = $auth_methods;
2501
  if (!strlen($response)) {
2502
  return false;
2503
  }
2670
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2671
  return true;
2672
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2673
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2674
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2675
  return false;
2676
  }
2677
 
2780
  if (strlen($response) < 4) {
2781
  return false;
2782
  }
2783
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2784
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2785
+ $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
2786
  return false;
2787
  case NET_SSH2_MSG_USERAUTH_PK_OK:
2788
  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2823
  switch ($type) {
2824
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2825
  // either the login is bad or the server employs multi-factor authentication
2826
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2827
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2828
  return false;
2829
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2830
  $this->bitmap |= self::MASK_LOGIN;
2951
  return false;
2952
  }
2953
 
2954
+ $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2955
+ if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) {
2956
+ user_error('Unable to request pseudo-terminal');
2957
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
 
 
 
 
 
2958
  }
 
2959
 
 
 
 
 
 
 
 
 
2960
  $this->in_request_pty_exec = true;
2961
  }
2962
 
3077
  return false;
3078
  }
3079
 
3080
+ $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
3081
+
3082
+ if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) {
3083
+ user_error('Unable to request pseudo-terminal');
3084
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3085
+ }
3086
+
3087
  $packet = pack(
3088
  'CNNa*C',
3089
  NET_SSH2_MSG_CHANNEL_REQUEST,
3096
  return false;
3097
  }
3098
 
3099
+ $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
3100
+ if ($response === false) {
3101
+ return false;
3102
+ }
3103
+
3104
+ $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
3105
 
3106
  $this->bitmap |= self::MASK_SHELL;
3107
 
3473
  $read = array($this->fsock);
3474
  $write = $except = null;
3475
 
3476
+ if (!$this->curTimeout) {
3477
  if ($this->keepAlive <= 0) {
3478
  @stream_select($read, $write, $except, null);
3479
  } else {
3504
  $this->curTimeout-= $elapsed;
3505
  }
3506
 
3507
+ $sec = (int)floor($this->curTimeout);
3508
+ $usec = (int)(1000000 * ($this->curTimeout - $sec));
3509
 
3510
  // on windows this returns a "Warning: Invalid CRT parameters detected" error
3511
  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3519
 
3520
  if (!is_resource($this->fsock) || feof($this->fsock)) {
3521
  $this->bitmap = 0;
3522
+ user_error('Connection closed (by server) prematurely ' . $elapsed . 's');
3523
  return false;
3524
  }
3525
 
3527
  $raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
3528
 
3529
  if (!strlen($raw)) {
3530
+ user_error('No data received from server');
3531
+ return false;
3532
  }
3533
 
3534
  if ($this->decrypt !== false) {
3591
  }
3592
  }
3593
 
3594
+ switch ($this->decompress) {
3595
+ case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
3596
+ if (!$this->isAuthenticated()) {
3597
+ break;
3598
+ }
3599
+ case self::NET_SSH2_COMPRESSION_ZLIB:
3600
+ if ($this->regenerate_decompression_context) {
3601
+ $this->regenerate_decompression_context = false;
3602
+
3603
+ $cmf = ord($payload[0]);
3604
+ $cm = $cmf & 0x0F;
3605
+ if ($cm != 8) { // deflate
3606
+ user_error("Only CM = 8 ('deflate') is supported ($cm)");
3607
+ }
3608
+ $cinfo = ($cmf & 0xF0) >> 4;
3609
+ if ($cinfo > 7) {
3610
+ user_error("CINFO above 7 is not allowed ($cinfo)");
3611
+ }
3612
+ $windowSize = 1 << ($cinfo + 8);
3613
+
3614
+ $flg = ord($payload[1]);
3615
+ //$fcheck = $flg && 0x0F;
3616
+ if ((($cmf << 8) | $flg) % 31) {
3617
+ user_error('fcheck failed');
3618
+ }
3619
+ $fdict = boolval($flg & 0x20);
3620
+ $flevel = ($flg & 0xC0) >> 6;
3621
+
3622
+ $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
3623
+ $payload = substr($payload, 2);
3624
+ }
3625
+ if ($this->decompress_context) {
3626
+ $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
3627
+ }
3628
+ }
3629
 
3630
  $this->get_seq_no++;
3631
 
3700
 
3701
  // only called when we've already logged in
3702
  if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) {
3703
+ if (is_bool($payload)) {
3704
+ return $payload;
3705
+ }
3706
+
3707
  switch (ord($payload[0])) {
3708
  case NET_SSH2_MSG_CHANNEL_REQUEST:
3709
  if (strlen($payload) == 31) {
3905
  function _get_channel_packet($client_channel, $skip_extended = false)
3906
  {
3907
  if (!empty($this->channel_buffers[$client_channel])) {
3908
+ switch ($this->channel_status[$client_channel]) {
3909
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
3910
+ foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
3911
+ switch (ord($packet[0])) {
3912
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
3913
+ case NET_SSH2_MSG_CHANNEL_FAILURE:
3914
+ unset($this->channel_buffers[$client_channel][$i]);
3915
+ return substr($packet, 1);
3916
+ }
3917
+ }
3918
+ break;
3919
+ default:
3920
+ return substr(array_shift($this->channel_buffers[$client_channel]), 1);
3921
+ }
3922
  }
3923
 
3924
  while (true) {
3992
  if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3993
  return $data;
3994
  }
3995
+ $this->channel_buffers[$channel][] = chr($type) . $data;
 
 
 
3996
 
3997
  continue 2;
3998
  case NET_SSH2_MSG_CHANNEL_REQUEST:
4071
  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
4072
  $this->_on_channel_open();
4073
  return $result;
4074
+ case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
 
4075
  user_error('Unable to open channel');
4076
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4077
+ default:
4078
+ if ($client_channel == $channel) {
4079
+ user_error('Unexpected response to open request');
4080
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4081
+ }
4082
+ return $this->_get_channel_packet($client_channel, $skip_extended);
 
 
 
 
4083
  }
4084
  break;
4085
  case NET_SSH2_MSG_CHANNEL_REQUEST:
4088
  return true;
4089
  case NET_SSH2_MSG_CHANNEL_FAILURE:
4090
  return false;
4091
+ case NET_SSH2_MSG_CHANNEL_DATA:
4092
+ if (strlen($response) < 4) {
4093
+ return false;
4094
+ }
4095
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
4096
+ $data = $this->_string_shift($response, $length);
4097
+ $this->channel_buffers[$channel][] = chr($type) . $data;
4098
+ return $this->_get_channel_packet($client_channel, $skip_extended);
4099
  default:
4100
  user_error('Unable to fulfill channel request');
4101
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4109
 
4110
  switch ($type) {
4111
  case NET_SSH2_MSG_CHANNEL_DATA:
 
 
 
 
4112
  /*
4113
  if ($channel == self::CHANNEL_EXEC) {
4114
  // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
4135
  if ($client_channel == $channel) {
4136
  return $data;
4137
  }
4138
+ $this->channel_buffers[$channel][] = chr($type) . $data;
 
 
 
4139
  break;
4140
  case NET_SSH2_MSG_CHANNEL_CLOSE:
4141
  $this->curTimeout = 5;
4154
  case NET_SSH2_MSG_CHANNEL_EOF:
4155
  break;
4156
  default:
4157
+ user_error("Error reading channel data ($type)");
4158
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4159
  }
4160
  }
4179
  return false;
4180
  }
4181
 
4182
+ if (!isset($logged)) {
4183
+ $logged = $data;
4184
+ }
4185
+
4186
+ switch ($this->compress) {
4187
+ case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
4188
+ if (!$this->isAuthenticated()) {
4189
+ break;
4190
+ }
4191
+ case self::NET_SSH2_COMPRESSION_ZLIB:
4192
+ if (!$this->regenerate_compression_context) {
4193
+ $header = '';
4194
+ } else {
4195
+ $this->regenerate_compression_context = false;
4196
+ $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15));
4197
+ $header = "\x78\x9C";
4198
+ }
4199
+ if ($this->compress_context) {
4200
+ $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
4201
+ }
4202
+ }
4203
 
4204
  // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
4205
  $packet_length = strlen($data) + 9;
4227
 
4228
  if (defined('NET_SSH2_LOGGING')) {
4229
  $current = microtime(true);
4230
+ $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
4231
  $message_number = '-> ' . $message_number .
4232
  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
4233
+ $this->_append_log($message_number, $logged);
4234
  $this->last_packet = $current;
4235
  }
4236
 
4925
  */
4926
  function getSupportedCompressionAlgorithms()
4927
  {
4928
+ $algos = array('none'); // REQUIRED no compression
4929
+ if (function_exists('deflate_init')) {
4930
+ $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
4931
+ $algos[] = 'zlib';
4932
+ }
4933
+ return $algos;
4934
  }
4935
 
4936
  /**
4945
  {
4946
  $this->_connect();
4947
 
4948
+ $compression_map = array(
4949
+ self::NET_SSH2_COMPRESSION_NONE => 'none',
4950
+ self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
4951
+ self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
4952
+ );
4953
+
4954
  return array(
4955
  'kex' => $this->kex_algorithm,
4956
  'hostkey' => $this->signature_format,
4957
  'client_to_server' => array(
4958
  'crypt' => $this->encrypt->name,
4959
  'mac' => $this->hmac_create->name,
4960
+ 'comp' => $compression_map[$this->compress],
4961
  ),
4962
  'server_to_client' => array(
4963
  'crypt' => $this->decrypt->name,
4964
  'mac' => $this->hmac_check->name,
4965
+ 'comp' => $compression_map[$this->decompress],
4966
  )
4967
  );
4968
  }
5364
  );
5365
  }
5366
  }
5367
+
5368
+ /**
5369
+ * Return the list of authentication methods that may productively continue authentication.
5370
+ *
5371
+ * @see https://tools.ietf.org/html/rfc4252#section-5.1
5372
+ * @return array|null
5373
+ */
5374
+ function getAuthMethodsToContinue()
5375
+ {
5376
+ return $this->auth_methods_to_continue;
5377
+ }
5378
+
5379
+ /**
5380
+ * Enables "smart" multi-factor authentication (MFA)
5381
+ */
5382
+ function enableSmartMFA()
5383
+ {
5384
+ $this->smartMFA = true;
5385
+ }
5386
+
5387
+ /**
5388
+ * Disables "smart" multi-factor authentication (MFA)
5389
+ */
5390
+ function disableSmartMFA()
5391
+ {
5392
+ $this->smartMFA = false;
5393
+ }
5394
  }
vendor/phpseclib/phpseclib/phpseclib/bootstrap.php CHANGED
@@ -7,7 +7,8 @@
7
 
8
  if (extension_loaded('mbstring')) {
9
  // 2 - MB_OVERLOAD_STRING
10
- if (ini_get('mbstring.func_overload') & 2) {
 
11
  throw new \UnexpectedValueException(
12
  'Overloading of string functions using mbstring.func_overload ' .
13
  'is not supported by phpseclib.'
7
 
8
  if (extension_loaded('mbstring')) {
9
  // 2 - MB_OVERLOAD_STRING
10
+ // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0.
11
+ if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
12
  throw new \UnexpectedValueException(
13
  'Overloading of string functions using mbstring.func_overload ' .
14
  'is not supported by phpseclib.'