The WP Remote WordPress Plugin - Version 2.0.4

Version Description

Download this release

Release Info

Developer willmot
Plugin Icon 128x128 The WP Remote WordPress Plugin
Version 2.0.4
Comparing to
See all releases

Code changes from version 2.0.3 to 2.0.4

Files changed (4) hide show
  1. hm-backup/hm-backup.php +431 -57
  2. plugin.php +1 -1
  3. readme.txt +1 -1
  4. wprp.backups.php +8 -1
hm-backup/hm-backup.php CHANGED
@@ -3,7 +3,7 @@
3
  /**
4
  * Generic file and database backup class
5
  *
6
- * @version 1.3
7
  */
8
  class HM_Backup {
9
 
@@ -94,7 +94,48 @@ class HM_Backup {
94
  * @static
95
  * @access public
96
  */
97
- public static $instance;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  /**
100
  * Sets up the default properties
@@ -108,8 +149,12 @@ class HM_Backup {
108
  @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
109
  @set_time_limit( 0 );
110
 
 
 
 
 
111
  // Defaults
112
- $this->root = ABSPATH;
113
 
114
  $this->path = $this->conform_dir( WP_CONTENT_DIR . '/backups' );
115
 
@@ -148,7 +193,17 @@ class HM_Backup {
148
  * @return string
149
  */
150
  public function archive_filepath() {
151
- return trailingslashit( $this->path ) . $this->archive_filename;
 
 
 
 
 
 
 
 
 
 
152
  }
153
 
154
  /**
@@ -158,7 +213,27 @@ class HM_Backup {
158
  * @return string
159
  */
160
  public function database_dump_filepath() {
161
- return trailingslashit( $this->path ) . $this->database_dump_filename;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  }
163
 
164
  /**
@@ -178,7 +253,7 @@ class HM_Backup {
178
  // Zip everything up
179
  $this->archive();
180
 
181
- do_action( 'hmbkp_backup_complete' );
182
 
183
  }
184
 
@@ -195,9 +270,14 @@ class HM_Backup {
195
 
196
  do_action( 'hmbkp_mysqldump_started' );
197
 
 
 
198
  // Use mysqldump if we can
199
  if ( $this->mysqldump_command_path ) {
200
 
 
 
 
201
  // Path to the mysqldump executable
202
  $cmd = escapeshellarg( $this->mysqldump_command_path );
203
 
@@ -215,18 +295,23 @@ class HM_Backup {
215
  $cmd .= ' -p' . escapeshellarg( DB_PASSWORD );
216
 
217
  // Set the host
218
- $cmd .= ' -h ' . escapeshellarg( DB_HOST );
219
 
220
- // Save the file
 
 
 
 
221
  $cmd .= ' -r ' . escapeshellarg( $this->database_dump_filepath() );
222
 
223
  // The database we're dumping
224
  $cmd .= ' ' . escapeshellarg( DB_NAME );
225
 
226
- // Send stdout to null
227
- $cmd .= ' 2> /dev/null';
228
 
229
- shell_exec( $cmd );
 
230
 
231
  }
232
 
@@ -246,13 +331,17 @@ class HM_Backup {
246
  */
247
  public function mysqldump_fallback() {
248
 
 
 
 
 
249
  $this->db = mysql_pconnect( DB_HOST, DB_USER, DB_PASSWORD );
250
 
251
  mysql_select_db( DB_NAME, $this->db );
252
  mysql_set_charset( DB_CHARSET, $this->db );
253
 
254
  // Begin new backup of MySql
255
- $tables = mysql_list_tables( DB_NAME );
256
 
257
  $sql_file = "# WordPress : " . get_bloginfo( 'url' ) . " MySQL database backup\n";
258
  $sql_file .= "#\n";
@@ -295,11 +384,11 @@ class HM_Backup {
295
  $this->zip();
296
 
297
  // If not or if the shell zip failed then use ZipArchive
298
- if ( ! file_exists( $this->archive_filepath() ) && class_exists( 'ZipArchive' ) && empty( $this->skip_zip_archive ) )
299
  $this->zip_archive();
300
 
301
  // If ZipArchive is unavailable or one of the above failed
302
- if ( ! file_exists( $this->archive_filepath() ) )
303
  $this->pcl_zip();
304
 
305
  // Delete the database dump file
@@ -318,17 +407,21 @@ class HM_Backup {
318
  */
319
  public function zip() {
320
 
 
 
321
  // Zip up $this->root with excludes
322
  if ( ! $this->database_only && $this->exclude_string( 'zip' ) )
323
- shell_exec( 'cd ' . escapeshellarg( $this->root ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' -x ' . $this->exclude_string( 'zip' ) . ' 2> /dev/null' );
324
 
325
  // Zip up $this->root without excludes
326
  elseif ( ! $this->database_only )
327
- shell_exec( 'cd ' . escapeshellarg( $this->root ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' 2> /dev/null' );
328
 
329
  // Add the database dump to the archive
330
  if ( ! $this->files_only )
331
- shell_exec( 'cd ' . escapeshellarg( $this->path ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -uq ' . escapeshellarg( $this->archive_filepath() ) . ' ' . escapeshellarg( $this->database_dump_filename ) . ' 2> /dev/null' );
 
 
332
 
333
  }
334
 
@@ -341,6 +434,9 @@ class HM_Backup {
341
  */
342
  public function zip_archive() {
343
 
 
 
 
344
  $zip = new ZipArchive();
345
 
346
  if ( ! class_exists( 'ZipArchive' ) || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
@@ -348,26 +444,15 @@ class HM_Backup {
348
 
349
  if ( ! $this->database_only ) {
350
 
351
- $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $this->root ), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
352
-
353
  $files_added = 0;
354
 
355
- $excludes = $this->exclude_string( 'regex' );
356
 
357
- foreach ( $files as $file ) {
 
358
 
359
- if ( ! $file->isReadable() )
360
- continue;
361
-
362
- // Excludes
363
- if ( $excludes && preg_match( '(' . $excludes . ')', str_replace( $this->root, '', $this->conform_dir( $file->getPathname() ) ) ) )
364
- continue;
365
-
366
- if ( $file->isDir() )
367
- $zip->addEmptyDir( str_replace( trailingslashit( $this->root ), '', trailingslashit( $file->getPathname() ) ) );
368
-
369
- elseif ( $file->isFile() )
370
- $zip->addFile( $file, str_replace( trailingslashit( $this->root ), '', $file->getPathname() ) );
371
 
372
  if ( ++$files_added % 500 === 0 )
373
  if ( ! $zip->close() || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
@@ -379,10 +464,18 @@ class HM_Backup {
379
 
380
  // Add the database
381
  if ( ! $this->files_only )
382
- $zip->addFile( $this->database_dump_filepath(), $this->database_dump_filename );
 
 
 
 
 
 
383
 
384
  $zip->close();
385
 
 
 
386
  }
387
 
388
  /**
@@ -396,27 +489,177 @@ class HM_Backup {
396
  */
397
  public function pcl_zip() {
398
 
 
 
 
399
  global $_hmbkp_exclude_string;
400
 
401
  $_hmbkp_exclude_string = $this->exclude_string( 'regex' );
402
 
403
- if ( ! defined( 'PCLZIP_TEMPORARY_DIR' ) )
404
- define( 'PCLZIP_TEMPORARY_DIR', $this->path );
405
-
406
- require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
407
 
408
  $archive = new PclZip( $this->archive_filepath() );
409
 
410
  // Zip up everything
411
  if ( ! $this->database_only )
412
- $archive->add( $this->root, PCLZIP_OPT_REMOVE_PATH, $this->root, PCLZIP_CB_PRE_ADD, 'hmbkp_pclzip_callback' );
 
413
 
414
  // Add the database
415
  if ( ! $this->files_only )
416
- $archive->add( $this->database_dump_filepath(), PCLZIP_OPT_REMOVE_PATH, $this->path );
 
417
 
418
  unset( $GLOBALS['_hmbkp_exclude_string'] );
419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
 
422
  /**
@@ -432,7 +675,6 @@ class HM_Backup {
432
 
433
  // List of possible mysqldump locations
434
  $mysqldump_locations = array(
435
- 'mysqldump',
436
  '/usr/local/bin/mysqldump',
437
  '/usr/local/mysql/bin/mysqldump',
438
  '/usr/mysql/bin/mysqldump',
@@ -440,19 +682,22 @@ class HM_Backup {
440
  '/opt/local/lib/mysql6/bin/mysqldump',
441
  '/opt/local/lib/mysql5/bin/mysqldump',
442
  '/opt/local/lib/mysql4/bin/mysqldump',
443
- '\xampp\mysql\bin\mysqldump',
444
- '\Program Files\xampp\mysql\bin\mysqldump',
445
- '\Program Files\MySQL\MySQL Server 6.0\bin\mysqldump',
446
- '\Program Files\MySQL\MySQL Server 5.5\bin\mysqldump',
447
- '\Program Files\MySQL\MySQL Server 5.4\bin\mysqldump',
448
- '\Program Files\MySQL\MySQL Server 5.1\bin\mysqldump',
449
- '\Program Files\MySQL\MySQL Server 5.0\bin\mysqldump',
450
- '\Program Files\MySQL\MySQL Server 4.1\bin\mysqldump'
451
  );
452
 
 
 
 
453
  // Find the one which works
454
  foreach ( $mysqldump_locations as $location )
455
- if ( ! shell_exec( 'hash ' . $location . ' 2>&1' ) )
456
  return $location;
457
 
458
  return '';
@@ -473,14 +718,16 @@ class HM_Backup {
473
 
474
  // List of possible zip locations
475
  $zip_locations = array(
476
- 'zip',
477
  '/usr/bin/zip'
478
  );
479
 
 
 
 
480
  // Find the one which works
481
  foreach ( $zip_locations as $location )
482
- if ( ! shell_exec( 'hash ' . $location . ' 2>&1' ) )
483
- return $location;
484
 
485
  return '';
486
 
@@ -517,6 +764,10 @@ class HM_Backup {
517
  // Sanitize the excludes
518
  $excludes = array_filter( array_unique( array_map( 'trim', (array) $this->excludes ) ) );
519
 
 
 
 
 
520
  foreach( $excludes as $key => &$rule ) {
521
 
522
  $file = $absolute = $fragment = false;
@@ -534,7 +785,7 @@ class HM_Backup {
534
  $fragment = true;
535
 
536
  // Strip $this->root and conform
537
- $rule = str_replace( $this->conform_dir( $this->root ), '', untrailingslashit( $this->conform_dir( $rule ) ) );
538
 
539
  // Strip the preceeding slash
540
  if ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) )
@@ -568,7 +819,7 @@ class HM_Backup {
568
 
569
  // Escape shell args for zip command
570
  if ( $context == 'zip' )
571
- $excludes = array_map( 'escapeshellarg', $excludes );
572
 
573
  return implode( $separator, $excludes );
574
 
@@ -605,6 +856,10 @@ class HM_Backup {
605
  if ( in_array( 'shell_exec', array_map( 'trim', explode( ',', ini_get( 'disable_functions' ) ) ) ) )
606
  return false;
607
 
 
 
 
 
608
  return true;
609
 
610
  }
@@ -631,6 +886,7 @@ class HM_Backup {
631
  return $this->conform_dir( $dir );
632
 
633
  return $dir;
 
634
  }
635
 
636
  /**
@@ -682,7 +938,7 @@ class HM_Backup {
682
  private function make_sql( $sql_file, $table ) {
683
 
684
  // Add SQL statement to drop existing table
685
- $sql_file = "\n";
686
  $sql_file .= "\n";
687
  $sql_file .= "#\n";
688
  $sql_file .= "# Delete any existing table " . $this->sql_backquote( $table ) . "\n";
@@ -736,11 +992,13 @@ class HM_Backup {
736
 
737
  // Checks whether the field is an integer or not
738
  for ( $j = 0; $j < $fields_cnt; $j++ ) {
 
739
  $field_set[$j] = $this->sql_backquote( mysql_field_name( $result, $j ) );
740
  $type = mysql_field_type( $result, $j );
741
 
742
  if ( $type == 'tinyint' || $type == 'smallint' || $type == 'mediumint' || $type == 'int' || $type == 'bigint' || $type == 'timestamp')
743
  $field_num[$j] = true;
 
744
  else
745
  $field_num[$j] = false;
746
 
@@ -855,6 +1113,122 @@ class HM_Backup {
855
 
856
  }
857
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
858
  }
859
 
860
  /**
@@ -872,7 +1246,7 @@ function hmbkp_pclzip_callback( $event, &$file ) {
872
  global $_hmbkp_exclude_string;
873
 
874
  // Don't try to add unreadable files.
875
- if ( ! is_readable( $file['filename'] ) || ! file_exists( $file['filename'] ) || is_link( $file['filename'] ) )
876
  return false;
877
 
878
  // Match everything else past the exclude list
3
  /**
4
  * Generic file and database backup class
5
  *
6
+ * @version 1.4
7
  */
8
  class HM_Backup {
9
 
94
  * @static
95
  * @access public
96
  */
97
+ private static $instance;
98
+
99
+ /**
100
+ * An array of all the files in root
101
+ * excluding excludes
102
+ *
103
+ * @var array
104
+ * @access private
105
+ */
106
+ private $files;
107
+
108
+ /**
109
+ * Contains an array of errors
110
+ *
111
+ * @var mixed
112
+ * @access private
113
+ */
114
+ private $errors;
115
+
116
+ /**
117
+ * Contains an array of warnings
118
+ *
119
+ * @var mixed
120
+ * @access private
121
+ */
122
+ private $warnings;
123
+
124
+ /**
125
+ * The archive method used
126
+ *
127
+ * @var string
128
+ * @access private
129
+ */
130
+ private $archive_method;
131
+
132
+ /**
133
+ * The mysqldump method used
134
+ *
135
+ * @var string
136
+ * @access private
137
+ */
138
+ private $mysqldump_method;
139
 
140
  /**
141
  * Sets up the default properties
149
  @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
150
  @set_time_limit( 0 );
151
 
152
+ $this->errors = array();
153
+
154
+ set_error_handler( array( &$this, 'error_handler' ) );
155
+
156
  // Defaults
157
+ $this->root = $this->conform_dir( ABSPATH );
158
 
159
  $this->path = $this->conform_dir( WP_CONTENT_DIR . '/backups' );
160
 
193
  * @return string
194
  */
195
  public function archive_filepath() {
196
+ return trailingslashit( $this->path() ) . $this->archive_filename();
197
+ }
198
+
199
+ /**
200
+ * The full filepath to the archive file.
201
+ *
202
+ * @access public
203
+ * @return string
204
+ */
205
+ public function archive_filename() {
206
+ return strtolower( sanitize_file_name( remove_accents( $this->archive_filename ) ) );
207
  }
208
 
209
  /**
213
  * @return string
214
  */
215
  public function database_dump_filepath() {
216
+ return trailingslashit( $this->path() ) . $this->database_dump_filename();
217
+ }
218
+
219
+ public function database_dump_filename() {
220
+ return strtolower( sanitize_file_name( remove_accents( $this->database_dump_filename ) ) );
221
+ }
222
+
223
+ public function root() {
224
+ return $this->conform_dir( $this->root );
225
+ }
226
+
227
+ public function path() {
228
+ return $this->conform_dir( $this->path );
229
+ }
230
+
231
+ public function archive_method() {
232
+ return $this->archive_method;
233
+ }
234
+
235
+ public function mysqldump_method() {
236
+ return $this->mysqldump_method;
237
  }
238
 
239
  /**
253
  // Zip everything up
254
  $this->archive();
255
 
256
+ do_action( 'hmbkp_backup_complete', $this );
257
 
258
  }
259
 
270
 
271
  do_action( 'hmbkp_mysqldump_started' );
272
 
273
+ $this->mysqldump_method = 'mysqldump';
274
+
275
  // Use mysqldump if we can
276
  if ( $this->mysqldump_command_path ) {
277
 
278
+ $host = reset( explode( ':', DB_HOST ) );
279
+ $port = strpos( DB_HOST, ':' ) ? end( explode( ':', DB_HOST ) ) : '';
280
+
281
  // Path to the mysqldump executable
282
  $cmd = escapeshellarg( $this->mysqldump_command_path );
283
 
295
  $cmd .= ' -p' . escapeshellarg( DB_PASSWORD );
296
 
297
  // Set the host
298
+ $cmd .= ' -h ' . escapeshellarg( $host );
299
 
300
+ // Set the port if it was set
301
+ if ( ! empty( $port ) )
302
+ $cmd .= ' -P ' . $port;
303
+
304
+ // The file we're saving too
305
  $cmd .= ' -r ' . escapeshellarg( $this->database_dump_filepath() );
306
 
307
  // The database we're dumping
308
  $cmd .= ' ' . escapeshellarg( DB_NAME );
309
 
310
+ // Pipe STDERR to STDOUT
311
+ $cmd .= ' 2>&1';
312
 
313
+ // Store any returned data in warning
314
+ $this->warning( $this->mysqldump_method, shell_exec( $cmd ) );
315
 
316
  }
317
 
331
  */
332
  public function mysqldump_fallback() {
333
 
334
+ $this->errors_to_warnings( $this->mysqldump_method );
335
+
336
+ $this->mysqldump_method = 'mysqldump_fallback';
337
+
338
  $this->db = mysql_pconnect( DB_HOST, DB_USER, DB_PASSWORD );
339
 
340
  mysql_select_db( DB_NAME, $this->db );
341
  mysql_set_charset( DB_CHARSET, $this->db );
342
 
343
  // Begin new backup of MySql
344
+ $tables = mysql_query( 'SHOW TABLES' );
345
 
346
  $sql_file = "# WordPress : " . get_bloginfo( 'url' ) . " MySQL database backup\n";
347
  $sql_file .= "#\n";
384
  $this->zip();
385
 
386
  // If not or if the shell zip failed then use ZipArchive
387
+ if ( empty( $this->archive_verified ) && class_exists( 'ZipArchive' ) && empty( $this->skip_zip_archive ) )
388
  $this->zip_archive();
389
 
390
  // If ZipArchive is unavailable or one of the above failed
391
+ if ( empty( $this->archive_verified ) )
392
  $this->pcl_zip();
393
 
394
  // Delete the database dump file
407
  */
408
  public function zip() {
409
 
410
+ $this->archive_method = 'zip';
411
+
412
  // Zip up $this->root with excludes
413
  if ( ! $this->database_only && $this->exclude_string( 'zip' ) )
414
+ $this->warning( $this->archive_method, shell_exec( 'cd ' . escapeshellarg( $this->root() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' -x ' . $this->exclude_string( 'zip' ) . ' 2>&1' ) );
415
 
416
  // Zip up $this->root without excludes
417
  elseif ( ! $this->database_only )
418
+ $this->warning( $this->archive_method, shell_exec( 'cd ' . escapeshellarg( $this->root() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -rq ' . escapeshellarg( $this->archive_filepath() ) . ' ./' . ' 2>&1' ) );
419
 
420
  // Add the database dump to the archive
421
  if ( ! $this->files_only )
422
+ $this->warning( $this->archive_method, shell_exec( 'cd ' . escapeshellarg( $this->path() ) . ' && ' . escapeshellarg( $this->zip_command_path ) . ' -uq ' . escapeshellarg( $this->archive_filepath() ) . ' ' . escapeshellarg( $this->database_dump_filename() ) . ' 2>&1' ) );
423
+
424
+ $this->check_archive();
425
 
426
  }
427
 
434
  */
435
  public function zip_archive() {
436
 
437
+ $this->errors_to_warnings( $this->archive_method );
438
+ $this->archive_method = 'ziparchive';
439
+
440
  $zip = new ZipArchive();
441
 
442
  if ( ! class_exists( 'ZipArchive' ) || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
444
 
445
  if ( ! $this->database_only ) {
446
 
 
 
447
  $files_added = 0;
448
 
449
+ foreach ( $this->files() as $file ) {
450
 
451
+ if ( is_dir( trailingslashit( $this->root() ) . $file ) )
452
+ $zip->addEmptyDir( trailingslashit( $file ) );
453
 
454
+ elseif ( is_file( trailingslashit( $this->root() ) . $file ) )
455
+ $zip->addFile( trailingslashit( $this->root() ) . $file, $file );
 
 
 
 
 
 
 
 
 
 
456
 
457
  if ( ++$files_added % 500 === 0 )
458
  if ( ! $zip->close() || ! $zip->open( $this->archive_filepath(), ZIPARCHIVE::CREATE ) )
464
 
465
  // Add the database
466
  if ( ! $this->files_only )
467
+ $zip->addFile( $this->database_dump_filepath(), $this->database_dump_filename() );
468
+
469
+ if ( $zip->status )
470
+ $this->warning( $this->archive_method, $zip->status );
471
+
472
+ if ( $zip->statusSys )
473
+ $this->warning( $this->archive_method, $zip->statusSys );
474
 
475
  $zip->close();
476
 
477
+ $this->check_archive();
478
+
479
  }
480
 
481
  /**
489
  */
490
  public function pcl_zip() {
491
 
492
+ $this->errors_to_warnings( $this->archive_method );
493
+ $this->archive_method = 'pclzip';
494
+
495
  global $_hmbkp_exclude_string;
496
 
497
  $_hmbkp_exclude_string = $this->exclude_string( 'regex' );
498
 
499
+ $this->load_pclzip();
 
 
 
500
 
501
  $archive = new PclZip( $this->archive_filepath() );
502
 
503
  // Zip up everything
504
  if ( ! $this->database_only )
505
+ if ( ! $archive->add( $this->root(), PCLZIP_OPT_REMOVE_PATH, $this->root(), PCLZIP_CB_PRE_ADD, 'hmbkp_pclzip_callback' ) )
506
+ $this->warning( $this->archive_method, $archive->errorInfo( true ) );
507
 
508
  // Add the database
509
  if ( ! $this->files_only )
510
+ if ( ! $archive->add( $this->database_dump_filepath(), PCLZIP_OPT_REMOVE_PATH, $this->path() ) )
511
+ $this->warning( $this->archive_method, $archive->errorInfo( true ) );
512
 
513
  unset( $GLOBALS['_hmbkp_exclude_string'] );
514
 
515
+ $this->check_archive();
516
+
517
+ }
518
+
519
+ /**
520
+ * Verify that the archive is valid and contains all the files it should contain.
521
+ *
522
+ * @access public
523
+ * @return bool
524
+ */
525
+ public function check_archive() {
526
+
527
+ // If we've already passed then no need to check again
528
+ if ( ! empty( $this->archive_verified ) )
529
+ return true;
530
+
531
+ if ( ! file_exists( $this->archive_filepath() ) )
532
+ $this->error( $this->archive_method(), __( 'The backup file was not created', 'hmbkp' ) );
533
+
534
+ // Verify using the zip command if possible
535
+ if ( $this->zip_command_path ) {
536
+
537
+ $verify = shell_exec( escapeshellarg( $this->zip_command_path ) . ' -T ' . escapeshellarg( $this->archive_filepath() ) . ' 2> /dev/null' );
538
+
539
+ if ( strpos( $verify, 'OK' ) === false )
540
+ $this->error( $this->archive_method(), $verify );
541
+
542
+ }
543
+
544
+ // If there are errors delete the backup file.
545
+ if ( $this->errors( $this->archive_method() ) && file_exists( $this->archive_filepath() ) )
546
+ unlink( $this->archive_filepath() );
547
+
548
+ if ( $this->errors( $this->archive_method() ) )
549
+ return false;
550
+
551
+ return $this->archive_verified = true;
552
+
553
+ }
554
+
555
+ /**
556
+ * Generate the array of files to be backed up by looping through
557
+ * root, ignored unreadable files and excludes
558
+ *
559
+ * @access public
560
+ * @return array
561
+ */
562
+ public function files() {
563
+
564
+ if ( ! empty( $this->files ) )
565
+ return $this->files;
566
+
567
+ $this->files = array();
568
+
569
+ if ( defined( 'RecursiveDirectoryIterator::FOLLOW_SYMLINKS' ) ) {
570
+
571
+ $filesystem = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $this->root(), RecursiveDirectoryIterator::FOLLOW_SYMLINKS ), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
572
+
573
+ $excludes = $this->exclude_string( 'regex' );
574
+
575
+ foreach ( $filesystem as $file ) {
576
+
577
+ if ( ! $file->isReadable() ) {
578
+ $this->unreadable_files[] = $file->getPathName();
579
+ continue;
580
+ }
581
+
582
+ $pathname = str_ireplace( trailingslashit( $this->root() ), '', $this->conform_dir( $file->getPathname() ) );
583
+
584
+ // Excludes
585
+ if ( $excludes && preg_match( '(' . $excludes . ')', $pathname ) )
586
+ continue;
587
+
588
+ // Don't include database dump as it's added separately
589
+ if ( basename( $pathname ) == $this->database_dump_filename() )
590
+ continue;
591
+
592
+ $this->files[] = $pathname;
593
+
594
+ }
595
+
596
+ } else {
597
+
598
+ $this->files = $this->files_fallback( $this->root() );
599
+
600
+ }
601
+
602
+ if ( ! empty( $this->unreadable_files ) )
603
+ $this->warning( $this->archive_method(), __( 'The following files are unreadable and couldn\'t be backed up: ', 'hmbkp' ) . implode( ', ', $this->unreadable_files ) );
604
+
605
+ return $this->files;
606
+
607
+ }
608
+
609
+ /**
610
+ * Fallback function for generating a filesystem
611
+ * array
612
+ *
613
+ * Used if RecursiveDirectoryIterator::FOLLOW_SYMLINKS isn't available
614
+ *
615
+ * @access private
616
+ * @param stromg $dir
617
+ * @param array $files. (default: array())
618
+ * @return array
619
+ */
620
+ private function files_fallback( $dir, $files = array() ) {
621
+
622
+ $handle = opendir( $dir );
623
+
624
+ $excludes = $this->exclude_string( 'regex' );
625
+
626
+ while ( $file = readdir( $handle ) ) :
627
+
628
+ // Ignore current dir and containing dir and any unreadable files or directories
629
+ if ( $file == '.' || $file == '..' )
630
+ continue;
631
+
632
+ $filepath = $this->conform_dir( trailingslashit( $dir ) . $file );
633
+ $file = str_ireplace( trailingslashit( $this->root() ), '', $filepath );
634
+
635
+ if ( ! is_readable( $filepath ) ) {
636
+ $this->unreadable_files[] = $filepath;
637
+ continue;
638
+ }
639
+
640
+ // Skip the backups dir and any excluded paths
641
+ if ( ( $excludes && preg_match( '(' . $excludes . ')', $file ) ) )
642
+ continue;
643
+
644
+ $files[] = $file;
645
+
646
+ if ( is_dir( $filepath ) )
647
+ $files = $this->files_fallback( $filepath, $files );
648
+
649
+ endwhile;
650
+
651
+ return $files;
652
+
653
+ }
654
+
655
+ private function load_pclzip() {
656
+
657
+ // Load PclZip
658
+ if ( ! defined( 'PCLZIP_TEMPORARY_DIR' ) )
659
+ define( 'PCLZIP_TEMPORARY_DIR', trailingslashit( $this->path() ) );
660
+
661
+ require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
662
+
663
  }
664
 
665
  /**
675
 
676
  // List of possible mysqldump locations
677
  $mysqldump_locations = array(
 
678
  '/usr/local/bin/mysqldump',
679
  '/usr/local/mysql/bin/mysqldump',
680
  '/usr/mysql/bin/mysqldump',
682
  '/opt/local/lib/mysql6/bin/mysqldump',
683
  '/opt/local/lib/mysql5/bin/mysqldump',
684
  '/opt/local/lib/mysql4/bin/mysqldump',
685
+ '/xampp/mysql/bin/mysqldump',
686
+ '/Program Files/xampp/mysql/bin/mysqldump',
687
+ '/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
688
+ '/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
689
+ '/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump',
690
+ '/Program Files/MySQL/MySQL Server 5.1/bin/mysqldump',
691
+ '/Program Files/MySQL/MySQL Server 5.0/bin/mysqldump',
692
+ '/Program Files/MySQL/MySQL Server 4.1/bin/mysqldump'
693
  );
694
 
695
+ if ( is_null( shell_exec( 'hash mysqldump 2>&1' ) ) )
696
+ return 'mysqldump';
697
+
698
  // Find the one which works
699
  foreach ( $mysqldump_locations as $location )
700
+ if ( @file_exists( $this->conform_dir( $location ) ) )
701
  return $location;
702
 
703
  return '';
718
 
719
  // List of possible zip locations
720
  $zip_locations = array(
 
721
  '/usr/bin/zip'
722
  );
723
 
724
+ if ( is_null( shell_exec( 'hash zip 2>&1' ) ) )
725
+ return 'zip';
726
+
727
  // Find the one which works
728
  foreach ( $zip_locations as $location )
729
+ if ( @file_exists( $this->conform_dir( $location ) ) )
730
+ return $location;
731
 
732
  return '';
733
 
764
  // Sanitize the excludes
765
  $excludes = array_filter( array_unique( array_map( 'trim', (array) $this->excludes ) ) );
766
 
767
+ // If path() is inside root(), exclude it
768
+ if ( strpos( $this->path(), $this->root() ) !== false )
769
+ $excludes[] = trailingslashit( $this->path() );
770
+
771
  foreach( $excludes as $key => &$rule ) {
772
 
773
  $file = $absolute = $fragment = false;
785
  $fragment = true;
786
 
787
  // Strip $this->root and conform
788
+ $rule = str_ireplace( $this->root(), '', untrailingslashit( $this->conform_dir( $rule ) ) );
789
 
790
  // Strip the preceeding slash
791
  if ( in_array( substr( $rule, 0, 1 ), array( '\\', '/' ) ) )
819
 
820
  // Escape shell args for zip command
821
  if ( $context == 'zip' )
822
+ $excludes = array_map( 'escapeshellarg', array_unique( $excludes ) );
823
 
824
  return implode( $separator, $excludes );
825
 
856
  if ( in_array( 'shell_exec', array_map( 'trim', explode( ',', ini_get( 'disable_functions' ) ) ) ) )
857
  return false;
858
 
859
+ // Can we issue a simple command
860
+ if ( ! @shell_exec( 'pwd' ) )
861
+ return false;
862
+
863
  return true;
864
 
865
  }
886
  return $this->conform_dir( $dir );
887
 
888
  return $dir;
889
+
890
  }
891
 
892
  /**
938
  private function make_sql( $sql_file, $table ) {
939
 
940
  // Add SQL statement to drop existing table
941
+ $sql_file .= "\n";
942
  $sql_file .= "\n";
943
  $sql_file .= "#\n";
944
  $sql_file .= "# Delete any existing table " . $this->sql_backquote( $table ) . "\n";
992
 
993
  // Checks whether the field is an integer or not
994
  for ( $j = 0; $j < $fields_cnt; $j++ ) {
995
+
996
  $field_set[$j] = $this->sql_backquote( mysql_field_name( $result, $j ) );
997
  $type = mysql_field_type( $result, $j );
998
 
999
  if ( $type == 'tinyint' || $type == 'smallint' || $type == 'mediumint' || $type == 'int' || $type == 'bigint' || $type == 'timestamp')
1000
  $field_num[$j] = true;
1001
+
1002
  else
1003
  $field_num[$j] = false;
1004
 
1113
 
1114
  }
1115
 
1116
+ /**
1117
+ * Get the errors
1118
+ *
1119
+ * @access public
1120
+ * @return null
1121
+ */
1122
+ public function errors( $context = null ) {
1123
+
1124
+ if ( ! empty( $context ) )
1125
+ return isset( $this->errors[$context] ) ? $this->errors[$context] : array();
1126
+
1127
+ return $this->errors;
1128
+
1129
+ }
1130
+
1131
+
1132
+ /**
1133
+ * Add an error to the errors stack
1134
+ *
1135
+ * @access private
1136
+ * @param string $context
1137
+ * @param mixed $error
1138
+ * @return null
1139
+ */
1140
+ private function error( $context, $error ) {
1141
+
1142
+ if ( empty( $context ) || empty( $error ) )
1143
+ return;
1144
+
1145
+ $this->errors[$context][$_key = md5( implode( ':' , (array) $error ) )] = $error;
1146
+
1147
+ }
1148
+
1149
+ /**
1150
+ * Migrate errors to warnings
1151
+ *
1152
+ * @access private
1153
+ * @param string $context. (default: null)
1154
+ * @return null
1155
+ */
1156
+ private function errors_to_warnings( $context = null ) {
1157
+
1158
+ $errors = empty( $context ) ? $this->errors() : array( $context => $this->errors( $context ) );
1159
+
1160
+ if ( empty( $errors ) )
1161
+ return;
1162
+
1163
+ foreach ( $errors as $error_context => $errors )
1164
+ foreach( $errors as $error )
1165
+ $this->warning( $error_context, $error );
1166
+
1167
+ if ( $context )
1168
+ unset( $this->errors[$context] );
1169
+
1170
+ else
1171
+ $this->errors = array();
1172
+
1173
+ }
1174
+
1175
+ /**
1176
+ * Get the warnings
1177
+ *
1178
+ * @access public
1179
+ * @return null
1180
+ */
1181
+ public function warnings( $context = null ) {
1182
+
1183
+ if ( ! empty( $context ) )
1184
+ return isset( $this->warnings[$context] ) ? $this->warnings[$context] : array();
1185
+
1186
+ return $this->warnings;
1187
+
1188
+ }
1189
+
1190
+
1191
+ /**
1192
+ * Add an warning to the warnings stack
1193
+ *
1194
+ * @access private
1195
+ * @param string $context
1196
+ * @param mixed $warning
1197
+ * @return null
1198
+ */
1199
+ private function warning( $context, $warning ) {
1200
+
1201
+ if ( empty( $context ) || empty( $warning ) )
1202
+ return;
1203
+
1204
+ $this->warnings[$context][$_key = md5( implode( ':' , (array) $warning ) )] = $warning;
1205
+
1206
+ }
1207
+
1208
+
1209
+ /**
1210
+ * Custom error handler for catching errors
1211
+ *
1212
+ * @access private
1213
+ * @param string $type
1214
+ * @param string $message
1215
+ * @param string $file
1216
+ * @param string $line
1217
+ * @return null
1218
+ */
1219
+ public function error_handler( $type ) {
1220
+
1221
+ if ( in_array( $type, array( E_STRICT, E_DEPRECATED ) ) || error_reporting() === 0 )
1222
+ return false;
1223
+
1224
+ $args = func_get_args();
1225
+
1226
+ $this->warning( 'php', array_splice( $args, 0, 4 ) );
1227
+
1228
+ return false;
1229
+
1230
+ }
1231
+
1232
  }
1233
 
1234
  /**
1246
  global $_hmbkp_exclude_string;
1247
 
1248
  // Don't try to add unreadable files.
1249
+ if ( ! is_readable( $file['filename'] ) || ! file_exists( $file['filename'] ) )
1250
  return false;
1251
 
1252
  // Match everything else past the exclude list
plugin.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Manage your WordPress site with <a href="https://wpremote.com/">WP Remote</a>. Deactivate to clear your API Key.
6
- Version: 2.0.3
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  */
3
  /*
4
  Plugin Name: WP Remote
5
  Description: Manage your WordPress site with <a href="https://wpremote.com/">WP Remote</a>. Deactivate to clear your API Key.
6
+ Version: 2.0.4
7
  Author: Human Made Limited
8
  Author URI: http://hmn.md/
9
  */
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: humanmade, joehoyle, mattheu, tcrsavage, willmot
3
  Tags: wpremote, remote administration, multiple wordpress
4
  Requires at least: 2.9
5
  Tested up to: 3.3.1
6
- Stable tag: 2.0.3
7
 
8
  WP Remote is a free web app that enables you to easily manage all of your WordPress powered sites from one place.
9
 
3
  Tags: wpremote, remote administration, multiple wordpress
4
  Requires at least: 2.9
5
  Tested up to: 3.3.1
6
+ Stable tag: 2.0.4
7
 
8
  WP Remote is a free web app that enables you to easily manage all of your WordPress powered sites from one place.
9
 
wprp.backups.php CHANGED
@@ -28,7 +28,14 @@ function _wprp_backups_api_call( $action ) {
28
 
29
  // Set a random backup filename
30
  $backup->archive_filename = md5( time() ) . '.zip';
31
-
 
 
 
 
 
 
 
32
  $backup->backup();
33
 
34
  if ( $errors = $backup->errors() ) {
28
 
29
  // Set a random backup filename
30
  $backup->archive_filename = md5( time() ) . '.zip';
31
+
32
+ // Excludes
33
+ if ( ! empty( $_REQUEST['backup_excludes'] ) ) {
34
+
35
+ $excludes = array_map( 'urldecode', (array) $_REQUEST['backup_excludes'] );
36
+ $backup->excludes = $excludes;
37
+ }
38
+
39
  $backup->backup();
40
 
41
  if ( $errors = $backup->errors() ) {