BackWPup – WordPress Backup Plugin - Version 3.4.0

Version Description

Download this release

Release Info

Developer cocreation
Plugin Icon 128x128 BackWPup – WordPress Backup Plugin
Version 3.4.0
Comparing to
See all releases

Code changes from version 3.3.7 to 3.4.0

Files changed (51) hide show
  1. .htaccess +9 -9
  2. assets/css/backwpup.css +0 -1
  3. assets/templates/{php52notice → phpnotice}/de/notice.php +0 -0
  4. assets/templates/{php52notice → phpnotice}/de/question.php +0 -0
  5. assets/templates/{php52notice → phpnotice}/de_DE_formal/notice.php +0 -0
  6. assets/templates/{php52notice → phpnotice}/de_DE_formal/question.php +0 -0
  7. assets/templates/{php52notice → phpnotice}/en/notice.php +0 -0
  8. assets/templates/{php52notice → phpnotice}/en/question.php +0 -0
  9. assets/templates/{php52notice → phpnotice}/it/notice.php +0 -0
  10. assets/templates/{php52notice → phpnotice}/it/question.php +0 -0
  11. backwpup.php +542 -562
  12. inc/class-become-inpsyder-widget.php +1 -8
  13. inc/class-betatester-admin-notice.php +1 -8
  14. inc/class-cron.php +20 -10
  15. inc/class-destination-dropbox.php +711 -290
  16. inc/class-destination-email.php +25 -4
  17. inc/class-destination-folder.php +31 -19
  18. inc/class-destination-ftp.php +2 -1
  19. inc/class-destination-msazure.php +463 -462
  20. inc/class-destination-rsc.php +2 -1
  21. inc/class-destination-s3.php +3 -4
  22. inc/class-destination-sugarsync.php +2 -1
  23. inc/class-directory.php +21 -0
  24. inc/class-file.php +17 -26
  25. inc/class-job.php +2580 -2552
  26. inc/class-jobtype-file.php +147 -244
  27. inc/class-jobtype-wpexp.php +3 -3
  28. inc/class-mysqldump.php +3 -0
  29. inc/class-option.php +76 -2
  30. inc/class-page-about.php +2 -2
  31. inc/class-page-backups.php +0 -1
  32. inc/class-page-backwpup.php +26 -18
  33. inc/class-page-editjob.php +4 -5
  34. inc/class-page-logs.php +6 -6
  35. inc/class-page-settings.php +16 -1
  36. inc/class-path-fixer.php +33 -0
  37. inc/class-recursive-directory.php +24 -0
  38. languages/backwpup.pot +1033 -998
  39. readme.txt +17 -4
  40. vendor/PEAR/HTTP/Request2.php +1030 -1030
  41. vendor/PEAR/HTTP/Request2/Adapter.php +137 -137
  42. vendor/PEAR/HTTP/Request2/Adapter/Curl.php +567 -567
  43. vendor/PEAR/HTTP/Request2/Adapter/Mock.php +165 -165
  44. vendor/PEAR/HTTP/Request2/Adapter/Socket.php +1120 -1120
  45. vendor/PEAR/HTTP/Request2/CookieJar.php +493 -493
  46. vendor/PEAR/HTTP/Request2/Exception.php +159 -159
  47. vendor/PEAR/HTTP/Request2/MultipartBody.php +268 -268
  48. vendor/PEAR/HTTP/Request2/Observer/Log.php +191 -191
  49. vendor/PEAR/HTTP/Request2/Response.php +630 -630
  50. vendor/PEAR/HTTP/Request2/SOCKS5.php +134 -134
  51. vendor/PEAR/HTTP/Request2/SocketWrapper.php +297 -297
.htaccess CHANGED
@@ -1,9 +1,9 @@
1
- <Files *.php>
2
- <IfModule mod_authz_core.c>
3
- Require all denied
4
- </IfModule>
5
- <IfModule !mod_authz_core.c>
6
- Order allow,deny
7
- Deny from all
8
- </IfModule>
9
- </Files>
1
+ <Files *.php>
2
+ <IfModule mod_authz_core.c>
3
+ Require all denied
4
+ </IfModule>
5
+ <IfModule !mod_authz_core.c>
6
+ Order allow,deny
7
+ Deny from all
8
+ </IfModule>
9
+ </Files>
assets/css/backwpup.css CHANGED
@@ -159,7 +159,6 @@
159
  #backwpup-page .backwpup-warning {
160
  border-left: 4px solid #ffba00; /* UI color .update-nag */
161
  }
162
-
163
  @media screen and (min-width: 720px) {
164
  #backwpup-page .backwpup-floated-postbox {
165
  float: left;
159
  #backwpup-page .backwpup-warning {
160
  border-left: 4px solid #ffba00; /* UI color .update-nag */
161
  }
 
162
  @media screen and (min-width: 720px) {
163
  #backwpup-page .backwpup-floated-postbox {
164
  float: left;
assets/templates/{php52notice → phpnotice}/de/notice.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/de/question.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/de_DE_formal/notice.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/de_DE_formal/question.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/en/notice.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/en/question.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/it/notice.php RENAMED
File without changes
assets/templates/{php52notice → phpnotice}/it/question.php RENAMED
File without changes
backwpup.php CHANGED
@@ -1,563 +1,543 @@
1
- <?php
2
- /**
3
- * Plugin Name: BackWPup
4
- * Plugin URI: http://backwpup.com
5
- * Description: WordPress Backup Plugin
6
- * Author: Inpsyde GmbH
7
- * Author URI: http://inpsyde.com
8
- * Version: 3.3.7
9
- * Text Domain: backwpup
10
- * Domain Path: /languages/
11
- * Network: true
12
- * License: GPLv3
13
- * License URI: http://www.gnu.org/licenses/gpl-3.0
14
- */
15
-
16
- /**
17
- * Copyright (C) 2012-2016 Inpsyde GmbH (email: info@inpsyde.com)
18
- *
19
- * This program is free software; you can redistribute it and/or
20
- * modify it under the terms of the GNU General Public License
21
- * as published by the Free Software Foundation; either version 2
22
- * of the License, or (at your option) any later version.
23
- *
24
- * This program is distributed in the hope that it will be useful,
25
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
- * GNU General Public License for more details.
28
- *
29
- * You should have received a copy of the GNU General Public License
30
- * along with this program; if not, write to the Free Software
31
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32
- */
33
-
34
- if ( ! class_exists( 'BackWPup' ) ) {
35
-
36
- // Don't activate on anything less than PHP 5.2.7 or WordPress 3.9
37
- if ( version_compare( PHP_VERSION, '5.2.7', '<' ) || version_compare( get_bloginfo( 'version' ), '3.9', '<' ) || ! function_exists( 'spl_autoload_register' ) ) {
38
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
39
- deactivate_plugins( __FILE__ );
40
- die( 'BackWPup requires PHP version 5.2.7 with spl extension or greater and WordPress 3.8 or greater.' );
41
- }
42
-
43
- //Start Plugin
44
- if ( function_exists( 'add_filter' ) ) {
45
- add_action( 'plugins_loaded', array( 'BackWPup', 'get_instance' ), 11 );
46
- }
47
-
48
- /**
49
- * Main BackWPup Plugin Class
50
- */
51
- final class BackWPup {
52
-
53
- private static $instance = NULL;
54
- private static $plugin_data = array();
55
- private static $autoload = array();
56
- private static $destinations = array();
57
- private static $registered_destinations = array();
58
- private static $job_types = array();
59
- private static $wizards = array();
60
-
61
- /**
62
- * Set needed filters and actions and load
63
- */
64
- private function __construct() {
65
-
66
- // Nothing else matters if we're not on the main site
67
- if ( ! is_main_site() ) {
68
- return;
69
- }
70
- //auto loader
71
- spl_autoload_register( array( $this, 'autoloader' ) );
72
-
73
- //start upgrade if needed
74
- if ( get_site_option( 'backwpup_version' ) !== self::get_plugin_data( 'Version' ) || ! wp_next_scheduled( 'backwpup_check_cleanup' ) ) {
75
- BackWPup_Install::activate();
76
- }
77
- //load pro features
78
- if ( class_exists( 'BackWPup_Pro' ) ) {
79
- BackWPup_Pro::get_instance();
80
- }
81
- //WP-Cron
82
- if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
83
- if ( ! empty( $_GET[ 'backwpup_run' ] ) && class_exists( 'BackWPup_Job' ) ) {
84
- //early disable caches
85
- BackWPup_Job::disable_caches();
86
- //add action for running jobs in wp-cron.php
87
- add_action( 'wp_loaded', array( 'BackWPup_Cron', 'cron_active' ), PHP_INT_MAX );
88
- } else {
89
- //add cron actions
90
- add_action( 'backwpup_cron', array( 'BackWPup_Cron', 'run' ) );
91
- add_action( 'backwpup_check_cleanup', array( 'BackWPup_Cron', 'check_cleanup' ) );
92
- }
93
- //if in cron the rest is not needed
94
- return;
95
- }
96
- //deactivation hook
97
- register_deactivation_hook( __FILE__, array( 'BackWPup_Install', 'deactivate' ) );
98
- //Admin bar
99
- if ( get_site_option( 'backwpup_cfg_showadminbar' ) ) {
100
- add_action( 'init', array( 'BackWPup_Adminbar', 'get_instance' ) );
101
- }
102
- //only in backend
103
- if ( is_admin() && class_exists( 'BackWPup_Admin' ) ) {
104
- BackWPup_Admin::get_instance();
105
- }
106
- //work with wp-cli
107
- if ( defined( 'WP_CLI' ) && WP_CLI && method_exists( 'WP_CLI', 'add_command' ) ) {
108
- WP_CLI::add_command( 'backwpup', 'BackWPup_WP_CLI' );
109
- }
110
-
111
- // Notices and messages in admin
112
- if ( is_admin() && current_user_can( 'backwpup' ) ) {
113
-
114
- /// Notice for PHP 5.2 users
115
- $php_notice = new BackWPup_Php_Admin_Notice();
116
- add_action( 'admin_notices', array( $php_notice, 'admin_notice' ), 0 );
117
- add_action( 'backwpup_admin_messages', array( $php_notice, 'admin_page_message' ) );
118
-
119
- // Work for Inpsyde widget
120
- $inpsyder_widget = new BackWPup_Become_Inpsyder_Widget();
121
- add_action( 'wp_dashboard_setup', array( $inpsyder_widget, 'setup_widget' ) );
122
- add_action( 'backwpup_admin_messages', array( $inpsyder_widget, 'print_plugin_widget_markup' ), 0 );
123
-
124
- // Beta Tester notice
125
- $beta_tester_notice = new BackWPup_BetaTester_Admin_Notice();
126
- add_action( 'backwpup_admin_messages', array( $beta_tester_notice, 'dashboard_message' ), 20 );
127
-
128
- // 40% discount
129
- $pro_discount_widget = new BackWPup_Discount_Widget();
130
- add_action( 'wp_dashboard_setup', array( $pro_discount_widget, 'setup_widget' ) );
131
- add_action( 'backwpup_admin_messages', array( $pro_discount_widget, 'print_plugin_widget_markup' ), 0 );
132
-
133
- // Setup "dismissible" option actions for notices
134
- BackWPup_Dismissible_Notice_Option::setup_actions(
135
- true,
136
- BackWPup_Php_Admin_Notice::NOTICE_ID,
137
- 'backwpup'
138
- );
139
- BackWPup_Dismissible_Notice_Option::setup_actions(
140
- false,
141
- BackWPup_Become_Inpsyder_Widget::NOTICE_ID,
142
- 'backwpup'
143
- );
144
- BackWPup_Dismissible_Notice_Option::setup_actions(
145
- false,
146
- BackWPup_BetaTester_Admin_Notice::NOTICE_ID,
147
- 'backwpup'
148
- );
149
- BackWPup_Dismissible_Notice_Option::setup_actions(
150
- false,
151
- BackWPup_Discount_Widget::NOTICE_ID,
152
- 'backwpup'
153
- );
154
- }
155
-
156
- // Phone Home
157
- require_once dirname( __FILE__ ) . '/vendor/inpsyde/phone-home-client/inc/autoload.php';
158
- Inpsyde_PhoneHome_FrontController::initialize_for_network(
159
- 'BackWPup',
160
- dirname( __FILE__ ) . '/assets/templates/php52notice',
161
- 'backwpup',
162
- array(
163
- Inpsyde_PhoneHome_Configuration::ANONYMIZE => true,
164
- Inpsyde_PhoneHome_Configuration::MINIMUM_CAPABILITY => 'manage_options',
165
- Inpsyde_PhoneHome_Configuration::COLLECT_PHP => true,
166
- Inpsyde_PhoneHome_Configuration::COLLECT_WP => true,
167
- Inpsyde_PhoneHome_Configuration::SERVER_ADDRESS => 'https://backwpup.com/wp-json',
168
- )
169
- );
170
-
171
- }
172
-
173
- /**
174
- * @static
175
- *
176
- * @return self
177
- */
178
- public static function get_instance() {
179
-
180
- if (NULL === self::$instance) {
181
- self::$instance = new self;
182
- }
183
- return self::$instance;
184
- }
185
-
186
-
187
- private function __clone() {}
188
-
189
- /**
190
- * get information about the Plugin
191
- *
192
- * @param string $name Name of info to get or NULL to get all
193
- * @return string|array
194
- */
195
- public static function get_plugin_data( $name = NULL ) {
196
-
197
- if ( $name )
198
- $name = strtolower( trim( $name ) );
199
-
200
- if ( empty( self::$plugin_data ) ) {
201
- self::$plugin_data = get_file_data( __FILE__, array(
202
- 'name' => 'Plugin Name',
203
- 'version' => 'Version'
204
- ), 'plugin' );
205
- self::$plugin_data[ 'name' ] = trim( self::$plugin_data[ 'name' ] );
206
- //set some extra vars
207
- self::$plugin_data[ 'basename' ] = plugin_basename( dirname( __FILE__ ) );
208
- self::$plugin_data[ 'mainfile' ] = __FILE__ ;
209
- self::$plugin_data[ 'plugindir' ] = untrailingslashit( dirname( __FILE__ ) ) ;
210
- self::$plugin_data[ 'hash' ] = get_site_option( 'backwpup_cfg_hash' );
211
- if ( empty( self::$plugin_data[ 'hash' ] ) || strlen( self::$plugin_data[ 'hash' ] ) < 6 || strlen( self::$plugin_data[ 'hash' ] ) > 12 ) {
212
- self::$plugin_data[ 'hash' ] = substr( md5( md5( __FILE__ ) ), 14, 6 );
213
- update_site_option( 'backwpup_cfg_hash', self::$plugin_data[ 'hash' ] );
214
- }
215
- if ( defined( 'WP_TEMP_DIR' ) && is_dir( WP_TEMP_DIR ) ) {
216
- self::$plugin_data['temp'] = str_replace( '\\', '/', get_temp_dir() ) . 'backwpup-' . self::$plugin_data['hash'] . '/';
217
- } else {
218
- $upload_dir = wp_upload_dir();
219
- self::$plugin_data['temp'] = str_replace( '\\', '/', $upload_dir['basedir'] ) . '/backwpup-' . self::$plugin_data['hash'] . '-temp/';
220
- }
221
- self::$plugin_data[ 'running_file' ] = self::$plugin_data[ 'temp' ] . 'backwpup-working.php';
222
- self::$plugin_data[ 'url' ] = plugins_url( '', __FILE__ );
223
- self::$plugin_data[ 'cacert' ] = apply_filters( 'backwpup_cacert_bundle', ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
224
- //get unmodified WP Versions
225
- include ABSPATH . WPINC . '/version.php';
226
- /** @var $wp_version string */
227
- self::$plugin_data[ 'wp_version' ] = $wp_version;
228
- //Build User Agent
229
- self::$plugin_data[ 'user-agent' ] = self::$plugin_data[ 'name' ].'/' . self::$plugin_data[ 'version' ] . '; WordPress/' . self::$plugin_data[ 'wp_version' ] . '; ' . home_url();
230
- }
231
-
232
- if ( ! empty( $name ) )
233
- return self::$plugin_data[ $name ];
234
- else
235
- return self::$plugin_data;
236
- }
237
-
238
-
239
- /**
240
- * include not existing classes automatically
241
- *
242
- * @param string $class Class to load from file
243
- */
244
- private function autoloader( $class ) {
245
-
246
- //BackWPup classes auto load
247
- if ( strstr( strtolower( $class ), 'backwpup_' ) ) {
248
- $dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR;
249
  $class_file_name = 'class-' . str_replace( array( 'backwpup_', '_' ), array( '', '-' ), strtolower( $class ) ) . '.php';
250
- if ( strstr( strtolower( $class ), 'backwpup_pro' ) ) {
251
- $dir .= 'pro' . DIRECTORY_SEPARATOR;
252
- $class_file_name = str_replace( 'pro-','', $class_file_name );
253
- }
254
- if ( file_exists( $dir . $class_file_name ) )
255
- require $dir . $class_file_name;
256
- }
257
-
258
- // namespaced PSR-0
259
- if ( ! empty( self::$autoload ) ) {
260
- $pos = strrpos( $class, '\\' );
261
- if ( $pos !== FALSE ) {
262
- $class_path = str_replace( '\\', DIRECTORY_SEPARATOR, substr( $class, 0, $pos ) ) . DIRECTORY_SEPARATOR . str_replace( '_', DIRECTORY_SEPARATOR, substr( $class, $pos + 1 ) ) . '.php';
263
- foreach ( self::$autoload as $prefix => $dir ) {
264
- if ( $class === strstr( $class, $prefix ) ) {
265
- if ( file_exists( $dir . DIRECTORY_SEPARATOR . $class_path ) )
266
- require $dir . DIRECTORY_SEPARATOR . $class_path;
267
- }
268
- }
269
- } // Single class file
270
- elseif ( ! empty( self::$autoload[ $class ] ) && is_file( self::$autoload[ $class ] ) ) {
271
- require self::$autoload[ $class ];
272
- }
273
- }
274
-
275
- //Google SDK Auto loading
276
- $classPath = explode( '_', $class );
277
- if ( $classPath[0] == 'Google' ) {
278
- if ( count( $classPath ) > 3 ) {
279
- $classPath = array_slice( $classPath, 0, 3 );
280
- }
281
- $filePath = self::get_plugin_data( 'plugindir' ) . '/vendor/' . implode( '/', $classPath ) . '.php';
282
- if ( file_exists( $filePath ) ) {
283
- require $filePath;
284
- }
285
- }
286
-
287
- }
288
-
289
- /**
290
- * Load Plugin Translation
291
- *
292
- * @return bool Text domain loaded
293
- */
294
- public static function load_text_domain() {
295
-
296
- if ( is_textdomain_loaded( 'backwpup' ) ) {
297
- return TRUE;
298
- }
299
-
300
- return load_plugin_textdomain( 'backwpup', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
301
- }
302
-
303
- /**
304
- * Get a array of instances for Backup Destination's
305
- *
306
- * @param $key string Key of Destination where get class instance from
307
- * @return array BackWPup_Destinations
308
- */
309
- public static function get_destination( $key ) {
310
-
311
- $key = strtoupper( $key );
312
-
313
- if ( isset( self::$destinations[ $key ] ) && is_object( self::$destinations[ $key ] ) )
314
- return self::$destinations[ $key ];
315
-
316
- $reg_dests = self::get_registered_destinations();
317
- if ( ! empty( $reg_dests[ $key ][ 'class' ] ) ) {
318
- self::$destinations[ $key ] = new $reg_dests[ $key ][ 'class' ];
319
- } else {
320
- return NULL;
321
- }
322
-
323
- return self::$destinations[ $key ];
324
- }
325
-
326
- /**
327
- * Get a array of registered Destination's for Backups
328
- *
329
- * @return array BackWPup_Destinations
330
- */
331
- public static function get_registered_destinations() {
332
-
333
- //only run it one time
334
- if ( ! empty( self::$registered_destinations ) )
335
- return self::$registered_destinations;
336
-
337
- //add BackWPup Destinations
338
- // to folder
339
- self::$registered_destinations[ 'FOLDER' ] = array(
340
- 'class' => 'BackWPup_Destination_Folder',
341
- 'info' => array(
342
- 'ID' => 'FOLDER',
343
- 'name' => __( 'Folder', 'backwpup' ),
344
- 'description' => __( 'Backup to Folder', 'backwpup' ),
345
- ),
346
- 'can_sync' => FALSE,
347
- 'needed' => array(
348
- 'php_version' => '',
349
- 'functions' => array(),
350
- 'classes' => array()
351
- ),
352
- 'autoload' => array()
353
- );
354
- // backup with mail
355
- self::$registered_destinations[ 'EMAIL' ] = array(
356
- 'class' => 'BackWPup_Destination_Email',
357
- 'info' => array(
358
- 'ID' => 'EMAIL',
359
- 'name' => __( 'Email', 'backwpup' ),
360
- 'description' => __( 'Backup sent via email', 'backwpup' ),
361
- ),
362
- 'can_sync' => FALSE,
363
- 'needed' => array(
364
- 'php_version' => '5.2.4',
365
- 'functions' => array(),
366
- 'classes' => array()
367
- ),
368
- 'autoload' => array()
369
- );
370
- // backup to ftp
371
- self::$registered_destinations[ 'FTP' ] = array(
372
- 'class' => 'BackWPup_Destination_Ftp',
373
- 'info' => array(
374
- 'ID' => 'FTP',
375
- 'name' => __( 'FTP', 'backwpup' ),
376
- 'description' => __( 'Backup to FTP', 'backwpup' ),
377
- ),
378
- 'can_sync' => FALSE,
379
- 'needed' => array(
380
- 'mphp_version' => '',
381
- 'functions' => array( 'ftp_nb_fput' ),
382
- 'classes' => array()
383
- ),
384
- 'autoload' => array()
385
- );
386
- // backup to dropbox
387
- self::$registered_destinations[ 'DROPBOX' ] = array(
388
- 'class' => 'BackWPup_Destination_Dropbox',
389
- 'info' => array(
390
- 'ID' => 'DROPBOX',
391
- 'name' => __( 'Dropbox', 'backwpup' ),
392
- 'description' => __( 'Backup to Dropbox', 'backwpup' ),
393
- ),
394
- 'can_sync' => FALSE,
395
- 'needed' => array(
396
- 'php_version' => '',
397
- 'functions' => array( 'curl_exec' ),
398
- 'classes' => array()
399
- ),
400
- 'autoload' => array()
401
- );
402
- // Backup to S3
403
- self::$registered_destinations[ 'S3' ] = array(
404
- 'class' => 'BackWPup_Destination_S3',
405
- 'info' => array(
406
- 'ID' => 'S3',
407
- 'name' => __( 'S3 Service', 'backwpup' ),
408
- 'description' => __( 'Backup to an S3 Service', 'backwpup' ),
409
- ),
410
- 'can_sync' => FALSE,
411
- 'needed' => array(
412
- 'php_version' => '5.3.3',
413
- 'functions' => array( 'curl_exec' ),
414
- 'classes' => array( 'XMLWriter' )
415
- ),
416
- 'autoload' => array( 'Aws\\Common' => dirname( __FILE__ ) .'/vendor',
417
- 'Aws\\S3' => dirname( __FILE__ ) .'/vendor',
418
- 'Symfony\\Component\\EventDispatcher' => dirname( __FILE__ ) . '/vendor',
419
- 'Guzzle' => dirname( __FILE__ ) . '/vendor' )
420
- );
421
- // backup to MS Azure
422
- self::$registered_destinations[ 'MSAZURE' ] = array(
423
- 'class' => 'BackWPup_Destination_MSAzure',
424
- 'info' => array(
425
- 'ID' => 'MSAZURE',
426
- 'name' => __( 'MS Azure', 'backwpup' ),
427
- 'description' => __( 'Backup to Microsoft Azure (Blob)', 'backwpup' ),
428
- ),
429
- 'can_sync' => FALSE,
430
- 'needed' => array(
431
- 'php_version' => '5.3.2',
432
- 'functions' => array(),
433
- 'classes' => array()
434
- ),
435
- 'autoload' => array( 'WindowsAzure' => dirname( __FILE__ ) . '/vendor' )
436
- );
437
- // backup to Rackspace Cloud
438
- self::$registered_destinations[ 'RSC' ] = array(
439
- 'class' => 'BackWPup_Destination_RSC',
440
- 'info' => array(
441
- 'ID' => 'RSC',
442
- 'name' => __( 'RSC', 'backwpup' ),
443
- 'description' => __( 'Backup to Rackspace Cloud Files', 'backwpup' ),
444
- ),
445
- 'can_sync' => FALSE,
446
- 'needed' => array(
447
- 'php_version' => '5.3.3',
448
- 'functions' => array( 'curl_exec' ),
449
- 'classes' => array()
450
- ),
451
- 'autoload' => array( 'OpenCloud' => dirname( __FILE__ ) . '/vendor',
452
- 'Guzzle' => dirname( __FILE__ ) . '/vendor',
453
- 'Psr' => dirname( __FILE__ ) . '/vendor' )
454
- );
455
- // backup to Sugarsync
456
- self::$registered_destinations[ 'SUGARSYNC' ] = array(
457
- 'class' => 'BackWPup_Destination_SugarSync',
458
- 'info' => array(
459
- 'ID' => 'SUGARSYNC',
460
- 'name' => __( 'SugarSync', 'backwpup' ),
461
- 'description' => __( 'Backup to SugarSync', 'backwpup' ),
462
- ),
463
- 'can_sync' => FALSE,
464
- 'needed' => array(
465
- 'php_version' => '',
466
- 'functions' => array( 'curl_exec' ),
467
- 'classes' => array()
468
- ),
469
- 'autoload' => array()
470
- );
471
-
472
- //Hook for adding Destinations like above
473
- self::$registered_destinations = apply_filters( 'backwpup_register_destination', self::$registered_destinations );
474
-
475
- //check BackWPup Destinations
476
- foreach ( self::$registered_destinations as $dest_key => $dest ) {
477
- self::$registered_destinations[ $dest_key ][ 'error'] = '';
478
- // check PHP Version
479
- if ( ! empty( $dest[ 'needed' ][ 'php_version' ] ) && version_compare( PHP_VERSION, $dest[ 'needed' ][ 'php_version' ], '<' ) ) {
480
- self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'PHP Version %1$s is to low, you need Version %2$s or above.', 'backwpup' ), PHP_VERSION, $dest[ 'needed' ][ 'php_version' ] ) . ' ';
481
- self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
482
- }
483
- //check functions exists
484
- if ( ! empty( $dest[ 'needed' ][ 'functions' ] ) ) {
485
- foreach ( $dest[ 'needed' ][ 'functions' ] as $function_need ) {
486
- if ( ! function_exists( $function_need ) ) {
487
- self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'Missing function "%s".', 'backwpup' ), $function_need ) . ' ';
488
- self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
489
- }
490
- }
491
- }
492
- //check classes exists
493
- if ( ! empty( $dest[ 'needed' ][ 'classes' ] ) ) {
494
- foreach ( $dest[ 'needed' ][ 'classes' ] as $class_need ) {
495
- if ( ! class_exists( $class_need ) ) {
496
- self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'Missing class "%s".', 'backwpup' ), $class_need ) . ' ';
497
- self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
498
- }
499
- }
500
- }
501
- //add class/namespace to auto load
502
- if ( ! empty( self::$registered_destinations[ $dest_key ][ 'class' ] ) && ! empty( self::$registered_destinations[ $dest_key ][ 'autoload' ] ) )
503
- self::$autoload = array_merge( self::$autoload, self::$registered_destinations[ $dest_key ][ 'autoload' ] );
504
-
505
- }
506
-
507
- return self::$registered_destinations;
508
- }
509
-
510
-
511
- /**
512
- * Gets a array of instances from Job types
513
- *
514
- * @return array BackWPup_JobTypes
515
- */
516
- public static function get_job_types() {
517
-
518
- if ( !empty( self::$job_types ) )
519
- return self::$job_types;
520
-
521
- self::$job_types[ 'DBDUMP' ] = new BackWPup_JobType_DBDump;
522
- self::$job_types[ 'FILE' ] = new BackWPup_JobType_File;
523
- self::$job_types[ 'WPEXP' ] = new BackWPup_JobType_WPEXP;
524
- self::$job_types[ 'WPPLUGIN' ] = new BackWPup_JobType_WPPlugin;
525
- self::$job_types[ 'DBCHECK' ] = new BackWPup_JobType_DBCheck;
526
-
527
- self::$job_types = apply_filters( 'backwpup_job_types', self::$job_types );
528
-
529
- //remove types can't load
530
- foreach ( self::$job_types as $key => $job_type ) {
531
- if ( empty( $job_type ) || ! is_object( $job_type ) )
532
- unset( self::$job_types[ $key ] );
533
- }
534
-
535
- return self::$job_types;
536
- }
537
-
538
-
539
- /**
540
- * Gets a array of instances from Wizards
541
- *
542
- * @return array BackWPup_Pro_Wizards
543
- */
544
- public static function get_wizards() {
545
-
546
- if ( !empty( self::$wizards ) )
547
- return self::$wizards;
548
-
549
- self::$wizards = apply_filters( 'backwpup_pro_wizards', self::$wizards );
550
-
551
- //remove wizards can't load
552
- foreach ( self::$wizards as $key => $wizard ) {
553
- if ( empty( $wizard ) || ! is_object( $wizard ) )
554
- unset( self::$wizards[ $key ] );
555
- }
556
-
557
- return self::$wizards;
558
-
559
- }
560
-
561
- }
562
-
563
- }
1
+ <?php
2
+ /**
3
+ * Plugin Name: BackWPup
4
+ * Plugin URI: http://backwpup.com
5
+ * Description: WordPress Backup Plugin
6
+ * Author: Inpsyde GmbH
7
+ * Author URI: http://inpsyde.com
8
+ * Version: 3.4.0
9
+ * Text Domain: backwpup
10
+ * Domain Path: /languages/
11
+ * Network: true
12
+ * License: GPLv3
13
+ * License URI: http://www.gnu.org/licenses/gpl-3.0
14
+ */
15
+
16
+ /**
17
+ * Copyright (C) 2012-2016 Inpsyde GmbH (email: info@inpsyde.com)
18
+ *
19
+ * This program is free software; you can redistribute it and/or
20
+ * modify it under the terms of the GNU General Public License
21
+ * as published by the Free Software Foundation; either version 2
22
+ * of the License, or (at your option) any later version.
23
+ *
24
+ * This program is distributed in the hope that it will be useful,
25
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ * GNU General Public License for more details.
28
+ *
29
+ * You should have received a copy of the GNU General Public License
30
+ * along with this program; if not, write to the Free Software
31
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32
+ */
33
+
34
+ if ( ! class_exists( 'BackWPup' ) ) {
35
+
36
+ // Don't activate on anything less than PHP 5.3 or WordPress 3.9
37
+ if ( version_compare( PHP_VERSION, '5.3.0', '<' ) || version_compare( get_bloginfo( 'version' ), '3.9', '<' ) || ! function_exists( 'spl_autoload_register' ) ) {
38
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
39
+ deactivate_plugins( __FILE__ );
40
+ die( 'BackWPup requires PHP version 5.3 with spl extension or greater and WordPress 3.9 or greater.' );
41
+ }
42
+
43
+ //Start Plugin
44
+ if ( function_exists( 'add_filter' ) ) {
45
+ add_action( 'plugins_loaded', array( 'BackWPup', 'get_instance' ), 11 );
46
+ }
47
+
48
+ /**
49
+ * Main BackWPup Plugin Class
50
+ */
51
+ final class BackWPup {
52
+
53
+ private static $instance = NULL;
54
+ private static $plugin_data = array();
55
+ private static $autoload = array();
56
+ private static $destinations = array();
57
+ private static $registered_destinations = array();
58
+ private static $job_types = array();
59
+ private static $wizards = array();
60
+
61
+ /**
62
+ * Set needed filters and actions and load
63
+ */
64
+ private function __construct() {
65
+
66
+ // Nothing else matters if we're not on the main site
67
+ if ( ! is_main_site() ) {
68
+ return;
69
+ }
70
+ //auto loader
71
+ spl_autoload_register( array( $this, 'autoloader' ) );
72
+
73
+ //start upgrade if needed
74
+ if ( get_site_option( 'backwpup_version' ) !== self::get_plugin_data( 'Version' ) || ! wp_next_scheduled( 'backwpup_check_cleanup' ) ) {
75
+ BackWPup_Install::activate();
76
+ }
77
+ //load pro features
78
+ if ( class_exists( 'BackWPup_Pro' ) ) {
79
+ BackWPup_Pro::get_instance();
80
+ }
81
+ //WP-Cron
82
+ if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
83
+ if ( ! empty( $_GET[ 'backwpup_run' ] ) && class_exists( 'BackWPup_Job' ) ) {
84
+ //early disable caches
85
+ BackWPup_Job::disable_caches();
86
+ //add action for running jobs in wp-cron.php
87
+ add_action( 'wp_loaded', array( 'BackWPup_Cron', 'cron_active' ), PHP_INT_MAX );
88
+ } else {
89
+ //add cron actions
90
+ add_action( 'backwpup_cron', array( 'BackWPup_Cron', 'run' ) );
91
+ add_action( 'backwpup_check_cleanup', array( 'BackWPup_Cron', 'check_cleanup' ) );
92
+ }
93
+ //if in cron the rest is not needed
94
+ return;
95
+ }
96
+ //deactivation hook
97
+ register_deactivation_hook( __FILE__, array( 'BackWPup_Install', 'deactivate' ) );
98
+ //Admin bar
99
+ if ( get_site_option( 'backwpup_cfg_showadminbar' ) ) {
100
+ add_action( 'init', array( 'BackWPup_Adminbar', 'get_instance' ) );
101
+ }
102
+ //only in backend
103
+ if ( is_admin() && class_exists( 'BackWPup_Admin' ) ) {
104
+ BackWPup_Admin::get_instance();
105
+ }
106
+ //work with wp-cli
107
+ if ( defined( 'WP_CLI' ) && WP_CLI && method_exists( 'WP_CLI', 'add_command' ) ) {
108
+ WP_CLI::add_command( 'backwpup', 'BackWPup_WP_CLI' );
109
+ }
110
+
111
+ // Notices and messages in admin
112
+ if ( is_admin() && current_user_can( 'backwpup' ) ) {
113
+
114
+ // Work for Inpsyde widget
115
+ $inpsyder_widget = new BackWPup_Become_Inpsyder_Widget();
116
+ add_action( 'wp_dashboard_setup', array( $inpsyder_widget, 'setup_widget' ) );
117
+ add_action( 'backwpup_admin_messages', array( $inpsyder_widget, 'print_plugin_widget_markup' ), 0 );
118
+
119
+ // Beta Tester notice
120
+ $beta_tester_notice = new BackWPup_BetaTester_Admin_Notice();
121
+ add_action( 'backwpup_admin_messages', array( $beta_tester_notice, 'dashboard_message' ), 20 );
122
+
123
+ // Setup "dismissible" option actions for notices
124
+ BackWPup_Dismissible_Notice_Option::setup_actions(
125
+ false,
126
+ BackWPup_Become_Inpsyder_Widget::NOTICE_ID,
127
+ 'backwpup'
128
+ );
129
+ BackWPup_Dismissible_Notice_Option::setup_actions(
130
+ false,
131
+ BackWPup_BetaTester_Admin_Notice::NOTICE_ID,
132
+ 'backwpup'
133
+ );
134
+ }
135
+
136
+ // Phone Home
137
+ require_once dirname( __FILE__ ) . '/vendor/inpsyde/phone-home-client/inc/autoload.php';
138
+ Inpsyde_PhoneHome_FrontController::initialize_for_network(
139
+ 'BackWPup',
140
+ dirname( __FILE__ ) . '/assets/templates/phpnotice',
141
+ 'backwpup',
142
+ array(
143
+ Inpsyde_PhoneHome_Configuration::ANONYMIZE => true,
144
+ Inpsyde_PhoneHome_Configuration::MINIMUM_CAPABILITY => 'manage_options',
145
+ Inpsyde_PhoneHome_Configuration::COLLECT_PHP => true,
146
+ Inpsyde_PhoneHome_Configuration::COLLECT_WP => true,
147
+ Inpsyde_PhoneHome_Configuration::SERVER_ADDRESS => 'https://backwpup.com/wp-json',
148
+ )
149
+ );
150
+
151
+ }
152
+
153
+ /**
154
+ * @static
155
+ *
156
+ * @return self
157
+ */
158
+ public static function get_instance() {
159
+
160
+ if (NULL === self::$instance) {
161
+ self::$instance = new self;
162
+ }
163
+ return self::$instance;
164
+ }
165
+
166
+
167
+ private function __clone() {}
168
+
169
+ /**
170
+ * get information about the Plugin
171
+ *
172
+ * @param string $name Name of info to get or NULL to get all
173
+ * @return string|array
174
+ */
175
+ public static function get_plugin_data( $name = NULL ) {
176
+
177
+ if ( $name )
178
+ $name = strtolower( trim( $name ) );
179
+
180
+ if ( empty( self::$plugin_data ) ) {
181
+ self::$plugin_data = get_file_data( __FILE__, array(
182
+ 'name' => 'Plugin Name',
183
+ 'version' => 'Version'
184
+ ), 'plugin' );
185
+ self::$plugin_data[ 'name' ] = trim( self::$plugin_data[ 'name' ] );
186
+ //set some extra vars
187
+ self::$plugin_data[ 'basename' ] = plugin_basename( dirname( __FILE__ ) );
188
+ self::$plugin_data[ 'mainfile' ] = __FILE__ ;
189
+ self::$plugin_data[ 'plugindir' ] = untrailingslashit( dirname( __FILE__ ) ) ;
190
+ self::$plugin_data[ 'hash' ] = get_site_option( 'backwpup_cfg_hash' );
191
+ if ( empty( self::$plugin_data[ 'hash' ] ) || strlen( self::$plugin_data[ 'hash' ] ) < 6 || strlen( self::$plugin_data[ 'hash' ] ) > 12 ) {
192
+ self::$plugin_data[ 'hash' ] = substr( md5( md5( __FILE__ ) ), 14, 6 );
193
+ update_site_option( 'backwpup_cfg_hash', self::$plugin_data[ 'hash' ] );
194
+ }
195
+ if ( defined( 'WP_TEMP_DIR' ) && is_dir( WP_TEMP_DIR ) ) {
196
+ self::$plugin_data['temp'] = str_replace( '\\', '/', get_temp_dir() ) . 'backwpup-' . self::$plugin_data['hash'] . '/';
197
+ } else {
198
+ $upload_dir = wp_upload_dir();
199
+ self::$plugin_data['temp'] = str_replace( '\\', '/', $upload_dir['basedir'] ) . '/backwpup-' . self::$plugin_data['hash'] . '-temp/';
200
+ }
201
+ self::$plugin_data[ 'running_file' ] = self::$plugin_data[ 'temp' ] . 'backwpup-working.php';
202
+ self::$plugin_data[ 'url' ] = plugins_url( '', __FILE__ );
203
+ self::$plugin_data[ 'cacert' ] = apply_filters( 'backwpup_cacert_bundle', ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
204
+ //get unmodified WP Versions
205
+ include ABSPATH . WPINC . '/version.php';
206
+ /** @var $wp_version string */
207
+ self::$plugin_data[ 'wp_version' ] = $wp_version;
208
+ //Build User Agent
209
+ self::$plugin_data[ 'user-agent' ] = self::$plugin_data[ 'name' ].'/' . self::$plugin_data[ 'version' ] . '; WordPress/' . self::$plugin_data[ 'wp_version' ] . '; ' . home_url();
210
+ }
211
+
212
+ if ( ! empty( $name ) )
213
+ return self::$plugin_data[ $name ];
214
+ else
215
+ return self::$plugin_data;
216
+ }
217
+
218
+
219
+ /**
220
+ * include not existing classes automatically
221
+ *
222
+ * @param string $class Class to load from file
223
+ */
224
+ private function autoloader( $class ) {
225
+
226
+ //BackWPup classes auto load
227
+ if ( strstr( strtolower( $class ), 'backwpup_' ) ) {
228
+ $dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'inc' . DIRECTORY_SEPARATOR;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  $class_file_name = 'class-' . str_replace( array( 'backwpup_', '_' ), array( '', '-' ), strtolower( $class ) ) . '.php';
230
+ if ( strstr( strtolower( $class ), 'backwpup_pro' ) ) {
231
+ $dir .= 'pro' . DIRECTORY_SEPARATOR;
232
+ $class_file_name = str_replace( 'pro-','', $class_file_name );
233
+ }
234
+ if ( file_exists( $dir . $class_file_name ) )
235
+ require $dir . $class_file_name;
236
+ }
237
+
238
+ // namespaced PSR-0
239
+ if ( ! empty( self::$autoload ) ) {
240
+ $pos = strrpos( $class, '\\' );
241
+ if ( $pos !== FALSE ) {
242
+ $class_path = str_replace( '\\', DIRECTORY_SEPARATOR, substr( $class, 0, $pos ) ) . DIRECTORY_SEPARATOR . str_replace( '_', DIRECTORY_SEPARATOR, substr( $class, $pos + 1 ) ) . '.php';
243
+ foreach ( self::$autoload as $prefix => $dir ) {
244
+ if ( $class === strstr( $class, $prefix ) ) {
245
+ if ( file_exists( $dir . DIRECTORY_SEPARATOR . $class_path ) )
246
+ require $dir . DIRECTORY_SEPARATOR . $class_path;
247
+ }
248
+ }
249
+ } // Single class file
250
+ elseif ( ! empty( self::$autoload[ $class ] ) && is_file( self::$autoload[ $class ] ) ) {
251
+ require self::$autoload[ $class ];
252
+ }
253
+ }
254
+
255
+ //Google SDK Auto loading
256
+ $classPath = explode( '_', $class );
257
+ if ( $classPath[0] == 'Google' ) {
258
+ if ( count( $classPath ) > 3 ) {
259
+ $classPath = array_slice( $classPath, 0, 3 );
260
+ }
261
+ $filePath = self::get_plugin_data( 'plugindir' ) . '/vendor/' . implode( '/', $classPath ) . '.php';
262
+ if ( file_exists( $filePath ) ) {
263
+ require $filePath;
264
+ }
265
+ }
266
+
267
+ }
268
+
269
+ /**
270
+ * Load Plugin Translation
271
+ *
272
+ * @return bool Text domain loaded
273
+ */
274
+ public static function load_text_domain() {
275
+
276
+ if ( is_textdomain_loaded( 'backwpup' ) ) {
277
+ return TRUE;
278
+ }
279
+
280
+ return load_plugin_textdomain( 'backwpup', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
281
+ }
282
+
283
+ /**
284
+ * Get a array of instances for Backup Destination's
285
+ *
286
+ * @param $key string Key of Destination where get class instance from
287
+ * @return array BackWPup_Destinations
288
+ */
289
+ public static function get_destination( $key ) {
290
+
291
+ $key = strtoupper( $key );
292
+
293
+ if ( isset( self::$destinations[ $key ] ) && is_object( self::$destinations[ $key ] ) )
294
+ return self::$destinations[ $key ];
295
+
296
+ $reg_dests = self::get_registered_destinations();
297
+ if ( ! empty( $reg_dests[ $key ][ 'class' ] ) ) {
298
+ self::$destinations[ $key ] = new $reg_dests[ $key ][ 'class' ];
299
+ } else {
300
+ return NULL;
301
+ }
302
+
303
+ return self::$destinations[ $key ];
304
+ }
305
+
306
+ /**
307
+ * Get a array of registered Destination's for Backups
308
+ *
309
+ * @return array BackWPup_Destinations
310
+ */
311
+ public static function get_registered_destinations() {
312
+
313
+ //only run it one time
314
+ if ( ! empty( self::$registered_destinations ) )
315
+ return self::$registered_destinations;
316
+
317
+ //add BackWPup Destinations
318
+ // to folder
319
+ self::$registered_destinations[ 'FOLDER' ] = array(
320
+ 'class' => 'BackWPup_Destination_Folder',
321
+ 'info' => array(
322
+ 'ID' => 'FOLDER',
323
+ 'name' => __( 'Folder', 'backwpup' ),
324
+ 'description' => __( 'Backup to Folder', 'backwpup' ),
325
+ ),
326
+ 'can_sync' => FALSE,
327
+ 'needed' => array(
328
+ 'php_version' => '',
329
+ 'functions' => array(),
330
+ 'classes' => array()
331
+ ),
332
+ 'autoload' => array()
333
+ );
334
+ // backup with mail
335
+ self::$registered_destinations[ 'EMAIL' ] = array(
336
+ 'class' => 'BackWPup_Destination_Email',
337
+ 'info' => array(
338
+ 'ID' => 'EMAIL',
339
+ 'name' => __( 'Email', 'backwpup' ),
340
+ 'description' => __( 'Backup sent via email', 'backwpup' ),
341
+ ),
342
+ 'can_sync' => FALSE,
343
+ 'needed' => array(
344
+ 'php_version' => '5.2.4',
345
+ 'functions' => array(),
346
+ 'classes' => array()
347
+ ),
348
+ 'autoload' => array()
349
+ );
350
+ // backup to ftp
351
+ self::$registered_destinations[ 'FTP' ] = array(
352
+ 'class' => 'BackWPup_Destination_Ftp',
353
+ 'info' => array(
354
+ 'ID' => 'FTP',
355
+ 'name' => __( 'FTP', 'backwpup' ),
356
+ 'description' => __( 'Backup to FTP', 'backwpup' ),
357
+ ),
358
+ 'can_sync' => FALSE,
359
+ 'needed' => array(
360
+ 'mphp_version' => '',
361
+ 'functions' => array( 'ftp_nb_fput' ),
362
+ 'classes' => array()
363
+ ),
364
+ 'autoload' => array()
365
+ );
366
+ // backup to dropbox
367
+ self::$registered_destinations[ 'DROPBOX' ] = array(
368
+ 'class' => 'BackWPup_Destination_Dropbox',
369
+ 'info' => array(
370
+ 'ID' => 'DROPBOX',
371
+ 'name' => __( 'Dropbox', 'backwpup' ),
372
+ 'description' => __( 'Backup to Dropbox', 'backwpup' ),
373
+ ),
374
+ 'can_sync' => FALSE,
375
+ 'needed' => array(
376
+ 'php_version' => '',
377
+ 'functions' => array( 'curl_exec' ),
378
+ 'classes' => array()
379
+ ),
380
+ 'autoload' => array()
381
+ );
382
+ // Backup to S3
383
+ self::$registered_destinations[ 'S3' ] = array(
384
+ 'class' => 'BackWPup_Destination_S3',
385
+ 'info' => array(
386
+ 'ID' => 'S3',
387
+ 'name' => __( 'S3 Service', 'backwpup' ),
388
+ 'description' => __( 'Backup to an S3 Service', 'backwpup' ),
389
+ ),
390
+ 'can_sync' => FALSE,
391
+ 'needed' => array(
392
+ 'php_version' => '5.3.3',
393
+ 'functions' => array( 'curl_exec' ),
394
+ 'classes' => array( 'XMLWriter' )
395
+ ),
396
+ 'autoload' => array( 'Aws\\Common' => dirname( __FILE__ ) .'/vendor',
397
+ 'Aws\\S3' => dirname( __FILE__ ) .'/vendor',
398
+ 'Symfony\\Component\\EventDispatcher' => dirname( __FILE__ ) . '/vendor',
399
+ 'Guzzle' => dirname( __FILE__ ) . '/vendor' )
400
+ );
401
+ // backup to MS Azure
402
+ self::$registered_destinations[ 'MSAZURE' ] = array(
403
+ 'class' => 'BackWPup_Destination_MSAzure',
404
+ 'info' => array(
405
+ 'ID' => 'MSAZURE',
406
+ 'name' => __( 'MS Azure', 'backwpup' ),
407
+ 'description' => __( 'Backup to Microsoft Azure (Blob)', 'backwpup' ),
408
+ ),
409
+ 'can_sync' => FALSE,
410
+ 'needed' => array(
411
+ 'php_version' => '5.3.2',
412
+ 'functions' => array(),
413
+ 'classes' => array()
414
+ ),
415
+ 'autoload' => array( 'WindowsAzure' => dirname( __FILE__ ) . '/vendor' )
416
+ );
417
+ // backup to Rackspace Cloud
418
+ self::$registered_destinations[ 'RSC' ] = array(
419
+ 'class' => 'BackWPup_Destination_RSC',
420
+ 'info' => array(
421
+ 'ID' => 'RSC',
422
+ 'name' => __( 'RSC', 'backwpup' ),
423
+ 'description' => __( 'Backup to Rackspace Cloud Files', 'backwpup' ),
424
+ ),
425
+ 'can_sync' => FALSE,
426
+ 'needed' => array(
427
+ 'php_version' => '5.3.3',
428
+ 'functions' => array( 'curl_exec' ),
429
+ 'classes' => array()
430
+ ),
431
+ 'autoload' => array( 'OpenCloud' => dirname( __FILE__ ) . '/vendor',
432
+ 'Guzzle' => dirname( __FILE__ ) . '/vendor',
433
+ 'Psr' => dirname( __FILE__ ) . '/vendor' )
434
+ );
435
+ // backup to Sugarsync
436
+ self::$registered_destinations[ 'SUGARSYNC' ] = array(
437
+ 'class' => 'BackWPup_Destination_SugarSync',
438
+ 'info' => array(
439
+ 'ID' => 'SUGARSYNC',
440
+ 'name' => __( 'SugarSync', 'backwpup' ),
441
+ 'description' => __( 'Backup to SugarSync', 'backwpup' ),
442
+ ),
443
+ 'can_sync' => FALSE,
444
+ 'needed' => array(
445
+ 'php_version' => '',
446
+ 'functions' => array( 'curl_exec' ),
447
+ 'classes' => array()
448
+ ),
449
+ 'autoload' => array()
450
+ );
451
+
452
+ //Hook for adding Destinations like above
453
+ self::$registered_destinations = apply_filters( 'backwpup_register_destination', self::$registered_destinations );
454
+
455
+ //check BackWPup Destinations
456
+ foreach ( self::$registered_destinations as $dest_key => $dest ) {
457
+ self::$registered_destinations[ $dest_key ][ 'error'] = '';
458
+ // check PHP Version
459
+ if ( ! empty( $dest[ 'needed' ][ 'php_version' ] ) && version_compare( PHP_VERSION, $dest[ 'needed' ][ 'php_version' ], '<' ) ) {
460
+ self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'PHP Version %1$s is to low, you need Version %2$s or above.', 'backwpup' ), PHP_VERSION, $dest[ 'needed' ][ 'php_version' ] ) . ' ';
461
+ self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
462
+ }
463
+ //check functions exists
464
+ if ( ! empty( $dest[ 'needed' ][ 'functions' ] ) ) {
465
+ foreach ( $dest[ 'needed' ][ 'functions' ] as $function_need ) {
466
+ if ( ! function_exists( $function_need ) ) {
467
+ self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'Missing function "%s".', 'backwpup' ), $function_need ) . ' ';
468
+ self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
469
+ }
470
+ }
471
+ }
472
+ //check classes exists
473
+ if ( ! empty( $dest[ 'needed' ][ 'classes' ] ) ) {
474
+ foreach ( $dest[ 'needed' ][ 'classes' ] as $class_need ) {
475
+ if ( ! class_exists( $class_need ) ) {
476
+ self::$registered_destinations[ $dest_key ][ 'error' ] .= sprintf( __( 'Missing class "%s".', 'backwpup' ), $class_need ) . ' ';
477
+ self::$registered_destinations[ $dest_key ][ 'class' ] = NULL;
478
+ }
479
+ }
480
+ }
481
+ //add class/namespace to auto load
482
+ if ( ! empty( self::$registered_destinations[ $dest_key ][ 'class' ] ) && ! empty( self::$registered_destinations[ $dest_key ][ 'autoload' ] ) )
483
+ self::$autoload = array_merge( self::$autoload, self::$registered_destinations[ $dest_key ][ 'autoload' ] );
484
+
485
+ }
486
+
487
+ return self::$registered_destinations;
488
+ }
489
+
490
+
491
+ /**
492
+ * Gets a array of instances from Job types
493
+ *
494
+ * @return array BackWPup_JobTypes
495
+ */
496
+ public static function get_job_types() {
497
+
498
+ if ( !empty( self::$job_types ) )
499
+ return self::$job_types;
500
+
501
+ self::$job_types[ 'DBDUMP' ] = new BackWPup_JobType_DBDump;
502
+ self::$job_types[ 'FILE' ] = new BackWPup_JobType_File;
503
+ self::$job_types[ 'WPEXP' ] = new BackWPup_JobType_WPEXP;
504
+ self::$job_types[ 'WPPLUGIN' ] = new BackWPup_JobType_WPPlugin;
505
+ self::$job_types[ 'DBCHECK' ] = new BackWPup_JobType_DBCheck;
506
+
507
+ self::$job_types = apply_filters( 'backwpup_job_types', self::$job_types );
508
+
509
+ //remove types can't load
510
+ foreach ( self::$job_types as $key => $job_type ) {
511
+ if ( empty( $job_type ) || ! is_object( $job_type ) )
512
+ unset( self::$job_types[ $key ] );
513
+ }
514
+
515
+ return self::$job_types;
516
+ }
517
+
518
+
519
+ /**
520
+ * Gets a array of instances from Wizards
521
+ *
522
+ * @return array BackWPup_Pro_Wizards
523
+ */
524
+ public static function get_wizards() {
525
+
526
+ if ( !empty( self::$wizards ) )
527
+ return self::$wizards;
528
+
529
+ self::$wizards = apply_filters( 'backwpup_pro_wizards', self::$wizards );
530
+
531
+ //remove wizards can't load
532
+ foreach ( self::$wizards as $key => $wizard ) {
533
+ if ( empty( $wizard ) || ! is_object( $wizard ) )
534
+ unset( self::$wizards[ $key ] );
535
+ }
536
+
537
+ return self::$wizards;
538
+
539
+ }
540
+
541
+ }
542
+
543
+ }
inc/class-become-inpsyder-widget.php CHANGED
@@ -81,13 +81,6 @@ class BackWPup_Become_Inpsyder_Widget {
81
  // If notice is dismissed for good, don't show it
82
  self::$should_show = ! $option->is_dismissed( self::NOTICE_ID );
83
 
84
- $expiration_date = new DateTime('2017-05-02 00:00:00');
85
- $now = new DateTime('now');
86
-
87
- if ( $now < $expiration_date ) {
88
- self::$should_show = false;
89
- }
90
-
91
  return self::$should_show;
92
  }
93
 
@@ -141,7 +134,7 @@ class BackWPup_Become_Inpsyder_Widget {
141
  </p>
142
  <p<?php echo $btn_float === 'right' ? ' align="right' : '' ?>">
143
  <a
144
- style="background: #8fba2b; border-color: #7ba617 #719c0d #719c0d; -webkit-box-shadow: 0 1px 0 #719c0d; box-shadow: 0 1px 0 #719c0d; text-shadow: 0 -1px 1px #719c0d, 1px 0 1px #719c0d, 0 1px 1px #719c0d, -1px 0 1px #719c0d;"
145
  class="button button-large button-primary"
146
  href="<?php echo esc_url( $job_url ) ?>"
147
  target="_blank">
81
  // If notice is dismissed for good, don't show it
82
  self::$should_show = ! $option->is_dismissed( self::NOTICE_ID );
83
 
 
 
 
 
 
 
 
84
  return self::$should_show;
85
  }
86
 
134
  </p>
135
  <p<?php echo $btn_float === 'right' ? ' align="right' : '' ?>">
136
  <a
137
+ style="background: #9FC65D; border-color: #7ba617 #719c0d #719c0d; -webkit-box-shadow: 0 1px 0 #719c0d; box-shadow: 0 1px 0 #719c0d; text-shadow: 0 -1px 1px #719c0d, 1px 0 1px #719c0d, 0 1px 1px #719c0d, -1px 0 1px #719c0d;"
138
  class="button button-large button-primary"
139
  href="<?php echo esc_url( $job_url ) ?>"
140
  target="_blank">
inc/class-betatester-admin-notice.php CHANGED
@@ -51,13 +51,6 @@ class BackWPup_BetaTester_Admin_Notice {
51
  self::$should_show = ! $option->is_dismissed( self::NOTICE_ID );
52
  }
53
 
54
- $expiration_date = new DateTime('2017-05-02 00:00:00');
55
- $now = new DateTime('now');
56
-
57
- if ( $now < $expiration_date ) {
58
- self::$should_show = false;
59
- }
60
-
61
  return self::$should_show;
62
  }
63
 
@@ -89,7 +82,7 @@ class BackWPup_BetaTester_Admin_Notice {
89
  </p>
90
  <p>
91
  <a
92
- style="background: #8fba2b; border-color: #7ba617 #719c0d #719c0d; -webkit-box-shadow: 0 1px 0 #719c0d; box-shadow: 0 1px 0 #719c0d; text-shadow: 0 -1px 1px #719c0d, 1px 0 1px #719c0d, 0 1px 1px #719c0d, -1px 0 1px #719c0d;"
93
  class="button button-primary"
94
  href="<?php echo esc_url( $join_us_url ) ?>"
95
  target="_blank">
51
  self::$should_show = ! $option->is_dismissed( self::NOTICE_ID );
52
  }
53
 
 
 
 
 
 
 
 
54
  return self::$should_show;
55
  }
56
 
82
  </p>
83
  <p>
84
  <a
85
+ style="background: #9FC65D; border-color: #7ba617 #719c0d #719c0d; -webkit-box-shadow: 0 1px 0 #719c0d; box-shadow: 0 1px 0 #719c0d; text-shadow: 0 -1px 1px #719c0d, 1px 0 1px #719c0d, 0 1px 1px #719c0d, -1px 0 1px #719c0d;"
86
  class="button button-primary"
87
  href="<?php echo esc_url( $join_us_url ) ?>"
88
  target="_blank">
inc/class-cron.php CHANGED
@@ -73,27 +73,32 @@ class BackWPup_Cron {
73
  }
74
 
75
  //Compress not compressed logs
76
- if ( is_readable( $log_folder ) && function_exists( 'gzopen' ) && get_site_option( 'backwpup_cfg_gzlogs' ) && ! is_object( $job_object ) ) {
 
77
  //Compress old not compressed logs
78
- if ( $dir = opendir( $log_folder ) ) {
 
 
79
  $jobids = BackWPup_Option::get_job_ids();
80
- while ( FALSE !== ( $file = readdir( $dir ) ) ) {
81
- if ( is_writeable( $log_folder . $file ) && '.html' == substr( $file, -5 ) ) {
82
- $compress = new BackWPup_Create_Archive( $log_folder . $file . '.gz' );
83
- if ( $compress->add_file( $log_folder . $file ) ) {
84
- unlink( $log_folder . $file );
85
  //change last logfile in jobs
86
  foreach( $jobids as $jobid ) {
87
  $job_logfile = BackWPup_Option::get( $jobid, 'logfile' );
88
- if ( ! empty( $job_logfile ) && $job_logfile === $log_folder . $file ) {
89
- BackWPup_Option::update( $jobid, 'logfile', $log_folder . $file . '.gz' );
90
  }
91
  }
92
  }
93
  unset( $compress );
94
  }
95
  }
96
- closedir( $dir );
 
 
97
  }
98
  }
99
 
@@ -168,6 +173,11 @@ class BackWPup_Cron {
168
 
169
  if ( $args['run'] === 'restart' ) {
170
  $job_object = BackWPup_Job::get_working_data();
 
 
 
 
 
171
  //restart job if not working or a restart wished
172
  $not_worked_time = microtime( TRUE ) - $job_object->timestamp_last_update;
173
  if ( ! $job_object->pid || $not_worked_time > 300 ) {
73
  }
74
 
75
  //Compress not compressed logs
76
+ if ( is_readable( $log_folder ) && function_exists( 'gzopen' )
77
+ && get_site_option( 'backwpup_cfg_gzlogs' ) && ! is_object( $job_object ) ) {
78
  //Compress old not compressed logs
79
+ try {
80
+ $dir = new BackWPup_Directory( $log_folder );
81
+
82
  $jobids = BackWPup_Option::get_job_ids();
83
+ foreach ( $dir as $file ) {
84
+ if ( $file->isWritable() && '.html' == substr( $file->getFilename(), -5 ) ) {
85
+ $compress = new BackWPup_Create_Archive( $file->getPathname() . '.gz' );
86
+ if ( $compress->add_file( $file->getPathname() ) ) {
87
+ unlink( $file->getPathname() );
88
  //change last logfile in jobs
89
  foreach( $jobids as $jobid ) {
90
  $job_logfile = BackWPup_Option::get( $jobid, 'logfile' );
91
+ if ( ! empty( $job_logfile ) && $job_logfile === $file->getPathname() ) {
92
+ BackWPup_Option::update( $jobid, 'logfile', $file->getPathname() . '.gz' );
93
  }
94
  }
95
  }
96
  unset( $compress );
97
  }
98
  }
99
+ }
100
+ catch ( UnexpectedValueException $e ) {
101
+ $job_object->log( sprintf( __( "Could not open path: %s", 'backwpup' ), $e->getMessage() ), E_USER_WARNING );
102
  }
103
  }
104
 
173
 
174
  if ( $args['run'] === 'restart' ) {
175
  $job_object = BackWPup_Job::get_working_data();
176
+ // Restart if cannot find job
177
+ if ( ! $job_object ) {
178
+ BackWPup_Job::start_http( 'restart' );
179
+ return;
180
+ }
181
  //restart job if not working or a restart wished
182
  $not_worked_time = microtime( TRUE ) - $job_object->timestamp_last_update;
183
  if ( ! $job_object->pid || $not_worked_time > 300 ) {
inc/class-destination-dropbox.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
2
 
3
  /**
4
- * Documentation: https://www.dropbox.com/developers/reference/api
 
 
5
  */
6
  class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
7
 
@@ -14,17 +16,15 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
14
  * @return array
15
  */
16
  public function option_defaults() {
17
-
18
  return array(
19
  'dropboxtoken' => array(),
20
  'dropboxroot' => 'sandbox',
21
  'dropboxmaxbackups' => 15,
22
  'dropboxsyncnodelete' => true,
23
- 'dropboxdir' => trailingslashit( sanitize_file_name( get_bloginfo( 'name' ) ) )
24
  );
25
  }
26
 
27
-
28
  /**
29
  * @param $jobid
30
  */
@@ -34,21 +34,14 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
34
  //disable token on dropbox
35
  try {
36
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
37
- if ( BackWPup_Option::get( $jobid, 'dropboxsecret' ) ) {
38
- $dropbox->setOAuthTokens( array(
39
- 'access_token' => BackWPup_Option::get( $jobid, 'dropboxtoken' ),
40
- 'oauth_token_secret' => BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'dropboxsecret' ) )
41
- ) );
42
- } else {
43
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
44
- }
45
- $dropbox->disable_access_token();
46
- } catch ( Exception $e ) {
47
  echo '<div id="message" class="error"><p>' . sprintf( __( 'Dropbox API: %s', 'backwpup' ), $e->getMessage() ) . '</p></div>';
48
  }
49
  BackWPup_Option::update( $jobid, 'dropboxtoken', array() );
50
  BackWPup_Option::update( $jobid, 'dropboxroot', 'sandbox' );
51
- BackWPup_Option::delete( $jobid, 'dropboxsecret' );
52
  }
53
 
54
  $dropbox = new BackWPup_Destination_Dropbox_API( 'dropbox' );
@@ -124,6 +117,7 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
124
  <input id="iddropboxmaxbackups" name="dropboxmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'dropboxmaxbackups' ) ); ?>" class="small-text" />
125
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
126
  </label>
 
127
  <?php } else { ?>
128
  <label for="iddropboxsyncnodelete">
129
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'dropboxsyncnodelete' ), true ); ?> name="dropboxsyncnodelete" id="iddropboxsyncnodelete" />
@@ -139,8 +133,6 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
139
 
140
  /**
141
  * @param $jobid
142
- *
143
- * @return string|void
144
  */
145
  public function edit_form_post_save( $jobid ) {
146
 
@@ -151,7 +143,8 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
151
  $dropboxtoken = $dropbox->oAuthToken( $_POST['sandbox_code'] );
152
  BackWPup_Option::update( $jobid, 'dropboxtoken', $dropboxtoken );
153
  BackWPup_Option::update( $jobid, 'dropboxroot', 'sandbox' );
154
- } catch ( Exception $e ) {
 
155
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
156
  }
157
  }
@@ -162,7 +155,8 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
162
  $dropboxtoken = $dropbox->oAuthToken( $_POST['dropbbox_code'] );
163
  BackWPup_Option::update( $jobid, 'dropboxtoken', $dropboxtoken );
164
  BackWPup_Option::update( $jobid, 'dropboxroot', 'dropbox' );
165
- } catch ( Exception $e ) {
 
166
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
167
  }
168
  }
@@ -171,9 +165,6 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
171
  BackWPup_Option::update( $jobid, 'dropboxmaxbackups', ! empty( $_POST['dropboxmaxbackups'] ) ? absint( $_POST['dropboxmaxbackups'] ) : 0 );
172
 
173
  $_POST['dropboxdir'] = trailingslashit( str_replace( '//', '/', str_replace( '\\', '/', trim( sanitize_text_field( $_POST['dropboxdir'] ) ) ) ) );
174
- if ( substr( $_POST['dropboxdir'], 0, 1 ) === '/' ) {
175
- $_POST['dropboxdir'] = substr( $_POST['dropboxdir'], 1 );
176
- }
177
  if ( $_POST['dropboxdir'] === '/' ) {
178
  $_POST['dropboxdir'] = '';
179
  }
@@ -186,14 +177,14 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
186
  * @param $backupfile
187
  */
188
  public function file_delete( $jobdest, $backupfile ) {
189
-
190
  $files = get_site_transient( 'backwpup_' . strtolower( $jobdest ) );
191
  list( $jobid, $dest ) = explode( '_', $jobdest );
192
 
193
  try {
194
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
195
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
196
- $dropbox->fileopsDelete( $backupfile );
 
197
  //update file list
198
  foreach ( $files as $key => $file ) {
199
  if ( is_array( $file ) && $file['file'] == $backupfile ) {
@@ -201,7 +192,8 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
201
  }
202
  }
203
  unset( $dropbox );
204
- } catch ( Exception $e ) {
 
205
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
206
  }
207
 
@@ -213,16 +205,16 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
213
  * @param $get_file
214
  */
215
  public function file_download( $jobid, $get_file ) {
216
-
217
  try {
218
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
219
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
220
- $media = $dropbox->media( $get_file );
221
- if ( ! empty( $media['url'] ) ) {
222
- header( "Location: " . $media['url'] );
223
  }
224
  die();
225
- } catch ( Exception $e ) {
 
226
  die( $e->getMessage() );
227
  }
228
  }
@@ -242,43 +234,35 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
242
  * @return bool
243
  */
244
  public function job_run_archive( BackWPup_Job $job_object ) {
245
-
246
  $job_object->substeps_todo = 2 + $job_object->backup_filesize;
247
  if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
248
  $job_object->log( sprintf( __( '%d. Try to send backup file to Dropbox&#160;&hellip;', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) );
249
  }
250
 
251
  try {
252
- $dropbox = new BackWPup_Destination_Dropbox_API( $job_object->job['dropboxroot'] );
253
- // cahnge oauth1 to oauth2 token
254
- if ( ! empty( $job_object->job['dropboxsecret'] ) && empty( $job_object->job['dropboxtoken']['access_token'] ) ) {
255
- $dropbox->setOAuthTokens( array(
256
- 'access_token' => $job_object->job['dropboxtoken'],
257
- 'oauth_token_secret' => BackWPup_Encryption::decrypt( $job_object->job['dropboxsecret'] )
258
- ) );
259
- $job_object->job['dropboxtoken'] = $dropbox->token_from_oauth1();
260
- BackWPup_Option::update( $job_object->job['jobid'], 'dropboxtoken', $job_object->job['dropboxtoken'] );
261
- BackWPup_Option::delete( $job_object->job['jobid'], 'dropboxsecret' );
262
- }
263
- // set the tokens
264
  $dropbox->setOAuthTokens( $job_object->job['dropboxtoken'] );
265
 
266
  //get account info
267
  if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
268
- $info = $dropbox->accountInfo();
269
- if ( ! empty( $info['uid'] ) ) {
270
  if ( $job_object->is_debug() ) {
271
- $user = $info['display_name'] . ' (' . $info['email'] . ')';
272
- } else {
273
- $user = $info['display_name'];
 
274
  }
275
  $job_object->log( sprintf( __( 'Authenticated with Dropbox of user: %s', 'backwpup' ), $user ) );
 
276
  //Quota
277
  if ( $job_object->is_debug() ) {
278
- $dropboxfreespase = $info['quota_info']['quota'] - $info['quota_info']['shared'] - $info['quota_info']['normal'];
 
279
  $job_object->log( sprintf( __( '%s available on your Dropbox', 'backwpup' ), size_format( $dropboxfreespase, 2 ) ) );
280
  }
281
- } else {
 
282
  $job_object->log( __( 'Not Authenticated with Dropbox!', 'backwpup' ), E_USER_ERROR );
283
 
284
  return false;
@@ -291,16 +275,18 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
291
 
292
  if ( $job_object->substeps_done < $job_object->backup_filesize ) { //only if upload not complete
293
  $response = $dropbox->upload( $job_object->backup_folder . $job_object->backup_file, $job_object->job['dropboxdir'] . $job_object->backup_file );
294
- if ( $response['bytes'] == $job_object->backup_filesize ) {
295
  if ( ! empty( $job_object->job['jobid'] ) ) {
296
- BackWPup_Option::update( $job_object->job['jobid'], 'lastbackupdownloadurl', network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloaddropbox&file=' . ltrim( $response['path'], '/' ) . '&jobid=' . $job_object->job['jobid'] );
297
  }
298
  $job_object->substeps_done = 1 + $job_object->backup_filesize;
299
- $job_object->log( sprintf( __( 'Backup transferred to %s', 'backwpup' ), 'https://content.dropboxapi.com/1/files/' . $job_object->job['dropboxroot'] . $response['path'] ), E_USER_NOTICE );
300
- } else {
301
- if ( $response['bytes'] != $job_object->backup_filesize ) {
 
302
  $job_object->log( __( 'Uploaded file size and local file size don\'t match.', 'backwpup' ), E_USER_ERROR );
303
- } else {
 
304
  $job_object->log(
305
  sprintf(
306
  __( 'Error transfering backup to %s.', 'backwpup' ) . ' ' . $response['error'],
@@ -312,28 +298,25 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
312
  }
313
  }
314
 
315
-
316
  $backupfilelist = array();
317
  $filecounter = 0;
318
  $files = array();
319
- $metadata = $dropbox->metadata( $job_object->job['dropboxdir'] );
320
- if ( is_array( $metadata ) ) {
321
- foreach ( $metadata['contents'] as $data ) {
322
- if ( $data['is_dir'] != true ) {
323
- $file = basename( $data['path'] );
324
  if ( $job_object->is_backup_archive( $file ) ) {
325
- $backupfilelist[ strtotime( $data['modified'] ) ] = $file;
326
  }
327
- $files[ $filecounter ]['folder'] = "https://content.dropboxapi.com/1/files/" . $job_object->job['dropboxroot'] . dirname( $data['path'] ) . "/";
328
- $files[ $filecounter ]['file'] = $data['path'];
329
- $files[ $filecounter ]['filename'] = basename( $data['path'] );
330
- $files[ $filecounter ]['downloadurl'] = network_admin_url( 'admin.php?page=backwpupbackups&action=downloaddropbox&file=' . $data['path'] . '&jobid=' . $job_object->job['jobid'] );
331
- $files[ $filecounter ]['filesize'] = $data['bytes'];
332
- $files[ $filecounter ]['time'] = strtotime( $data['modified'] ) + ( get_option( 'gmt_offset' ) * 3600 );
333
  $filecounter ++;
334
  }
335
  }
336
- }
337
  if ( $job_object->job['dropboxmaxbackups'] > 0 && is_object( $dropbox ) ) { //Delete old backups
338
  if ( count( $backupfilelist ) > $job_object->job['dropboxmaxbackups'] ) {
339
  ksort( $backupfilelist );
@@ -342,17 +325,13 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
342
  if ( count( $backupfilelist ) < $job_object->job['dropboxmaxbackups'] ) {
343
  break;
344
  }
345
- $response = $dropbox->fileopsDelete( $job_object->job['dropboxdir'] . $file ); //delete files on Cloud
346
- if ( $response['is_deleted'] == 'true' ) {
347
  foreach ( $files as $key => $filedata ) {
348
  if ( $filedata['file'] == '/' . $job_object->job['dropboxdir'] . $file ) {
349
  unset( $files[ $key ] );
350
  }
351
  }
352
  $numdeltefiles ++;
353
- } else {
354
- $job_object->log( sprintf( __( 'Error while deleting file from Dropbox: %s', 'backwpup' ), $file ), E_USER_ERROR );
355
- }
356
  }
357
  if ( $numdeltefiles > 0 ) {
358
  $job_object->log( sprintf( _n( 'One file deleted from Dropbox', '%d files deleted on Dropbox', $numdeltefiles, 'backwpup' ), $numdeltefiles ), E_USER_NOTICE );
@@ -360,9 +339,9 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
360
  }
361
  }
362
  set_site_transient( 'backwpup_' . $job_object->job['jobid'] . '_dropbox', $files, YEAR_IN_SECONDS );
363
- } catch ( Exception $e ) {
364
- $job_object->log( E_USER_ERROR, sprintf( __( 'Dropbox API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine() );
365
-
366
  return false;
367
  }
368
  $job_object->substeps_done ++;
@@ -376,7 +355,6 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
376
  * @return bool
377
  */
378
  public function can_run( array $job_settings ) {
379
-
380
  if ( empty( $job_settings['dropboxtoken'] ) ) {
381
  return false;
382
  }
@@ -386,38 +364,30 @@ class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
386
 
387
  }
388
 
389
-
390
  /**
391
- *
392
  */
393
  final class BackWPup_Destination_Dropbox_API {
394
 
395
  /**
396
- *
397
  */
398
  const API_URL = 'https://api.dropboxapi.com/';
399
 
400
  /**
401
- *
402
  */
403
  const API_CONTENT_URL = 'https://content.dropboxapi.com/';
404
 
405
  /**
406
- *
407
  */
408
  const API_WWW_URL = 'https://www.dropbox.com/';
409
 
410
  /**
411
- *
412
- */
413
- const API_VERSION_URL = '1/';
414
-
415
- /**
416
- * dropbox vars
417
- *
418
- * @var string
419
  */
420
- private $root = 'sandbox';
421
 
422
  /**
423
  * oAuth vars
@@ -430,82 +400,80 @@ final class BackWPup_Destination_Dropbox_API {
430
  * @var string
431
  */
432
  private $oauth_app_secret = '';
 
433
  /**
434
  * @var string
435
  */
436
  private $oauth_token = '';
437
 
 
 
 
 
 
 
438
 
439
  /**
440
  * @param string $boxtype
441
  *
442
  * @throws BackWPup_Destination_Dropbox_API_Exception
443
  */
444
- public function __construct( $boxtype = 'dropbox' ) {
445
-
446
  if ( $boxtype == 'dropbox' ) {
447
  $this->oauth_app_key = get_site_option( 'backwpup_cfg_dropboxappkey', base64_decode( "dHZkcjk1MnRhZnM1NmZ2" ) );
448
  $this->oauth_app_secret = BackWPup_Encryption::decrypt( get_site_option( 'backwpup_cfg_dropboxappsecret', base64_decode( "OWV2bDR5MHJvZ2RlYmx1" ) ) );
449
- $this->root = 'dropbox';
450
- } else {
451
  $this->oauth_app_key = get_site_option( 'backwpup_cfg_dropboxsandboxappkey', base64_decode( "cHVrZmp1a3JoZHR5OTFk" ) );
452
  $this->oauth_app_secret = BackWPup_Encryption::decrypt( get_site_option( 'backwpup_cfg_dropboxsandboxappsecret', base64_decode( "eGNoYzhxdTk5eHE0eWdq" ) ) );
453
- $this->root = 'sandbox';
454
  }
455
 
456
  if ( empty( $this->oauth_app_key ) || empty( $this->oauth_app_secret ) ) {
457
  throw new BackWPup_Destination_Dropbox_API_Exception( "No App key or App Secret specified." );
458
  }
459
- }
460
-
461
- /**
462
- * @param $token
463
- *
464
- * @throws BackWPup_Destination_Dropbox_API_Exception
465
- */
466
- public function setOAuthTokens( $token ) {
467
-
468
- if ( empty( $token['access_token'] ) ) {
469
- throw new BackWPup_Destination_Dropbox_API_Exception( "No oAuth token specified." );
470
- }
471
 
472
- $this->oauth_token = $token;
473
  }
474
 
475
- public function token_from_oauth1() {
476
-
477
- $url = self::API_URL . self::API_VERSION_URL . 'oauth2/token_from_oauth1';
478
-
479
- return $this->request( $url, array(), 'POST' );
480
- }
481
 
482
  /**
483
- * @return array|mixed|string
 
 
 
 
 
 
 
 
484
  */
485
- public function accountInfo() {
 
 
 
486
 
487
- $url = self::API_URL . self::API_VERSION_URL . 'account/info';
488
 
489
- return $this->request( $url );
490
- }
491
-
492
- public function disable_access_token() {
493
-
494
- $url = self::API_URL . self::API_VERSION_URL . 'disable_access_token';
495
 
496
- return $this->request( $url, array(), 'POST' );
497
  }
498
 
499
  /**
 
 
500
  * @param $file
501
  * @param string $path
502
  * @param bool $overwrite
503
  *
504
- * @return array|mixed|string
505
  * @throws BackWPup_Destination_Dropbox_API_Exception
506
  */
507
  public function upload( $file, $path = '', $overwrite = true ) {
508
-
509
  $file = str_replace( "\\", "/", $file );
510
 
511
  if ( ! is_readable( $file ) ) {
@@ -513,10 +481,14 @@ final class BackWPup_Destination_Dropbox_API {
513
  }
514
 
515
  if ( filesize( $file ) < 5242880 ) { //chunk transfer on bigger uploads
516
- $url = self::API_CONTENT_URL . self::API_VERSION_URL . 'files_put/' . $this->root . '/' . $this->encode_path( $path );
517
- $output = $this->request( $url, array( 'overwrite' => ( $overwrite ) ? 'true' : 'false' ), 'PUT', file_get_contents( $file ) );
518
- } else {
519
- $output = $this->chunked_upload( $file, $path, $overwrite );
 
 
 
 
520
  }
521
 
522
  return $output;
@@ -530,10 +502,7 @@ final class BackWPup_Destination_Dropbox_API {
530
  * @return array|mixed|string
531
  * @throws BackWPup_Destination_Dropbox_API_Exception
532
  */
533
- public function chunked_upload( $file, $path = '', $overwrite = true ) {
534
-
535
- $backwpup_job_object = BackWPup_Destination_Dropbox::$backwpup_job_object;
536
-
537
  $file = str_replace( "\\", "/", $file );
538
 
539
  if ( ! is_readable( $file ) ) {
@@ -547,33 +516,43 @@ final class BackWPup_Destination_Dropbox_API {
547
  throw new BackWPup_Destination_Dropbox_API_Exception( "Can not open source file for transfer." );
548
  }
549
 
550
- if ( ! isset( $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid'] ) ) {
551
- $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid'] = null;
 
 
 
 
 
552
  }
553
- if ( ! isset( $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] ) ) {
554
- $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] = 0;
555
  }
556
 
557
  //seek to current position
558
- if ( $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] > 0 ) {
559
- fseek( $file_handel, $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] );
560
  }
561
 
562
  while ( $data = fread( $file_handel, $chunk_size ) ) {
563
  $chunk_upload_start = microtime( true );
564
- $url = self::API_CONTENT_URL . self::API_VERSION_URL . 'chunked_upload';
565
- $output = $this->request( $url, array(
566
- 'upload_id' => $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid'],
567
- 'offset' => $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset']
568
- ), 'PUT', $data );
 
 
 
 
569
  $chunk_upload_time = microtime( true ) - $chunk_upload_start;
 
 
570
  //args for next chunk
571
- $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] = $output['offset'];
572
- $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid'] = $output['upload_id'];
573
- if ( $backwpup_job_object->job['backuptype'] === 'archive' ) {
574
- $backwpup_job_object->substeps_done = $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'];
575
  if ( strlen( $data ) == $chunk_size ) {
576
- $time_remaining = $backwpup_job_object->do_restart_time();
577
  //calc next chunk
578
  if ( $time_remaining < $chunk_upload_time ) {
579
  $chunk_size = floor( $chunk_size / $chunk_upload_time * ( $time_remaining - 3 ) );
@@ -586,151 +565,372 @@ final class BackWPup_Destination_Dropbox_API {
586
  }
587
  }
588
  }
589
- $backwpup_job_object->update_working_data();
590
  //correct position
591
- fseek( $file_handel, $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] );
592
  }
593
 
594
  fclose( $file_handel );
595
 
596
- $url = self::API_CONTENT_URL . self::API_VERSION_URL . 'commit_chunked_upload/' . $this->root . '/' . $this->encode_path( $path );
 
 
 
 
 
 
 
 
 
 
 
 
 
597
 
598
- $request = $this->request( $url, array(
599
- 'overwrite' => ( $overwrite ) ? 'true' : 'false',
600
- 'upload_id' => $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid']
601
- ), 'POST' );
602
 
603
- unset( $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['uploadid'] );
604
- unset( $backwpup_job_object->steps_data[ $backwpup_job_object->step_working ]['offset'] );
 
 
 
 
 
 
 
 
 
 
 
605
 
606
- return $request;
607
  }
608
 
609
  /**
610
- * @param $path
611
- * @param bool $echo
 
 
 
 
 
 
 
 
 
 
612
  *
613
- * @return string
 
614
  */
615
- public function download( $path, $echo = false ) {
 
 
 
 
 
 
 
616
 
617
- $url = self::API_CONTENT_URL . self::API_VERSION_URL . 'files/' . $this->root . '/' . $path;
618
- if ( ! $echo ) {
619
- return $this->request( $url );
620
- } else {
621
- $this->request( $url, null, 'GET', '', true );
622
 
623
- return '';
624
- }
 
 
 
 
 
625
  }
626
 
 
 
627
  /**
628
- * @param string $path
629
- * @param bool $listContents
630
- * @param int $fileLimit
631
- * @param string $hash
632
  *
633
- * @return array|mixed|string
 
 
634
  */
635
- public function metadata( $path = '', $listContents = true, $fileLimit = 10000, $hash = '' ) {
 
636
 
637
- $url = self::API_URL . self::API_VERSION_URL . 'metadata/' . $this->root . '/' . $this->encode_path( $path );
 
 
 
 
 
 
638
 
639
- return $this->request( $url, array(
640
- 'list' => ( $listContents ) ? 'true' : 'false',
641
- 'hash' => ( $hash ) ? $hash : '',
642
- 'file_limit' => $fileLimit
643
- ) );
 
 
 
 
 
 
 
 
 
 
644
  }
645
 
646
  /**
647
- * @param string $path
648
  *
649
- * @return array|mixed|string
 
 
650
  */
651
- public function media( $path = '' ) {
 
 
 
 
 
 
 
 
652
 
653
- $url = self::API_URL . self::API_VERSION_URL . 'media/' . $this->root . '/' . $path;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
 
655
- return $this->request( $url );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  }
657
 
658
  /**
659
- * @param $path
660
  *
661
- * @return array|mixed|string
 
 
 
 
662
  */
663
- public function fileopsDelete( $path ) {
 
664
 
665
- $url = self::API_URL . self::API_VERSION_URL . 'fileops/delete';
 
 
 
666
 
667
- return $this->request( $url, array(
668
- 'path' => '/' . $path,
669
- 'root' => $this->root
670
- ) );
 
 
671
  }
672
 
673
- public function oAuthAuthorize() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
 
675
- return self::API_WWW_URL . self::API_VERSION_URL . 'oauth2/authorize?response_type=code&client_id=' . $this->oauth_app_key;
 
 
676
  }
677
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
678
 
679
- public function oAuthToken( $code ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
680
 
681
- $url = self::API_URL . self::API_VERSION_URL . 'oauth2/token';
682
 
683
- return $this->request( $url, array(
684
- 'code' => trim( $code ),
685
- 'grant_type' => 'authorization_code',
686
- 'client_id' => $this->oauth_app_key,
687
- 'client_secret' => $this->oauth_app_secret
688
- ), 'POST' );
 
 
689
 
 
 
 
 
 
 
 
690
  }
691
 
 
692
 
693
  /**
694
  * @param $url
695
  * @param array $args
696
- * @param string $method
697
  * @param string $data
698
  * @param bool $echo
699
  *
700
  * @throws BackWPup_Destination_Dropbox_API_Exception
701
- * @internal param null $file
702
  * @return array|mixed|string
703
  */
704
- private function request( $url, $args = array(), $method = 'GET', $data = '', $echo = false ) {
705
-
706
- /* Header*/
707
- // oAuth 2
708
- if ( ! empty( $this->oauth_token['access_token'] ) && ! empty( $this->oauth_token['token_type'] ) && strtolower( $this->oauth_token['token_type'] ) == 'bearer' ) {
709
- $headers[] = 'Authorization: Bearer ' . $this->oauth_token['access_token'];
710
- } // oAuth 1
711
- elseif ( ! empty( $this->oauth_token['access_token'] ) && ! empty( $this->oauth_token['oauth_token_secret'] ) ) {
712
- $headers[] = 'Authorization: OAuth oauth_version="1.0", oauth_signature_method="PLAINTEXT", oauth_consumer_key="' . $this->oauth_app_key . '", oauth_token="' . $this->oauth_token['access_token'] . '", oauth_signature="' . $this->oauth_app_secret . '&' . $this->oauth_token['oauth_token_secret'] . '"';
 
 
 
 
 
 
 
713
  }
714
 
715
- $headers[] = 'Expect:';
 
 
 
 
 
 
 
 
 
 
 
716
 
717
- /* Build cURL Request */
718
  $ch = curl_init();
719
- if ( $method == 'POST' ) {
720
- curl_setopt( $ch, CURLOPT_POST, true );
721
- curl_setopt( $ch, CURLOPT_POSTFIELDS, $args );
722
  curl_setopt( $ch, CURLOPT_URL, $url );
723
- } elseif ( $method == 'PUT' ) {
724
- curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'PUT' );
725
- curl_setopt( $ch, CURLOPT_POSTFIELDS, $data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
726
  $headers[] = 'Content-Type: application/octet-stream';
727
- $args = ( is_array( $args ) ) ? '?' . http_build_query( $args, '', '&' ) : $args;
728
- curl_setopt( $ch, CURLOPT_URL, $url . $args );
729
- } else {
 
 
 
 
 
730
  curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
731
- $args = ( is_array( $args ) ) ? '?' . http_build_query( $args, '', '&' ) : $args;
732
- curl_setopt( $ch, CURLOPT_URL, $url . $args );
733
  }
 
734
  curl_setopt( $ch, CURLOPT_USERAGENT, BackWPup::get_plugin_data( 'User-Agent' ) );
735
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
736
  if ( BackWPup::get_plugin_data( 'cacert' ) ) {
@@ -768,102 +968,302 @@ final class BackWPup_Destination_Dropbox_API {
768
  }
769
  curl_setopt( $ch, CURLOPT_CAINFO, BackWPup::get_plugin_data( 'cacert' ) );
770
  curl_setopt( $ch, CURLOPT_CAPATH, dirname( BackWPup::get_plugin_data( 'cacert' ) ) );
771
- } else {
 
772
  curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
773
  }
774
  curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
775
  $output = '';
776
  if ( $echo ) {
777
  echo curl_exec( $ch );
778
- } else {
 
779
  curl_setopt( $ch, CURLOPT_HEADER, true );
780
- if ( 0 == curl_errno( $ch ) ) {
781
- $responce = explode( "\r\n\r\n", curl_exec( $ch ), 2 );
782
- if ( ! empty( $responce[1] ) ) {
783
- $output = json_decode( $responce[1], true );
784
- }
785
  }
786
  }
787
  $status = curl_getinfo( $ch );
788
- if ( $status['http_code'] == 503 ) {
 
 
 
 
 
789
  $wait = 0;
790
- if ( preg_match( "/retry-after:(.*?)\r/i", $responce[0], $matches ) ) {
791
  $wait = trim( $matches[1] );
792
  }
793
  //only wait if we get a retry-after header.
794
  if ( ! empty( $wait ) ) {
795
- trigger_error( sprintf( '(503) Your app is making too many requests and is being rate limited. Error 503 can be triggered on a per-app or per-user basis. Wait for %d seconds.', $wait ), E_USER_WARNING );
796
  sleep( $wait );
797
- } else {
798
- throw new BackWPup_Destination_Dropbox_API_Exception( '(503) This indicates a transient server error.' );
 
799
  }
800
 
801
  //redo request
802
- return $this->request( $url, $args, $method, $data, $echo );
803
- } elseif ( $status['http_code'] === 400 && $method === 'PUT' && strstr( $url, '/chunked_upload' ) ) { //correct offset on chunk uploads
804
- trigger_error( '(' . $status['http_code'] . ') False offset will corrected', E_USER_NOTICE );
805
-
806
- return $output;
807
- } elseif ( $status['http_code'] === 404 && ! empty( $output['error'] ) ) {
808
- trigger_error( '(' . $status['http_code'] . ') ' . $output['error'], E_USER_WARNING );
809
-
810
- return false;
811
- } elseif ( isset( $output['error'] ) || $status['http_code'] >= 300 || $status['http_code'] < 200 || curl_errno( $ch ) > 0 ) {
812
- if ( isset( $output['error'] ) && is_string( $output['error'] ) ) {
813
- $args = ( is_array( $args ) ) ? '?' . http_build_query( $args, '', '&' ) : $args;
814
- $message = '(' . $status['http_code'] . ') ' . $output['error'] . ' ' . $url . $args;
815
- } elseif ( isset( $output['error']['hash'] ) && $output['error']['hash'] != '' ) {
816
- $message = (string) '(' . $status['http_code'] . ') ' . $output['error']['hash'] . ' ' . $url . $args;
817
- } elseif ( 0 != curl_errno( $ch ) ) {
818
  $message = '(' . curl_errno( $ch ) . ') ' . curl_error( $ch );
819
- } elseif ( $status['http_code'] == 304 ) {
820
- $message = '(304) Folder contents have not changed (relies on hash parameter).';
821
- } elseif ( $status['http_code'] == 400 ) {
822
  $message = '(400) Bad input parameter: ' . strip_tags( $responce[1] );
823
- } elseif ( $status['http_code'] == 401 ) {
 
824
  $message = '(401) Bad or expired token. This can happen if the user or Dropbox revoked or expired an access token. To fix, you should re-authenticate the user.';
825
- } elseif ( $status['http_code'] == 403 ) {
826
- $message = '(403) Bad OAuth request (wrong consumer key, bad nonce, expired timestamp...). Unfortunately, re-authenticating the user won\'t help here.';
827
- } elseif ( $status['http_code'] == 404 ) {
828
- $message = '(404) File or folder not found at the specified path.';
829
- } elseif ( $status['http_code'] == 405 ) {
830
- $message = '(405) Request method not expected (generally should be GET or POST).';
831
- } elseif ( $status['http_code'] == 406 ) {
832
- $message = '(406) There are too many file entries to return.';
833
- } elseif ( $status['http_code'] == 411 ) {
834
- $message = '(411) Missing Content-Length header (this endpoint doesn\'t support HTTP chunked transfer encoding).';
835
- } elseif ( $status['http_code'] == 415 ) {
836
- $message = '(415) The image is invalid and cannot be converted to a thumbnail.';
837
- } elseif ( $status['http_code'] == 429 ) {
838
- $message = '(429) Your app is making too many requests and is being rate limited. 429s can trigger on a per-app or per-user basis.';
839
- } elseif ( $status['http_code'] == 507 ) {
840
- $message = '(507) User is over Dropbox storage quota.';
841
- } else {
842
  $message = '(' . $status['http_code'] . ') Invalid response.';
843
  }
844
- throw new BackWPup_Destination_Dropbox_API_Exception( $message );
845
- } else {
 
 
 
 
846
  curl_close( $ch );
847
  if ( ! is_array( $output ) ) {
848
  return $responce[1];
849
- } else {
 
850
  return $output;
851
  }
852
  }
853
  }
854
 
855
  /**
856
- * @param $path
857
  *
858
- * @return mixed
 
 
859
  */
860
- private function encode_path( $path ) {
861
-
862
- $path = preg_replace( '#/+#', '/', trim( $path, '/' ) );
863
- $path = str_replace( '%2F', '/', rawurlencode( $path ) );
864
 
865
  return $path;
866
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
867
  }
868
 
869
  /**
@@ -872,3 +1272,24 @@ final class BackWPup_Destination_Dropbox_API {
872
  class BackWPup_Destination_Dropbox_API_Exception extends Exception {
873
 
874
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
  /**
4
+ * This class allows the user to back up to Dropbox.
5
+ *
6
+ * Documentation: https://www.dropbox.com/developers/documentation/http/overview
7
  */
8
  class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
9
 
16
  * @return array
17
  */
18
  public function option_defaults() {
 
19
  return array(
20
  'dropboxtoken' => array(),
21
  'dropboxroot' => 'sandbox',
22
  'dropboxmaxbackups' => 15,
23
  'dropboxsyncnodelete' => true,
24
+ 'dropboxdir' => '/' . trailingslashit( sanitize_file_name( get_bloginfo( 'name' ) ) )
25
  );
26
  }
27
 
 
28
  /**
29
  * @param $jobid
30
  */
34
  //disable token on dropbox
35
  try {
36
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
 
 
 
 
 
 
37
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
38
+ $dropbox->authTokenRevoke();
39
+ }
40
+ catch ( Exception $e ) {
41
  echo '<div id="message" class="error"><p>' . sprintf( __( 'Dropbox API: %s', 'backwpup' ), $e->getMessage() ) . '</p></div>';
42
  }
43
  BackWPup_Option::update( $jobid, 'dropboxtoken', array() );
44
  BackWPup_Option::update( $jobid, 'dropboxroot', 'sandbox' );
 
45
  }
46
 
47
  $dropbox = new BackWPup_Destination_Dropbox_API( 'dropbox' );
117
  <input id="iddropboxmaxbackups" name="dropboxmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'dropboxmaxbackups' ) ); ?>" class="small-text" />
118
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
119
  </label>
120
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
121
  <?php } else { ?>
122
  <label for="iddropboxsyncnodelete">
123
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'dropboxsyncnodelete' ), true ); ?> name="dropboxsyncnodelete" id="iddropboxsyncnodelete" />
133
 
134
  /**
135
  * @param $jobid
 
 
136
  */
137
  public function edit_form_post_save( $jobid ) {
138
 
143
  $dropboxtoken = $dropbox->oAuthToken( $_POST['sandbox_code'] );
144
  BackWPup_Option::update( $jobid, 'dropboxtoken', $dropboxtoken );
145
  BackWPup_Option::update( $jobid, 'dropboxroot', 'sandbox' );
146
+ }
147
+ catch ( Exception $e ) {
148
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
149
  }
150
  }
155
  $dropboxtoken = $dropbox->oAuthToken( $_POST['dropbbox_code'] );
156
  BackWPup_Option::update( $jobid, 'dropboxtoken', $dropboxtoken );
157
  BackWPup_Option::update( $jobid, 'dropboxroot', 'dropbox' );
158
+ }
159
+ catch ( Exception $e ) {
160
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
161
  }
162
  }
165
  BackWPup_Option::update( $jobid, 'dropboxmaxbackups', ! empty( $_POST['dropboxmaxbackups'] ) ? absint( $_POST['dropboxmaxbackups'] ) : 0 );
166
 
167
  $_POST['dropboxdir'] = trailingslashit( str_replace( '//', '/', str_replace( '\\', '/', trim( sanitize_text_field( $_POST['dropboxdir'] ) ) ) ) );
 
 
 
168
  if ( $_POST['dropboxdir'] === '/' ) {
169
  $_POST['dropboxdir'] = '';
170
  }
177
  * @param $backupfile
178
  */
179
  public function file_delete( $jobdest, $backupfile ) {
 
180
  $files = get_site_transient( 'backwpup_' . strtolower( $jobdest ) );
181
  list( $jobid, $dest ) = explode( '_', $jobdest );
182
 
183
  try {
184
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
185
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
186
+ $dropbox->filesDelete( array( 'path' => $backupfile ) );
187
+
188
  //update file list
189
  foreach ( $files as $key => $file ) {
190
  if ( is_array( $file ) && $file['file'] == $backupfile ) {
192
  }
193
  }
194
  unset( $dropbox );
195
+ }
196
+ catch ( Exception $e ) {
197
  BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
198
  }
199
 
205
  * @param $get_file
206
  */
207
  public function file_download( $jobid, $get_file ) {
 
208
  try {
209
  $dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $jobid, 'dropboxroot' ) );
210
  $dropbox->setOAuthTokens( BackWPup_Option::get( $jobid, 'dropboxtoken' ) );
211
+ $tempLink = $dropbox->filesGetTemporaryLink( array( 'path' => $get_file ) );
212
+ if ( ! empty( $tempLink['link'] ) ) {
213
+ header( "Location: " . $tempLink['link'] );
214
  }
215
  die();
216
+ }
217
+ catch ( Exception $e ) {
218
  die( $e->getMessage() );
219
  }
220
  }
234
  * @return bool
235
  */
236
  public function job_run_archive( BackWPup_Job $job_object ) {
 
237
  $job_object->substeps_todo = 2 + $job_object->backup_filesize;
238
  if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
239
  $job_object->log( sprintf( __( '%d. Try to send backup file to Dropbox&#160;&hellip;', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) );
240
  }
241
 
242
  try {
243
+ $dropbox = new BackWPup_Destination_Dropbox_API( $job_object->job['dropboxroot'], $job_object );
 
 
 
 
 
 
 
 
 
 
 
244
  $dropbox->setOAuthTokens( $job_object->job['dropboxtoken'] );
245
 
246
  //get account info
247
  if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
248
+ $info = $dropbox->usersGetCurrentAccount();
249
+ if ( ! empty( $info['account_id'] ) ) {
250
  if ( $job_object->is_debug() ) {
251
+ $user = $info['name']['display_name'] . ' (' . $info['email'] . ')';
252
+ }
253
+ else {
254
+ $user = $info['name']['display_name'];
255
  }
256
  $job_object->log( sprintf( __( 'Authenticated with Dropbox of user: %s', 'backwpup' ), $user ) );
257
+
258
  //Quota
259
  if ( $job_object->is_debug() ) {
260
+ $quota = $dropbox->usersGetSpaceUsage();
261
+ $dropboxfreespase = $quota['allocation']['allocated'] - $quota['used'];
262
  $job_object->log( sprintf( __( '%s available on your Dropbox', 'backwpup' ), size_format( $dropboxfreespase, 2 ) ) );
263
  }
264
+ }
265
+ else {
266
  $job_object->log( __( 'Not Authenticated with Dropbox!', 'backwpup' ), E_USER_ERROR );
267
 
268
  return false;
275
 
276
  if ( $job_object->substeps_done < $job_object->backup_filesize ) { //only if upload not complete
277
  $response = $dropbox->upload( $job_object->backup_folder . $job_object->backup_file, $job_object->job['dropboxdir'] . $job_object->backup_file );
278
+ if ( $response['size'] == $job_object->backup_filesize ) {
279
  if ( ! empty( $job_object->job['jobid'] ) ) {
280
+ BackWPup_Option::update( $job_object->job['jobid'], 'lastbackupdownloadurl', network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloaddropbox&file=' . ltrim( $response['path_display'], '/' ) . '&jobid=' . $job_object->job['jobid'] );
281
  }
282
  $job_object->substeps_done = 1 + $job_object->backup_filesize;
283
+ $job_object->log( sprintf( __( 'Backup transferred to %s', 'backwpup' ), $response['path_display'] ), E_USER_NOTICE );
284
+ }
285
+ else {
286
+ if ( $response['size'] != $job_object->backup_filesize ) {
287
  $job_object->log( __( 'Uploaded file size and local file size don\'t match.', 'backwpup' ), E_USER_ERROR );
288
+ }
289
+ else {
290
  $job_object->log(
291
  sprintf(
292
  __( 'Error transfering backup to %s.', 'backwpup' ) . ' ' . $response['error'],
298
  }
299
  }
300
 
 
301
  $backupfilelist = array();
302
  $filecounter = 0;
303
  $files = array();
304
+ $filesList = $dropbox->listFolder( $job_object->job['dropboxdir'] );
305
+ foreach ( $filesList as $data ) {
306
+ if ( $data['.tag'] == 'file' && $job_object->owns_backup_archive( $data['name'] ) == true ) {
307
+ $file = $data['name'];
 
308
  if ( $job_object->is_backup_archive( $file ) ) {
309
+ $backupfilelist[ strtotime( $data['server_modified'] ) ] = $file;
310
  }
311
+ $files[ $filecounter ]['folder'] = dirname( $data['path_display'] );
312
+ $files[ $filecounter ]['file'] = $data['path_display'];
313
+ $files[ $filecounter ]['filename'] = $data['name'];
314
+ $files[ $filecounter ]['downloadurl'] = network_admin_url( 'admin.php?page=backwpupbackups&action=downloaddropbox&file=' . $data['path_display'] . '&jobid=' . $job_object->job['jobid'] );
315
+ $files[ $filecounter ]['filesize'] = $data['size'];
316
+ $files[ $filecounter ]['time'] = strtotime( $data['server_modified'] ) + ( get_option( 'gmt_offset' ) * 3600 );
317
  $filecounter ++;
318
  }
319
  }
 
320
  if ( $job_object->job['dropboxmaxbackups'] > 0 && is_object( $dropbox ) ) { //Delete old backups
321
  if ( count( $backupfilelist ) > $job_object->job['dropboxmaxbackups'] ) {
322
  ksort( $backupfilelist );
325
  if ( count( $backupfilelist ) < $job_object->job['dropboxmaxbackups'] ) {
326
  break;
327
  }
328
+ $response = $dropbox->filesDelete( array( 'path' => $job_object->job['dropboxdir'] . $file ) ); //delete files on Cloud
 
329
  foreach ( $files as $key => $filedata ) {
330
  if ( $filedata['file'] == '/' . $job_object->job['dropboxdir'] . $file ) {
331
  unset( $files[ $key ] );
332
  }
333
  }
334
  $numdeltefiles ++;
 
 
 
335
  }
336
  if ( $numdeltefiles > 0 ) {
337
  $job_object->log( sprintf( _n( 'One file deleted from Dropbox', '%d files deleted on Dropbox', $numdeltefiles, 'backwpup' ), $numdeltefiles ), E_USER_NOTICE );
339
  }
340
  }
341
  set_site_transient( 'backwpup_' . $job_object->job['jobid'] . '_dropbox', $files, YEAR_IN_SECONDS );
342
+ }
343
+ catch ( Exception $e ) {
344
+ $job_object->log( sprintf( __( 'Dropbox API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine(), E_USER_ERROR );
345
  return false;
346
  }
347
  $job_object->substeps_done ++;
355
  * @return bool
356
  */
357
  public function can_run( array $job_settings ) {
 
358
  if ( empty( $job_settings['dropboxtoken'] ) ) {
359
  return false;
360
  }
364
 
365
  }
366
 
 
367
  /**
368
+ * Class for communicating with Dropbox API V2.
369
  */
370
  final class BackWPup_Destination_Dropbox_API {
371
 
372
  /**
373
+ * URL to Dropbox API endpoint.
374
  */
375
  const API_URL = 'https://api.dropboxapi.com/';
376
 
377
  /**
378
+ * URL to Dropbox content endpoint.
379
  */
380
  const API_CONTENT_URL = 'https://content.dropboxapi.com/';
381
 
382
  /**
383
+ * URL to Dropbox for authentication.
384
  */
385
  const API_WWW_URL = 'https://www.dropbox.com/';
386
 
387
  /**
388
+ * API version.
 
 
 
 
 
 
 
389
  */
390
+ const API_VERSION_URL = '2/';
391
 
392
  /**
393
  * oAuth vars
400
  * @var string
401
  */
402
  private $oauth_app_secret = '';
403
+
404
  /**
405
  * @var string
406
  */
407
  private $oauth_token = '';
408
 
409
+ /**
410
+ * Job object for logging.
411
+ *
412
+ * @var BackWPup_Job
413
+ */
414
+ private $job_object;
415
 
416
  /**
417
  * @param string $boxtype
418
  *
419
  * @throws BackWPup_Destination_Dropbox_API_Exception
420
  */
421
+ public function __construct( $boxtype = 'dropbox', BackWPup_Job $job_object = null ) {
 
422
  if ( $boxtype == 'dropbox' ) {
423
  $this->oauth_app_key = get_site_option( 'backwpup_cfg_dropboxappkey', base64_decode( "dHZkcjk1MnRhZnM1NmZ2" ) );
424
  $this->oauth_app_secret = BackWPup_Encryption::decrypt( get_site_option( 'backwpup_cfg_dropboxappsecret', base64_decode( "OWV2bDR5MHJvZ2RlYmx1" ) ) );
425
+ }
426
+ else {
427
  $this->oauth_app_key = get_site_option( 'backwpup_cfg_dropboxsandboxappkey', base64_decode( "cHVrZmp1a3JoZHR5OTFk" ) );
428
  $this->oauth_app_secret = BackWPup_Encryption::decrypt( get_site_option( 'backwpup_cfg_dropboxsandboxappsecret', base64_decode( "eGNoYzhxdTk5eHE0eWdq" ) ) );
 
429
  }
430
 
431
  if ( empty( $this->oauth_app_key ) || empty( $this->oauth_app_secret ) ) {
432
  throw new BackWPup_Destination_Dropbox_API_Exception( "No App key or App Secret specified." );
433
  }
 
 
 
 
 
 
 
 
 
 
 
 
434
 
435
+ $this->job_object = $job_object;
436
  }
437
 
438
+ // Helper methods
 
 
 
 
 
439
 
440
  /**
441
+ * List a folder
442
+ *
443
+ * This is a helper method to use filesListFolder and
444
+ * filesListFolderContinue to construct an array of files within a given
445
+ * folder path.
446
+ *
447
+ * @param string $path
448
+ *
449
+ * @return array
450
  */
451
+ public function listFolder( $path ) {
452
+ $files = array();
453
+ $result = $this->filesListFolder( array( 'path' => $path ) );
454
+ $files = array_merge( $files, $result['entries'] );
455
 
456
+ $args = array( 'cursor' => $result['cursor'] );
457
 
458
+ while ( $result['has_more'] == true ) {
459
+ $result = $this->filesListFolderContinue( $args );
460
+ $files = array_merge( $files, $result['entries'] );
461
+ }
 
 
462
 
463
+ return $files;
464
  }
465
 
466
  /**
467
+ * Uploads a file to Dropbox.
468
+ *
469
  * @param $file
470
  * @param string $path
471
  * @param bool $overwrite
472
  *
473
+ * @return array
474
  * @throws BackWPup_Destination_Dropbox_API_Exception
475
  */
476
  public function upload( $file, $path = '', $overwrite = true ) {
 
477
  $file = str_replace( "\\", "/", $file );
478
 
479
  if ( ! is_readable( $file ) ) {
481
  }
482
 
483
  if ( filesize( $file ) < 5242880 ) { //chunk transfer on bigger uploads
484
+ $output = $this->filesUpload( array(
485
+ 'contents' => file_get_contents( $file ),
486
+ 'path' => $path,
487
+ 'mode' => ( $overwrite ) ? 'overwrite' : 'add',
488
+ ) );
489
+ }
490
+ else {
491
+ $output = $this->multipartUpload( $file, $path, $overwrite );
492
  }
493
 
494
  return $output;
502
  * @return array|mixed|string
503
  * @throws BackWPup_Destination_Dropbox_API_Exception
504
  */
505
+ public function multipartUpload( $file, $path = '', $overwrite = true ) {
 
 
 
506
  $file = str_replace( "\\", "/", $file );
507
 
508
  if ( ! is_readable( $file ) ) {
516
  throw new BackWPup_Destination_Dropbox_API_Exception( "Can not open source file for transfer." );
517
  }
518
 
519
+ if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] ) ) {
520
+ $this->job_object->log( __( 'Beginning new file upload session', 'backwpup' ) );
521
+ $session = $this->filesUploadSessionStart();
522
+ $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] = $session['session_id'];
523
+ }
524
+ if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] ) ) {
525
+ $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] = 0;
526
  }
527
+ if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] ) ) {
528
+ $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] = 0;
529
  }
530
 
531
  //seek to current position
532
+ if ( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] > 0 ) {
533
+ fseek( $file_handel, $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] );
534
  }
535
 
536
  while ( $data = fread( $file_handel, $chunk_size ) ) {
537
  $chunk_upload_start = microtime( true );
538
+
539
+ $this->job_object->log( sprintf( __( 'Uploading %s of data', 'backwpup' ), size_format( strlen( $data ) ) ) );
540
+ $this->filesUploadSessionAppendV2( array(
541
+ 'contents' => $data,
542
+ 'cursor' => array(
543
+ 'session_id' => $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'],
544
+ 'offset' => $this->job_object->steps_data[ $this->job_object->step_working ]['offset']
545
+ ),
546
+ ) );
547
  $chunk_upload_time = microtime( true ) - $chunk_upload_start;
548
+ $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] += strlen( $data );
549
+
550
  //args for next chunk
551
+ $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] += $chunk_size;
552
+ if ( $this->job_object->job['backuptype'] === 'archive' ) {
553
+ $this->job_object->substeps_done = $this->job_object->steps_data[ $this->job_object->step_working ]['offset'];
 
554
  if ( strlen( $data ) == $chunk_size ) {
555
+ $time_remaining = $this->job_object->do_restart_time();
556
  //calc next chunk
557
  if ( $time_remaining < $chunk_upload_time ) {
558
  $chunk_size = floor( $chunk_size / $chunk_upload_time * ( $time_remaining - 3 ) );
565
  }
566
  }
567
  }
568
+ $this->job_object->update_working_data();
569
  //correct position
570
+ fseek( $file_handel, $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] );
571
  }
572
 
573
  fclose( $file_handel );
574
 
575
+ $this->job_object->log( sprintf( __( 'Finishing upload session with a total of %s uploaded', 'backwpup' ), size_format( $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] ) ) );
576
+ $response = $this->filesUploadSessionFinish( array(
577
+ 'cursor' => array(
578
+ 'session_id' => $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'],
579
+ 'offset' => $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'],
580
+ ),
581
+ 'commit' => array(
582
+ 'path' => $path,
583
+ 'mode' => ( $overwrite ) ? 'overwrite' : 'add',
584
+ ),
585
+ ) );
586
+
587
+ unset( $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] );
588
+ unset( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] );
589
 
590
+ return $response;
591
+ }
 
 
592
 
593
+ // Authentication
594
+
595
+ /**
596
+ * Set the oauth tokens for this request.
597
+ *
598
+ * @param $token
599
+ *
600
+ * @throws BackWPup_Destination_Dropbox_API_Exception
601
+ */
602
+ public function setOAuthTokens( $token ) {
603
+ if ( empty( $token['access_token'] ) ) {
604
+ throw new BackWPup_Destination_Dropbox_API_Exception( "No oAuth token specified." );
605
+ }
606
 
607
+ $this->oauth_token = $token;
608
  }
609
 
610
  /**
611
+ * Returns the URL to authorize the user.
612
+ *
613
+ * @return string The authorization URL
614
+ */
615
+ public function oAuthAuthorize() {
616
+ return self::API_WWW_URL . 'oauth2/authorize?response_type=code&client_id=' . $this->oauth_app_key;
617
+ }
618
+
619
+ /**
620
+ * Tkes the oauth code and returns the access token.
621
+ *
622
+ * @param string $code The oauth code
623
  *
624
+ * @return array An array including the access token, account ID, and
625
+ * other information.
626
  */
627
+ public function oAuthToken( $code ) {
628
+ return $this->request( 'oauth2/token', array(
629
+ 'code' => trim( $code ),
630
+ 'grant_type' => 'authorization_code',
631
+ 'client_id' => $this->oauth_app_key,
632
+ 'client_secret' => $this->oauth_app_secret
633
+ ), 'oauth' );
634
+ }
635
 
636
+ // Auth Endpoints
 
 
 
 
637
 
638
+ /**
639
+ * Revokes the auth token.
640
+ *
641
+ * @return array
642
+ */
643
+ public function authTokenRevoke() {
644
+ return $this->request( 'auth/token/revoke' );
645
  }
646
 
647
+ // Files Endpoints
648
+
649
  /**
650
+ * Deletes a file.
 
 
 
651
  *
652
+ * @param array $args An array of arguments
653
+ *
654
+ * @return array Information on the deleted file
655
  */
656
+ public function filesDelete( $args ) {
657
+ $args['path'] = $this->formatPath( $args['path'] );
658
 
659
+ try {
660
+ return $this->request( 'files/delete', $args );
661
+ }
662
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
663
+ $this->handleFilesDeleteError( $e->getError() );
664
+ }
665
+ }
666
 
667
+ /**
668
+ * Gets the metadata of a file.
669
+ *
670
+ * @param array $args An array of arguments
671
+ *
672
+ * @return array The file's metadata
673
+ */
674
+ public function filesGetMetadata( $args ) {
675
+ $args['path'] = $this->formatPath( $args['path'] );
676
+ try {
677
+ return $this->request( 'files/get_metadata', $args );
678
+ }
679
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
680
+ $this->handleFilesGetMetadataError( $e->error() );
681
+ }
682
  }
683
 
684
  /**
685
+ * Gets a temporary link from Dropbox to access the file.
686
  *
687
+ * @param array $args An array of arguments
688
+ *
689
+ * @return array Information on the file and link
690
  */
691
+ public function filesGetTemporaryLink( $args ) {
692
+ $args['path'] = $this->formatPath( $args['path'] );
693
+ try {
694
+ return $this->request( 'files/get_temporary_link', $args );
695
+ }
696
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
697
+ $this->handleFilesGetTemporaryLinkError( $e->getError() );
698
+ }
699
+ }
700
 
701
+ /**
702
+ * Lists all the files within a folder.
703
+ *
704
+ * @param array $args An array of arguments
705
+ *
706
+ * @return array A list of files
707
+ */
708
+ public function filesListFolder( $args ) {
709
+ $args['path'] = $this->formatPath( $args['path'] );
710
+ try {
711
+ Return $this->request( 'files/list_folder', $args );
712
+ }
713
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
714
+ $this->handleFilesListFolderError( $e->error() );
715
+ }
716
+ }
717
 
718
+ /**
719
+ * Continue to list more files.
720
+ *
721
+ * When a folder has a lot of files, the API won't return all at once.
722
+ * So this method is to fetch more of them.
723
+ *
724
+ * @param array $args An array of arguments
725
+ *
726
+ * @return array An array of files
727
+ */
728
+ public function filesListFolderContinue( $args ) {
729
+ try {
730
+ Return $this->request( 'files/list_folder/continue', $args );
731
+ }
732
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
733
+ $this->handleFilesListFolderContinueError( $e->error() );
734
+ }
735
  }
736
 
737
  /**
738
+ * Uploads a file to Dropbox.
739
  *
740
+ * The file must be no greater than 150 MB.
741
+ *
742
+ * @param array $args An array of arguments
743
+ *
744
+ * @return array The uploaded file's information.
745
  */
746
+ public function filesUpload( $args ) {
747
+ $args['path'] = $this->formatPath( $args['path'] );
748
 
749
+ if ( isset( $args['client_modified'] )
750
+ && $args['client_modified'] instanceof DateTime ) {
751
+ $args['client_modified'] = $args['client_modified']->format( 'Y-m-d\TH:m:s\Z' );
752
+ }
753
 
754
+ try {
755
+ return $this->request( 'files/upload', $args, 'upload' );
756
+ }
757
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
758
+ $this->handleFilesUploadError( $e->getError() );
759
+ }
760
  }
761
 
762
+ /**
763
+ * Append more data to an uploading file
764
+ *
765
+ * @param array $args An array of arguments
766
+ */
767
+ public function filesUploadSessionAppendV2( $args ) {
768
+ try {
769
+ return $this->request( 'files/upload_session/append_v2', $args,
770
+ 'upload' );
771
+ }
772
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
773
+ $error = $e->getError();
774
+
775
+ // See if we can fix the error first
776
+ if ( $error['.tag'] == 'incorrect_offset' ) {
777
+ $args['cursor']['offset'] = $error['correct_offset'];
778
+ return $this->request( 'files/upload_session/append_v2', $args,
779
+ 'upload' );
780
+ }
781
 
782
+ // Otherwise, can't fix
783
+ $this->handleFilesUploadSessionLookupError( $error );
784
+ }
785
  }
786
 
787
+ /**
788
+ * Finish an upload session.
789
+ *
790
+ * @param array $args
791
+ *
792
+ * @return array Information on the uploaded file
793
+ */
794
+ public function filesUploadSessionFinish( $args ) {
795
+ $args['commit']['path'] = $this->formatPath( $args['commit']['path'] );;
796
+ try {
797
+ return $this->request( 'files/upload_session/finish', $args, 'upload' );
798
+ }
799
+ catch ( BackWPup_Destination_Dropbox_API_Request_Exception $e ) {
800
+ $error = $e->getError();
801
+ if ( $error['.tag'] == 'lookup_failed' ) {
802
+ if ( $error['lookup_failed']['.tag'] == 'incorrect_offset' ) {
803
+ $args['cursor']['offset'] = $error['lookup_failed']['correct_offset'];
804
+ return $this->request( 'files/upload_session/finish', $args, 'upload' );
805
+ }
806
+ }
807
+ $this->handleFilesUploadSessionFinishError( $e->getError() );
808
+ }
809
+ }
810
 
811
+ /**
812
+ * Starts an upload session.
813
+ *
814
+ * When a file larger than 150 MB needs to be uploaded, then this API
815
+ * endpoint is used to start a session to allow the file to be uploaded in
816
+ * chunks.
817
+ *
818
+ * @param array $args
819
+ *
820
+ * @return array An array containing the session's ID.
821
+ */
822
+ public function filesUploadSessionStart( $args = array() ) {
823
+ return $this->request( 'files/upload_session/start', $args, 'upload' );
824
+ }
825
 
826
+ // Users endpoints
827
 
828
+ /**
829
+ * Get user's current account info.
830
+ *
831
+ * @return array
832
+ */
833
+ public function usersGetCurrentAccount() {
834
+ return $this->request( 'users/get_current_account' );
835
+ }
836
 
837
+ /**
838
+ * Get quota info for this user.
839
+ *
840
+ * @return array
841
+ */
842
+ public function usersGetSpaceUsage() {
843
+ return $this->request( 'users/get_space_usage' );
844
  }
845
 
846
+ // Private functions
847
 
848
  /**
849
  * @param $url
850
  * @param array $args
851
+ * @param string $endpointFormat
852
  * @param string $data
853
  * @param bool $echo
854
  *
855
  * @throws BackWPup_Destination_Dropbox_API_Exception
 
856
  * @return array|mixed|string
857
  */
858
+ private function request( $endpoint, $args = array(), $endpointFormat = 'rpc', $echo = false ) {
859
+
860
+ // Get complete URL
861
+ switch ( $endpointFormat ) {
862
+ case 'oauth':
863
+ $url = self::API_URL . $endpoint;
864
+ break;
865
+
866
+ case 'rpc':
867
+ $url = self::API_URL . self::API_VERSION_URL . $endpoint;
868
+ break;
869
+
870
+ case 'upload':
871
+ case 'download':
872
+ $url = self::API_CONTENT_URL . self::API_VERSION_URL . $endpoint;
873
+ break;
874
  }
875
 
876
+ if ( $this->job_object && $this->job_object->is_debug() && $endpointFormat != 'oauth' ) {
877
+ $message = 'Call to ' . $endpoint;
878
+ $parameters = $args;
879
+ if ( isset( $parameters['contents'] ) ) {
880
+ $message .= ', with ' . size_format( strlen( $parameters['contents'] ) ) . ' of data';
881
+ unset( $parameters['contents'] );
882
+ }
883
+ if ( ! empty( $parameters ) ) {
884
+ $message .= ', with parameters: ' . json_encode( $parameters );
885
+ }
886
+ $this->job_object->log( $message );
887
+ }
888
 
889
+ // Build cURL Request
890
  $ch = curl_init();
 
 
 
891
  curl_setopt( $ch, CURLOPT_URL, $url );
892
+ curl_setopt( $ch, CURLOPT_POST, true );
893
+
894
+ $headers[] = 'Expect:';
895
+
896
+ if ( $endpointFormat != 'oauth' ) {
897
+ $headers[] = 'Authorization: Bearer ' . $this->oauth_token['access_token'];
898
+ }
899
+
900
+ if ( $endpointFormat == 'oauth' ) {
901
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $args ) );
902
+ $headers[] = 'Content-Type: application/x-www-form-urlencoded';
903
+ }
904
+ elseif ( $endpointFormat == 'rpc' ) {
905
+ if ( ! empty( $args ) ) {
906
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $args ) );
907
+ }
908
+ else {
909
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( null ) );
910
+ }
911
+ $headers[] = 'Content-Type: application/json';
912
+ }
913
+ elseif ( $endpointFormat == 'upload' ) {
914
+ if ( isset( $args['contents'] ) ) {
915
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['contents'] );
916
+ unset( $args['contents'] );
917
+ }
918
+ else {
919
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, '' );
920
+ }
921
  $headers[] = 'Content-Type: application/octet-stream';
922
+ if ( ! empty( $args ) ) {
923
+ $headers[] = 'Dropbox-API-Arg: ' . json_encode( $args );
924
+ }
925
+ else {
926
+ $headers[] = 'Dropbox-API-Arg: {}';
927
+ }
928
+ }
929
+ else {
930
  curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
931
+ $headers[] = 'Dropbox-API-Arg: ' . json_encode( $args );
 
932
  }
933
+
934
  curl_setopt( $ch, CURLOPT_USERAGENT, BackWPup::get_plugin_data( 'User-Agent' ) );
935
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
936
  if ( BackWPup::get_plugin_data( 'cacert' ) ) {
968
  }
969
  curl_setopt( $ch, CURLOPT_CAINFO, BackWPup::get_plugin_data( 'cacert' ) );
970
  curl_setopt( $ch, CURLOPT_CAPATH, dirname( BackWPup::get_plugin_data( 'cacert' ) ) );
971
+ }
972
+ else {
973
  curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
974
  }
975
  curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
976
  $output = '';
977
  if ( $echo ) {
978
  echo curl_exec( $ch );
979
+ }
980
+ else {
981
  curl_setopt( $ch, CURLOPT_HEADER, true );
982
+ $responce = explode( "\r\n\r\n", curl_exec( $ch ), 2 );
983
+ if ( ! empty( $responce[1] ) ) {
984
+ $output = json_decode( $responce[1], true );
 
 
985
  }
986
  }
987
  $status = curl_getinfo( $ch );
988
+
989
+ // Handle error codes
990
+ // If 409 (endpoint-specific error), let the calling method handle it
991
+
992
+ // Code 429 = rate limited
993
+ if ( $status['http_code'] == 429 ) {
994
  $wait = 0;
995
+ if ( preg_match( "/retry-after:\s*(.*?)\r/i", $responce[0], $matches ) ) {
996
  $wait = trim( $matches[1] );
997
  }
998
  //only wait if we get a retry-after header.
999
  if ( ! empty( $wait ) ) {
1000
+ trigger_error( sprintf( '(429) Your app is making too many requests and is being rate limited. Error 429 can be triggered on a per-app or per-user basis. Wait for %d seconds.', $wait ), E_USER_WARNING );
1001
  sleep( $wait );
1002
+ }
1003
+ else {
1004
+ throw new BackWPup_Destination_Dropbox_API_Exception( '(429) This indicates a transient server error.' );
1005
  }
1006
 
1007
  //redo request
1008
+ return $this->request( $url, $args, $endpointFormat, $data, $echo );
1009
+ }
1010
+ // We can't really handle anything else, so throw it back to the caller
1011
+ elseif ( isset( $output['error'] ) || $status['http_code'] >= 400 || curl_errno( $ch ) > 0 ) {
1012
+ $code = $status['http_code'];
1013
+ if ( curl_errno( $ch ) != 0 ) {
 
 
 
 
 
 
 
 
 
 
1014
  $message = '(' . curl_errno( $ch ) . ') ' . curl_error( $ch );
1015
+ $code = 0;
1016
+ }
1017
+ elseif ( $status['http_code'] == 400 ) {
1018
  $message = '(400) Bad input parameter: ' . strip_tags( $responce[1] );
1019
+ }
1020
+ elseif ( $status['http_code'] == 401 ) {
1021
  $message = '(401) Bad or expired token. This can happen if the user or Dropbox revoked or expired an access token. To fix, you should re-authenticate the user.';
1022
+ }
1023
+ elseif ( $status['http_code'] == 409 ) {
1024
+ $message = $output['error_summary'];
1025
+ }
1026
+ elseif ( $status['http_code'] >= 500 ) {
1027
+ $message = '(' . $status['http_code'] . ') There is an error on the Dropbox server.';
1028
+ }
1029
+ else {
 
 
 
 
 
 
 
 
 
1030
  $message = '(' . $status['http_code'] . ') Invalid response.';
1031
  }
1032
+ if ( $this->job_object && $this->job_object->is_debug() ) {
1033
+ $this->job_object->log( 'Response with header: ' . $responce[0] );
1034
+ }
1035
+ throw new BackWPup_Destination_Dropbox_API_Request_Exception( $message, $code, null, isset( $output['error'] ) ? $output['error'] : null );
1036
+ }
1037
+ else {
1038
  curl_close( $ch );
1039
  if ( ! is_array( $output ) ) {
1040
  return $responce[1];
1041
+ }
1042
+ else {
1043
  return $output;
1044
  }
1045
  }
1046
  }
1047
 
1048
  /**
1049
+ * Formats a path to be valid for Dropbox.
1050
  *
1051
+ * @param string $path
1052
+ *
1053
+ * @return string The formatted path
1054
  */
1055
+ private function formatPath( $path ) {
1056
+ if ( substr( $path, 0, 1 ) != '/' ) {
1057
+ $path = "/$path";
1058
+ }
1059
 
1060
  return $path;
1061
  }
1062
+
1063
+ // Error Handlers
1064
+
1065
+ private function handleFilesDeleteError( $error ) {
1066
+ switch ( $error['.tag'] ) {
1067
+ case 'path_lookup':
1068
+ $this->handleFilesLookupError( $error['path_lookup'] );
1069
+ break;
1070
+
1071
+ case 'path_write':
1072
+ $this->handleFilesWriteError( $error['path_write'] );
1073
+ break;
1074
+
1075
+ case 'other':
1076
+ trigger_error( 'Could not delete file.', E_USER_WARNING );
1077
+ break;
1078
+ }
1079
+ }
1080
+
1081
+ private function handleFilesGetMetadataError( $error ) {
1082
+ switch ( $error['.tag'] ) {
1083
+ case 'path':
1084
+ $this->handleFilesLookupError( $error['path'] );
1085
+ break;
1086
+
1087
+ case 'other':
1088
+ trigger_error( 'Cannot look up file metadata.', E_USER_WARNING );
1089
+ break;
1090
+ }
1091
+ }
1092
+
1093
+ private function handleFilesGetTemporaryLinkError( $error ) {
1094
+ switch ( $error['.tag'] ) {
1095
+ case 'path':
1096
+ $this->handleFilesLookupError( $error['path'] );
1097
+ break;
1098
+
1099
+ case 'other':
1100
+ trigger_error( 'Cannot get temporary link.', E_USER_WARNING );
1101
+ break;
1102
+ }
1103
+ }
1104
+
1105
+ private function handleFilesListFolderError( $error ) {
1106
+ switch ( $error['.tag'] ) {
1107
+ case 'path':
1108
+ $this->handleFilesLookupError( $error['path'] );
1109
+ break;
1110
+
1111
+ case 'other':
1112
+ trigger_error( 'Cannot list files in folder.', E_USER_WARNING );
1113
+ break;
1114
+ }
1115
+ }
1116
+
1117
+ private function handleFilesListFolderContinueError( $error ) {
1118
+ switch ( $error['.tag'] ) {
1119
+ case 'path':
1120
+ $this->handleFilesLookupError( $error['path'] );
1121
+ break;
1122
+
1123
+ case 'reset':
1124
+ trigger_error( 'This cursor has been invalidated.', E_USER_WARNING );
1125
+ break;
1126
+
1127
+ case 'other':
1128
+ trigger_error( 'Cannot list files in folder.', E_USER_WARNING );
1129
+ break;
1130
+ }
1131
+ }
1132
+
1133
+ private function handleFilesLookupError( $error ) {
1134
+ switch ( $error['.tag'] ) {
1135
+ case 'malformed_path':
1136
+ trigger_error( 'The path was malformed.', E_USER_WARNING );
1137
+ break;
1138
+
1139
+ case 'not_found':
1140
+ trigger_error( 'File could not be found.', E_USER_WARNING );
1141
+ break;
1142
+
1143
+ case 'not_file':
1144
+ trigger_error( 'That is not a file.', E_USER_WARNING );
1145
+ break;
1146
+
1147
+ case 'not_folder':
1148
+ trigger_error( 'That is not a folder.', E_USER_WARNING );
1149
+ break;
1150
+
1151
+ case 'restricted_content':
1152
+ trigger_error( 'This content is restricted.', E_USER_WARNING );
1153
+ break;
1154
+
1155
+ case 'invalid_path_root':
1156
+ trigger_error( 'Path root is invalid.', E_USER_WARNING );
1157
+ break;
1158
+
1159
+ case 'other':
1160
+ trigger_error( 'File could not be found.', E_USER_WARNING );
1161
+ break;
1162
+ }
1163
+ }
1164
+
1165
+ private function handleFilesUploadSessionFinishError( $error ) {
1166
+ switch ( $error['.tag'] ) {
1167
+ case 'lookup_failed':
1168
+ $this->handleFilesUploadSessionLookupError(
1169
+ $error['lookup_failed'] );
1170
+ break;
1171
+
1172
+ case 'path':
1173
+ $this->handleFilesWriteError( $error['path'] );
1174
+ break;
1175
+
1176
+ case 'too_many_shared_folder_targets':
1177
+ trigger_error( 'Too many shared folder targets.', E_USER_WARNING );
1178
+ break;
1179
+
1180
+ case 'other':
1181
+ trigger_error( 'The file could not be uploaded.', E_USER_WARNING );
1182
+ break;
1183
+ }
1184
+ }
1185
+
1186
+ private function handleFilesUploadSessionLookupError( $error ) {
1187
+ switch ( $error['.tag'] ) {
1188
+ case 'not_found':
1189
+ trigger_error( 'Session not found.', E_USER_WARNING );
1190
+ break;
1191
+
1192
+ case 'incorrect_offset':
1193
+ trigger_error( 'Incorrect offset given. Correct offset is ' .
1194
+ $error['correct_offset'] . '.',
1195
+ E_USER_WARNING );
1196
+ break;
1197
+
1198
+ case 'closed':
1199
+ trigger_error( 'This session has been closed already.',
1200
+ E_USER_WARNING );
1201
+ break;
1202
+
1203
+ case 'not_closed':
1204
+ trigger_error( 'This session is not closed.', E_USER_WARNING );
1205
+ break;
1206
+
1207
+ case 'other':
1208
+ trigger_error( 'Could not look up the file session.',
1209
+ E_USER_WARNING );
1210
+ break;
1211
+ }
1212
+ }
1213
+
1214
+ private function handleFilesUploadError( $error ) {
1215
+ switch ( $error['.tag'] ) {
1216
+ case 'path':
1217
+ $this->handleFilesUploadWriteFailed( $error['path'] );
1218
+ break;
1219
+
1220
+ case 'other':
1221
+ trigger_error( 'There was an unknown error when uploading the file.', E_USER_WARNING );
1222
+ break;
1223
+ }
1224
+ }
1225
+
1226
+ private function handleFilesUploadWriteFailed( $error ) {
1227
+ $this->handleFilesWriteError( $error['reason'] );
1228
+ }
1229
+
1230
+ private function handleFilesWriteError( $error ) {
1231
+ $message = '';
1232
+
1233
+ // Type of error
1234
+ switch ( $error['.tag'] ) {
1235
+ case 'malformed_path':
1236
+ $message = 'The path was malformed.';
1237
+ break;
1238
+
1239
+ case 'conflict':
1240
+ $message = 'Cannot write to the target path due to conflict.';
1241
+ break;
1242
+
1243
+ case 'no_write_permission':
1244
+ $message = 'You do not have permission to save to this location.';
1245
+ break;
1246
+
1247
+ case 'insufficient_space':
1248
+ $message = 'You do not have enough space in your Dropbox.';
1249
+ break;
1250
+
1251
+ case 'disallowed_name':
1252
+ $message = 'The given name is disallowed by Dropbox.';
1253
+ break;
1254
+
1255
+ case 'team_folder':
1256
+ $message = 'Unable to modify team folders.';
1257
+ break;
1258
+
1259
+ case 'other':
1260
+ $message = 'There was an unknown error when uploading the file.';
1261
+ break;
1262
+ }
1263
+
1264
+ trigger_error( $message, E_USER_WARNING );
1265
+ }
1266
+
1267
  }
1268
 
1269
  /**
1272
  class BackWPup_Destination_Dropbox_API_Exception extends Exception {
1273
 
1274
  }
1275
+
1276
+ /**
1277
+ * Exception thrown when there is an error in the Dropbox request.
1278
+ */
1279
+ class BackWPup_Destination_Dropbox_API_Request_Exception extends BackWPup_Destination_Dropbox_API_Exception {
1280
+
1281
+ /**
1282
+ * The request error array.
1283
+ */
1284
+ protected $error;
1285
+
1286
+ public function __construct( $message, $code = 0, $previous = null, $error = null ) {
1287
+ $this->error = $error;
1288
+ parent::__construct( $message, $code, $previous );
1289
+ }
1290
+
1291
+ public function getError() {
1292
+ return $this->error;
1293
+ }
1294
+
1295
+ }
inc/class-destination-email.php CHANGED
@@ -38,7 +38,7 @@ class BackWPup_Destination_Email extends BackWPup_Destinations {
38
  <h3 class="title"><?php esc_html_e( 'Email address', 'backwpup' ); ?></h3>
39
  <table class="form-table">
40
  <tr>
41
- <th scope="row"><label for="emailaddress"><?php esc_html_e( 'To email address', 'backwpup' ); ?></label></th>
42
  <td>
43
  <input name="emailaddress" id="emailaddress" type="text" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'emailaddress' ) );?>" class="regular-text" />
44
  </td>
@@ -176,7 +176,7 @@ class BackWPup_Destination_Email extends BackWPup_Destinations {
176
  */
177
  public function edit_form_post_save( $jobid ) {
178
 
179
- BackWPup_Option::update( $jobid, 'emailaddress', isset( $_POST[ 'emailaddress' ] ) ? sanitize_email( $_POST[ 'emailaddress' ] ) : '' );
180
  BackWPup_Option::update( $jobid, 'emailefilesize', ! empty( $_POST[ 'emailefilesize' ] ) ? absint( $_POST[ 'emailefilesize' ] ) : 0 );
181
  BackWPup_Option::update( $jobid, 'emailsndemail', sanitize_email( $_POST[ 'emailsndemail' ] ) );
182
  BackWPup_Option::update( $jobid, 'emailmethod', ( $_POST[ 'emailmethod' ] === '' || $_POST[ 'emailmethod' ] === 'mail' || $_POST[ 'emailmethod' ] === 'sendmail' || $_POST[ 'emailmethod' ] === 'smtp' ) ? $_POST[ 'emailmethod' ] : '' );
@@ -288,7 +288,7 @@ class BackWPup_Destination_Email extends BackWPup_Destinations {
288
  // Create a message
289
  $message = Swift_Message::newInstance( sprintf( __( 'BackWPup archive from %1$s: %2$s', 'backwpup' ), date_i18n( 'd-M-Y H:i', $job_object->start_time, TRUE ), esc_attr($job_object->job[ 'name' ] ) ) );
290
  $message->setFrom( array( $job_object->job[ 'emailsndemail' ] => $job_object->job[ 'emailsndemailname' ] ) );
291
- $message->setTo( array( $job_object->job[ 'emailaddress' ] ) );
292
  $message->setBody( sprintf( __( 'Backup archive: %s', 'backwpup' ), $job_object->backup_file ), 'text/plain', strtolower( get_bloginfo( 'charset' ) ) );
293
  $message->attach( Swift_Attachment::fromPath( $job_object->backup_folder . $job_object->backup_file, $job_object->get_mime_type( $job_object->backup_folder . $job_object->backup_file ) ) );
294
  // Send the message
@@ -413,7 +413,7 @@ class BackWPup_Destination_Email extends BackWPup_Destinations {
413
  // Create a message
414
  $message = Swift_Message::newInstance( __( 'BackWPup archive sending TEST Message', 'backwpup' ) );
415
  $message->setFrom( array( $_POST[ 'emailsndemail' ] => sanitize_email( $_POST[ 'emailsndemailname' ] ) ) );
416
- $message->setTo( array( sanitize_email( $_POST[ 'emailaddress' ] ) ) );
417
  $message->setBody( __( 'If this message reaches your inbox, sending backup archives via email should work for you.', 'backwpup' ) );
418
  // Send the message
419
  $result = $emailer->send( $message );
@@ -433,4 +433,25 @@ class BackWPup_Destination_Email extends BackWPup_Destinations {
433
  }
434
  die();
435
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
  }
38
  <h3 class="title"><?php esc_html_e( 'Email address', 'backwpup' ); ?></h3>
39
  <table class="form-table">
40
  <tr>
41
+ <th scope="row"><label for="emailaddress"><?php esc_html_e( 'To email address (separate with commas for multiple addresses)', 'backwpup' ); ?></label></th>
42
  <td>
43
  <input name="emailaddress" id="emailaddress" type="text" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'emailaddress' ) );?>" class="regular-text" />
44
  </td>
176
  */
177
  public function edit_form_post_save( $jobid ) {
178
 
179
+ BackWPup_Option::update( $jobid, 'emailaddress', isset( $_POST['emailaddress'] ) ? implode( ', ', $this->get_email_array( $_POST['emailaddress'] ) ) : '' );
180
  BackWPup_Option::update( $jobid, 'emailefilesize', ! empty( $_POST[ 'emailefilesize' ] ) ? absint( $_POST[ 'emailefilesize' ] ) : 0 );
181
  BackWPup_Option::update( $jobid, 'emailsndemail', sanitize_email( $_POST[ 'emailsndemail' ] ) );
182
  BackWPup_Option::update( $jobid, 'emailmethod', ( $_POST[ 'emailmethod' ] === '' || $_POST[ 'emailmethod' ] === 'mail' || $_POST[ 'emailmethod' ] === 'sendmail' || $_POST[ 'emailmethod' ] === 'smtp' ) ? $_POST[ 'emailmethod' ] : '' );
288
  // Create a message
289
  $message = Swift_Message::newInstance( sprintf( __( 'BackWPup archive from %1$s: %2$s', 'backwpup' ), date_i18n( 'd-M-Y H:i', $job_object->start_time, TRUE ), esc_attr($job_object->job[ 'name' ] ) ) );
290
  $message->setFrom( array( $job_object->job[ 'emailsndemail' ] => $job_object->job[ 'emailsndemailname' ] ) );
291
+ $message->setTo( $this->get_email_array( $job_object->job['emailaddress'] ) );
292
  $message->setBody( sprintf( __( 'Backup archive: %s', 'backwpup' ), $job_object->backup_file ), 'text/plain', strtolower( get_bloginfo( 'charset' ) ) );
293
  $message->attach( Swift_Attachment::fromPath( $job_object->backup_folder . $job_object->backup_file, $job_object->get_mime_type( $job_object->backup_folder . $job_object->backup_file ) ) );
294
  // Send the message
413
  // Create a message
414
  $message = Swift_Message::newInstance( __( 'BackWPup archive sending TEST Message', 'backwpup' ) );
415
  $message->setFrom( array( $_POST[ 'emailsndemail' ] => sanitize_email( $_POST[ 'emailsndemailname' ] ) ) );
416
+ $message->setTo( $this->get_email_array( $_POST['emailaddress'] ) );
417
  $message->setBody( __( 'If this message reaches your inbox, sending backup archives via email should work for you.', 'backwpup' ) );
418
  // Send the message
419
  $result = $emailer->send( $message );
433
  }
434
  die();
435
  }
436
+
437
+ /**
438
+ * Get an array of emails from comma-separated string.
439
+ *
440
+ * @param string $emailString
441
+ *
442
+ * @return array
443
+ */
444
+ private function get_email_array( $emailString ) {
445
+ $emails = explode( ',', sanitize_text_field( $emailString ) );
446
+
447
+ foreach ( $emails as $key => $email ) {
448
+ $emails[ $key ] = sanitize_email( trim( $email ) );
449
+ if ( ! is_email( $emails[ $key ] ) ) {
450
+ unset( $emails[ $key ] );
451
+ }
452
+ }
453
+
454
+ return $emails;
455
+ }
456
+
457
  }
inc/class-destination-folder.php CHANGED
@@ -45,6 +45,7 @@ class BackWPup_Destination_Folder extends BackWPup_Destinations {
45
  <input id="idmaxbackups" name="maxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'maxbackups' ) ); ?>" class="small-text"/>
46
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
47
  </label>
 
48
  <?php } else { ?>
49
  <label for="idbackupsyncnodelete">
50
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'backupsyncnodelete' ), true ); ?> name="backupsyncnodelete" id="idbackupsyncnodelete"/>
@@ -143,28 +144,31 @@ class BackWPup_Destination_Folder extends BackWPup_Destinations {
143
  $files = array();
144
  $backup_folder = BackWPup_Option::get( $jobid, 'backupdir' );
145
  $backup_folder = BackWPup_File::get_absolute_path( $backup_folder );
146
- if ( is_dir( $backup_folder ) && $dir = opendir( $backup_folder ) ) { //make file list
147
- while ( false !== ( $file = readdir( $dir ) ) ) {
148
- if ( in_array( $file, array( '.', '..', 'index.php', '.htaccess', '.donotbackup' ), true ) || is_dir( $backup_folder . $file ) || is_link( $backup_folder . $file ) ) {
 
 
 
149
  continue;
150
  }
151
- if ( is_readable( $backup_folder . $file ) ) {
 
152
  //file list for backups
153
  $files[ $filecounter ][ 'folder' ] = $backup_folder;
154
- $files[ $filecounter ][ 'file' ] = $backup_folder . $file;
155
- $files[ $filecounter ][ 'filename' ] = $file;
156
  $files[ $filecounter ][ 'downloadurl' ] = add_query_arg( array(
157
  'page' => 'backwpupbackups',
158
  'action' => 'downloadfolder',
159
- 'file' => $file,
160
  'jobid' => $jobid
161
  ), network_admin_url( 'admin.php' ) );
162
- $files[ $filecounter ][ 'filesize' ] = filesize( $backup_folder . $file );
163
- $files[ $filecounter ][ 'time' ] = filemtime( $backup_folder . $file );
164
  $filecounter ++;
165
  }
166
  }
167
- closedir( $dir );
168
  }
169
 
170
  return $files;
@@ -187,17 +191,25 @@ class BackWPup_Destination_Folder extends BackWPup_Destinations {
187
  //Delete old Backupfiles
188
  $backupfilelist = array();
189
  $files = array();
190
- if ( is_writable( $job_object->backup_folder ) && $dir = opendir( $job_object->backup_folder ) ) { //make file list
191
- while ( FALSE !== ( $file = readdir( $dir ) ) ) {
192
- if ( is_writeable( $job_object->backup_folder . $file ) && ! is_dir( $job_object->backup_folder . $file ) && ! is_link( $job_object->backup_folder . $file ) ) {
193
- //list for deletion
194
- if ( $job_object->is_backup_archive( $file ) ) {
195
- $backupfilelist[ filemtime( $job_object->backup_folder . $file ) ] = $file;
 
 
 
 
 
196
  }
197
  }
198
  }
199
- closedir( $dir );
 
 
200
  }
 
201
  if ( $job_object->job[ 'maxbackups' ] > 0 ) {
202
  if ( count( $backupfilelist ) > $job_object->job[ 'maxbackups' ] ) {
203
  ksort( $backupfilelist );
@@ -205,9 +217,9 @@ class BackWPup_Destination_Folder extends BackWPup_Destinations {
205
  while ( $file = array_shift( $backupfilelist ) ) {
206
  if ( count( $backupfilelist ) < $job_object->job[ 'maxbackups' ] )
207
  break;
208
- unlink( $job_object->backup_folder . $file );
209
  foreach ( $files as $key => $filedata ) {
210
- if ( $filedata[ 'file' ] == $job_object->backup_folder . $file ) {
211
  unset( $files[ $key ] );
212
  }
213
  }
45
  <input id="idmaxbackups" name="maxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'maxbackups' ) ); ?>" class="small-text"/>
46
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
47
  </label>
48
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
49
  <?php } else { ?>
50
  <label for="idbackupsyncnodelete">
51
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'backupsyncnodelete' ), true ); ?> name="backupsyncnodelete" id="idbackupsyncnodelete"/>
144
  $files = array();
145
  $backup_folder = BackWPup_Option::get( $jobid, 'backupdir' );
146
  $backup_folder = BackWPup_File::get_absolute_path( $backup_folder );
147
+
148
+ if ( is_dir( $backup_folder ) ) { //make file list
149
+ $dir = new BackWPup_Directory( $backup_folder );
150
+
151
+ foreach ( $dir as $file ) {
152
+ if ( $file->isDot() || in_array( $file->getFilename(), array( 'index.php', '.htaccess', '.donotbackup', 'Web.config' ), true ) || $file->isDir() || $file->isLink() ) {
153
  continue;
154
  }
155
+
156
+ if ( $file->isReadable() ) {
157
  //file list for backups
158
  $files[ $filecounter ][ 'folder' ] = $backup_folder;
159
+ $files[ $filecounter ][ 'file' ] = str_replace( '\\', '/', $file->getPathname() );
160
+ $files[ $filecounter ][ 'filename' ] = $file->getFilename();
161
  $files[ $filecounter ][ 'downloadurl' ] = add_query_arg( array(
162
  'page' => 'backwpupbackups',
163
  'action' => 'downloadfolder',
164
+ 'file' => $file->getFilename(),
165
  'jobid' => $jobid
166
  ), network_admin_url( 'admin.php' ) );
167
+ $files[ $filecounter ][ 'filesize' ] = $file->getSize();
168
+ $files[ $filecounter ][ 'time' ] = $file->getMTime() + ( get_option( 'gmt_offset' ) * 3600 );
169
  $filecounter ++;
170
  }
171
  }
 
172
  }
173
 
174
  return $files;
191
  //Delete old Backupfiles
192
  $backupfilelist = array();
193
  $files = array();
194
+
195
+ if ( is_writable( $job_object->backup_folder ) ) { //make file list
196
+ try {
197
+ $dir = new BackWPup_Directory( $job_object->backup_folder );
198
+
199
+ foreach ( $dir as $file ) {
200
+ if ( $file->isWritable() && ! $file->isDir() && ! $file->isLink() ) {
201
+ //list for deletion
202
+ if ( $job_object->is_backup_archive( $file->getFilename() ) && $job_object->owns_backup_archive( $file->getFilename() ) ) {
203
+ $backupfilelist[ $file->getMTime() ] = clone $file;
204
+ }
205
  }
206
  }
207
  }
208
+ catch ( UnexpectedValueException $e ) {
209
+ $job_object->log( sprintf( __( "Could not open path: %s", 'backwpup' ), $e->getMessage() ), E_USER_WARNING );
210
+ }
211
  }
212
+
213
  if ( $job_object->job[ 'maxbackups' ] > 0 ) {
214
  if ( count( $backupfilelist ) > $job_object->job[ 'maxbackups' ] ) {
215
  ksort( $backupfilelist );
217
  while ( $file = array_shift( $backupfilelist ) ) {
218
  if ( count( $backupfilelist ) < $job_object->job[ 'maxbackups' ] )
219
  break;
220
+ unlink( $file->getPathname() );
221
  foreach ( $files as $key => $filedata ) {
222
+ if ( $filedata[ 'file' ] == $file->getPathname() ) {
223
  unset( $files[ $key ] );
224
  }
225
  }
inc/class-destination-ftp.php CHANGED
@@ -65,6 +65,7 @@ class BackWPup_Destination_Ftp extends BackWPup_Destinations {
65
  <input id="idftpmaxbackups" name="ftpmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'ftpmaxbackups' ) ); ?>" class="small-text" />
66
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
67
  </label>
 
68
  <?php } else { ?>
69
  <label for="idftpsyncnodelete">
70
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'ftpsyncnodelete' ), true ); ?> name="ftpsyncnodelete" id="idftpsyncnodelete" />
@@ -351,7 +352,7 @@ class BackWPup_Destination_Ftp extends BackWPup_Destinations {
351
  if ( $filelist = ftp_nlist( $ftp_conn_id, '.' ) ) {
352
  foreach ( $filelist as $file ) {
353
  if ( basename( $file ) != '.' && basename( $file ) != '..' ) {
354
- if ( $job_object->is_backup_archive( $file ) ) {
355
  $time = ftp_mdtm( $ftp_conn_id, $file );
356
  if ( $time != - 1 )
357
  $backupfilelist[ $time ] = basename( $file );
65
  <input id="idftpmaxbackups" name="ftpmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'ftpmaxbackups' ) ); ?>" class="small-text" />
66
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
67
  </label>
68
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
69
  <?php } else { ?>
70
  <label for="idftpsyncnodelete">
71
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'ftpsyncnodelete' ), true ); ?> name="ftpsyncnodelete" id="idftpsyncnodelete" />
352
  if ( $filelist = ftp_nlist( $ftp_conn_id, '.' ) ) {
353
  foreach ( $filelist as $file ) {
354
  if ( basename( $file ) != '.' && basename( $file ) != '..' ) {
355
+ if ( $job_object->is_backup_archive( $file ) && $job_object->owns_backup_archive( $file ) == true ) {
356
  $time = ftp_mdtm( $ftp_conn_id, $file );
357
  if ( $time != - 1 )
358
  $backupfilelist[ $time ] = basename( $file );
inc/class-destination-msazure.php CHANGED
@@ -1,462 +1,463 @@
1
- <?php
2
- // Windows Azure SDK v0.4.1
3
- // http://www.windowsazure.com/en-us/develop/php/
4
- // https://github.com/WindowsAzure/azure-sdk-for-php
5
-
6
- /**
7
- * Documentation: http://www.windowsazure.com/en-us/develop/php/how-to-guides/blob-service/
8
- */
9
- class BackWPup_Destination_MSAzure extends BackWPup_Destinations {
10
-
11
- /**
12
- * @return array
13
- */
14
- public function option_defaults() {
15
-
16
- return array( 'msazureaccname' => '', 'msazurekey' => '', 'msazurecontainer' => '', 'msazuredir' => trailingslashit( sanitize_file_name( get_bloginfo( 'name' ) ) ), 'msazuremaxbackups' => 15, 'msazuresyncnodelete' => TRUE );
17
- }
18
-
19
-
20
- /**
21
- * @param $jobid
22
- */
23
- public function edit_tab( $jobid ) {
24
- ?>
25
- <h3 class="title"><?php esc_html_e( 'MS Azure access keys', 'backwpup' ); ?></h3>
26
- <p></p>
27
- <table class="form-table">
28
- <tr>
29
- <th scope="row"><label for="msazureaccname"><?php esc_html_e( 'Account name', 'backwpup' ); ?></label></th>
30
- <td>
31
- <input id="msazureaccname" name="msazureaccname" type="text"
32
- value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazureaccname' ) );?>" class="regular-text" autocomplete="off" />
33
- </td>
34
- </tr>
35
- <tr>
36
- <th scope="row"><label for="msazurekey"><?php esc_html_e( 'Access key', 'backwpup' ); ?></label></th>
37
- <td>
38
- <input id="msazurekey" name="msazurekey" type="password"
39
- value="<?php echo esc_attr( BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );?>" class="regular-text" autocomplete="off" />
40
- </td>
41
- </tr>
42
- </table>
43
-
44
- <h3 class="title"><?php esc_html_e( 'Blob container', 'backwpup' ); ?></h3>
45
- <p></p>
46
- <table class="form-table">
47
- <tr>
48
- <th scope="row"><label for="msazurecontainerselected"><?php esc_html_e( 'Container selection', 'backwpup' ); ?></label></th>
49
- <td>
50
- <input id="msazurecontainerselected" name="msazurecontainerselected" type="hidden" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazurecontainer' ) );?>" />
51
- <?php if ( BackWPup_Option::get( $jobid, 'msazureaccname' ) && BackWPup_Option::get( $jobid, 'msazurekey' ) ) $this->edit_ajax( array(
52
- 'msazureaccname' => BackWPup_Option::get( $jobid, 'msazureaccname' ),
53
- 'msazurekey' => BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ),
54
- 'msazureselected' => BackWPup_Option::get( $jobid, 'msazurecontainer' )
55
- ) ); ?>
56
- </td>
57
- </tr>
58
- <tr>
59
- <th scope="row"><label for="newmsazurecontainer"><?php esc_html_e( 'Create a new container', 'backwpup' ); ?></label></th>
60
- <td>
61
- <input id="newmsazurecontainer" name="newmsazurecontainer" type="text" value="" class="small-text" autocomplete="off" />
62
- </td>
63
- </tr>
64
- </table>
65
-
66
- <h3 class="title"><?php esc_html_e( 'Backup settings', 'backwpup' ); ?></h3>
67
- <p></p>
68
- <table class="form-table">
69
- <tr>
70
- <th scope="row"><label for="idmsazuredir"><?php esc_html_e( 'Folder in container', 'backwpup' ); ?></label></th>
71
- <td>
72
- <input id="idmsazuredir" name="msazuredir" type="text" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazuredir' ) ); ?>" class="regular-text" />
73
- </td>
74
- </tr>
75
- <tr>
76
- <th scope="row"><?php esc_html_e( 'File deletion', 'backwpup' ); ?></th>
77
- <td>
78
- <?php
79
- if ( BackWPup_Option::get( $jobid, 'backuptype' ) === 'archive' ) {
80
- ?>
81
- <label for="idmsazuremaxbackups">
82
- <input id="idmsazuremaxbackups" name="msazuremaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazuremaxbackups' ) ); ?>" class="small-text" />
83
- &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
84
- </label>
85
- <?php } else { ?>
86
- <label for="idmsazuresyncnodelete">
87
- <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'msazuresyncnodelete' ), true ); ?> name="msazuresyncnodelete" id="idmsazuresyncnodelete" />
88
- &nbsp;<?php esc_html_e( 'Do not delete files while syncing to destination!', 'backwpup' ); ?>
89
- </label>
90
- <?php } ?>
91
- </td>
92
- </tr>
93
- </table>
94
- <?php
95
- }
96
-
97
-
98
- /**
99
- * @param $jobid
100
- * @return string
101
- */
102
- public function edit_form_post_save( $jobid ) {
103
-
104
- BackWPup_Option::update( $jobid, 'msazureaccname', sanitize_text_field( $_POST[ 'msazureaccname' ] ) );
105
- BackWPup_Option::update( $jobid, 'msazurekey', sanitize_text_field( $_POST[ 'msazurekey' ] ) );
106
- BackWPup_Option::update( $jobid, 'msazurecontainer', sanitize_text_field( $_POST[ 'msazurecontainer' ] ) );
107
-
108
- $_POST[ 'msazuredir' ] = trailingslashit( str_replace( '//', '/', str_replace( '\\', '/', trim( sanitize_text_field( $_POST[ 'msazuredir' ] ) ) ) ) );
109
- if ( substr( $_POST[ 'msazuredir' ], 0, 1 ) == '/' )
110
- $_POST[ 'msazuredir' ] = substr( $_POST[ 'msazuredir' ], 1 );
111
- if ( $_POST[ 'msazuredir' ] == '/' )
112
- $_POST[ 'msazuredir' ] = '';
113
- BackWPup_Option::update( $jobid, 'msazuredir', $_POST[ 'msazuredir' ] );
114
-
115
- BackWPup_Option::update( $jobid, 'msazuremaxbackups', ! empty( $_POST[ 'msazuremaxbackups' ] ) ? absint( $_POST[ 'msazuremaxbackups' ] ) : 0 );
116
- BackWPup_Option::update( $jobid, 'msazuresyncnodelete', ! empty( $_POST[ 'msazuresyncnodelete' ] ) );
117
-
118
- //create a new container
119
- if ( ! empty( $_POST[ 'newmsazurecontainer' ] ) && ! empty( $_POST[ 'msazureaccname' ] ) && ! empty( $_POST[ 'msazurekey' ] ) ) {
120
- try {
121
- set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
122
- $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . sanitize_text_field( $_POST[ 'msazureaccname' ] ) . ';AccountKey=' . sanitize_text_field( $_POST[ 'msazurekey' ] ) );
123
- $container_options = new WindowsAzure\Blob\Models\CreateContainerOptions();
124
- $container_options->setPublicAccess( WindowsAzure\Blob\Models\PublicAccessType::NONE );
125
- $blobRestProxy->createContainer( $_POST[ 'newmsazurecontainer' ], $container_options );
126
- BackWPup_Option::update( $jobid, 'msazurecontainer', sanitize_text_field( $_POST[ 'newmsazurecontainer' ] ) );
127
- BackWPup_Admin::message( sprintf( __( 'MS Azure container "%s" created.', 'backwpup' ), esc_html( sanitize_text_field( $_POST[ 'newmsazurecontainer' ] ) ) ) );
128
- }
129
- catch ( Exception $e ) {
130
- BackWPup_Admin::message( sprintf( __( 'MS Azure container create: %s', 'backwpup' ), $e->getMessage() ), TRUE );
131
- }
132
- }
133
- }
134
-
135
-
136
- /**
137
- * @param $jobdest
138
- * @param $backupfile
139
- */
140
- public function file_delete( $jobdest, $backupfile ) {
141
-
142
- $files = get_site_transient( 'backwpup_'. strtolower( $jobdest ) );
143
- list( $jobid, $dest ) = explode( '_', $jobdest );
144
-
145
- if ( BackWPup_Option::get( $jobid, 'msazureaccname' ) && BackWPup_Option::get( $jobid, 'msazurekey' ) && BackWPup_Option::get( $jobid, 'msazurecontainer' ) ) {
146
- try {
147
- set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
148
- $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . BackWPup_Option::get( $jobid, 'msazureaccname' ) . ';AccountKey=' . BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );
149
- $blobRestProxy->deleteBlob( BackWPup_Option::get( $jobid, 'msazurecontainer' ), $backupfile );
150
- //update file list
151
- foreach ( $files as $key => $file ) {
152
- if ( is_array( $file ) && $file[ 'file' ] == $backupfile )
153
- unset( $files[ $key ] );
154
- }
155
- }
156
- catch ( Exception $e ) {
157
- BackWPup_Admin::message( 'MS AZURE: ' . $e->getMessage(), TRUE );
158
- }
159
- }
160
-
161
- set_site_transient( 'backwpup_' . strtolower( $jobdest ), $files, YEAR_IN_SECONDS );
162
- }
163
-
164
- /**
165
- * @param $jobid
166
- * @param $get_file
167
- */
168
- public function file_download( $jobid, $get_file ) {
169
- try {
170
- set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
171
- $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . BackWPup_Option::get( $jobid, 'msazureaccname' ) . ';AccountKey=' . BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );
172
- $blob = $blobRestProxy->getBlob( BackWPup_Option::get( $jobid, 'msazurecontainer' ), $get_file );
173
- if ( $level = ob_get_level() ) {
174
- for ( $i = 0; $i < $level; $i ++ ) {
175
- ob_end_clean();
176
- }
177
- }
178
- @set_time_limit( 300 );
179
- nocache_headers();
180
- header( 'Content-Description: File Transfer' );
181
- header( 'Content-Type: ' . BackWPup_Job::get_mime_type( $get_file ) );
182
- header( 'Content-Disposition: attachment; filename="' . basename( $get_file ) . '"' );
183
- header( 'Content-Transfer-Encoding: binary' );
184
- header( 'Content-Length: ' . $blob->getProperties()->getContentLength() );
185
- fpassthru( $blob->getContentStream() );
186
- die();
187
- }
188
- catch ( Exception $e ) {
189
- die( $e->getMessage() );
190
- }
191
- }
192
-
193
- /**
194
- * @param $jobdest
195
- * @return mixed
196
- */
197
- public function file_get_list( $jobdest ) {
198
- return get_site_transient( 'backwpup_' . $jobdest );
199
- }
200
-
201
- /**
202
- * @param $job_object
203
- * @return bool
204
- */
205
- public function job_run_archive( BackWPup_Job $job_object ) {
206
-
207
- $job_object->substeps_todo = $job_object->backup_filesize + 2;
208
-
209
- if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] )
210
- $job_object->log( sprintf( __( '%d. Try sending backup to a Microsoft Azure (Blob)&#160;&hellip;', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] ), E_USER_NOTICE );
211
-
212
- try {
213
- set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
214
- /* @var $blobRestProxy WindowsAzure\Blob\BlobRestProxy */ //https causes an error SSL: Connection reset by peer that is why http
215
- $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService('DefaultEndpointsProtocol=http;AccountName=' . $job_object->job[ 'msazureaccname' ] . ';AccountKey=' . BackWPup_Encryption::decrypt( $job_object->job[ 'msazurekey' ] ) );
216
-
217
-
218
- if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] ) {
219
-
220
- //test vor existing container
221
- $containers = $blobRestProxy->listContainers()->getContainers();
222
-
223
- $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] = '';
224
- foreach( $containers as $container ) {
225
- if ( $container->getName() == $job_object->job[ 'msazurecontainer' ] ) {
226
- $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] = $container->getUrl();
227
- break;
228
- }
229
- }
230
-
231
- if ( ! $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] ) {
232
- $job_object->log( sprintf( __( 'MS Azure container "%s" does not exist!', 'backwpup'), $job_object->job[ 'msazurecontainer' ] ), E_USER_ERROR );
233
-
234
- return TRUE;
235
- } else {
236
- $job_object->log( sprintf( __( 'Connected to MS Azure container "%s".', 'backwpup'), $job_object->job[ 'msazurecontainer' ] ), E_USER_NOTICE );
237
- }
238
-
239
- $job_object->log( __( 'Starting upload to MS Azure&#160;&hellip;', 'backwpup' ), E_USER_NOTICE );
240
- }
241
-
242
- //Prepare Upload
243
- if ( $file_handel = fopen( $job_object->backup_folder . $job_object->backup_file, 'rb' ) ) {
244
- fseek( $file_handel, $job_object->substeps_done );
245
-
246
- if ( empty( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] ) ) {
247
- $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] = array();
248
- }
249
-
250
- while ( ! feof( $file_handel ) ) {
251
- $data = fread( $file_handel, 1048576 * 4 ); //4MB
252
- if ( strlen( $data ) == 0 ) {
253
- continue;
254
- }
255
- $chunk_upload_start = microtime( TRUE );
256
- $block_count = count( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] ) + 1;
257
- $block_id = md5( $data ) . str_pad( $block_count, 6, "0", STR_PAD_LEFT );
258
- $blobRestProxy->createBlobBlock( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $job_object->backup_file, $block_id, $data );
259
- $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ][] = $block_id;
260
- $chunk_upload_time = microtime( TRUE ) - $chunk_upload_start;
261
- $job_object->substeps_done = $job_object->substeps_done + strlen( $data );
262
- $time_remaining = $job_object->do_restart_time();
263
- if ( $time_remaining < $chunk_upload_time ) {
264
- $job_object->do_restart_time( TRUE );
265
- }
266
- $job_object->update_working_data();
267
- }
268
- fclose( $file_handel );
269
- } else {
270
- $job_object->log( __( 'Can not open source file for transfer.', 'backwpup' ), E_USER_ERROR );
271
- return FALSE;
272
- }
273
-
274
- //crate blog list
275
- $blocklist = new WindowsAzure\Blob\Models\BlockList();
276
- foreach( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] as $block_id ) {
277
- $blocklist->addUncommittedEntry( $block_id );
278
- }
279
- unset( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] );
280
-
281
- //Commit Blocks
282
- $blobRestProxy->commitBlobBlocks( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $job_object->backup_file, $blocklist->getEntries() );
283
-
284
- $job_object->substeps_done ++;
285
- $job_object->log( sprintf( __( 'Backup transferred to %s', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] . '/' . $job_object->job[ 'msazuredir' ] . $job_object->backup_file ), E_USER_NOTICE );
286
- if ( !empty( $job_object->job[ 'jobid' ] ) ) {
287
- BackWPup_Option::update( $job_object->job[ 'jobid' ] , 'lastbackupdownloadurl', network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloadmsazure&file=' . $job_object->job[ 'msazuredir' ] . $job_object->backup_file . '&jobid=' . $job_object->job[ 'jobid' ] );
288
- }
289
- }
290
- catch ( Exception $e ) {
291
- $job_object->log( E_USER_ERROR, sprintf( __( 'Microsoft Azure API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine() );
292
- $job_object->substeps_done = 0;
293
- unset( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] );
294
- if ( isset( $file_handel ) && is_resource( $file_handel ) )
295
- fclose( $file_handel );
296
-
297
- return FALSE;
298
- }
299
-
300
-
301
- try {
302
-
303
- $backupfilelist = array();
304
- $filecounter = 0;
305
- $files = array();
306
- $blob_options = new WindowsAzure\Blob\Models\ListBlobsOptions();
307
- $blob_options->setPrefix( $job_object->job[ 'msazuredir' ] );
308
- $blobs = $blobRestProxy->listBlobs( $job_object->job[ 'msazurecontainer' ], $blob_options )->getBlobs();
309
-
310
- if ( is_array( $blobs ) ) {
311
- foreach ( $blobs as $blob ) {
312
- $file = basename( $blob->getName() );
313
- if ( $job_object->is_backup_archive( $file ) )
314
- $backupfilelist[ $blob->getProperties()->getLastModified()->getTimestamp() ] = $file;
315
- $files[ $filecounter ][ 'folder' ] = $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] . "/" . dirname( $blob->getName() ) . "/";
316
- $files[ $filecounter ][ 'file' ] = $blob->getName();
317
- $files[ $filecounter ][ 'filename' ] = basename( $blob->getName() );
318
- $files[ $filecounter ][ 'downloadurl' ] = network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloadmsazure&file=' . $blob->getName() . '&jobid=' . $job_object->job[ 'jobid' ];
319
- $files[ $filecounter ][ 'filesize' ] = $blob->getProperties()->getContentLength();
320
- $files[ $filecounter ][ 'time' ] = $blob->getProperties()->getLastModified()->getTimestamp() + ( get_option( 'gmt_offset' ) * 3600 );
321
- $filecounter ++;
322
- }
323
- }
324
- // Delete old backups
325
- if ( ! empty ($job_object->job[ 'msazuremaxbackups' ] ) && $job_object->job[ 'msazuremaxbackups' ] > 0 ) {
326
- if ( count( $backupfilelist ) > $job_object->job[ 'msazuremaxbackups' ] ) {
327
- ksort( $backupfilelist );
328
- $numdeltefiles = 0;
329
- while ( $file = array_shift( $backupfilelist ) ) {
330
- if ( count( $backupfilelist ) < $job_object->job[ 'msazuremaxbackups' ] )
331
- break;
332
- $blobRestProxy->deleteBlob( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $file );
333
- foreach ( $files as $key => $filedata ) {
334
- if ( $filedata[ 'file' ] == $job_object->job[ 'msazuredir' ] . $file )
335
- unset( $files[ $key ] );
336
- }
337
- $numdeltefiles ++;
338
- }
339
- if ( $numdeltefiles > 0 )
340
- $job_object->log( sprintf( _n( 'One file deleted on Microsoft Azure container.', '%d files deleted on Microsoft Azure container.', $numdeltefiles, 'backwpup' ), $numdeltefiles ), E_USER_NOTICE );
341
-
342
- }
343
- }
344
- set_site_transient( 'backwpup_' . $job_object->job[ 'jobid' ] . '_msazure', $files, YEAR_IN_SECONDS );
345
- }
346
- catch ( Exception $e ) {
347
- $job_object->log( E_USER_ERROR, sprintf( __( 'Microsoft Azure API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine() );
348
-
349
- return FALSE;
350
- }
351
-
352
- $job_object->substeps_done = $job_object->backup_filesize + 2;
353
-
354
- return TRUE;
355
- }
356
-
357
- /**
358
- * @param $job_settings array
359
- * @return bool
360
- */
361
- public function can_run( array $job_settings ) {
362
-
363
- if ( empty( $job_settings[ 'msazureaccname' ] ) )
364
- return FALSE;
365
-
366
- if ( empty( $job_settings[ 'msazurekey' ]) )
367
- return FALSE;
368
-
369
- if ( empty( $job_settings[ 'msazurecontainer' ] ) )
370
- return FALSE;
371
-
372
- return TRUE;
373
- }
374
-
375
- /**
376
- *
377
- */
378
- public function edit_inline_js() {
379
- ?>
380
- <script type="text/javascript">
381
- jQuery(document).ready(function ($) {
382
- function msazuregetcontainer() {
383
- var data = {
384
- action: 'backwpup_dest_msazure',
385
- msazureaccname: $('#msazureaccname').val(),
386
- msazurekey: $('#msazurekey').val(),
387
- msazureselected: $('#msazurecontainerselected').val(),
388
- _ajax_nonce: $('#backwpupajaxnonce').val()
389
- };
390
- $.post(ajaxurl, data, function (response) {
391
- $('#msazurecontainererror').remove();
392
- $('#msazurecontainer').remove();
393
- $('#msazurecontainerselected').after(response);
394
- });
395
- }
396
-
397
- $('#msazureaccname').backwpupDelayKeyup(function () {
398
- msazuregetcontainer();
399
- });
400
- $('#msazurekey').backwpupDelayKeyup(function () {
401
- msazuregetcontainer();
402
- });
403
- });
404
- </script>
405
- <?php
406
- }
407
-
408
- /**
409
- * @param string $args
410
- */
411
- public function edit_ajax( $args = '' ) {
412
-
413
- $error = '';
414
-
415
- if ( is_array( $args ) ) {
416
- $ajax = FALSE;
417
- }
418
- else {
419
- if ( ! current_user_can( 'backwpup_jobs_edit' ) )
420
- wp_die( -1 );
421
- check_ajax_referer( 'backwpup_ajax_nonce' );
422
- $args[ 'msazureaccname' ] = sanitize_text_field( $_POST[ 'msazureaccname' ] );
423
- $args[ 'msazurekey' ] = sanitize_text_field( $_POST[ 'msazurekey' ] );
424
- $args[ 'msazureselected' ] = sanitize_text_field( $_POST[ 'msazureselected' ] );
425
- $ajax = TRUE;
426
- }
427
- echo '<span id="msazurecontainererror" style="color:red;">';
428
-
429
- if ( ! empty( $args[ 'msazureaccname' ] ) && ! empty( $args[ 'msazurekey' ] ) ) {
430
- try {
431
- set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
432
- $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . $args[ 'msazureaccname' ] . ';AccountKey=' . BackWPup_Encryption::decrypt( $args[ 'msazurekey' ] ) );
433
- $containers = $blobRestProxy->listContainers()->getContainers();
434
- }
435
- catch ( Exception $e ) {
436
- $error = $e->getMessage();
437
- }
438
- }
439
-
440
- if ( empty( $args[ 'msazureaccname' ] ) )
441
- _e( 'Missing account name!', 'backwpup' );
442
- elseif ( empty( $args[ 'msazurekey' ] ) )
443
- _e( 'Missing access key!', 'backwpup' );
444
- elseif ( ! empty( $error ) )
445
- echo esc_html( $error );
446
- elseif ( empty( $containers ) )
447
- _e( 'No container found!', 'backwpup' );
448
- echo '</span>';
449
-
450
- if ( !empty( $containers ) ) {
451
- echo '<select name="msazurecontainer" id="msazurecontainer">';
452
- foreach ( $containers as $container ) {
453
- echo "<option " . selected( strtolower( $args[ 'msazureselected' ] ), strtolower( $container->getName() ), FALSE ) . ">" . esc_html( $container->getName() ) . "</option>";
454
- }
455
- echo '</select>';
456
- }
457
- if ( $ajax )
458
- die();
459
- else
460
- return;
461
- }
462
- }
 
1
+ <?php
2
+ // Windows Azure SDK v0.4.1
3
+ // http://www.windowsazure.com/en-us/develop/php/
4
+ // https://github.com/WindowsAzure/azure-sdk-for-php
5
+
6
+ /**
7
+ * Documentation: http://www.windowsazure.com/en-us/develop/php/how-to-guides/blob-service/
8
+ */
9
+ class BackWPup_Destination_MSAzure extends BackWPup_Destinations {
10
+
11
+ /**
12
+ * @return array
13
+ */
14
+ public function option_defaults() {
15
+
16
+ return array( 'msazureaccname' => '', 'msazurekey' => '', 'msazurecontainer' => '', 'msazuredir' => trailingslashit( sanitize_file_name( get_bloginfo( 'name' ) ) ), 'msazuremaxbackups' => 15, 'msazuresyncnodelete' => TRUE );
17
+ }
18
+
19
+
20
+ /**
21
+ * @param $jobid
22
+ */
23
+ public function edit_tab( $jobid ) {
24
+ ?>
25
+ <h3 class="title"><?php esc_html_e( 'MS Azure access keys', 'backwpup' ); ?></h3>
26
+ <p></p>
27
+ <table class="form-table">
28
+ <tr>
29
+ <th scope="row"><label for="msazureaccname"><?php esc_html_e( 'Account name', 'backwpup' ); ?></label></th>
30
+ <td>
31
+ <input id="msazureaccname" name="msazureaccname" type="text"
32
+ value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazureaccname' ) );?>" class="regular-text" autocomplete="off" />
33
+ </td>
34
+ </tr>
35
+ <tr>
36
+ <th scope="row"><label for="msazurekey"><?php esc_html_e( 'Access key', 'backwpup' ); ?></label></th>
37
+ <td>
38
+ <input id="msazurekey" name="msazurekey" type="password"
39
+ value="<?php echo esc_attr( BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );?>" class="regular-text" autocomplete="off" />
40
+ </td>
41
+ </tr>
42
+ </table>
43
+
44
+ <h3 class="title"><?php esc_html_e( 'Blob container', 'backwpup' ); ?></h3>
45
+ <p></p>
46
+ <table class="form-table">
47
+ <tr>
48
+ <th scope="row"><label for="msazurecontainerselected"><?php esc_html_e( 'Container selection', 'backwpup' ); ?></label></th>
49
+ <td>
50
+ <input id="msazurecontainerselected" name="msazurecontainerselected" type="hidden" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazurecontainer' ) );?>" />
51
+ <?php if ( BackWPup_Option::get( $jobid, 'msazureaccname' ) && BackWPup_Option::get( $jobid, 'msazurekey' ) ) $this->edit_ajax( array(
52
+ 'msazureaccname' => BackWPup_Option::get( $jobid, 'msazureaccname' ),
53
+ 'msazurekey' => BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ),
54
+ 'msazureselected' => BackWPup_Option::get( $jobid, 'msazurecontainer' )
55
+ ) ); ?>
56
+ </td>
57
+ </tr>
58
+ <tr>
59
+ <th scope="row"><label for="newmsazurecontainer"><?php esc_html_e( 'Create a new container', 'backwpup' ); ?></label></th>
60
+ <td>
61
+ <input id="newmsazurecontainer" name="newmsazurecontainer" type="text" value="" class="small-text" autocomplete="off" />
62
+ </td>
63
+ </tr>
64
+ </table>
65
+
66
+ <h3 class="title"><?php esc_html_e( 'Backup settings', 'backwpup' ); ?></h3>
67
+ <p></p>
68
+ <table class="form-table">
69
+ <tr>
70
+ <th scope="row"><label for="idmsazuredir"><?php esc_html_e( 'Folder in container', 'backwpup' ); ?></label></th>
71
+ <td>
72
+ <input id="idmsazuredir" name="msazuredir" type="text" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazuredir' ) ); ?>" class="regular-text" />
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <th scope="row"><?php esc_html_e( 'File deletion', 'backwpup' ); ?></th>
77
+ <td>
78
+ <?php
79
+ if ( BackWPup_Option::get( $jobid, 'backuptype' ) === 'archive' ) {
80
+ ?>
81
+ <label for="idmsazuremaxbackups">
82
+ <input id="idmsazuremaxbackups" name="msazuremaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'msazuremaxbackups' ) ); ?>" class="small-text" />
83
+ &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
84
+ </label>
85
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
86
+ <?php } else { ?>
87
+ <label for="idmsazuresyncnodelete">
88
+ <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'msazuresyncnodelete' ), true ); ?> name="msazuresyncnodelete" id="idmsazuresyncnodelete" />
89
+ &nbsp;<?php esc_html_e( 'Do not delete files while syncing to destination!', 'backwpup' ); ?>
90
+ </label>
91
+ <?php } ?>
92
+ </td>
93
+ </tr>
94
+ </table>
95
+ <?php
96
+ }
97
+
98
+
99
+ /**
100
+ * @param $jobid
101
+ * @return string
102
+ */
103
+ public function edit_form_post_save( $jobid ) {
104
+
105
+ BackWPup_Option::update( $jobid, 'msazureaccname', sanitize_text_field( $_POST[ 'msazureaccname' ] ) );
106
+ BackWPup_Option::update( $jobid, 'msazurekey', sanitize_text_field( $_POST[ 'msazurekey' ] ) );
107
+ BackWPup_Option::update( $jobid, 'msazurecontainer', sanitize_text_field( $_POST[ 'msazurecontainer' ] ) );
108
+
109
+ $_POST[ 'msazuredir' ] = trailingslashit( str_replace( '//', '/', str_replace( '\\', '/', trim( sanitize_text_field( $_POST[ 'msazuredir' ] ) ) ) ) );
110
+ if ( substr( $_POST[ 'msazuredir' ], 0, 1 ) == '/' )
111
+ $_POST[ 'msazuredir' ] = substr( $_POST[ 'msazuredir' ], 1 );
112
+ if ( $_POST[ 'msazuredir' ] == '/' )
113
+ $_POST[ 'msazuredir' ] = '';
114
+ BackWPup_Option::update( $jobid, 'msazuredir', $_POST[ 'msazuredir' ] );
115
+
116
+ BackWPup_Option::update( $jobid, 'msazuremaxbackups', ! empty( $_POST[ 'msazuremaxbackups' ] ) ? absint( $_POST[ 'msazuremaxbackups' ] ) : 0 );
117
+ BackWPup_Option::update( $jobid, 'msazuresyncnodelete', ! empty( $_POST[ 'msazuresyncnodelete' ] ) );
118
+
119
+ //create a new container
120
+ if ( ! empty( $_POST[ 'newmsazurecontainer' ] ) && ! empty( $_POST[ 'msazureaccname' ] ) && ! empty( $_POST[ 'msazurekey' ] ) ) {
121
+ try {
122
+ set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
123
+ $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . sanitize_text_field( $_POST[ 'msazureaccname' ] ) . ';AccountKey=' . sanitize_text_field( $_POST[ 'msazurekey' ] ) );
124
+ $container_options = new WindowsAzure\Blob\Models\CreateContainerOptions();
125
+ $container_options->setPublicAccess( WindowsAzure\Blob\Models\PublicAccessType::NONE );
126
+ $blobRestProxy->createContainer( $_POST[ 'newmsazurecontainer' ], $container_options );
127
+ BackWPup_Option::update( $jobid, 'msazurecontainer', sanitize_text_field( $_POST[ 'newmsazurecontainer' ] ) );
128
+ BackWPup_Admin::message( sprintf( __( 'MS Azure container "%s" created.', 'backwpup' ), esc_html( sanitize_text_field( $_POST[ 'newmsazurecontainer' ] ) ) ) );
129
+ }
130
+ catch ( Exception $e ) {
131
+ BackWPup_Admin::message( sprintf( __( 'MS Azure container create: %s', 'backwpup' ), $e->getMessage() ), TRUE );
132
+ }
133
+ }
134
+ }
135
+
136
+
137
+ /**
138
+ * @param $jobdest
139
+ * @param $backupfile
140
+ */
141
+ public function file_delete( $jobdest, $backupfile ) {
142
+
143
+ $files = get_site_transient( 'backwpup_'. strtolower( $jobdest ) );
144
+ list( $jobid, $dest ) = explode( '_', $jobdest );
145
+
146
+ if ( BackWPup_Option::get( $jobid, 'msazureaccname' ) && BackWPup_Option::get( $jobid, 'msazurekey' ) && BackWPup_Option::get( $jobid, 'msazurecontainer' ) ) {
147
+ try {
148
+ set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
149
+ $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . BackWPup_Option::get( $jobid, 'msazureaccname' ) . ';AccountKey=' . BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );
150
+ $blobRestProxy->deleteBlob( BackWPup_Option::get( $jobid, 'msazurecontainer' ), $backupfile );
151
+ //update file list
152
+ foreach ( $files as $key => $file ) {
153
+ if ( is_array( $file ) && $file[ 'file' ] == $backupfile )
154
+ unset( $files[ $key ] );
155
+ }
156
+ }
157
+ catch ( Exception $e ) {
158
+ BackWPup_Admin::message( 'MS AZURE: ' . $e->getMessage(), TRUE );
159
+ }
160
+ }
161
+
162
+ set_site_transient( 'backwpup_' . strtolower( $jobdest ), $files, YEAR_IN_SECONDS );
163
+ }
164
+
165
+ /**
166
+ * @param $jobid
167
+ * @param $get_file
168
+ */
169
+ public function file_download( $jobid, $get_file ) {
170
+ try {
171
+ set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
172
+ $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . BackWPup_Option::get( $jobid, 'msazureaccname' ) . ';AccountKey=' . BackWPup_Encryption::decrypt( BackWPup_Option::get( $jobid, 'msazurekey' ) ) );
173
+ $blob = $blobRestProxy->getBlob( BackWPup_Option::get( $jobid, 'msazurecontainer' ), $get_file );
174
+ if ( $level = ob_get_level() ) {
175
+ for ( $i = 0; $i < $level; $i ++ ) {
176
+ ob_end_clean();
177
+ }
178
+ }
179
+ @set_time_limit( 300 );
180
+ nocache_headers();
181
+ header( 'Content-Description: File Transfer' );
182
+ header( 'Content-Type: ' . BackWPup_Job::get_mime_type( $get_file ) );
183
+ header( 'Content-Disposition: attachment; filename="' . basename( $get_file ) . '"' );
184
+ header( 'Content-Transfer-Encoding: binary' );
185
+ header( 'Content-Length: ' . $blob->getProperties()->getContentLength() );
186
+ fpassthru( $blob->getContentStream() );
187
+ die();
188
+ }
189
+ catch ( Exception $e ) {
190
+ die( $e->getMessage() );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * @param $jobdest
196
+ * @return mixed
197
+ */
198
+ public function file_get_list( $jobdest ) {
199
+ return get_site_transient( 'backwpup_' . $jobdest );
200
+ }
201
+
202
+ /**
203
+ * @param $job_object
204
+ * @return bool
205
+ */
206
+ public function job_run_archive( BackWPup_Job $job_object ) {
207
+
208
+ $job_object->substeps_todo = $job_object->backup_filesize + 2;
209
+
210
+ if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] )
211
+ $job_object->log( sprintf( __( '%d. Try sending backup to a Microsoft Azure (Blob)&#160;&hellip;', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] ), E_USER_NOTICE );
212
+
213
+ try {
214
+ set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
215
+ /* @var $blobRestProxy WindowsAzure\Blob\BlobRestProxy */ //https causes an error SSL: Connection reset by peer that is why http
216
+ $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService('DefaultEndpointsProtocol=http;AccountName=' . $job_object->job[ 'msazureaccname' ] . ';AccountKey=' . BackWPup_Encryption::decrypt( $job_object->job[ 'msazurekey' ] ) );
217
+
218
+
219
+ if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] != $job_object->steps_data[ $job_object->step_working ][ 'STEP_TRY' ] ) {
220
+
221
+ //test vor existing container
222
+ $containers = $blobRestProxy->listContainers()->getContainers();
223
+
224
+ $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] = '';
225
+ foreach( $containers as $container ) {
226
+ if ( $container->getName() == $job_object->job[ 'msazurecontainer' ] ) {
227
+ $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] = $container->getUrl();
228
+ break;
229
+ }
230
+ }
231
+
232
+ if ( ! $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] ) {
233
+ $job_object->log( sprintf( __( 'MS Azure container "%s" does not exist!', 'backwpup'), $job_object->job[ 'msazurecontainer' ] ), E_USER_ERROR );
234
+
235
+ return TRUE;
236
+ } else {
237
+ $job_object->log( sprintf( __( 'Connected to MS Azure container "%s".', 'backwpup'), $job_object->job[ 'msazurecontainer' ] ), E_USER_NOTICE );
238
+ }
239
+
240
+ $job_object->log( __( 'Starting upload to MS Azure&#160;&hellip;', 'backwpup' ), E_USER_NOTICE );
241
+ }
242
+
243
+ //Prepare Upload
244
+ if ( $file_handel = fopen( $job_object->backup_folder . $job_object->backup_file, 'rb' ) ) {
245
+ fseek( $file_handel, $job_object->substeps_done );
246
+
247
+ if ( empty( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] ) ) {
248
+ $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] = array();
249
+ }
250
+
251
+ while ( ! feof( $file_handel ) ) {
252
+ $data = fread( $file_handel, 1048576 * 4 ); //4MB
253
+ if ( strlen( $data ) == 0 ) {
254
+ continue;
255
+ }
256
+ $chunk_upload_start = microtime( TRUE );
257
+ $block_count = count( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] ) + 1;
258
+ $block_id = md5( $data ) . str_pad( $block_count, 6, "0", STR_PAD_LEFT );
259
+ $blobRestProxy->createBlobBlock( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $job_object->backup_file, $block_id, $data );
260
+ $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ][] = $block_id;
261
+ $chunk_upload_time = microtime( TRUE ) - $chunk_upload_start;
262
+ $job_object->substeps_done = $job_object->substeps_done + strlen( $data );
263
+ $time_remaining = $job_object->do_restart_time();
264
+ if ( $time_remaining < $chunk_upload_time ) {
265
+ $job_object->do_restart_time( TRUE );
266
+ }
267
+ $job_object->update_working_data();
268
+ }
269
+ fclose( $file_handel );
270
+ } else {
271
+ $job_object->log( __( 'Can not open source file for transfer.', 'backwpup' ), E_USER_ERROR );
272
+ return FALSE;
273
+ }
274
+
275
+ //crate blog list
276
+ $blocklist = new WindowsAzure\Blob\Models\BlockList();
277
+ foreach( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] as $block_id ) {
278
+ $blocklist->addUncommittedEntry( $block_id );
279
+ }
280
+ unset( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] );
281
+
282
+ //Commit Blocks
283
+ $blobRestProxy->commitBlobBlocks( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $job_object->backup_file, $blocklist->getEntries() );
284
+
285
+ $job_object->substeps_done ++;
286
+ $job_object->log( sprintf( __( 'Backup transferred to %s', 'backwpup' ), $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] . '/' . $job_object->job[ 'msazuredir' ] . $job_object->backup_file ), E_USER_NOTICE );
287
+ if ( !empty( $job_object->job[ 'jobid' ] ) ) {
288
+ BackWPup_Option::update( $job_object->job[ 'jobid' ] , 'lastbackupdownloadurl', network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloadmsazure&file=' . $job_object->job[ 'msazuredir' ] . $job_object->backup_file . '&jobid=' . $job_object->job[ 'jobid' ] );
289
+ }
290
+ }
291
+ catch ( Exception $e ) {
292
+ $job_object->log( E_USER_ERROR, sprintf( __( 'Microsoft Azure API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine() );
293
+ $job_object->substeps_done = 0;
294
+ unset( $job_object->steps_data[ $job_object->step_working ][ 'BlockList' ] );
295
+ if ( isset( $file_handel ) && is_resource( $file_handel ) )
296
+ fclose( $file_handel );
297
+
298
+ return FALSE;
299
+ }
300
+
301
+
302
+ try {
303
+
304
+ $backupfilelist = array();
305
+ $filecounter = 0;
306
+ $files = array();
307
+ $blob_options = new WindowsAzure\Blob\Models\ListBlobsOptions();
308
+ $blob_options->setPrefix( $job_object->job[ 'msazuredir' ] );
309
+ $blobs = $blobRestProxy->listBlobs( $job_object->job[ 'msazurecontainer' ], $blob_options )->getBlobs();
310
+
311
+ if ( is_array( $blobs ) ) {
312
+ foreach ( $blobs as $blob ) {
313
+ $file = basename( $blob->getName() );
314
+ if ( $job_object->is_backup_archive( $file ) && $job_object->owns_backup_archive( $file ) == true )
315
+ $backupfilelist[ $blob->getProperties()->getLastModified()->getTimestamp() ] = $file;
316
+ $files[ $filecounter ][ 'folder' ] = $job_object->steps_data[ $job_object->step_working ][ 'container_url' ] . "/" . dirname( $blob->getName() ) . "/";
317
+ $files[ $filecounter ][ 'file' ] = $blob->getName();
318
+ $files[ $filecounter ][ 'filename' ] = basename( $blob->getName() );
319
+ $files[ $filecounter ][ 'downloadurl' ] = network_admin_url( 'admin.php' ) . '?page=backwpupbackups&action=downloadmsazure&file=' . $blob->getName() . '&jobid=' . $job_object->job[ 'jobid' ];
320
+ $files[ $filecounter ][ 'filesize' ] = $blob->getProperties()->getContentLength();
321
+ $files[ $filecounter ][ 'time' ] = $blob->getProperties()->getLastModified()->getTimestamp() + ( get_option( 'gmt_offset' ) * 3600 );
322
+ $filecounter ++;
323
+ }
324
+ }
325
+ // Delete old backups
326
+ if ( ! empty ($job_object->job[ 'msazuremaxbackups' ] ) && $job_object->job[ 'msazuremaxbackups' ] > 0 ) {
327
+ if ( count( $backupfilelist ) > $job_object->job[ 'msazuremaxbackups' ] ) {
328
+ ksort( $backupfilelist );
329
+ $numdeltefiles = 0;
330
+ while ( $file = array_shift( $backupfilelist ) ) {
331
+ if ( count( $backupfilelist ) < $job_object->job[ 'msazuremaxbackups' ] )
332
+ break;
333
+ $blobRestProxy->deleteBlob( $job_object->job[ 'msazurecontainer' ], $job_object->job[ 'msazuredir' ] . $file );
334
+ foreach ( $files as $key => $filedata ) {
335
+ if ( $filedata[ 'file' ] == $job_object->job[ 'msazuredir' ] . $file )
336
+ unset( $files[ $key ] );
337
+ }
338
+ $numdeltefiles ++;
339
+ }
340
+ if ( $numdeltefiles > 0 )
341
+ $job_object->log( sprintf( _n( 'One file deleted on Microsoft Azure container.', '%d files deleted on Microsoft Azure container.', $numdeltefiles, 'backwpup' ), $numdeltefiles ), E_USER_NOTICE );
342
+
343
+ }
344
+ }
345
+ set_site_transient( 'backwpup_' . $job_object->job[ 'jobid' ] . '_msazure', $files, YEAR_IN_SECONDS );
346
+ }
347
+ catch ( Exception $e ) {
348
+ $job_object->log( E_USER_ERROR, sprintf( __( 'Microsoft Azure API: %s', 'backwpup' ), $e->getMessage() ), $e->getFile(), $e->getLine() );
349
+
350
+ return FALSE;
351
+ }
352
+
353
+ $job_object->substeps_done = $job_object->backup_filesize + 2;
354
+
355
+ return TRUE;
356
+ }
357
+
358
+ /**
359
+ * @param $job_settings array
360
+ * @return bool
361
+ */
362
+ public function can_run( array $job_settings ) {
363
+
364
+ if ( empty( $job_settings[ 'msazureaccname' ] ) )
365
+ return FALSE;
366
+
367
+ if ( empty( $job_settings[ 'msazurekey' ]) )
368
+ return FALSE;
369
+
370
+ if ( empty( $job_settings[ 'msazurecontainer' ] ) )
371
+ return FALSE;
372
+
373
+ return TRUE;
374
+ }
375
+
376
+ /**
377
+ *
378
+ */
379
+ public function edit_inline_js() {
380
+ ?>
381
+ <script type="text/javascript">
382
+ jQuery(document).ready(function ($) {
383
+ function msazuregetcontainer() {
384
+ var data = {
385
+ action: 'backwpup_dest_msazure',
386
+ msazureaccname: $('#msazureaccname').val(),
387
+ msazurekey: $('#msazurekey').val(),
388
+ msazureselected: $('#msazurecontainerselected').val(),
389
+ _ajax_nonce: $('#backwpupajaxnonce').val()
390
+ };
391
+ $.post(ajaxurl, data, function (response) {
392
+ $('#msazurecontainererror').remove();
393
+ $('#msazurecontainer').remove();
394
+ $('#msazurecontainerselected').after(response);
395
+ });
396
+ }
397
+
398
+ $('#msazureaccname').backwpupDelayKeyup(function () {
399
+ msazuregetcontainer();
400
+ });
401
+ $('#msazurekey').backwpupDelayKeyup(function () {
402
+ msazuregetcontainer();
403
+ });
404
+ });
405
+ </script>
406
+ <?php
407
+ }
408
+
409
+ /**
410
+ * @param string $args
411
+ */
412
+ public function edit_ajax( $args = '' ) {
413
+
414
+ $error = '';
415
+
416
+ if ( is_array( $args ) ) {
417
+ $ajax = FALSE;
418
+ }
419
+ else {
420
+ if ( ! current_user_can( 'backwpup_jobs_edit' ) )
421
+ wp_die( -1 );
422
+ check_ajax_referer( 'backwpup_ajax_nonce' );
423
+ $args[ 'msazureaccname' ] = sanitize_text_field( $_POST[ 'msazureaccname' ] );
424
+ $args[ 'msazurekey' ] = sanitize_text_field( $_POST[ 'msazurekey' ] );
425
+ $args[ 'msazureselected' ] = sanitize_text_field( $_POST[ 'msazureselected' ] );
426
+ $ajax = TRUE;
427
+ }
428
+ echo '<span id="msazurecontainererror" style="color:red;">';
429
+
430
+ if ( ! empty( $args[ 'msazureaccname' ] ) && ! empty( $args[ 'msazurekey' ] ) ) {
431
+ try {
432
+ set_include_path( get_include_path() . PATH_SEPARATOR . BackWPup::get_plugin_data( 'plugindir' ) .'/vendor/PEAR/');
433
+ $blobRestProxy = WindowsAzure\Common\ServicesBuilder::getInstance()->createBlobService( 'DefaultEndpointsProtocol=https;AccountName=' . $args[ 'msazureaccname' ] . ';AccountKey=' . BackWPup_Encryption::decrypt( $args[ 'msazurekey' ] ) );
434
+ $containers = $blobRestProxy->listContainers()->getContainers();
435
+ }
436
+ catch ( Exception $e ) {
437
+ $error = $e->getMessage();
438
+ }
439
+ }
440
+
441
+ if ( empty( $args[ 'msazureaccname' ] ) )
442
+ _e( 'Missing account name!', 'backwpup' );
443
+ elseif ( empty( $args[ 'msazurekey' ] ) )
444
+ _e( 'Missing access key!', 'backwpup' );
445
+ elseif ( ! empty( $error ) )
446
+ echo esc_html( $error );
447
+ elseif ( empty( $containers ) )
448
+ _e( 'No container found!', 'backwpup' );
449
+ echo '</span>';
450
+
451
+ if ( !empty( $containers ) ) {
452
+ echo '<select name="msazurecontainer" id="msazurecontainer">';
453
+ foreach ( $containers as $container ) {
454
+ echo "<option " . selected( strtolower( $args[ 'msazureselected' ] ), strtolower( $container->getName() ), FALSE ) . ">" . esc_html( $container->getName() ) . "</option>";
455
+ }
456
+ echo '</select>';
457
+ }
458
+ if ( $ajax )
459
+ die();
460
+ else
461
+ return;
462
+ }
463
+ }
inc/class-destination-rsc.php CHANGED
@@ -111,6 +111,7 @@ class BackWPup_Destination_RSC extends BackWPup_Destinations {
111
  <input id="idrscmaxbackups" name="rscmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'rscmaxbackups' ) ); ?>" class="small-text" />
112
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
113
  </label>
 
114
  <?php } else { ?>
115
  <label for="idrscsyncnodelete">
116
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'rscsyncnodelete' ), true ); ?> name="rscsyncnodelete" id="idrscsyncnodelete" />
@@ -323,7 +324,7 @@ class BackWPup_Destination_RSC extends BackWPup_Destinations {
323
  while ( $object = $objlist->next() ) {
324
  $file = basename( $object->getName() );
325
  if ( $job_object->job[ 'rscdir' ] . $file == $object->getName() ) { //only in the folder and not in complete bucket
326
- if ( $job_object->is_backup_archive( $file ) )
327
  $backupfilelist[ strtotime( $object->getLastModified() ) ] = $object;
328
  }
329
  $files[ $filecounter ][ 'folder' ] = "RSC://" . $job_object->job[ 'rsccontainer' ] . "/" . dirname( $object->getName() ) . "/";
111
  <input id="idrscmaxbackups" name="rscmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'rscmaxbackups' ) ); ?>" class="small-text" />
112
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
113
  </label>
114
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
115
  <?php } else { ?>
116
  <label for="idrscsyncnodelete">
117
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'rscsyncnodelete' ), true ); ?> name="rscsyncnodelete" id="idrscsyncnodelete" />
324
  while ( $object = $objlist->next() ) {
325
  $file = basename( $object->getName() );
326
  if ( $job_object->job[ 'rscdir' ] . $file == $object->getName() ) { //only in the folder and not in complete bucket
327
+ if ( $job_object->is_backup_archive( $file ) && $job_object->owns_backup_archive( $file ) == true )
328
  $backupfilelist[ strtotime( $object->getLastModified() ) ] = $object;
329
  }
330
  $files[ $filecounter ][ 'folder' ] = "RSC://" . $job_object->job[ 'rsccontainer' ] . "/" . dirname( $object->getName() ) . "/";
inc/class-destination-s3.php CHANGED
@@ -47,7 +47,6 @@ class BackWPup_Destination_S3 extends BackWPup_Destinations {
47
  <option value="google-storage-us" <?php selected( 'google-storage-us', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Google Storage: USA', 'backwpup' ); ?></option>
48
  <option value="google-storage-asia" <?php selected( 'google-storage-asia', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Google Storage: Asia', 'backwpup' ); ?></option>
49
  <option value="dreamhost" <?php selected( 'dreamhost', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Dream Host Cloud Storage', 'backwpup' ); ?></option>
50
- <option value="greenqloud" <?php selected( 'greenqloud', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'GreenQloud Storage Qloud', 'backwpup' ); ?></option>
51
  </select>
52
  </td>
53
  </tr>
@@ -121,6 +120,7 @@ class BackWPup_Destination_S3 extends BackWPup_Destinations {
121
  <input id="ids3maxbackups" name="s3maxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 's3maxbackups' ) ); ?>" class="small-text" />
122
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
123
  </label>
 
124
  <?php } else { ?>
125
  <label for="ids3syncnodelete">
126
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 's3syncnodelete' ), true ); ?> name="s3syncnodelete" id="ids3syncnodelete" />
@@ -182,6 +182,7 @@ class BackWPup_Destination_S3 extends BackWPup_Destinations {
182
  wp_die( -1 );
183
  }
184
  check_ajax_referer( 'backwpup_ajax_nonce' );
 
185
  $args[ 's3accesskey' ] = sanitize_text_field( $_POST[ 's3accesskey' ] );
186
  $args[ 's3secretkey' ] = sanitize_text_field( $_POST[ 's3secretkey' ] );
187
  $args[ 's3bucketselected' ] = sanitize_text_field( $_POST[ 's3bucketselected' ] );
@@ -285,8 +286,6 @@ class BackWPup_Destination_S3 extends BackWPup_Destinations {
285
  return 'https://storage.googleapis.com';
286
  case 'dreamhost':
287
  return 'https://objects-us-west-1.dream.io';
288
- case 'greenqloud':
289
- return 'http://s.greenqloud.com';
290
  default:
291
  return '';
292
  }
@@ -626,7 +625,7 @@ class BackWPup_Destination_S3 extends BackWPup_Destinations {
626
  foreach ( $objects as $object ) {
627
  $file = basename( $object[ 'Key' ] );
628
  $changetime = strtotime( $object[ 'LastModified' ] ) + ( get_option( 'gmt_offset' ) * 3600 );
629
- if ( $job_object->is_backup_archive( $file ) )
630
  $backupfilelist[ $changetime ] = $file;
631
  $files[ $filecounter ][ 'folder' ] = $this->get_s3_base_url( $job_object->job[ 's3region' ], $job_object->job[ 's3base_url' ] ). '/' .$job_object->job[ 's3bucket' ] . '/' . dirname( $object[ 'Key' ] );
632
  $files[ $filecounter ][ 'file' ] = $object[ 'Key' ];
47
  <option value="google-storage-us" <?php selected( 'google-storage-us', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Google Storage: USA', 'backwpup' ); ?></option>
48
  <option value="google-storage-asia" <?php selected( 'google-storage-asia', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Google Storage: Asia', 'backwpup' ); ?></option>
49
  <option value="dreamhost" <?php selected( 'dreamhost', BackWPup_Option::get( $jobid, 's3region' ), TRUE ) ?>><?php esc_html_e( 'Dream Host Cloud Storage', 'backwpup' ); ?></option>
 
50
  </select>
51
  </td>
52
  </tr>
120
  <input id="ids3maxbackups" name="s3maxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 's3maxbackups' ) ); ?>" class="small-text" />
121
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
122
  </label>
123
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
124
  <?php } else { ?>
125
  <label for="ids3syncnodelete">
126
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 's3syncnodelete' ), true ); ?> name="s3syncnodelete" id="ids3syncnodelete" />
182
  wp_die( -1 );
183
  }
184
  check_ajax_referer( 'backwpup_ajax_nonce' );
185
+ $args = array();
186
  $args[ 's3accesskey' ] = sanitize_text_field( $_POST[ 's3accesskey' ] );
187
  $args[ 's3secretkey' ] = sanitize_text_field( $_POST[ 's3secretkey' ] );
188
  $args[ 's3bucketselected' ] = sanitize_text_field( $_POST[ 's3bucketselected' ] );
286
  return 'https://storage.googleapis.com';
287
  case 'dreamhost':
288
  return 'https://objects-us-west-1.dream.io';
 
 
289
  default:
290
  return '';
291
  }
625
  foreach ( $objects as $object ) {
626
  $file = basename( $object[ 'Key' ] );
627
  $changetime = strtotime( $object[ 'LastModified' ] ) + ( get_option( 'gmt_offset' ) * 3600 );
628
+ if ( $job_object->is_backup_archive( $file ) && $job_object->owns_backup_archive( $file ) == true )
629
  $backupfilelist[ $changetime ] = $file;
630
  $files[ $filecounter ][ 'folder' ] = $this->get_s3_base_url( $job_object->job[ 's3region' ], $job_object->job[ 's3base_url' ] ). '/' .$job_object->job[ 's3bucket' ] . '/' . dirname( $object[ 'Key' ] );
631
  $files[ $filecounter ][ 'file' ] = $object[ 'Key' ];
inc/class-destination-sugarsync.php CHANGED
@@ -97,6 +97,7 @@ class BackWPup_Destination_SugarSync extends BackWPup_Destinations {
97
  <input id="idsugarmaxbackups" name="sugarmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'sugarmaxbackups' ) ); ?>" class="small-text" />
98
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
99
  </label>
 
100
  <?php } else { ?>
101
  <label for="idsugarsyncnodelete">
102
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'sugarsyncnodelete' ), true ); ?> name="sugarsyncnodelete" id="idsugarsyncnodelete" />
@@ -272,7 +273,7 @@ class BackWPup_Destination_SugarSync extends BackWPup_Destinations {
272
  if ( is_object( $getfiles ) ) {
273
  foreach ( $getfiles->file as $getfile ) {
274
  $getfile->displayName = utf8_decode( (string)$getfile->displayName );
275
- if ( $job_object->is_backup_archive( $getfile->displayName ) )
276
  $backupfilelist[ strtotime( (string)$getfile->lastModified ) ] = (string)$getfile->ref;
277
  $files[ $filecounter ][ 'folder' ] = 'https://' . (string)$user->nickname . '.sugarsync.com/' . $dir;
278
  $files[ $filecounter ][ 'file' ] = (string)$getfile->ref;
97
  <input id="idsugarmaxbackups" name="sugarmaxbackups" type="number" min="0" step="1" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'sugarmaxbackups' ) ); ?>" class="small-text" />
98
  &nbsp;<?php esc_html_e( 'Number of files to keep in folder.', 'backwpup' ); ?>
99
  </label>
100
+ <p><?php _e( '<strong>Warning</strong>: Files belonging to this job are now tracked. Old backup archives which are untracked will not be automatically deleted.', 'backwpup' ) ?></p>
101
  <?php } else { ?>
102
  <label for="idsugarsyncnodelete">
103
  <input class="checkbox" value="1" type="checkbox" <?php checked( BackWPup_Option::get( $jobid, 'sugarsyncnodelete' ), true ); ?> name="sugarsyncnodelete" id="idsugarsyncnodelete" />
273
  if ( is_object( $getfiles ) ) {
274
  foreach ( $getfiles->file as $getfile ) {
275
  $getfile->displayName = utf8_decode( (string)$getfile->displayName );
276
+ if ( $job_object->is_backup_archive( $getfile->displayName ) && $job_object->owns_backup_archive( $getfile->displayName ) == true )
277
  $backupfilelist[ strtotime( (string)$getfile->lastModified ) ] = (string)$getfile->ref;
278
  $files[ $filecounter ][ 'folder' ] = 'https://' . (string)$user->nickname . '.sugarsync.com/' . $dir;
279
  $files[ $filecounter ][ 'file' ] = (string)$getfile->ref;
inc/class-directory.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Wraps directory functions in PHP.
5
+ *
6
+ * @since 3.4.0
7
+ */
8
+ class BackWPup_Directory extends DirectoryIterator {
9
+
10
+ /**
11
+ * Creates the iterator.
12
+ *
13
+ * Fixes the path before calling the parent constructor.
14
+ *
15
+ * @param string $path
16
+ */
17
+ public function __construct( $path ) {
18
+ parent::__construct( BackWPup_Path_Fixer::fix_path( $path ) );
19
+ }
20
+
21
+ }
inc/class-file.php CHANGED
@@ -70,36 +70,20 @@ class BackWPup_File {
70
  * @param bool $deep went thrue suborders
71
  * @return int folder size in byte
72
  */
73
- public static function get_folder_size( $folder, $deep = TRUE ) {
74
-
75
  $files_size = 0;
76
 
77
- if ( ! is_readable( $folder ) )
78
  return $files_size;
 
79
 
80
- if ( $dir = opendir( $folder ) ) {
81
- while ( FALSE !== ( $file = readdir( $dir ) ) ) {
82
- if ( in_array( $file, array( '.', '..' ), true ) || is_link( $folder . '/' . $file ) ) {
83
- continue;
84
- }
85
- if ( $deep && is_dir( $folder . '/' . $file ) ) {
86
- $files_size = $files_size + self::get_folder_size( $folder . '/' . $file, TRUE );
87
- }
88
- elseif ( is_link( $folder . '/' . $file ) ) {
89
- continue;
90
- }
91
- elseif ( is_readable( $folder . '/' . $file ) ) {
92
- $file_size = filesize( $folder . '/' . $file );
93
- if ( empty( $file_size ) || ! is_int( $file_size ) ) {
94
- continue;
95
- }
96
- $files_size = $files_size + $file_size;
97
- }
98
- }
99
- closedir( $dir );
100
  }
101
 
102
- return $files_size;
103
  }
104
 
105
  /**
@@ -176,8 +160,15 @@ class BackWPup_File {
176
  $server_software = strtolower( $_SERVER[ 'SERVER_SOFTWARE' ] );
177
  //IIS
178
  if ( strstr( $server_software, 'microsoft-iis' ) ) {
179
- if ( ! file_exists( $folder . '/web.config' ) ) {
180
- file_put_contents( $folder . '/web.config', "<configuration>" . PHP_EOL . "\t<system.webServer>" . PHP_EOL . "\t\t<authorization>" . PHP_EOL . "\t\t\t<deny users=" * " />" . PHP_EOL . "\t\t</authorization>" . PHP_EOL . "\t</system.webServer>" . PHP_EOL . "</configuration>" );
 
 
 
 
 
 
 
181
  }
182
  } //Nginx
183
  elseif ( strstr( $server_software, 'nginx' ) ) {
70
  * @param bool $deep went thrue suborders
71
  * @return int folder size in byte
72
  */
73
+ public static function get_folder_size( $folder ) {
 
74
  $files_size = 0;
75
 
76
+ if ( ! is_readable( $folder ) ) {
77
  return $files_size;
78
+ }
79
 
80
+ $iterator = new RecursiveIteratorIterator( new BackWPup_Recursive_Directory( $folder, FilesystemIterator::SKIP_DOTS ) );
81
+
82
+ foreach ( $iterator as $file ) {
83
+ $files_size += $file->getSize();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
86
+ return $files_size;
87
  }
88
 
89
  /**
160
  $server_software = strtolower( $_SERVER[ 'SERVER_SOFTWARE' ] );
161
  //IIS
162
  if ( strstr( $server_software, 'microsoft-iis' ) ) {
163
+ if ( ! file_exists( $folder . '/Web.config' ) ) {
164
+ file_put_contents( $folder . '/Web.config',
165
+ "<configuration>" . PHP_EOL .
166
+ "\t<system.webServer>" . PHP_EOL .
167
+ "\t\t<authorization>" . PHP_EOL .
168
+ "\t\t\t<deny users=\"*\" />" . PHP_EOL .
169
+ "\t\t</authorization>" . PHP_EOL .
170
+ "\t</system.webServer>" . PHP_EOL .
171
+ "</configuration>" );
172
  }
173
  } //Nginx
174
  elseif ( strstr( $server_software, 'nginx' ) ) {
inc/class-job.php CHANGED
@@ -1,2552 +1,2580 @@
1
- <?php
2
-
3
- /**
4
- * Class in that the BackWPup job runs
5
- */
6
- final class BackWPup_Job {
7
-
8
- /**
9
- * @var array of the job settings
10
- */
11
- public $job = array();
12
-
13
- /**
14
- * @var int The timestamp when the job starts
15
- */
16
- public $start_time = 0;
17
-
18
- /**
19
- * @var string the logfile
20
- */
21
- public $logfile = '';
22
- /**
23
- * @var array for temp values
24
- */
25
- public $temp = array();
26
- /**
27
- * @var string Folder where is Backup files in
28
- */
29
- public $backup_folder = '';
30
- /**
31
- * @var string the name of the Backup archive file
32
- */
33
- public $backup_file = '';
34
- /**
35
- * @var int The size of the Backup archive file
36
- */
37
- public $backup_filesize = 0;
38
- /**
39
- * @var int PID of script
40
- */
41
- public $pid = 0;
42
- /**
43
- * @var float Timestamp of last update off .running file
44
- */
45
- public $timestamp_last_update = 0;
46
- /**
47
- * @var int Number of warnings
48
- */
49
- public $warnings = 0;
50
- /**
51
- * @var int Number of errors
52
- */
53
- public $errors = 0;
54
- /**
55
- * @var string the last log notice message
56
- */
57
- public $lastmsg = '';
58
- /**
59
- * @var string the last log error/waring message
60
- */
61
- public $lasterrormsg = '';
62
- /**
63
- * @var array of steps to do
64
- */
65
- public $steps_todo = array( 'CREATE' );
66
- /**
67
- * @var array of done steps
68
- */
69
- public $steps_done = array();
70
- /**
71
- * @var array of steps data
72
- */
73
- public $steps_data = array();
74
- /**
75
- * @var string working on step
76
- */
77
- public $step_working = 'CREATE';
78
- /**
79
- * @var int Number of sub steps must do in step
80
- */
81
- public $substeps_todo = 0;
82
- /**
83
- * @var int Number of sub steps done in step
84
- */
85
- public $substeps_done = 0;
86
- /**
87
- * @var int Percent of steps done
88
- */
89
- public $step_percent = 1;
90
- /**
91
- * @var int Percent of sub steps done
92
- */
93
- public $substep_percent = 1;
94
- /**
95
- * @var array of files to additional to backup
96
- */
97
- public $additional_files_to_backup = array();
98
- /**
99
- * @var array of files/folder to exclude from backup
100
- */
101
- public $exclude_from_backup = array();
102
- /**
103
- * @var int count of affected files
104
- */
105
- public $count_files = 0;
106
- /**
107
- * @var int count of affected file sizes
108
- */
109
- public $count_files_size = 0;
110
- /**
111
- * @var int count of affected folders
112
- */
113
- public $count_folder = 0;
114
- /**
115
- * If job aborted from user
116
- * @var bool
117
- */
118
- public $user_abort = false;
119
- /**
120
- * A uniqid ID uniqid('', true); to identify process
121
- * @var string
122
- */
123
- public $uniqid = '';
124
- /**
125
- * @var float Timestamp of script start
126
- */
127
- private $timestamp_script_start = 0;
128
- /**
129
- * Stores data that will only used in a single run
130
- * @var array
131
- */
132
- private $run = array();
133
- /**
134
- * @var string logging level (normal|normal_untranslated|debug|debug_untranslated)
135
- */
136
- private $log_level = 'normal';
137
-
138
- /**
139
- * @var int Signal of signal handler
140
- */
141
- private $signal = 0;
142
-
143
- /**
144
- *
145
- */
146
- public static function start_http( $starttype, $jobid = 0 ) {
147
-
148
- //load text domain
149
- $log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
150
- if ( strstr( $log_level, 'translated' ) ) {
151
- BackWPup::load_text_domain();
152
- } else {
153
- add_filter( 'override_load_textdomain', '__return_true' );
154
- $GLOBALS['l10n'] = array();
155
- }
156
-
157
- if ( $starttype !== 'restart' ) {
158
-
159
- //check job id exists
160
- if ( $jobid !== BackWPup_Option::get( $jobid, 'jobid' ) ) {
161
- return false;
162
- }
163
-
164
- //check folders
165
- $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
166
- $folder_message_log = BackWPup_File::check_folder( BackWPup_File::get_absolute_path( $log_folder ) );
167
- $folder_message_temp = BackWPup_File::check_folder( BackWPup::get_plugin_data( 'TEMP' ), true );
168
- if ( ! empty( $folder_message_log ) || ! empty( $folder_message_temp ) ) {
169
- BackWPup_Admin::message( $folder_message_log, true );
170
- BackWPup_Admin::message( $folder_message_temp, true );
171
- return false;
172
- }
173
- }
174
-
175
- // redirect
176
- if ( $starttype === 'runnowalt' ) {
177
- ob_start();
178
- wp_redirect( add_query_arg( array( 'page' => 'backwpupjobs' ), network_admin_url( 'admin.php' ) ) );
179
- echo ' ';
180
- flush();
181
- if ( $level = ob_get_level() ) {
182
- for ( $i = 0; $i < $level; $i ++ ) {
183
- ob_end_clean();
184
- }
185
- }
186
- }
187
-
188
- // Should be preventing doubled running job's on http requests
189
- $random = mt_rand( 10, 90 ) * 10000;
190
- usleep( $random );
191
-
192
- //check running job
193
- $backwpup_job_object = self::get_working_data();
194
- //start class
195
- if ( ! $backwpup_job_object && in_array( $starttype, array( 'runnow', 'runnowalt', 'runext', 'cronrun' ), true ) && $jobid ) {
196
- //schedule restart event
197
- wp_schedule_single_event( time() + 60, 'backwpup_cron', array( 'id' => 'restart' ) );
198
- //start job
199
- $backwpup_job_object = new self();
200
- $backwpup_job_object->create( $starttype, $jobid );
201
- }
202
- if ( $backwpup_job_object ) {
203
- $backwpup_job_object->run();
204
- }
205
- }
206
-
207
- /**
208
- *
209
- * Get data off a working job
210
- *
211
- * @return bool|object BackWPup_Job Object or Bool if file not exits
212
- */
213
- public static function get_working_data() {
214
-
215
- if ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
216
- clearstatcache( true, BackWPup::get_plugin_data( 'running_file' ) );
217
- } else {
218
- clearstatcache();
219
- }
220
-
221
- if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
222
- return false;
223
- }
224
-
225
- $file_data = file_get_contents( BackWPup::get_plugin_data( 'running_file' ), false, null, 8 );
226
- if ( empty( $file_data ) ) {
227
- return false;
228
- }
229
-
230
- if ( $job_object = unserialize( $file_data ) ) {
231
- if ( $job_object instanceof BackWPup_Job ) {
232
- return $job_object;
233
- }
234
- }
235
-
236
- return false;
237
-
238
- }
239
-
240
- /**
241
- *
242
- * This starts or restarts the job working
243
- *
244
- * @param string $start_type Start types are 'runnow', 'runnowalt', 'cronrun', 'runext', 'runcli'
245
- * @param array|int $job_id The id of job of a job to start
246
- */
247
- private function create( $start_type, $job_id = 0 ) {
248
- global $wpdb;
249
- /* @var wpdb $wpdb */
250
-
251
- //check startype
252
- if ( ! in_array( $start_type, array( 'runnow', 'runnowalt', 'cronrun', 'runext', 'runcli' ), true ) ) {
253
- return;
254
- }
255
-
256
- if ( $job_id ) {
257
- $this->job = BackWPup_Option::get_job( $job_id );
258
- } else {
259
- return;
260
- }
261
-
262
- $this->start_time = current_time( 'timestamp' );
263
- $this->lastmsg = __( 'Starting job', 'backwpup' );
264
- //set Logfile
265
- $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
266
- $log_folder = BackWPup_File::get_absolute_path( $log_folder );
267
- $this->logfile = $log_folder . 'backwpup_log_' . BackWPup::get_plugin_data( 'hash' ) . '_' . date( 'Y-m-d_H-i-s', current_time( 'timestamp' ) ) . '.html';
268
- //write settings to job
269
- BackWPup_Option::update( $this->job['jobid'], 'lastrun', $this->start_time );
270
- BackWPup_Option::update( $this->job['jobid'], 'logfile', $this->logfile ); //Set current logfile
271
- BackWPup_Option::update( $this->job['jobid'], 'lastbackupdownloadurl', '' );
272
- //Set needed job values
273
- $this->timestamp_last_update = microtime( true );
274
- $this->exclude_from_backup = explode( ',', trim( $this->job['fileexclude'] ) );
275
- $this->exclude_from_backup = array_unique( $this->exclude_from_backup );
276
- //setup job steps
277
- $this->steps_data['CREATE']['CALLBACK'] = '';
278
- $this->steps_data['CREATE']['NAME'] = __( 'Job Start', 'backwpup' );
279
- $this->steps_data['CREATE']['STEP_TRY'] = 0;
280
- //ADD Job types file
281
- /* @var $job_type_class BackWPup_JobTypes */
282
- $job_need_dest = false;
283
- if ( $job_types = BackWPup::get_job_types() ) {
284
- foreach ( $job_types as $id => $job_type_class ) {
285
- if ( in_array( $id, $this->job['type'], true ) && $job_type_class->creates_file() ) {
286
- $this->steps_todo[] = 'JOB_' . $id;
287
- $this->steps_data[ 'JOB_' . $id ]['NAME'] = $job_type_class->info['description'];
288
- $this->steps_data[ 'JOB_' . $id ]['STEP_TRY'] = 0;
289
- $this->steps_data[ 'JOB_' . $id ]['SAVE_STEP_TRY'] = 0;
290
- $job_need_dest = true;
291
- }
292
- }
293
- }
294
- //add destinations and create archive if a job where files to backup
295
- if ( $job_need_dest ) {
296
- //Create manifest file
297
- $this->steps_todo[] = 'CREATE_MANIFEST';
298
- $this->steps_data['CREATE_MANIFEST']['NAME'] = __( 'Creates manifest file', 'backwpup' );
299
- $this->steps_data['CREATE_MANIFEST']['STEP_TRY'] = 0;
300
- $this->steps_data['CREATE_MANIFEST']['SAVE_STEP_TRY'] = 0;
301
- //Add archive creation and backup filename on backup type archive
302
- if ( $this->job['backuptype'] == 'archive' ) {
303
- //get Backup folder if destination folder set
304
- if ( in_array( 'FOLDER', $this->job['destinations'], true ) ) {
305
- $this->backup_folder = $this->job['backupdir'];
306
- //check backup folder
307
- if ( ! empty( $this->backup_folder ) ) {
308
- $this->backup_folder = BackWPup_File::get_absolute_path( $this->backup_folder );
309
- $this->job['backupdir'] = $this->backup_folder;
310
- }
311
- }
312
- //set temp folder to backup folder if not set because we need one
313
- if ( ! $this->backup_folder || $this->backup_folder == '/' ) {
314
- $this->backup_folder = BackWPup::get_plugin_data( 'TEMP' );
315
- }
316
- //Create backup archive full file name
317
- $this->backup_file = $this->generate_filename( $this->job['archivename'], $this->job['archiveformat'] );
318
- //add archive create
319
- $this->steps_todo[] = 'CREATE_ARCHIVE';
320
- $this->steps_data['CREATE_ARCHIVE']['NAME'] = __( 'Creates archive', 'backwpup' );
321
- $this->steps_data['CREATE_ARCHIVE']['STEP_TRY'] = 0;
322
- $this->steps_data['CREATE_ARCHIVE']['SAVE_STEP_TRY'] = 0;
323
- }
324
- //ADD Destinations
325
- /* @var BackWPup_Destinations $dest_class */
326
- foreach ( BackWPup::get_registered_destinations() as $id => $dest ) {
327
- if ( ! in_array( $id, $this->job['destinations'], true ) || empty( $dest['class'] ) ) {
328
- continue;
329
- }
330
- $dest_class = BackWPup::get_destination( $id );
331
- if ( $dest_class->can_run( $this->job ) ) {
332
- if ( $this->job['backuptype'] == 'sync' ) {
333
- if ( $dest['can_sync'] ) {
334
- $this->steps_todo[] = 'DEST_SYNC_' . $id;
335
- $this->steps_data[ 'DEST_SYNC_' . $id ]['NAME'] = $dest['info']['description'];
336
- $this->steps_data[ 'DEST_SYNC_' . $id ]['STEP_TRY'] = 0;
337
- $this->steps_data[ 'DEST_SYNC_' . $id ]['SAVE_STEP_TRY'] = 0;
338
- }
339
- } else {
340
- $this->steps_todo[] = 'DEST_' . $id;
341
- $this->steps_data[ 'DEST_' . $id ]['NAME'] = $dest['info']['description'];
342
- $this->steps_data[ 'DEST_' . $id ]['STEP_TRY'] = 0;
343
- $this->steps_data[ 'DEST_' . $id ]['SAVE_STEP_TRY'] = 0;
344
- }
345
- }
346
- }
347
- }
348
- //ADD Job type no file
349
- if ( $job_types = BackWPup::get_job_types() ) {
350
- foreach ( $job_types as $id => $job_type_class ) {
351
- if ( in_array( $id, $this->job['type'], true ) && ! $job_type_class->creates_file() ) {
352
- $this->steps_todo[] = 'JOB_' . $id;
353
- $this->steps_data[ 'JOB_' . $id ]['NAME'] = $job_type_class->info['description'];
354
- $this->steps_data[ 'JOB_' . $id ]['STEP_TRY'] = 0;
355
- $this->steps_data[ 'JOB_' . $id ]['SAVE_STEP_TRY'] = 0;
356
- }
357
- }
358
- }
359
- $this->steps_todo[] = 'END';
360
- $this->steps_data['END']['NAME'] = __( 'End of Job', 'backwpup' );
361
- $this->steps_data['END']['STEP_TRY'] = 1;
362
- //must write working data
363
- $this->write_running_file();
364
-
365
- //set log level
366
- $this->log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
367
- if ( ! in_array( $this->log_level, array( 'normal_translated', 'normal', 'debug_translated', 'debug' ), true ) ) {
368
- $this->log_level = 'normal_translated';
369
- }
370
- //create log file
371
- $head = '';
372
- $info = '';
373
- $head .= "<!DOCTYPE html>" . PHP_EOL;
374
- $head .= "<html lang=\"" . str_replace( '_', '-', get_locale() ) . "\">" . PHP_EOL;
375
- $head .= "<head>" . PHP_EOL;
376
- $head .= "<meta charset=\"" . get_bloginfo( 'charset' ) . "\" />" . PHP_EOL;
377
- $head .= "<title>" . sprintf( __( 'BackWPup log for %1$s from %2$s at %3$s', 'backwpup' ), $this->job['name'], date_i18n( get_option( 'date_format' ) ), date_i18n( get_option( 'time_format' ) ) ) . "</title>" . PHP_EOL;
378
- $head .= "<meta name=\"robots\" content=\"noindex, nofollow\" />" . PHP_EOL;
379
- $head .= "<meta name=\"copyright\" content=\"Copyright &copy; 2012 - " . date( 'Y' ) . " Inpsyde GmbH\" />" . PHP_EOL;
380
- $head .= "<meta name=\"author\" content=\"Inpsyde GmbH\" />" . PHP_EOL;
381
- $head .= "<meta name=\"generator\" content=\"BackWPup " . BackWPup::get_plugin_data( 'Version' ) . "\" />" . PHP_EOL;
382
- $head .= "<meta http-equiv=\"cache-control\" content=\"no-cache\" />" . PHP_EOL;
383
- $head .= "<meta http-equiv=\"pragma\" content=\"no-cache\" />" . PHP_EOL;
384
- $head .= "<meta name=\"date\" content=\"" . date( 'c' ) . "\" />" . PHP_EOL;
385
- $head .= str_pad( '<meta name="backwpup_errors" content="0" />', 100 ) . PHP_EOL;
386
- $head .= str_pad( '<meta name="backwpup_warnings" content="0" />', 100 ) . PHP_EOL;
387
- $head .= "<meta name=\"backwpup_jobid\" content=\"" . $this->job['jobid'] . "\" />" . PHP_EOL;
388
- $head .= "<meta name=\"backwpup_jobname\" content=\"" . esc_attr( $this->job['name'] ) . "\" />" . PHP_EOL;
389
- $head .= "<meta name=\"backwpup_jobtype\" content=\"" . implode( '+', $this->job['type'] ) . "\" />" . PHP_EOL;
390
- $head .= str_pad( '<meta name="backwpup_backupfilesize" content="0" />', 100 ) . PHP_EOL;
391
- $head .= str_pad( '<meta name="backwpup_jobruntime" content="0" />', 100 ) . PHP_EOL;
392
- $head .= '</head>' . PHP_EOL;
393
- $head .= '<body style="margin:0;padding:3px;font-family:monospace;font-size:12px;line-height:15px;background-color:black;color:#c0c0c0;white-space:nowrap;">' . PHP_EOL;
394
- $info .= sprintf( _x( '[INFO] %1$s %2$s; A project of Inpsyde GmbH', 'Plugin name; Plugin Version; plugin url', 'backwpup' ), BackWPup::get_plugin_data( 'name' ), BackWPup::get_plugin_data( 'Version' ), __( 'http://backwpup.com', 'backwpup' ) ) . '<br />' . PHP_EOL;
395
- $info .= sprintf( _x( '[INFO] WordPress %1$s on %2$s', 'WordPress Version; Blog url', 'backwpup' ), BackWPup::get_plugin_data( 'wp_version' ), esc_attr( site_url( '/' ) ) ) . '<br />' . PHP_EOL;
396
- $level = __( 'Normal', 'backwpup' );
397
- $translated = '';
398
- if ( $this->is_debug() ) {
399
- $level = __( 'Debug', 'backwpup' );
400
- }
401
- if ( is_textdomain_loaded( 'backwpup' ) ) {
402
- $translated = __( '(translated)', 'backwpup' );
403
- }
404
- $info .= sprintf( __( '[INFO] Log Level: %1$s %2$s', 'backwpup' ), $level, $translated ) . '<br />' . PHP_EOL;
405
- $job_name = esc_attr( $this->job['name'] );
406
- if ( $this->is_debug() ) {
407
- $job_name .= '; ' . implode( '+', $this->job['type'] );
408
- }
409
- $info .= sprintf( __( '[INFO] BackWPup job: %1$s', 'backwpup' ), $job_name ) . '<br />' . PHP_EOL;
410
- if ( $this->is_debug() ) {
411
- $current_user = wp_get_current_user();
412
- $info .= sprintf( __( '[INFO] Runs with user: %1$s (%2$d) ', 'backwpup' ), $current_user->user_login, $current_user->ID ) . '<br />' . PHP_EOL;
413
- }
414
- if ( $this->job['activetype'] === 'wpcron' ) {
415
- //check next run
416
- $cron_next = wp_next_scheduled( 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
417
- if ( ! $cron_next || $cron_next < time() ) {
418
- wp_unschedule_event( $cron_next, 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
419
- $cron_next = BackWPup_Cron::cron_next( $this->job['cron'] );
420
- wp_schedule_single_event( $cron_next, 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
421
- $cron_next = wp_next_scheduled( 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
422
- }
423
- //output scheduling
424
- if ( $this->is_debug() ) {
425
- if ( ! $cron_next ) {
426
- $cron_next = __( 'Not scheduled!', 'backwpup' );
427
- } else {
428
- $cron_next = date_i18n( 'D, j M Y @ H:i', $cron_next + ( get_option( 'gmt_offset' ) * 3600 ), true );
429
- }
430
- $info .= sprintf( __( '[INFO] Cron: %s; Next: %s ', 'backwpup' ), $this->job['cron'], $cron_next ) . '<br />' . PHP_EOL;
431
- }
432
- } elseif ( $this->job['activetype'] == 'link' && $this->is_debug() ) {
433
- $info .= __( '[INFO] BackWPup job start with link is active', 'backwpup' ) . '<br />' . PHP_EOL;
434
- } elseif ( $this->job['activetype'] == 'easycron' && $this->is_debug() ) {
435
- $info .= __( '[INFO] BackWPup job start with EasyCron.com', 'backwpup' ) . '<br />' . PHP_EOL;
436
- //output scheduling
437
- if ( $this->is_debug() ) {
438
- $cron_next = BackWPup_Cron::cron_next( $this->job['cron'] );
439
- $cron_next = date_i18n( 'D, j M Y @ H:i', $cron_next + ( get_option( 'gmt_offset' ) * 3600 ), true );
440
- $info .= sprintf( __( '[INFO] Cron: %s; Next: %s ', 'backwpup' ), $this->job['cron'], $cron_next ) . '<br />' . PHP_EOL;
441
- }
442
- } elseif ( $this->is_debug() ) {
443
- $info .= __( '[INFO] BackWPup no automatic job start configured', 'backwpup' ) . '<br />' . PHP_EOL;
444
- }
445
- if ( $this->is_debug() ) {
446
- if ( $start_type == 'cronrun' ) {
447
- $info .= __( '[INFO] BackWPup job started from wp-cron', 'backwpup' ) . '<br />' . PHP_EOL;
448
- } elseif ( $start_type == 'runnow' || $start_type == 'runnowalt' ) {
449
- $info .= __( '[INFO] BackWPup job started manually', 'backwpup' ) . '<br />' . PHP_EOL;
450
- } elseif ( $start_type == 'runext' ) {
451
- $info .= __( '[INFO] BackWPup job started from external url', 'backwpup' ) . '<br />' . PHP_EOL;
452
- } elseif ( $start_type == 'runcli' ) {
453
- $info .= __( '[INFO] BackWPup job started form commandline interface', 'backwpup' ) . '<br />' . PHP_EOL;
454
- }
455
- $bit = '';
456
- if ( PHP_INT_SIZE === 4 ) {
457
- $bit = ' (32bit)';
458
- }
459
- if ( PHP_INT_SIZE === 8 ) {
460
- $bit = ' (64bit)';
461
- }
462
- $info .= __( '[INFO] PHP ver.:', 'backwpup' ) . ' ' . PHP_VERSION . $bit . '; ' . PHP_SAPI . '; ' . PHP_OS . '<br />' . PHP_EOL;
463
- $info .= sprintf( __( '[INFO] Maximum PHP script execution time is %1$d seconds', 'backwpup' ), ini_get( 'max_execution_time' ) ) . '<br />' . PHP_EOL;
464
- if ( php_sapi_name() != 'cli' ) {
465
- $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
466
- if ( ! empty( $job_max_execution_time ) ) {
467
- $info .= sprintf( __( '[INFO] Script restart time is configured to %1$d seconds', 'backwpup' ), $job_max_execution_time ) . '<br />' . PHP_EOL;
468
- }
469
- }
470
- $info .= sprintf( __( '[INFO] MySQL ver.: %s', 'backwpup' ), $wpdb->get_var( "SELECT VERSION() AS version" ) ) . '<br />' . PHP_EOL;
471
- if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
472
- $info .= sprintf( __( '[INFO] Web Server: %s', 'backwpup' ), $_SERVER['SERVER_SOFTWARE'] ) . '<br />' . PHP_EOL;
473
- }
474
- if ( function_exists( 'curl_init' ) ) {
475
- $curlversion = curl_version();
476
- $info .= sprintf( __( '[INFO] curl ver.: %1$s; %2$s', 'backwpup' ), $curlversion['version'], $curlversion['ssl_version'] ) . '<br />' . PHP_EOL;
477
- }
478
- $info .= sprintf( __( '[INFO] Temp folder is: %s', 'backwpup' ), BackWPup::get_plugin_data( 'TEMP' ) ) . '<br />' . PHP_EOL;
479
- }
480
- if ( $this->is_debug() ) {
481
- $logfile = $this->logfile;
482
- } else {
483
- $logfile = basename( $this->logfile );
484
- }
485
- $info .= sprintf( __( '[INFO] Logfile is: %s', 'backwpup' ), $logfile ) . '<br />' . PHP_EOL;
486
- if ( ! empty( $this->backup_file ) && $this->job['backuptype'] === 'archive' ) {
487
- if ( $this->is_debug() ) {
488
- $backupfile = $this->backup_folder . $this->backup_file;
489
- } else {
490
- $backupfile = $this->backup_file;
491
- }
492
- $info .= sprintf( __( '[INFO] Backup file is: %s', 'backwpup' ), $backupfile ) . '<br />' . PHP_EOL;
493
- } else {
494
- $info .= sprintf( __( '[INFO] Backup type is: %s', 'backwpup' ), $this->job['backuptype'] ) . '<br />' . PHP_EOL;
495
- }
496
- //output info on cli
497
- if ( php_sapi_name() == 'cli' && defined( 'STDOUT' ) ) {
498
- fwrite( STDOUT, strip_tags( $info ) );
499
- }
500
- if ( ! file_put_contents( $this->logfile, $head . $info, FILE_APPEND ) ) {
501
- $this->logfile = '';
502
- $this->log( __( 'Could not write log file', 'backwpup' ), E_USER_ERROR );
503
- }
504
- //test for destinations
505
- if ( $job_need_dest ) {
506
- $desttest = false;
507
- foreach ( $this->steps_todo as $deststeptest ) {
508
- if ( substr( $deststeptest, 0, 5 ) == 'DEST_' ) {
509
- $desttest = true;
510
- break;
511
- }
512
- }
513
- if ( ! $desttest ) {
514
- $this->log( __( 'No destination correctly defined for backup! Please correct job settings.', 'backwpup' ), E_USER_ERROR );
515
- $this->steps_todo = array( 'END' );
516
- }
517
- }
518
- //test backup folder
519
- if ( ! empty( $this->backup_folder ) ) {
520
- $folder_message = BackWPup_File::check_folder( $this->backup_folder, true );
521
- if ( ! empty( $folder_message ) ) {
522
- $this->log( $folder_message, E_USER_ERROR );
523
- $this->steps_todo = array( 'END' );
524
- }
525
- }
526
-
527
- //Set start as done
528
- $this->steps_done[] = 'CREATE';
529
- }
530
-
531
- /**
532
- * @param $name
533
- * @param string $suffix
534
- * @param bool $delete_temp_file
535
- *
536
- * @return string
537
- */
538
- public function generate_filename( $name, $suffix = '', $delete_temp_file = true ) {
539
-
540
- $local_time = current_time( 'timestamp' );
541
-
542
- $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
543
- $datevalues = array(
544
- date( 'd', $local_time ),
545
- date( 'j', $local_time ),
546
- date( 'm', $local_time ),
547
- date( 'n', $local_time ),
548
- date( 'Y', $local_time ),
549
- date( 'y', $local_time ),
550
- date( 'a', $local_time ),
551
- date( 'A', $local_time ),
552
- date( 'B', $local_time ),
553
- date( 'g', $local_time ),
554
- date( 'G', $local_time ),
555
- date( 'h', $local_time ),
556
- date( 'H', $local_time ),
557
- date( 'i', $local_time ),
558
- date( 's', $local_time )
559
- );
560
-
561
- if ( $suffix ) {
562
- $suffix = '.' . trim( $suffix, '. ' );
563
- }
564
-
565
- $name = str_replace( $datevars, $datevalues, self::sanitize_file_name( $name ) );
566
- $name .= $suffix;
567
- if ( $delete_temp_file && is_writeable( BackWPup::get_plugin_data( 'TEMP' ) . $name ) && ! is_dir( BackWPup::get_plugin_data( 'TEMP' ) . $name ) && ! is_link( BackWPup::get_plugin_data( 'TEMP' ) . $name ) ) {
568
- unlink( BackWPup::get_plugin_data( 'TEMP' ) . $name );
569
- }
570
-
571
- return $name;
572
- }
573
-
574
- /**
575
- * Sanitizes a filename, replacing whitespace with underscores.
576
- *
577
- * @param $filename
578
- *
579
- * @return mixed
580
- */
581
- public static function sanitize_file_name( $filename ) {
582
-
583
- $filename = trim( $filename );
584
-
585
- $special_chars = array(
586
- "?",
587
- "[",
588
- "]",
589
- "/",
590
- "\\",
591
- "=",
592
- "<",
593
- ">",
594
- ":",
595
- ";",
596
- ",",
597
- "'",
598
- "\"",
599
- "&",
600
- "$",
601
- "#",
602
- "*",
603
- "(",
604
- ")",
605
- "|",
606
- "~",
607
- "`",
608
- "!",
609
- "{",
610
- "}",
611
- chr( 0 )
612
- );
613
-
614
- $filename = str_replace( $special_chars, '', $filename );
615
-
616
- $filename = str_replace( array( ' ', '%20', '+' ), '_', $filename );
617
- $filename = str_replace( array( "\n", "\t", "\r" ), '-', $filename );
618
- $filename = trim( $filename, '.-_' );
619
-
620
- return $filename;
621
- }
622
-
623
- private function write_running_file() {
624
-
625
- $clone = clone $this;
626
- $data = '<?php //' . serialize( $clone );
627
-
628
- $write = file_put_contents( BackWPup::get_plugin_data( 'running_file' ), $data );
629
- if ( ! $write || $write < strlen( $data ) ) {
630
- unlink( BackWPup::get_plugin_data( 'running_file' ) );
631
- $this->log( __( 'Cannot write progress to working file. Job will be aborted.', 'backwpup' ), E_USER_ERROR );
632
- }
633
- }
634
-
635
- /**
636
- * Write messages to log file
637
- *
638
- * @param string $message the error message
639
- * @param int $type the error number (E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE, ...)
640
- * @param string $file the full path of file with error (__FILE__)
641
- * @param int $line the line in that is the error (__LINE__)
642
- *
643
- * @return bool true
644
- */
645
- public function log( $message, $type = E_USER_NOTICE, $file = '', $line = 0 ) {
646
-
647
- // if error has been suppressed with an @
648
- if ( error_reporting() == 0 ) {
649
- return true;
650
- }
651
-
652
- //if first the type an second the message switch it on user errors
653
- if ( ! is_int( $type ) && is_int( $message ) && in_array( $message, array(
654
- 1,
655
- 2,
656
- 4,
657
- 8,
658
- 16,
659
- 32,
660
- 64,
661
- 128,
662
- 256,
663
- 512,
664
- 1024,
665
- 2048,
666
- 4096,
667
- 8192,
668
- 16384
669
- ), true )
670
- ) {
671
- $temp = $message;
672
- $message = $type;
673
- $type = $temp;
674
- }
675
-
676
- //json message if array or object
677
- if ( is_array( $message ) || is_object( $message ) ) {
678
- $message = json_encode( $message );
679
- }
680
-
681
- //if not set line and file get it
682
- if ( $this->is_debug() ) {
683
- if ( empty( $file ) || empty( $line ) ) {
684
- $debug_info = debug_backtrace();
685
- $file = $debug_info[0]['file'];
686
- $line = $debug_info[0]['line'];
687
- }
688
- }
689
-
690
- $error = false;
691
- $warning = false;
692
-
693
- switch ( $type ) {
694
- case E_NOTICE:
695
- case E_USER_NOTICE:
696
- break;
697
- case E_WARNING:
698
- case E_CORE_WARNING:
699
- case E_COMPILE_WARNING:
700
- case E_USER_WARNING:
701
- $this->warnings ++;
702
- $warning = true;
703
- $message = __( 'WARNING:', 'backwpup' ) . ' ' . $message;
704
- break;
705
- case E_ERROR:
706
- case E_PARSE:
707
- case E_CORE_ERROR:
708
- case E_COMPILE_ERROR:
709
- case E_USER_ERROR:
710
- $this->errors ++;
711
- $error = true;
712
- $message = __( 'ERROR:', 'backwpup' ) . ' ' . $message;
713
- break;
714
- case 8192: //E_DEPRECATED comes with php 5.3
715
- case 16384: //E_USER_DEPRECATED comes with php 5.3
716
- $message = __( 'DEPRECATED:', 'backwpup' ) . ' ' . $message;
717
- break;
718
- case E_STRICT:
719
- $message = __( 'STRICT NOTICE:', 'backwpup' ) . ' ' . $message;
720
- break;
721
- case E_RECOVERABLE_ERROR:
722
- $this->errors ++;
723
- $error = true;
724
- $message = __( 'RECOVERABLE ERROR:', 'backwpup' ) . ' ' . $message;
725
- break;
726
- default:
727
- $message = $type . ': ' . $message;
728
- break;
729
- }
730
-
731
- $in_file = $this->get_destination_path_replacement( $file );
732
-
733
- //print message to cli
734
- if ( defined( 'WP_CLI' ) && WP_CLI ) {
735
- $output_message = str_replace( array( '&hellip;', '&#160;' ), array( '...', ' ' ), esc_html( $message ) );
736
- if ( ! call_user_func( array( '\cli\Shell', 'isPiped' ) ) ) {
737
- if ( $error ) {
738
- $output_message = '%r' . $output_message . '%n';
739
- }
740
- if ( $warning ) {
741
- $output_message = '%y' . $output_message . '%n';
742
- }
743
- $output_message = call_user_func( array( '\cli\Colors', 'colorize' ), $output_message, true );
744
- }
745
- WP_CLI::line( $output_message );
746
- } elseif ( php_sapi_name() == 'cli' && defined( 'STDOUT' ) ) {
747
- $output_message = str_replace( array( '&hellip;', '&#160;' ), array( '...', ' ' ), esc_html( $message ) ) . PHP_EOL;
748
- fwrite( STDOUT, $output_message );
749
- }
750
-
751
- //timestamp for log file
752
- $debug_info = '';
753
- if ( $this->is_debug() ) {
754
- $debug_info = ' title="[Type: ' . $type . '|Line: ' . $line . '|File: ' . $in_file . '|Mem: ' . size_format( @memory_get_usage( true ), 2 ) . '|Mem Max: ' . size_format( @memory_get_peak_usage( true ), 2 ) . '|Mem Limit: ' . ini_get( 'memory_limit' ) . '|PID: ' . self::get_pid() . ' | UniqID: ' . $this->uniqid . '|Query\'s: ' . get_num_queries() . ']"';
755
- }
756
- $timestamp = '<span datetime="' . date( 'c' ) . '" ' . $debug_info . '>[' . date( 'd-M-Y H:i:s', current_time( 'timestamp' ) ) . ']</span> ';
757
-
758
- //set last Message
759
- if ( $error ) {
760
- $output_message = '<span style="background-color:#ff6766;color:black;padding:0 2px;">' . esc_html( $message ) . '</span>';
761
- $this->lasterrormsg = $output_message;
762
- }
763
- elseif ( $warning ) {
764
- $output_message = '<span style="background-color:#ffc766;color:black;padding:0 2px;">' . esc_html( $message ) . '</span>';
765
- $this->lasterrormsg = $output_message;
766
- }
767
- else {
768
- $output_message = esc_html( $message );
769
- $this->lastmsg = $output_message;
770
- }
771
- //write log file
772
- if ( $this->logfile ) {
773
- if ( ! file_put_contents( $this->logfile, $timestamp . $output_message . '<br />' . PHP_EOL, FILE_APPEND ) ) {
774
- $this->logfile = '';
775
- restore_error_handler();
776
- trigger_error( esc_html( $message ), $type );
777
- }
778
-
779
- //write new log header
780
- if ( ( $error || $warning ) && $this->logfile ) {
781
- if ( $fd = fopen( $this->logfile, 'r+' ) ) {
782
- $file_pos = ftell( $fd );
783
- while ( ! feof( $fd ) ) {
784
- $line = fgets( $fd );
785
- if ( $error && stripos( $line, '<meta name="backwpup_errors" content="' ) !== false ) {
786
- fseek( $fd, $file_pos );
787
- fwrite( $fd, str_pad( '<meta name="backwpup_errors" content="' . $this->errors . '" />', 100 ) . PHP_EOL );
788
- break;
789
- }
790
- if ( $warning && stripos( $line, '<meta name="backwpup_warnings" content="' ) !== false ) {
791
- fseek( $fd, $file_pos );
792
- fwrite( $fd, str_pad( '<meta name="backwpup_warnings" content="' . $this->warnings . '" />', 100 ) . PHP_EOL );
793
- break;
794
- }
795
- $file_pos = ftell( $fd );
796
- }
797
- fclose( $fd );
798
- }
799
- }
800
- }
801
-
802
- //write working data
803
- $this->update_working_data( $error || $warning );
804
-
805
- //true for no more php error handling.
806
- return true;
807
- }
808
-
809
- /**
810
- * Is debug log active
811
- *
812
- * @return bool
813
- */
814
- public function is_debug() {
815
-
816
- return strstr( $this->log_level, 'debug' ) ? true : false;
817
- }
818
-
819
- /**
820
- * Change path of a given path
821
- * for better storing in archives or on sync destinations
822
- *
823
- * @param $path string path to change to wp default path
824
- *
825
- * @return string
826
- */
827
- public function get_destination_path_replacement( $path ) {
828
-
829
- $path = str_replace( '\\', '/', $path );
830
-
831
- $abs_path = realpath( ABSPATH );
832
- if ( $this->job['backupabsfolderup'] ) {
833
- $abs_path = dirname( $abs_path );
834
- }
835
-
836
- $abs_path = trailingslashit( str_replace( '\\', '/', $abs_path ) );
837
-
838
- $path = str_replace( $abs_path, '/', $path );
839
-
840
- return $path;
841
- }
842
-
843
- /**
844
- * Get the Process id of working script
845
- *
846
- * @return int
847
- */
848
- private static function get_pid() {
849
-
850
- if ( function_exists( 'posix_getpid' ) ) {
851
-
852
- return posix_getpid();
853
- } elseif ( function_exists( 'getmypid' ) ) {
854
-
855
- return getmypid();
856
- }
857
-
858
- return - 1;
859
- }
860
-
861
- /**
862
- *
863
- * Write the Working data to display the process or that i can executes again
864
- * The write will only done every second
865
- *
866
- * @param bool $must
867
- */
868
- public function update_working_data( $must = false ) {
869
- global $wpdb;
870
-
871
- //to reduce server load
872
- if ( get_site_option( 'backwpup_cfg_jobwaittimems' ) > 0 && get_site_option( 'backwpup_cfg_jobwaittimems' ) <= 500000 ) {
873
- usleep( get_site_option( 'backwpup_cfg_jobwaittimems' ) );
874
- }
875
-
876
- //check free memory
877
- $this->need_free_memory( '10M' );
878
-
879
- //only run every 1 sec.
880
- $time_to_update = microtime( true ) - $this->timestamp_last_update;
881
- if ( $time_to_update < 1 && ! $must ) {
882
- return;
883
- }
884
-
885
- //FCGI must have a permanent output so that it not broke
886
- if ( get_site_option( 'backwpup_cfg_jobdooutput' ) && ! defined( 'STDOUT' ) ) {
887
- echo str_repeat( ' ', 12 );
888
- flush();
889
- }
890
-
891
- // check WPDB connection. WP will do it after a query that will cause messages.
892
- $wpdb->check_connection( false );
893
-
894
- //set execution time again for 5 min
895
- @set_time_limit( 300 );
896
-
897
- //calc sub step percent
898
- if ( $this->substeps_todo > 0 && $this->substeps_done > 0 ) {
899
- $this->substep_percent = round( $this->substeps_done / $this->substeps_todo * 100 );
900
- } else {
901
- $this->substep_percent = 1;
902
- }
903
-
904
- //check if job aborted
905
- if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
906
- if ( $this->step_working !== 'END' ) {
907
- $this->end();
908
- }
909
- } else {
910
- $this->timestamp_last_update = microtime( true ); //last update of working file
911
- $this->write_running_file();
912
- }
913
-
914
- if ( $this->signal !== 0 ) {
915
- $this->do_restart();
916
- }
917
- }
918
-
919
- /**
920
- *
921
- * Increase automatically the memory that is needed
922
- *
923
- * @param int|string $memneed of the needed memory
924
- */
925
- public function need_free_memory( $memneed ) {
926
-
927
- //need memory
928
- $needmemory = @memory_get_usage( true ) + self::convert_hr_to_bytes( $memneed );
929
- // increase Memory
930
- if ( $needmemory > self::convert_hr_to_bytes( ini_get( 'memory_limit' ) ) ) {
931
- $newmemory = round( $needmemory / 1024 / 1024 ) + 1 . 'M';
932
- if ( $needmemory >= 1073741824 ) {
933
- $newmemory = round( $needmemory / 1024 / 1024 / 1024 ) . 'G';
934
- }
935
- @ini_set( 'memory_limit', $newmemory );
936
- }
937
- }
938
-
939
- /**
940
- *
941
- * Converts hr to bytes
942
- *
943
- * @param $size
944
- *
945
- * @return int
946
- */
947
- public static function convert_hr_to_bytes( $size ) {
948
- $size = strtolower( $size );
949
- $bytes = (int) $size;
950
- if ( strpos( $size, 'k' ) !== false ) {
951
- $bytes = intval( $size ) * 1024;
952
- } elseif ( strpos( $size, 'm' ) !== false ) {
953
- $bytes = intval( $size ) * 1024 * 1024;
954
- } elseif ( strpos( $size, 'g' ) !== false ) {
955
- $bytes = intval( $size ) * 1024 * 1024 * 1024;
956
- }
957
-
958
- return $bytes;
959
- }
960
-
961
- /**
962
- *
963
- * Called on job stop makes cleanup and terminates the script
964
- *
965
- */
966
- private function end() {
967
-
968
- $this->step_working = 'END';
969
- $this->substeps_todo = 1;
970
-
971
- if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
972
- $this->log( __( 'Aborted by user!', 'backwpup' ), E_USER_ERROR );
973
- }
974
-
975
- //delete old logs
976
- if ( get_site_option( 'backwpup_cfg_maxlogs' ) ) {
977
- $log_file_list = array();
978
- $log_folder = trailingslashit( dirname( $this->logfile ) );
979
- if ( is_readable( $log_folder ) && $dir = opendir( $log_folder ) ) { //make file list
980
- while ( ( $file = readdir( $dir ) ) !== false ) {
981
- if ( strpos( $file, 'backwpup_log_' ) == 0 && false !== strpos( $file, '.html' ) ) {
982
- $log_file_list[ filemtime( $log_folder . $file ) ] = $file;
983
- }
984
- }
985
- closedir( $dir );
986
- }
987
- if ( sizeof( $log_file_list ) > 0 ) {
988
- krsort( $log_file_list, SORT_NUMERIC );
989
- $num_delete_files = 0;
990
- $i = - 1;
991
- foreach ( $log_file_list AS $log_file ) {
992
- $i ++;
993
- if ( $i < get_site_option( 'backwpup_cfg_maxlogs' ) ) {
994
- continue;
995
- }
996
- unlink( $log_folder . $log_file );
997
- $num_delete_files ++;
998
- }
999
- if ( $num_delete_files > 0 ) {
1000
- $this->log( sprintf( _n( 'One old log deleted', '%d old logs deleted', $num_delete_files, 'backwpup' ), $num_delete_files ) );
1001
- }
1002
- }
1003
- }
1004
-
1005
- //Display job working time
1006
- if ( $this->errors > 0 ) {
1007
- $this->log( sprintf( __( 'Job has ended with errors in %s seconds. You must resolve the errors for correct execution.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ), E_USER_ERROR );
1008
- } elseif ( $this->warnings > 0 ) {
1009
- $this->log( sprintf( __( 'Job finished with warnings in %s seconds. Please resolve them for correct execution.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ), E_USER_WARNING );
1010
- } else {
1011
- $this->log( sprintf( __( 'Job done in %s seconds.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ) );
1012
- }
1013
-
1014
- //Update job options
1015
- $this->job['lastruntime'] = current_time( 'timestamp' ) - $this->start_time;
1016
- BackWPup_Option::update( $this->job['jobid'], 'lastruntime', $this->job['lastruntime'] );
1017
-
1018
-
1019
- //write header info
1020
- if ( ! empty( $this->logfile ) ) {
1021
-
1022
- if ( $fd = fopen( $this->logfile, 'r+' ) ) {
1023
- $filepos = ftell( $fd );
1024
- $found = 0;
1025
- while ( ! feof( $fd ) ) {
1026
- $line = fgets( $fd );
1027
- if ( stripos( $line, '<meta name="backwpup_jobruntime"' ) !== false ) {
1028
- fseek( $fd, $filepos );
1029
- fwrite( $fd, str_pad( '<meta name="backwpup_jobruntime" content="' . $this->job['lastruntime'] . '" />', 100 ) . PHP_EOL );
1030
- $found ++;
1031
- }
1032
- if ( stripos( $line, '<meta name="backwpup_backupfilesize"' ) !== false ) {
1033
- fseek( $fd, $filepos );
1034
- fwrite( $fd, str_pad( '<meta name="backwpup_backupfilesize" content="' . $this->backup_filesize . '" />', 100 ) . PHP_EOL );
1035
- $found ++;
1036
- }
1037
- if ( $found >= 2 ) {
1038
- break;
1039
- }
1040
- $filepos = ftell( $fd );
1041
- }
1042
- fclose( $fd );
1043
- }
1044
-
1045
- //Send mail with log
1046
- $sendmail = false;
1047
- if ( $this->job['mailaddresslog'] ) {
1048
- $sendmail = true;
1049
- }
1050
- if ( $this->errors === 0 && $this->job['mailerroronly'] ) {
1051
- $sendmail = false;
1052
- }
1053
- if ( $sendmail ) {
1054
- //special subject
1055
- $status = __( 'SUCCESSFUL', 'backwpup' );
1056
- if ( $this->warnings > 0 ) {
1057
- $status = __( 'WARNING', 'backwpup' );
1058
- }
1059
- if ( $this->errors > 0 ) {
1060
- $status = __( 'ERROR', 'backwpup' );
1061
- }
1062
-
1063
- $subject = sprintf( __( '[%3$s] BackWPup log %1$s: %2$s', 'backwpup' ), date_i18n( 'd-M-Y H:i', $this->start_time, true ), esc_attr( $this->job['name'] ), $status );
1064
- $headers = array();
1065
- $headers[] = 'Content-Type: text/html; charset=' . get_bloginfo( 'charset' );
1066
- if ( $this->job['mailaddresssenderlog'] ) {
1067
- $this->job['mailaddresssenderlog'] = str_replace( array( '&lt;', '&gt;' ), array( '<', '>' ), $this->job['mailaddresssenderlog'] );
1068
-
1069
- $bracket_pos = strpos( $this->job['mailaddresssenderlog'], '<' );
1070
- $at_pos = strpos( $this->job['mailaddresssenderlog'], '@' );
1071
- if ( $bracket_pos === false || $at_pos === false ) {
1072
- $this->job['mailaddresssenderlog'] = str_replace( array( '<', '>' ), '', $this->job['mailaddresssenderlog'] ) . ' <' . get_bloginfo( 'admin_email' ) . '>';
1073
- }
1074
-
1075
- $headers[] = 'From: ' . $this->job['mailaddresssenderlog'];
1076
- }
1077
- wp_mail( $this->job['mailaddresslog'], $subject, file_get_contents( $this->logfile ), $headers );
1078
- }
1079
- }
1080
-
1081
- //set done
1082
- $this->substeps_done = 1;
1083
- $this->steps_done[] = 'END';
1084
-
1085
- //clean up temp
1086
- self::clean_temp_folder();
1087
-
1088
- //remove shutdown action
1089
- remove_action( 'shutdown', array( $this, 'shutdown' ) );
1090
- restore_exception_handler();
1091
- restore_error_handler();
1092
-
1093
- //logfile end
1094
- file_put_contents( $this->logfile, "</body>" . PHP_EOL . "</html>", FILE_APPEND );
1095
-
1096
- BackWPup_Cron::check_cleanup();
1097
-
1098
- exit();
1099
- }
1100
-
1101
- /**
1102
- * Cleanup Temp Folder
1103
- */
1104
- public static function clean_temp_folder() {
1105
-
1106
- $temp_dir = BackWPup::get_plugin_data( 'TEMP' );
1107
- $do_not_delete_files = array( '.htaccess', 'nginx.conf', 'index.php', '.', '..', '.donotbackup' );
1108
-
1109
- if ( is_writable( $temp_dir ) && $dir = opendir( $temp_dir ) ) {
1110
- while ( false !== ( $file = readdir( $dir ) ) ) {
1111
- if ( in_array( $file, $do_not_delete_files, true ) || is_dir( $temp_dir . $file ) || is_link( $temp_dir . $file ) ) {
1112
- continue;
1113
- }
1114
- if ( is_writeable( $temp_dir . $file ) ) {
1115
- unlink( $temp_dir . $file );
1116
- }
1117
- }
1118
- closedir( $dir );
1119
- }
1120
- }
1121
-
1122
- /**
1123
- * Do a job restart
1124
- *
1125
- * @param bool $must Restart must done
1126
- */
1127
- public function do_restart( $must = false ) {
1128
-
1129
- //restart must done if signal
1130
- if ( $this->signal !== 0 ) {
1131
- $must = true;
1132
- }
1133
-
1134
- //no restart if in end step
1135
- if ( $this->step_working === 'END' || ( count( $this->steps_done ) + 1 ) >= count( $this->steps_todo ) ) {
1136
- return;
1137
- }
1138
-
1139
- //no restart on cli usage
1140
- if ( php_sapi_name() == 'cli' ) {
1141
- return;
1142
- }
1143
-
1144
- //no restart if no restart time configured
1145
- $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1146
- if ( ! $must && empty( $job_max_execution_time ) ) {
1147
- return;
1148
- }
1149
-
1150
- //no restart when restart was 3 Seconds before
1151
- $execution_time = microtime( true ) - $this->timestamp_script_start;
1152
- if ( ! $must && $execution_time < 3 ) {
1153
- return;
1154
- }
1155
-
1156
- //no restart if no working job
1157
- if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
1158
- return;
1159
- }
1160
-
1161
- //print message
1162
- if ( $this->is_debug() ) {
1163
- if ( $execution_time !== 0 ) {
1164
- $this->log( sprintf( __( 'Restart after %1$d seconds.', 'backwpup' ), ceil( $execution_time ) ) );
1165
- } elseif ( $this->signal !== 0 ) {
1166
- $this->log( __( 'Restart after getting signal.', 'backwpup' ) );
1167
- }
1168
- }
1169
-
1170
- //do things for a clean restart
1171
- $this->pid = 0;
1172
- $this->uniqid = '';
1173
- $this->write_running_file();
1174
- remove_action( 'shutdown', array( $this, 'shutdown' ) );
1175
- //do restart
1176
- wp_clear_scheduled_hook( 'backwpup_cron', array( 'id' => 'restart' ) );
1177
- wp_schedule_single_event( time() + 5, 'backwpup_cron', array( 'id' => 'restart' ) );
1178
- self::get_jobrun_url( 'restart' );
1179
-
1180
- exit();
1181
- }
1182
-
1183
- /**
1184
- *
1185
- * Get a url to run a job of BackWPup
1186
- *
1187
- * @param string $starttype Start types are 'runnow', 'runnowlink', 'cronrun', 'runext', 'restart', 'restartalt', 'test'
1188
- * @param int $jobid The id of job to start else 0
1189
- *
1190
- * @return array|object [url] is the job url [header] for auth header or object form wp_remote_get()
1191
- */
1192
- public static function get_jobrun_url( $starttype, $jobid = 0 ) {
1193
-
1194
- $authentication = get_site_option( 'backwpup_cfg_authentication', array(
1195
- 'method' => '',
1196
- 'basic_user' => '',
1197
- 'basic_password' => '',
1198
- 'user_id' => 0,
1199
- 'query_arg' => ''
1200
- ) );
1201
- $url = site_url( 'wp-cron.php' );
1202
- $header = array( 'Cache-Control' => 'no-cache' );
1203
- $authurl = '';
1204
- $query_args = array(
1205
- '_nonce' => substr( wp_hash( wp_nonce_tick() . 'backwpup_job_run-' . $starttype, 'nonce' ), - 12, 10 ),
1206
- 'doing_wp_cron' => sprintf( '%.22F', microtime( true ) )
1207
- );
1208
-
1209
- if ( in_array( $starttype, array( 'restart', 'runnow', 'cronrun', 'runext', 'test' ), true ) ) {
1210
- $query_args['backwpup_run'] = $starttype;
1211
- }
1212
-
1213
- if ( in_array( $starttype, array( 'runnowlink', 'runnow', 'cronrun', 'runext' ), true ) && ! empty( $jobid ) ) {
1214
- $query_args['jobid'] = $jobid;
1215
- }
1216
-
1217
- if ( ! empty( $authentication['basic_user'] ) && ! empty( $authentication['basic_password'] ) && $authentication['method'] == 'basic' ) {
1218
- $header['Authorization'] = 'Basic ' . base64_encode( $authentication['basic_user'] . ':' . BackWPup_Encryption::decrypt( $authentication['basic_password'] ) );
1219
- $authurl = urlencode( $authentication['basic_user'] ) . ':' . urlencode( BackWPup_Encryption::decrypt( $authentication['basic_password'] ) ) . '@';
1220
- }
1221
-
1222
- if ( ! empty( $authentication['query_arg'] ) && $authentication['method'] == 'query_arg' ) {
1223
- $url .= '?' . $authentication['query_arg'];
1224
- }
1225
-
1226
- if ( $starttype === 'runext' ) {
1227
- $query_args['_nonce'] = get_site_option( 'backwpup_cfg_jobrunauthkey' );
1228
- $query_args['doing_wp_cron'] = null;
1229
- if ( ! empty( $authurl ) ) {
1230
- $url = str_replace( 'https://', 'https://' . $authurl, $url );
1231
- $url = str_replace( 'http://', 'http://' . $authurl, $url );
1232
- }
1233
- }
1234
-
1235
- if ( $starttype === 'runnowlink' && ( ! defined( 'ALTERNATE_WP_CRON' ) || ! ALTERNATE_WP_CRON ) ) {
1236
- $url = wp_nonce_url( network_admin_url( 'admin.php' ), 'backwpup_job_run-' . $starttype );
1237
- $query_args['page'] = 'backwpupjobs';
1238
- $query_args['action'] = 'runnow';
1239
- $query_args['doing_wp_cron'] = null;
1240
- unset( $query_args['_nonce'] );
1241
- }
1242
-
1243
- if ( $starttype === 'runnowlink' && defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
1244
- $query_args['backwpup_run'] = 'runnowalt';
1245
- $query_args['_nonce'] = substr( wp_hash( wp_nonce_tick() . 'backwpup_job_run-runnowalt', 'nonce' ), - 12, 10 );
1246
- $query_args['doing_wp_cron'] = null;
1247
- }
1248
-
1249
- if ( $starttype === 'restartalt' && defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
1250
- $query_args['backwpup_run'] = 'restart';
1251
- $query_args['_nonce'] = null;
1252
- }
1253
-
1254
- if ( $starttype === 'restart' || $starttype === 'test' ) {
1255
- $query_args['_nonce'] = null;
1256
- }
1257
-
1258
- if ( ! empty( $authentication['user_id'] ) && $authentication['method'] === 'user' ) {
1259
- //cache cookies for auth some
1260
- $cookies = get_site_transient( 'backwpup_cookies' );
1261
- if ( empty( $cookies ) ) {
1262
- $wp_admin_user = get_users( array( 'role' => 'administrator', 'number' => 1 ) );
1263
- if ( empty( $wp_admin_user ) ) {
1264
- $wp_admin_user = get_users( array( 'role' => 'backwpup_admin', 'number' => 1 ) );
1265
- }
1266
- if ( ! empty( $wp_admin_user[0]->ID ) ) {
1267
- $expiration = time() + ( 356 * DAY_IN_SECONDS );
1268
- $manager = WP_Session_Tokens::get_instance( $wp_admin_user[0]->ID );
1269
- $token = $manager->create( $expiration );
1270
- $cookies[ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $wp_admin_user[0]->ID, $expiration, 'logged_in', $token );
1271
- }
1272
- set_site_transient( 'backwpup_cookies', $cookies, HOUR_IN_SECONDS - 30 );
1273
- }
1274
- } else {
1275
- $cookies = '';
1276
- }
1277
-
1278
- $cron_request = array(
1279
- 'url' => add_query_arg( $query_args, $url ),
1280
- 'key' => $query_args['doing_wp_cron'],
1281
- 'args' => array(
1282
- 'blocking' => false,
1283
- 'sslverify' => false,
1284
- 'timeout' => 0.01,
1285
- 'headers' => $header,
1286
- 'user-agent' => BackWPup::get_plugin_data( 'User-Agent' )
1287
- )
1288
- );
1289
-
1290
- if ( ! empty( $cookies ) ) {
1291
- foreach ( $cookies as $name => $value ) {
1292
- $cron_request['args']['cookies'][] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
1293
- }
1294
- }
1295
-
1296
- $cron_request = apply_filters( 'cron_request', $cron_request );
1297
-
1298
- if ( $starttype === 'test' ) {
1299
- $cron_request['args']['timeout'] = 15;
1300
- $cron_request['args']['blocking'] = true;
1301
- }
1302
-
1303
- if ( ! in_array( $starttype, array( 'runnowlink', 'runext', 'restartalt' ), true ) ) {
1304
- delete_transient( 'doing_cron' );
1305
-
1306
- return wp_remote_post( $cron_request['url'], $cron_request['args'] );
1307
- }
1308
-
1309
- return $cron_request;
1310
- }
1311
-
1312
- /**
1313
- * Run baby run
1314
- */
1315
- public function run() {
1316
- global $wpdb;
1317
- /* @var wpdb $wpdb */
1318
-
1319
- //disable output buffering
1320
- if ( $level = ob_get_level() ) {
1321
- for ( $i = 0; $i < $level; $i ++ ) {
1322
- ob_end_clean();
1323
- }
1324
- }
1325
-
1326
- // Job can't run it is not created
1327
- if ( empty( $this->steps_todo ) || empty( $this->logfile ) ) {
1328
- $running_file = BackWPup::get_plugin_data( 'running_file' );
1329
- if ( file_exists( $running_file ) ) {
1330
- unlink( $running_file );
1331
- }
1332
-
1333
- return;
1334
- }
1335
-
1336
- //Check double running and inactivity
1337
- $last_update = microtime( true ) - $this->timestamp_last_update;
1338
- if ( ! empty( $this->pid ) && $last_update > 300 ) {
1339
- $this->log( __( 'Job restarts due to inactivity for more than 5 minutes.', 'backwpup' ), E_USER_WARNING );
1340
- } elseif ( ! empty( $this->pid ) ) {
1341
- return;
1342
- }
1343
- // set timestamp of script start
1344
- $this->timestamp_script_start = microtime( true );
1345
- //set Pid
1346
- $this->pid = self::get_pid();
1347
- $this->uniqid = uniqid( '', true );
1348
- //Early write new working file
1349
- $this->write_running_file();
1350
- if ( $this->is_debug() ) {
1351
- @ini_set( 'error_log', $this->logfile );
1352
- error_reporting( - 1 );
1353
- }
1354
- @ini_set( 'display_errors', '0' );
1355
- @ini_set( 'log_errors', '1' );
1356
- @ini_set( 'html_errors', '0' );
1357
- @ini_set( 'report_memleaks', '1' );
1358
- @ini_set( 'zlib.output_compression', '0' );
1359
- @ini_set( 'implicit_flush', '0' );
1360
- @putenv( 'TMPDIR=' . BackWPup::get_plugin_data( 'TEMP' ) );
1361
- //Write Wordpress DB errors to log
1362
- $wpdb->suppress_errors( false );
1363
- $wpdb->hide_errors();
1364
- //set wp max memory limit
1365
- @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
1366
- //set error handler
1367
- if ( ! empty( $this->logfile ) ) {
1368
- if ( $this->is_debug() ) {
1369
- set_error_handler( array( $this, 'log' ) );
1370
- } else {
1371
- set_error_handler( array( $this, 'log' ), E_ALL ^ E_NOTICE );
1372
- }
1373
- }
1374
- set_exception_handler( array( $this, 'exception_handler' ) );
1375
- // execute function on job shutdown register_shutdown_function( array( $this, 'shutdown' ) );
1376
- add_action( 'shutdown', array( $this, 'shutdown' ) );
1377
-
1378
- if ( function_exists( 'pcntl_signal' ) ) {
1379
- $signals = array(
1380
- 'SIGHUP', //Term
1381
- 'SIGINT', //Term
1382
- 'SIGQUIT', //Core
1383
- 'SIGILL', //Core
1384
- //'SIGTRAP', //Core
1385
- 'SIGABRT', //Core
1386
- 'SIGBUS', //Core
1387
- 'SIGFPE', //Core
1388
- //'SIGKILL', //Term
1389
- 'SIGSEGV', //Core
1390
- //'SIGPIPE', Term
1391
- //'SIGALRM', Term
1392
- 'SIGTERM', //Term
1393
- 'SIGSTKFLT', //Term
1394
- 'SIGUSR1',//Term
1395
- 'SIGUSR2', //Term
1396
- //'SIGCHLD', //Ign
1397
- //'SIGCONT', //Cont
1398
- //'SIGSTOP', //Stop
1399
- //'SIGTSTP', //Stop
1400
- //'SIGTTIN', //Stop
1401
- //'SIGTTOU', //Stop
1402
- //'SIGURG', //Ign
1403
- 'SIGXCPU', //Core
1404
- 'SIGXFSZ', //Core
1405
- //'SIGVTALRM', //Term
1406
- //'SIGPROF', //Term
1407
- //'SIGWINCH', //Ign
1408
- //'SIGIO', //Term
1409
- 'SIGPWR', //Term
1410
- 'SIGSYS' //Core
1411
- );
1412
- $signals = apply_filters( 'backwpup_job_signals_to_handel', $signals );
1413
- declare( ticks = 1 );
1414
- $this->signal = 0;
1415
- foreach ( $signals as $signal ) {
1416
- if ( defined( $signal ) ) {
1417
- pcntl_signal( constant( $signal ), array( $this, 'signal_handler' ), false );
1418
- }
1419
- }
1420
- }
1421
- $job_types = BackWPup::get_job_types();
1422
- //go step by step
1423
- foreach ( $this->steps_todo as $this->step_working ) {
1424
- //Check if step already done
1425
- if ( in_array( $this->step_working, $this->steps_done, true ) ) {
1426
- continue;
1427
- }
1428
- //calc step percent
1429
- if ( count( $this->steps_done ) > 0 ) {
1430
- $this->step_percent = round( count( $this->steps_done ) / count( $this->steps_todo ) * 100 );
1431
- } else {
1432
- $this->step_percent = 1;
1433
- }
1434
- // do step tries
1435
- while ( true ) {
1436
- if ( $this->steps_data[ $this->step_working ]['STEP_TRY'] >= get_site_option( 'backwpup_cfg_jobstepretry' ) ) {
1437
- $this->log( __( 'Step aborted: too many attempts!', 'backwpup' ), E_USER_ERROR );
1438
- $this->temp = array();
1439
- $this->steps_done[] = $this->step_working;
1440
- $this->substeps_done = 0;
1441
- $this->substeps_todo = 0;
1442
- $this->do_restart();
1443
- break;
1444
- }
1445
-
1446
- $this->steps_data[ $this->step_working ]['STEP_TRY'] ++;
1447
- $done = false;
1448
-
1449
- //executes the methods of job process
1450
- if ( $this->step_working == 'CREATE_ARCHIVE' ) {
1451
- $done = $this->create_archive();
1452
- } elseif ( $this->step_working == 'CREATE_MANIFEST' ) {
1453
- $done = $this->create_manifest();
1454
- } elseif ( $this->step_working == 'END' ) {
1455
- $this->end();
1456
- break 2;
1457
- } elseif ( strstr( $this->step_working, 'JOB_' ) ) {
1458
- $done = $job_types[ str_replace( 'JOB_', '', $this->step_working ) ]->job_run( $this );
1459
- } elseif ( strstr( $this->step_working, 'DEST_SYNC_' ) ) {
1460
- $done = BackWPup::get_destination( str_replace( 'DEST_SYNC_', '', $this->step_working ) )->job_run_sync( $this );
1461
- } elseif ( strstr( $this->step_working, 'DEST_' ) ) {
1462
- $done = BackWPup::get_destination( str_replace( 'DEST_', '', $this->step_working ) )->job_run_archive( $this );
1463
- } elseif ( ! empty( $this->steps_data[ $this->step_working ]['CALLBACK'] ) ) {
1464
- $done = $this->steps_data[ $this->step_working ]['CALLBACK']( $this );
1465
- }
1466
-
1467
- // set step as done
1468
- if ( $done === true ) {
1469
- $this->temp = array();
1470
- $this->steps_done[] = $this->step_working;
1471
- $this->substeps_done = 0;
1472
- $this->substeps_todo = 0;
1473
- $this->update_working_data( true );
1474
- }
1475
- if ( count( $this->steps_done ) < count( $this->steps_todo ) - 1 ) {
1476
- $this->do_restart();
1477
- }
1478
- if ( $done === true ) {
1479
- break;
1480
- }
1481
- }
1482
- }
1483
- }
1484
-
1485
- /**
1486
- * Creates the backup archive
1487
- */
1488
- private function create_archive() {
1489
-
1490
- //load folders to backup
1491
- $folders_to_backup = $this->get_folders_to_backup();
1492
-
1493
- $this->substeps_todo = $this->count_folder + 1;
1494
-
1495
- //initial settings for restarts in archiving
1496
- if ( ! isset( $this->steps_data[ $this->step_working ]['on_file'] ) ) {
1497
- $this->steps_data[ $this->step_working ]['on_file'] = '';
1498
- }
1499
- if ( ! isset( $this->steps_data[ $this->step_working ]['on_folder'] ) ) {
1500
- $this->steps_data[ $this->step_working ]['on_folder'] = '';
1501
- }
1502
-
1503
- if ( $this->steps_data[ $this->step_working ]['on_folder'] == '' && $this->steps_data[ $this->step_working ]['on_file'] == '' && is_file( $this->backup_folder . $this->backup_file ) ) {
1504
- unlink( $this->backup_folder . $this->backup_file );
1505
- }
1506
-
1507
- if ( $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] != $this->steps_data[ $this->step_working ]['STEP_TRY'] ) {
1508
- $this->log( sprintf( __( '%d. Trying to create backup archive &hellip;', 'backwpup' ), $this->steps_data[ $this->step_working ]['STEP_TRY'] ), E_USER_NOTICE );
1509
- }
1510
-
1511
- try {
1512
- $backup_archive = new BackWPup_Create_Archive( $this->backup_folder . $this->backup_file );
1513
-
1514
- //show method for creation
1515
- if ( $this->substeps_done == 0 ) {
1516
- $this->log( sprintf( _x( 'Compressing files as %s. Please be patient, this may take a moment.', 'Archive compression method', 'backwpup' ), $backup_archive->get_method() ) );
1517
- }
1518
-
1519
- //add extra files
1520
- if ( $this->substeps_done == 0 ) {
1521
- if ( ! empty( $this->additional_files_to_backup ) && $this->substeps_done == 0 ) {
1522
- if ( $this->is_debug() ) {
1523
- $this->log( __( 'Adding Extra files to Archive', 'backwpup' ) );
1524
- }
1525
- foreach ( $this->additional_files_to_backup as $file ) {
1526
- if ( $backup_archive->add_file( $file, basename( $file ) ) ) {
1527
- ;
1528
- $this->count_files ++;
1529
- $this->count_files_size = $this->count_files_size + filesize( $file );
1530
- $this->update_working_data();
1531
- } else {
1532
- $backup_archive->close();
1533
- $this->steps_data[ $this->step_working ]['on_file'] = '';
1534
- $this->steps_data[ $this->step_working ]['on_folder'] = '';
1535
- $this->log( __( 'Cannot create backup archive correctly. Aborting creation.', 'backwpup' ), E_USER_ERROR );
1536
-
1537
- return false;
1538
- }
1539
- }
1540
- }
1541
- $this->substeps_done ++;
1542
- }
1543
-
1544
- //add normal files
1545
- while ( $folder = array_shift( $folders_to_backup ) ) {
1546
- //jump over already done folders
1547
- if ( in_array( $this->steps_data[ $this->step_working ]['on_folder'], $folders_to_backup, true ) ) {
1548
- continue;
1549
- }
1550
- if ( $this->is_debug() ) {
1551
- $this->log( sprintf( __( 'Archiving Folder: %s', 'backwpup' ), $folder ) );
1552
- }
1553
- $this->steps_data[ $this->step_working ]['on_folder'] = $folder;
1554
- $files_in_folder = $this->get_files_in_folder( $folder );
1555
- //add empty folders
1556
- if ( empty( $files_in_folder ) ) {
1557
- $folder_name_in_archive = trim( ltrim( $this->get_destination_path_replacement( $folder ), '/' ) );
1558
- if ( ! empty ( $folder_name_in_archive ) ) {
1559
- $backup_archive->add_empty_folder( $folder, $folder_name_in_archive );
1560
- }
1561
- continue;
1562
- }
1563
- //add files
1564
- while ( $file = array_shift( $files_in_folder ) ) {
1565
- //jump over already done files
1566
- if ( in_array( $this->steps_data[ $this->step_working ]['on_file'], $files_in_folder, true ) ) {
1567
- continue;
1568
- }
1569
- $this->steps_data[ $this->step_working ]['on_file'] = $file;
1570
- //restart if needed
1571
- $restart_time = $this->get_restart_time();
1572
- if ( $restart_time <= 0 ) {
1573
- unset( $backup_archive );
1574
- $this->do_restart_time( true );
1575
-
1576
- return false;
1577
- }
1578
- //generate filename in archive
1579
- $in_archive_filename = ltrim( $this->get_destination_path_replacement( $file ), '/' );
1580
- //add file to archive
1581
- if ( $backup_archive->add_file( $file, $in_archive_filename ) ) {
1582
- $this->count_files ++;
1583
- $this->count_files_size = $this->count_files_size + filesize( $file );
1584
- $this->update_working_data();
1585
- } else {
1586
- $backup_archive->close();
1587
- unset( $backup_archive );
1588
- $this->steps_data[ $this->step_working ]['on_file'] = '';
1589
- $this->steps_data[ $this->step_working ]['on_folder'] = '';
1590
- $this->substeps_done = 0;
1591
- $this->backup_filesize = filesize( $this->backup_folder . $this->backup_file );
1592
- if ( $this->backup_filesize === false ) {
1593
- $this->backup_filesize = PHP_INT_MAX;
1594
- }
1595
- $this->log( __( 'Cannot create backup archive correctly. Aborting creation.', 'backwpup' ), E_USER_ERROR );
1596
-
1597
- return false;
1598
- }
1599
- }
1600
- $this->steps_data[ $this->step_working ]['on_file'] = '';
1601
- $this->substeps_done ++;
1602
- }
1603
- $backup_archive->close();
1604
- unset( $backup_archive );
1605
- $this->log( __( 'Backup archive created.', 'backwpup' ), E_USER_NOTICE );
1606
- } catch ( Exception $e ) {
1607
- $this->log( $e->getMessage(), E_USER_ERROR, $e->getFile(), $e->getLine() );
1608
- unset( $backup_archive );
1609
-
1610
- return false;
1611
- }
1612
-
1613
- $this->backup_filesize = filesize( $this->backup_folder . $this->backup_file );
1614
- if ( $this->backup_filesize === false ) {
1615
- $this->backup_filesize = PHP_INT_MAX;
1616
- }
1617
-
1618
- if ( $this->backup_filesize >= PHP_INT_MAX ) {
1619
- $this->log( __( 'The Backup archive will be too large for file operations with this PHP Version. You might want to consider splitting the backup job in multiple jobs with less files each.', 'backwpup' ), E_USER_ERROR );
1620
- $this->end();
1621
- } else {
1622
- $this->log( sprintf( __( 'Archive size is %s.', 'backwpup' ), size_format( $this->backup_filesize, 2 ) ), E_USER_NOTICE );
1623
- }
1624
-
1625
- $this->log( sprintf( __( '%1$d Files with %2$s in Archive.', 'backwpup' ), $this->count_files, size_format( $this->count_files_size, 2 ) ), E_USER_NOTICE );
1626
-
1627
- return true;
1628
- }
1629
-
1630
- /**
1631
- * Get list of Folder for backup
1632
- *
1633
- * @return array folder list
1634
- */
1635
- public function get_folders_to_backup() {
1636
-
1637
- $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-folder.php';
1638
-
1639
- if ( ! file_exists( $file ) ) {
1640
- return array();
1641
- }
1642
-
1643
- $folders = array();
1644
-
1645
- $file_data = file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
1646
-
1647
- foreach ( $file_data as $folder ) {
1648
- $folder = trim( str_replace( array( '<?php', '//' ), '', $folder ) );
1649
- if ( ! empty( $folder ) && is_dir( $folder ) ) {
1650
- $folders[] = $folder;
1651
- }
1652
- }
1653
- $folders = array_unique( $folders );
1654
- sort( $folders );
1655
- $this->count_folder = count( $folders );
1656
-
1657
- return $folders;
1658
- }
1659
-
1660
- /**
1661
- *
1662
- * Get back a array of files to backup in the selected folder
1663
- *
1664
- * @param string $folder the folder to get the files from
1665
- *
1666
- * @return array files to backup
1667
- */
1668
- public function get_files_in_folder( $folder ) {
1669
-
1670
- $files = array();
1671
- $folder = trailingslashit( $folder );
1672
-
1673
- if ( ! is_dir( $folder ) ) {
1674
- $this->log( sprintf( _x( 'Folder %s not exists', 'Folder name', 'backwpup' ), $folder ), E_USER_WARNING );
1675
-
1676
- return $files;
1677
- }
1678
-
1679
- if ( ! is_readable( $folder ) ) {
1680
- $this->log( sprintf( _x( 'Folder %s not readable', 'Folder name', 'backwpup' ), $folder ), E_USER_WARNING );
1681
-
1682
- return $files;
1683
- }
1684
-
1685
- if ( $dir = opendir( $folder ) ) {
1686
- while ( false !== ( $file = readdir( $dir ) ) ) {
1687
- if ( in_array( $file, array( '.', '..' ), true ) || is_dir( $folder . $file ) ) {
1688
- continue;
1689
- }
1690
- foreach ( $this->exclude_from_backup as $exclusion ) { //exclude files
1691
- $exclusion = trim( $exclusion );
1692
- if ( false !== stripos( $folder . $file, trim( $exclusion ) ) && ! empty( $exclusion ) ) {
1693
- continue 2;
1694
- }
1695
- }
1696
- if ( $this->job['backupexcludethumbs'] && strpos( $folder, BackWPup_File::get_upload_dir() ) !== false && preg_match( "/\-[0-9]{1,4}x[0-9]{1,4}.+\.(jpg|png|gif)$/i", $file ) ) {
1697
- continue;
1698
- }
1699
- if ( is_link( $folder . $file ) ) {
1700
- $this->log( sprintf( __( 'Link "%s" not following.', 'backwpup' ), $folder . $file ), E_USER_WARNING );
1701
- } elseif ( ! is_readable( $folder . $file ) ) {
1702
- $this->log( sprintf( __( 'File "%s" is not readable!', 'backwpup' ), $folder . $file ), E_USER_WARNING );
1703
- } else {
1704
- $file_size = filesize( $folder . $file );
1705
- if ( ! is_int( $file_size ) || $file_size < 0 || $file_size > 2147483647 ) {
1706
- $this->log( sprintf( __( 'File size of “%s” cannot be retrieved. File might be too large and will not be added to queue.', 'backwpup' ), $folder . $file . ' ' . $file_size ), E_USER_WARNING );
1707
- continue;
1708
- }
1709
- $files[] = $folder . $file;
1710
- }
1711
- }
1712
- closedir( $dir );
1713
- }
1714
-
1715
- return $files;
1716
- }
1717
-
1718
- /**
1719
- * Get job restart time
1720
- *
1721
- * @return int remaining time
1722
- */
1723
- public function get_restart_time() {
1724
-
1725
- if ( php_sapi_name() == 'cli' ) {
1726
- return 300;
1727
- }
1728
-
1729
- $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1730
-
1731
- if ( empty( $job_max_execution_time ) ) {
1732
- return 300;
1733
- }
1734
-
1735
- $execution_time = microtime( true ) - $this->timestamp_script_start;
1736
-
1737
- return $job_max_execution_time - $execution_time - 3;
1738
- }
1739
-
1740
- /**
1741
- * Do a job restart
1742
- *
1743
- * @param bool $do_restart_now should time restart now be done
1744
- *
1745
- * @return int remaining time
1746
- */
1747
- public function do_restart_time( $do_restart_now = false ) {
1748
-
1749
- if ( php_sapi_name() == 'cli' ) {
1750
- return 300;
1751
- }
1752
-
1753
- //do restart after signal is send
1754
- if ( $this->signal !== 0 ) {
1755
- $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] = $this->steps_data[ $this->step_working ]['STEP_TRY'];
1756
- $this->steps_data[ $this->step_working ]['STEP_TRY'] -= 1;
1757
- $this->do_restart( true );
1758
- }
1759
-
1760
- $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1761
-
1762
- if ( empty( $job_max_execution_time ) ) {
1763
- return 300;
1764
- }
1765
-
1766
- $execution_time = microtime( true ) - $this->timestamp_script_start;
1767
-
1768
- // do restart 3 sec. before max. execution time
1769
- if ( $do_restart_now || $execution_time >= ( $job_max_execution_time - 3 ) ) {
1770
- $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] = $this->steps_data[ $this->step_working ]['STEP_TRY'];
1771
- $this->steps_data[ $this->step_working ]['STEP_TRY'] -= 1;
1772
- $this->do_restart( true );
1773
- }
1774
-
1775
- return $job_max_execution_time - $execution_time;
1776
- }
1777
-
1778
- /**
1779
- * create manifest file
1780
- * @return bool
1781
- */
1782
- public function create_manifest() {
1783
-
1784
- $this->substeps_todo = 3;
1785
-
1786
- $this->log( sprintf( __( '%d. Trying to generate a manifest file&#160;&hellip;', 'backwpup' ), $this->steps_data[ $this->step_working ]['STEP_TRY'] ) );
1787
-
1788
- //build manifest
1789
- $manifest = array();
1790
- // add blog information
1791
- $manifest['blog_info']['url'] = home_url();
1792
- $manifest['blog_info']['wpurl'] = site_url();
1793
- $manifest['blog_info']['prefix'] = $GLOBALS['wpdb']->prefix;
1794
- $manifest['blog_info']['description'] = get_option( 'blogdescription' );
1795
- $manifest['blog_info']['stylesheet_directory'] = get_template_directory_uri();
1796
- $manifest['blog_info']['activate_plugins'] = wp_get_active_and_valid_plugins();
1797
- $manifest['blog_info']['activate_theme'] = wp_get_theme()->get( 'Name' );
1798
- $manifest['blog_info']['admin_email'] = get_option( 'admin_email' );
1799
- $manifest['blog_info']['charset'] = get_bloginfo( 'charset' );
1800
- $manifest['blog_info']['version'] = BackWPup::get_plugin_data( 'wp_version' );
1801
- $manifest['blog_info']['backwpup_version'] = BackWPup::get_plugin_data( 'version' );
1802
- $manifest['blog_info']['language'] = get_bloginfo( 'language' );
1803
- $manifest['blog_info']['name'] = get_bloginfo( 'name' );
1804
- $manifest['blog_info']['abspath'] = ABSPATH;
1805
- $manifest['blog_info']['uploads'] = wp_upload_dir( null, false, true );
1806
- $manifest['blog_info']['contents']['basedir'] = WP_CONTENT_DIR;
1807
- $manifest['blog_info']['contents']['baseurl'] = WP_CONTENT_URL;
1808
- $manifest['blog_info']['plugins']['basedir'] = WP_PLUGIN_DIR;
1809
- $manifest['blog_info']['plugins']['baseurl'] = WP_PLUGIN_URL;
1810
- $manifest['blog_info']['themes']['basedir'] = get_theme_root();
1811
- $manifest['blog_info']['themes']['baseurl'] = get_theme_root_uri();
1812
- // add job settings
1813
- $manifest['job_settings'] = $this->job;
1814
- // add archive info
1815
- foreach ( $this->additional_files_to_backup as $file ) {
1816
- $manifest['archive']['extra_files'][] = basename( $file );
1817
- }
1818
- if ( isset( $this->steps_data['JOB_FILE'] ) ) {
1819
- if ( $this->job['backuproot'] ) {
1820
- $manifest['archive']['abspath'] = trailingslashit( $this->get_destination_path_replacement( ABSPATH ) );
1821
- }
1822
- if ( $this->job['backupuploads'] ) {
1823
- $manifest['archive']['uploads'] = trailingslashit( $this->get_destination_path_replacement( BackWPup_File::get_upload_dir() ) );
1824
- }
1825
- if ( $this->job['backupcontent'] ) {
1826
- $manifest['archive']['contents'] = trailingslashit( $this->get_destination_path_replacement( WP_CONTENT_DIR ) );
1827
- }
1828
- if ( $this->job['backupplugins'] ) {
1829
- $manifest['archive']['plugins'] = trailingslashit( $this->get_destination_path_replacement( WP_PLUGIN_DIR ) );
1830
- }
1831
- if ( $this->job['backupthemes'] ) {
1832
- $manifest['archive']['themes'] = trailingslashit( $this->get_destination_path_replacement( get_theme_root() ) );
1833
- }
1834
- }
1835
-
1836
- if ( ! file_put_contents( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json', json_encode( $manifest ) ) ) {
1837
- return false;
1838
- }
1839
- $this->substeps_done = 1;
1840
-
1841
- //Create backwpup_readme.txt
1842
- $readme_text = __( 'You may have noticed the manifest.json file in this archive.', 'backwpup' ) . PHP_EOL;
1843
- $readme_text .= __( 'manifest.json might be needed for later restoring a backup from this archive.', 'backwpup' ) . PHP_EOL;
1844
- $readme_text .= __( 'Please leave manifest.json untouched and in place. Otherwise it is safe to be ignored.', 'backwpup' ) . PHP_EOL;
1845
- if ( ! file_put_contents( BackWPup::get_plugin_data( 'TEMP' ) . 'backwpup_readme.txt', $readme_text ) ) {
1846
- return false;
1847
- }
1848
- $this->substeps_done = 2;
1849
-
1850
- //add file to backup files
1851
- if ( is_readable( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json' ) ) {
1852
- $this->additional_files_to_backup[] = BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json';
1853
- $this->additional_files_to_backup[] = BackWPup::get_plugin_data( 'TEMP' ) . 'backwpup_readme.txt';
1854
- $this->log( sprintf( __( 'Added manifest.json file with %1$s to backup file list.', 'backwpup' ), size_format( filesize( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json' ), 2 ) ) );
1855
- }
1856
- $this->substeps_done = 3;
1857
-
1858
- return true;
1859
- }
1860
-
1861
- /**
1862
- * @param $jobid
1863
- */
1864
- public static function start_cli( $jobid ) {
1865
-
1866
- if ( php_sapi_name() != 'cli' ) {
1867
- return;
1868
- }
1869
-
1870
- //define DOING_CRON to prevent caching
1871
- if ( ! defined( 'DOING_CRON' ) ) {
1872
- define( 'DOING_CRON', true );
1873
- }
1874
-
1875
- //load text domain
1876
- $log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
1877
- if ( strstr( $log_level, 'translated' ) ) {
1878
- BackWPup::load_text_domain();
1879
- } else {
1880
- add_filter( 'override_load_textdomain', '__return_true' );
1881
- $GLOBALS['l10n'] = array();
1882
- }
1883
-
1884
- $jobid = absint( $jobid );
1885
-
1886
- //Logs Folder
1887
- $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
1888
- $log_folder = BackWPup_File::get_absolute_path( $log_folder );
1889
-
1890
- //check job id exists
1891
- $jobids = BackWPup_Option::get_job_ids();
1892
- if ( ! in_array( $jobid, $jobids, true ) ) {
1893
- die( __( 'Wrong BackWPup JobID', 'backwpup' ) );
1894
- }
1895
- //check folders
1896
- $log_folder_message = BackWPup_File::check_folder( $log_folder );
1897
- if ( ! empty( $log_folder_message ) ) {
1898
- die( $log_folder_message );
1899
- }
1900
- $log_folder_message = BackWPup_File::check_folder( BackWPup::get_plugin_data( 'TEMP' ), true );
1901
- if ( ! empty( $log_folder_message ) ) {
1902
- die( $log_folder_message );
1903
- }
1904
- //check running job
1905
- if ( file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
1906
- die( __( 'A BackWPup job is already running', 'backwpup' ) );
1907
- }
1908
-
1909
- //start class
1910
- $backwpup_job_object = new self();
1911
- $backwpup_job_object->create( 'runcli', (int) $jobid );
1912
- $backwpup_job_object->run();
1913
- }
1914
-
1915
- /**
1916
- * disable caches
1917
- */
1918
- public static function disable_caches() {
1919
-
1920
- //Special settings
1921
- @putenv( 'nokeepalive=1' );
1922
- @ini_set( 'zlib.output_compression', 'Off' );
1923
-
1924
- // deactivate caches
1925
- if ( ! defined( 'DONOTCACHEDB' ) ) {
1926
- define( 'DONOTCACHEDB', true );
1927
- }
1928
- if ( ! defined( 'DONOTCACHEPAGE' ) ) {
1929
- define( 'DONOTCACHEPAGE', true );
1930
- }
1931
- }
1932
-
1933
- /**
1934
- *
1935
- * Reads a BackWPup logfile header and gives back a array of information
1936
- *
1937
- * @param string $logfile full logfile path
1938
- *
1939
- * @return array|bool
1940
- */
1941
- public static function read_logheader( $logfile ) {
1942
-
1943
- $usedmetas = array(
1944
- "date" => "logtime",
1945
- "backwpup_logtime" => "logtime", //old value of date
1946
- "backwpup_errors" => "errors",
1947
- "backwpup_warnings" => "warnings",
1948
- "backwpup_jobid" => "jobid",
1949
- "backwpup_jobname" => "name",
1950
- "backwpup_jobtype" => "type",
1951
- "backwpup_jobruntime" => "runtime",
1952
- "backwpup_backupfilesize" => "backupfilesize"
1953
- );
1954
-
1955
- //get metadata of logfile
1956
- $metas = array();
1957
- if ( is_readable( $logfile ) ) {
1958
- if ( '.gz' == substr( $logfile, - 3 ) ) {
1959
- $metas = (array) get_meta_tags( 'compress.zlib://' . $logfile );
1960
- } else {
1961
- $metas = (array) get_meta_tags( $logfile );
1962
- }
1963
- }
1964
-
1965
- //only output needed data
1966
- foreach ( $usedmetas as $keyword => $field ) {
1967
- if ( isset( $metas[ $keyword ] ) ) {
1968
- $joddata[ $field ] = $metas[ $keyword ];
1969
- } else {
1970
- $joddata[ $field ] = '';
1971
- }
1972
- }
1973
-
1974
- //convert date
1975
- if ( isset( $metas['date'] ) ) {
1976
- $joddata['logtime'] = strtotime( $metas['date'] ) + ( get_option( 'gmt_offset' ) * 3600 );
1977
- }
1978
-
1979
- //use file create date if none
1980
- if ( empty( $joddata['logtime'] ) ) {
1981
- $joddata['logtime'] = filectime( $logfile );
1982
- }
1983
-
1984
- return $joddata;
1985
- }
1986
-
1987
- public static function user_abort() {
1988
-
1989
- /* @var $job_object BackWPup_Job */
1990
- $job_object = BackWPup_Job::get_working_data();
1991
-
1992
- unlink( BackWPup::get_plugin_data( 'running_file' ) );
1993
-
1994
- //if job not working currently abort it this way for message
1995
- $not_worked_time = microtime( true ) - $job_object->timestamp_last_update;
1996
- $restart_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1997
- if ( empty( $restart_time ) ) {
1998
- $restart_time = 60;
1999
- }
2000
- if ( empty( $job_object->pid ) || $not_worked_time > $restart_time ) {
2001
- $job_object->user_abort = true;
2002
- $job_object->update_working_data();
2003
- }
2004
-
2005
- }
2006
-
2007
- /**
2008
- *
2009
- * Get the mime type of a file
2010
- *
2011
- * @param string $file The full file name
2012
- *
2013
- * @return bool|string the mime type or false
2014
- */
2015
- public static function get_mime_type( $file ) {
2016
-
2017
- if ( is_dir( $file ) || is_link( $file ) ) {
2018
- return 'application/octet-stream';
2019
- }
2020
-
2021
- $mime_types = array(
2022
- 'zip' => 'application/zip',
2023
- 'gz' => 'application/gzip',
2024
- 'bz2' => 'application/x-bzip',
2025
- 'tar' => 'application/x-tar',
2026
- '3gp' => 'video/3gpp',
2027
- 'ai' => 'application/postscript',
2028
- 'aif' => 'audio/x-aiff',
2029
- 'aifc' => 'audio/x-aiff',
2030
- 'aiff' => 'audio/x-aiff',
2031
- 'asc' => 'text/plain',
2032
- 'atom' => 'application/atom+xml',
2033
- 'au' => 'audio/basic',
2034
- 'avi' => 'video/x-msvideo',
2035
- 'bcpio' => 'application/x-bcpio',
2036
- 'bin' => 'application/octet-stream',
2037
- 'bmp' => 'image/bmp',
2038
- 'cdf' => 'application/x-netcdf',
2039
- 'cgm' => 'image/cgm',
2040
- 'class' => 'application/octet-stream',
2041
- 'cpio' => 'application/x-cpio',
2042
- 'cpt' => 'application/mac-compactpro',
2043
- 'csh' => 'application/x-csh',
2044
- 'css' => 'text/css',
2045
- 'dcr' => 'application/x-director',
2046
- 'dif' => 'video/x-dv',
2047
- 'dir' => 'application/x-director',
2048
- 'djv' => 'image/vnd.djvu',
2049
- 'djvu' => 'image/vnd.djvu',
2050
- 'dll' => 'application/octet-stream',
2051
- 'dmg' => 'application/octet-stream',
2052
- 'dms' => 'application/octet-stream',
2053
- 'doc' => 'application/msword',
2054
- 'dtd' => 'application/xml-dtd',
2055
- 'dv' => 'video/x-dv',
2056
- 'dvi' => 'application/x-dvi',
2057
- 'dxr' => 'application/x-director',
2058
- 'eps' => 'application/postscript',
2059
- 'etx' => 'text/x-setext',
2060
- 'exe' => 'application/octet-stream',
2061
- 'ez' => 'application/andrew-inset',
2062
- 'flv' => 'video/x-flv',
2063
- 'gif' => 'image/gif',
2064
- 'gram' => 'application/srgs',
2065
- 'grxml' => 'application/srgs+xml',
2066
- 'gtar' => 'application/x-gtar',
2067
- 'hdf' => 'application/x-hdf',
2068
- 'hqx' => 'application/mac-binhex40',
2069
- 'htm' => 'text/html',
2070
- 'html' => 'text/html',
2071
- 'ice' => 'x-conference/x-cooltalk',
2072
- 'ico' => 'image/x-icon',
2073
- 'ics' => 'text/calendar',
2074
- 'ief' => 'image/ief',
2075
- 'ifb' => 'text/calendar',
2076
- 'iges' => 'model/iges',
2077
- 'igs' => 'model/iges',
2078
- 'jnlp' => 'application/x-java-jnlp-file',
2079
- 'jp2' => 'image/jp2',
2080
- 'jpe' => 'image/jpeg',
2081
- 'jpeg' => 'image/jpeg',
2082
- 'jpg' => 'image/jpeg',
2083
- 'js' => 'application/x-javascript',
2084
- 'kar' => 'audio/midi',
2085
- 'latex' => 'application/x-latex',
2086
- 'lha' => 'application/octet-stream',
2087
- 'lzh' => 'application/octet-stream',
2088
- 'm3u' => 'audio/x-mpegurl',
2089
- 'm4a' => 'audio/mp4a-latm',
2090
- 'm4p' => 'audio/mp4a-latm',
2091
- 'm4u' => 'video/vnd.mpegurl',
2092
- 'm4v' => 'video/x-m4v',
2093
- 'mac' => 'image/x-macpaint',
2094
- 'man' => 'application/x-troff-man',
2095
- 'mathml' => 'application/mathml+xml',
2096
- 'me' => 'application/x-troff-me',
2097
- 'mesh' => 'model/mesh',
2098
- 'mid' => 'audio/midi',
2099
- 'midi' => 'audio/midi',
2100
- 'mif' => 'application/vnd.mif',
2101
- 'mov' => 'video/quicktime',
2102
- 'movie' => 'video/x-sgi-movie',
2103
- 'mp2' => 'audio/mpeg',
2104
- 'mp3' => 'audio/mpeg',
2105
- 'mp4' => 'video/mp4',
2106
- 'mpe' => 'video/mpeg',
2107
- 'mpeg' => 'video/mpeg',
2108
- 'mpg' => 'video/mpeg',
2109
- 'mpga' => 'audio/mpeg',
2110
- 'ms' => 'application/x-troff-ms',
2111
- 'msh' => 'model/mesh',
2112
- 'mxu' => 'video/vnd.mpegurl',
2113
- 'nc' => 'application/x-netcdf',
2114
- 'oda' => 'application/oda',
2115
- 'ogg' => 'application/ogg',
2116
- 'ogv' => 'video/ogv',
2117
- 'pbm' => 'image/x-portable-bitmap',
2118
- 'pct' => 'image/pict',
2119
- 'pdb' => 'chemical/x-pdb',
2120
- 'pdf' => 'application/pdf',
2121
- 'pgm' => 'image/x-portable-graymap',
2122
- 'pgn' => 'application/x-chess-pgn',
2123
- 'pic' => 'image/pict',
2124
- 'pict' => 'image/pict',
2125
- 'png' => 'image/png',
2126
- 'pnm' => 'image/x-portable-anymap',
2127
- 'pnt' => 'image/x-macpaint',
2128
- 'pntg' => 'image/x-macpaint',
2129
- 'ppm' => 'image/x-portable-pixmap',
2130
- 'ppt' => 'application/vnd.ms-powerpoint',
2131
- 'ps' => 'application/postscript',
2132
- 'qt' => 'video/quicktime',
2133
- 'qti' => 'image/x-quicktime',
2134
- 'qtif' => 'image/x-quicktime',
2135
- 'ra' => 'audio/x-pn-realaudio',
2136
- 'ram' => 'audio/x-pn-realaudio',
2137
- 'ras' => 'image/x-cmu-raster',
2138
- 'rdf' => 'application/rdf+xml',
2139
- 'rgb' => 'image/x-rgb',
2140
- 'rm' => 'application/vnd.rn-realmedia',
2141
- 'roff' => 'application/x-troff',
2142
- 'rtf' => 'text/rtf',
2143
- 'rtx' => 'text/richtext',
2144
- 'sgm' => 'text/sgml',
2145
- 'sgml' => 'text/sgml',
2146
- 'sh' => 'application/x-sh',
2147
- 'shar' => 'application/x-shar',
2148
- 'silo' => 'model/mesh',
2149
- 'sit' => 'application/x-stuffit',
2150
- 'skd' => 'application/x-koan',
2151
- 'skm' => 'application/x-koan',
2152
- 'skp' => 'application/x-koan',
2153
- 'skt' => 'application/x-koan',
2154
- 'smi' => 'application/smil',
2155
- 'smil' => 'application/smil',
2156
- 'snd' => 'audio/basic',
2157
- 'so' => 'application/octet-stream',
2158
- 'spl' => 'application/x-futuresplash',
2159
- 'src' => 'application/x-wais-source',
2160
- 'sv4cpio' => 'application/x-sv4cpio',
2161
- 'sv4crc' => 'application/x-sv4crc',
2162
- 'svg' => 'image/svg+xml',
2163
- 'swf' => 'application/x-shockwave-flash',
2164
- 't' => 'application/x-troff',
2165
- 'tcl' => 'application/x-tcl',
2166
- 'tex' => 'application/x-tex',
2167
- 'texi' => 'application/x-texinfo',
2168
- 'texinfo' => 'application/x-texinfo',
2169
- 'tif' => 'image/tiff',
2170
- 'tiff' => 'image/tiff',
2171
- 'tr' => 'application/x-troff',
2172
- 'tsv' => 'text/tab-separated-values',
2173
- 'txt' => 'text/plain',
2174
- 'ustar' => 'application/x-ustar',
2175
- 'vcd' => 'application/x-cdlink',
2176
- 'vrml' => 'model/vrml',
2177
- 'vxml' => 'application/voicexml+xml',
2178
- 'wav' => 'audio/x-wav',
2179
- 'wbmp' => 'image/vnd.wap.wbmp',
2180
- 'wbxml' => 'application/vnd.wap.wbxml',
2181
- 'webm' => 'video/webm',
2182
- 'wml' => 'text/vnd.wap.wml',
2183
- 'wmlc' => 'application/vnd.wap.wmlc',
2184
- 'wmls' => 'text/vnd.wap.wmlscript',
2185
- 'wmlsc' => 'application/vnd.wap.wmlscriptc',
2186
- 'wmv' => 'video/x-ms-wmv',
2187
- 'wrl' => 'model/vrml',
2188
- 'xbm' => 'image/x-xbitmap',
2189
- 'xht' => 'application/xhtml+xml',
2190
- 'xhtml' => 'application/xhtml+xml',
2191
- 'xls' => 'application/vnd.ms-excel',
2192
- 'xml' => 'application/xml',
2193
- 'xpm' => 'image/x-xpixmap',
2194
- 'xsl' => 'application/xml',
2195
- 'xslt' => 'application/xslt+xml',
2196
- 'xul' => 'application/vnd.mozilla.xul+xml',
2197
- 'xwd' => 'image/x-xwindowdump',
2198
- 'xyz' => 'chemical/x-xyz',
2199
- );
2200
-
2201
- $filesuffix = pathinfo( $file, PATHINFO_EXTENSION );
2202
- $suffix = strtolower( $filesuffix );
2203
- if ( isset( $mime_types[ $suffix ] ) ) {
2204
- return $mime_types[ $suffix ];
2205
- }
2206
-
2207
- if ( ! is_readable( $file ) ) {
2208
- return 'application/octet-stream';
2209
- }
2210
-
2211
- if ( function_exists( 'fileinfo' ) ) {
2212
- $finfo = finfo_open( FILEINFO_MIME_TYPE );
2213
- $mime = finfo_file( $finfo, $file );
2214
- }
2215
-
2216
- if ( empty( $mime ) && function_exists( 'mime_content_type' ) ) {
2217
- $mime = mime_content_type( $file );
2218
- }
2219
-
2220
- if ( ! empty( $mime ) ) {
2221
- return $mime;
2222
- }
2223
-
2224
- return 'application/octet-stream';
2225
- }
2226
-
2227
- /**
2228
- * Check whether exec has been disabled.
2229
- *
2230
- * @access public
2231
- * @static
2232
- * @return bool
2233
- */
2234
- public static function is_exec() {
2235
-
2236
- // Is function avail
2237
- if ( ! function_exists( 'exec' ) ) {
2238
- return false;
2239
- }
2240
-
2241
- // Is shell_exec disabled?
2242
- if ( in_array( 'exec', array_map( 'trim', explode( ',', @ini_get( 'disable_functions' ) ) ), true ) ) {
2243
- return false;
2244
- }
2245
-
2246
- // Can we issue a simple echo command?
2247
- $output = exec( 'echo backwpupechotest' );
2248
- if ( $output != 'backwpupechotest' ) {
2249
- return false;
2250
- }
2251
-
2252
- return true;
2253
-
2254
- }
2255
-
2256
- /**
2257
- * Delete some data on cloned objects
2258
- */
2259
- public function __clone() {
2260
-
2261
- $this->temp = array();
2262
- $this->run = array();
2263
- }
2264
-
2265
- /**
2266
- * Signal handler
2267
- * @param $signal_send
2268
- */
2269
- public function signal_handler( $signal_send ) {
2270
-
2271
- //known signals
2272
- $signals = array(
2273
- 'SIGHUP' => array(
2274
- 'description' => _x( 'Hangup detected on controlling terminal or death of controlling process', 'SIGHUP: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2275
- 'error' => E_USER_ERROR
2276
- ),
2277
- 'SIGINT' => array(
2278
- 'description' => _x( 'Interrupt from keyboard', 'SIGINT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2279
- 'error' => E_USER_ERROR
2280
- ),
2281
- 'SIGQUIT' => array(
2282
- 'description' => _x( 'Quit from keyboard', 'SIGQUIT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2283
- 'error' => E_USER_ERROR
2284
- ),
2285
- 'SIGILL' => array(
2286
- 'description' => _x( 'Illegal Instruction', 'SIGILL: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2287
- 'error' => E_USER_ERROR
2288
- ),
2289
- 'SIGABRT' => array(
2290
- 'description' => _x( 'Abort signal from abort(3)', 'SIGABRT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2291
- 'error' => E_USER_NOTICE
2292
- ),
2293
- 'SIGBUS' => array(
2294
- 'description' => _x( 'Bus error (bad memory access)', 'SIGBUS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2295
- 'error' => E_USER_ERROR
2296
- ),
2297
- 'SIGFPE' => array(
2298
- 'description' => _x( 'Floating point exception', 'SIGFPE: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2299
- 'error' => E_USER_ERROR
2300
- ),
2301
- 'SIGSEGV' => array(
2302
- 'description' => _x( 'Invalid memory reference', 'SIGSEGV: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2303
- 'error' => E_USER_ERROR
2304
- ),
2305
- 'SIGTERM' => array(
2306
- 'description' => _x( 'Termination signal', 'SIGTERM: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2307
- 'error' => E_USER_WARNING
2308
- ),
2309
- 'SIGSTKFLT' => array(
2310
- 'description' => _x( 'Stack fault on coprocessor', 'SIGSTKFLT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2311
- 'error' => E_USER_ERROR
2312
- ),
2313
- 'SIGUSR1' => array(
2314
- 'description' => _x( 'User-defined signal 1', 'SIGUSR1: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2315
- 'error' => E_USER_NOTICE
2316
- ),
2317
- 'SIGUSR2' => array(
2318
- 'description' => _x( 'User-defined signal 2', 'SIGUSR2: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2319
- 'error' => E_USER_NOTICE
2320
- ),
2321
- 'SIGURG' => array(
2322
- 'description' => _x( 'Urgent condition on socket', 'SIGURG: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2323
- 'error' => E_USER_NOTICE
2324
- ),
2325
- 'SIGXCPU' => array(
2326
- 'description' => _x( 'CPU time limit exceeded', 'SIGXCPU: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2327
- 'error' => E_USER_ERROR
2328
- ),
2329
- 'SIGXFSZ' => array(
2330
- 'description' => _x( 'File size limit exceeded', 'SIGXFSZ: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2331
- 'error' => E_USER_ERROR
2332
- ),
2333
- 'SIGPWR' => array(
2334
- 'description' => _x( 'Power failure', 'SIGPWR: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2335
- 'error' => E_USER_ERROR
2336
- ),
2337
- 'SIGSYS' => array(
2338
- 'description' => _x( 'Bad argument to routine', 'SIGSYS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2339
- 'error' => E_USER_ERROR
2340
- ),
2341
- );
2342
-
2343
- foreach ( $signals as $signal => $config ) {
2344
- if ( defined( $signal ) && $signal_send === constant( $signal ) ) {
2345
- $this->log( sprintf( __( 'Signal "%1$s" (%2$s) is sent to script!', 'backwpup' ), $signal, $config['description'] ), $config['error'] );
2346
- $this->signal = $signal_send;
2347
- break;
2348
- }
2349
- }
2350
-
2351
- }
2352
-
2353
- /**
2354
- *
2355
- * Shutdown function is call if script terminates try to make a restart if needed
2356
- *
2357
- * Prepare the job for start
2358
- *
2359
- * @internal param int the signal that terminates the job
2360
- */
2361
- public function shutdown() {
2362
-
2363
- //Put last error to log if one
2364
- $lasterror = error_get_last();
2365
- if ( $lasterror['type'] === E_ERROR || $lasterror['type'] === E_PARSE || $lasterror['type'] === E_CORE_ERROR || $lasterror['type'] === E_CORE_WARNING || $lasterror['type'] === E_COMPILE_ERROR || $lasterror['type'] === E_COMPILE_WARNING ) {
2366
- $this->log( $lasterror['type'], $lasterror['message'], $lasterror['file'], $lasterror['line'] );
2367
- }
2368
-
2369
- $error = false;
2370
- if ( function_exists( 'pcntl_get_last_error' ) ) {
2371
- $error = pcntl_get_last_error();
2372
- if ( ! empty( $error ) ) {
2373
- $error_msg = pcntl_strerror( $error );
2374
- if ( ! empty( $error_msg ) ) {
2375
- $error = '(' . $error . ') ' . $error_msg;
2376
- }
2377
- }
2378
- if ( ! empty( $error ) ) {
2379
- $this->log( sprintf( __( 'System: %s', 'backwpup' ), $error ), E_USER_ERROR );
2380
- }
2381
- }
2382
-
2383
- if ( function_exists( 'posix_get_last_error' ) && ! $error ) {
2384
- $error = posix_get_last_error();
2385
- if ( ! empty( $error ) ) {
2386
- $error_msg = posix_strerror( $error );
2387
- if ( ! empty( $error_msg ) ) {
2388
- $error = '(' . $error . ') ' . $error_msg;
2389
- }
2390
- }
2391
- if ( ! empty( $error ) ) {
2392
- $this->log( sprintf( __( 'System: %s', 'backwpup' ), $error ), E_USER_ERROR );
2393
- }
2394
- }
2395
-
2396
- $this->do_restart( true );
2397
- }
2398
-
2399
- /**
2400
- *
2401
- * The uncouth exception handler
2402
- *
2403
- * @param object $exception
2404
- */
2405
- public function exception_handler( $exception ) {
2406
-
2407
- $this->log( sprintf( __( 'Exception caught in %1$s: %2$s', 'backwpup' ), get_class( $exception ), $exception->getMessage() ), E_USER_ERROR, $exception->getFile(), $exception->getLine() );
2408
- }
2409
-
2410
- /**
2411
- *
2412
- * Callback for the CURLOPT_READFUNCTION that submit the transferred bytes
2413
- * to build the process bar
2414
- *
2415
- * @param $curl_handle
2416
- * @param $file_handle
2417
- * @param $read_count
2418
- *
2419
- * @return string
2420
- * @internal param $out
2421
- */
2422
- public function curl_read_callback( $curl_handle, $file_handle, $read_count ) {
2423
-
2424
- $data = null;
2425
- if ( ! empty( $file_handle ) && is_numeric( $read_count ) ) {
2426
- $data = fread( $file_handle, $read_count );
2427
- }
2428
-
2429
- if ( $this->job['backuptype'] == 'sync' ) {
2430
- return $data;
2431
- }
2432
-
2433
- $length = ( is_numeric( $read_count ) ) ? $read_count : strlen( $read_count );
2434
- $this->substeps_done = $this->substeps_done + $length;
2435
- $this->update_working_data();
2436
-
2437
- return $data;
2438
- }
2439
-
2440
- /**
2441
- * @param $file
2442
- *
2443
- * @return bool
2444
- */
2445
- public function is_backup_archive( $file ) {
2446
-
2447
- $extensions = array(
2448
- '.tar.gz',
2449
- '.tar.bz2',
2450
- '.tar',
2451
- '.zip'
2452
- );
2453
-
2454
- $file = trim( basename( $file ) );
2455
- $filename = '';
2456
-
2457
- foreach ( $extensions as $extension ) {
2458
- if ( substr( $file, ( strlen( $extension ) * - 1 ) ) === $extension ) {
2459
- $filename = substr( $file, 0, ( strlen( $extension ) * - 1 ) );
2460
- }
2461
- }
2462
-
2463
- if ( ! $filename ) {
2464
- return false;
2465
- }
2466
-
2467
- $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
2468
- $dateregex = array(
2469
- '(0[1-9]|[12][0-9]|3[01])',
2470
- '([1-9]|[12][0-9]|3[01])',
2471
- '(0[1-9]|1[012])',
2472
- '([1-9]|1[012])',
2473
- '((19|20|21)[0-9]{2})',
2474
- '([0-9]{2})',
2475
- '(am|pm)',
2476
- '(AM|PM)',
2477
- '([0-9]{3})',
2478
- '([1-9]|1[012])',
2479
- '([0-9]|1[0-9]|2[0-3])',
2480
- '(0[1-9]|1[012])',
2481
- '([01][0-9]|2[0-3])',
2482
- '([0-5][0-9])',
2483
- '([0-5][0-9])'
2484
- );
2485
-
2486
- $regex = "/^" . str_replace( $datevars, $dateregex, preg_quote( self::sanitize_file_name( $this->job['archivename'] ) ) ) . "$/i";
2487
-
2488
- preg_match( $regex, $filename, $matches );
2489
- if ( ! empty( $matches[0] ) && $matches[0] === $filename ) {
2490
- return true;
2491
- }
2492
-
2493
- return false;
2494
- }
2495
-
2496
- /**
2497
- * For storing and getting data in/from a extra temp file
2498
- *
2499
- * @param string $storage The name of the storage
2500
- * @param array $data data to save in storage
2501
- *
2502
- * @return array|mixed|null data from storage
2503
- */
2504
- public function data_storage( $storage = null, $data = null ) {
2505
-
2506
- if ( empty( $storage ) ) {
2507
- return $data;
2508
- }
2509
-
2510
- $storage = strtolower( $storage );
2511
-
2512
- $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-' . $storage . '.json';
2513
-
2514
- if ( ! empty( $data ) ) {
2515
- file_put_contents( $file, json_encode( $data ) );
2516
- } elseif ( is_readable( $file ) ) {
2517
- $json = file_get_contents( $file );
2518
- $data = json_decode( $json, true );
2519
- }
2520
-
2521
- return $data;
2522
- }
2523
-
2524
- /**
2525
- * Add a Folders to Folder list that should be backup
2526
- *
2527
- * @param array $folders folder to add
2528
- * @param bool $new overwrite existing file
2529
- */
2530
- public function add_folders_to_backup( $folders = array(), $new = false ) {
2531
-
2532
- if ( ! is_array( $folders ) ) {
2533
- $folders = (array) $folders;
2534
- }
2535
-
2536
- $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-folder.php';
2537
-
2538
- if ( ! file_exists( $file ) || $new ) {
2539
- file_put_contents( $file, '<?php' . PHP_EOL );
2540
- }
2541
-
2542
- $content = '';
2543
- foreach ( $folders AS $folder ) {
2544
- $content .= '//' . $folder . PHP_EOL;
2545
- }
2546
-
2547
- if ( $content ) {
2548
- file_put_contents( $file, $content, FILE_APPEND );
2549
- }
2550
- }
2551
-
2552
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class in that the BackWPup job runs
5
+ */
6
+ final class BackWPup_Job {
7
+
8
+ /**
9
+ * @var array of the job settings
10
+ */
11
+ public $job = array();
12
+
13
+ /**
14
+ * @var int The timestamp when the job starts
15
+ */
16
+ public $start_time = 0;
17
+
18
+ /**
19
+ * @var string the logfile
20
+ */
21
+ public $logfile = '';
22
+ /**
23
+ * @var array for temp values
24
+ */
25
+ public $temp = array();
26
+ /**
27
+ * @var string Folder where is Backup files in
28
+ */
29
+ public $backup_folder = '';
30
+ /**
31
+ * @var string the name of the Backup archive file
32
+ */
33
+ public $backup_file = '';
34
+ /**
35
+ * @var int The size of the Backup archive file
36
+ */
37
+ public $backup_filesize = 0;
38
+ /**
39
+ * @var int PID of script
40
+ */
41
+ public $pid = 0;
42
+ /**
43
+ * @var float Timestamp of last update off .running file
44
+ */
45
+ public $timestamp_last_update = 0;
46
+ /**
47
+ * @var int Number of warnings
48
+ */
49
+ public $warnings = 0;
50
+ /**
51
+ * @var int Number of errors
52
+ */
53
+ public $errors = 0;
54
+ /**
55
+ * @var string the last log notice message
56
+ */
57
+ public $lastmsg = '';
58
+ /**
59
+ * @var string the last log error/waring message
60
+ */
61
+ public $lasterrormsg = '';
62
+ /**
63
+ * @var array of steps to do
64
+ */
65
+ public $steps_todo = array( 'CREATE' );
66
+ /**
67
+ * @var array of done steps
68
+ */
69
+ public $steps_done = array();
70
+ /**
71
+ * @var array of steps data
72
+ */
73
+ public $steps_data = array();
74
+ /**
75
+ * @var string working on step
76
+ */
77
+ public $step_working = 'CREATE';
78
+ /**
79
+ * @var int Number of sub steps must do in step
80
+ */
81
+ public $substeps_todo = 0;
82
+ /**
83
+ * @var int Number of sub steps done in step
84
+ */
85
+ public $substeps_done = 0;
86
+ /**
87
+ * @var int Percent of steps done
88
+ */
89
+ public $step_percent = 1;
90
+ /**
91
+ * @var int Percent of sub steps done
92
+ */
93
+ public $substep_percent = 1;
94
+ /**
95
+ * @var array of files to additional to backup
96
+ */
97
+ public $additional_files_to_backup = array();
98
+ /**
99
+ * @var array of files/folder to exclude from backup
100
+ */
101
+ public $exclude_from_backup = array();
102
+ /**
103
+ * @var int count of affected files
104
+ */
105
+ public $count_files = 0;
106
+ /**
107
+ * @var int count of affected file sizes
108
+ */
109
+ public $count_files_size = 0;
110
+ /**
111
+ * @var int count of affected folders
112
+ */
113
+ public $count_folder = 0;
114
+ /**
115
+ * If job aborted from user
116
+ * @var bool
117
+ */
118
+ public $user_abort = false;
119
+ /**
120
+ * A uniqid ID uniqid('', true); to identify process
121
+ * @var string
122
+ */
123
+ public $uniqid = '';
124
+ /**
125
+ * @var float Timestamp of script start
126
+ */
127
+ private $timestamp_script_start = 0;
128
+ /**
129
+ * Stores data that will only used in a single run
130
+ * @var array
131
+ */
132
+ private $run = array();
133
+ /**
134
+ * @var string logging level (normal|normal_untranslated|debug|debug_untranslated)
135
+ */
136
+ private $log_level = 'normal';
137
+
138
+ /**
139
+ * @var int Signal of signal handler
140
+ */
141
+ private $signal = 0;
142
+
143
+ /**
144
+ *
145
+ */
146
+ public static function start_http( $starttype, $jobid = 0 ) {
147
+
148
+ //load text domain
149
+ $log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
150
+ if ( strstr( $log_level, 'translated' ) ) {
151
+ BackWPup::load_text_domain();
152
+ } else {
153
+ add_filter( 'override_load_textdomain', '__return_true' );
154
+ $GLOBALS['l10n'] = array();
155
+ }
156
+
157
+ if ( $starttype !== 'restart' ) {
158
+
159
+ //check job id exists
160
+ if ( $jobid !== BackWPup_Option::get( $jobid, 'jobid' ) ) {
161
+ return false;
162
+ }
163
+
164
+ //check folders
165
+ $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
166
+ $folder_message_log = BackWPup_File::check_folder( BackWPup_File::get_absolute_path( $log_folder ) );
167
+ $folder_message_temp = BackWPup_File::check_folder( BackWPup::get_plugin_data( 'TEMP' ), true );
168
+ if ( ! empty( $folder_message_log ) || ! empty( $folder_message_temp ) ) {
169
+ BackWPup_Admin::message( $folder_message_log, true );
170
+ BackWPup_Admin::message( $folder_message_temp, true );
171
+ return false;
172
+ }
173
+ }
174
+
175
+ // redirect
176
+ if ( $starttype === 'runnowalt' ) {
177
+ ob_start();
178
+ wp_redirect( add_query_arg( array( 'page' => 'backwpupjobs' ), network_admin_url( 'admin.php' ) ) );
179
+ echo ' ';
180
+ flush();
181
+ if ( $level = ob_get_level() ) {
182
+ for ( $i = 0; $i < $level; $i ++ ) {
183
+ ob_end_clean();
184
+ }
185
+ }
186
+ }
187
+
188
+ // Should be preventing doubled running job's on http requests
189
+ $random = mt_rand( 10, 90 ) * 10000;
190
+ usleep( $random );
191
+
192
+ //check running job
193
+ $backwpup_job_object = self::get_working_data();
194
+ //start class
195
+ if ( ! $backwpup_job_object && in_array( $starttype, array( 'runnow', 'runnowalt', 'runext', 'cronrun' ), true ) && $jobid ) {
196
+ //schedule restart event
197
+ wp_schedule_single_event( time() + 60, 'backwpup_cron', array( 'id' => 'restart' ) );
198
+ //start job
199
+ $backwpup_job_object = new self();
200
+ $backwpup_job_object->create( $starttype, $jobid );
201
+ }
202
+ if ( $backwpup_job_object ) {
203
+ $backwpup_job_object->run();
204
+ }
205
+ }
206
+
207
+ /**
208
+ *
209
+ * Get data off a working job
210
+ *
211
+ * @return bool|object BackWPup_Job Object or Bool if file not exits
212
+ */
213
+ public static function get_working_data() {
214
+
215
+ if ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
216
+ clearstatcache( true, BackWPup::get_plugin_data( 'running_file' ) );
217
+ } else {
218
+ clearstatcache();
219
+ }
220
+
221
+ if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
222
+ return false;
223
+ }
224
+
225
+ $file_data = file_get_contents( BackWPup::get_plugin_data( 'running_file' ), false, null, 8 );
226
+ if ( empty( $file_data ) ) {
227
+ return false;
228
+ }
229
+
230
+ if ( $job_object = unserialize( $file_data ) ) {
231
+ if ( $job_object instanceof BackWPup_Job ) {
232
+ return $job_object;
233
+ }
234
+ }
235
+
236
+ return false;
237
+
238
+ }
239
+
240
+ /**
241
+ *
242
+ * This starts or restarts the job working
243
+ *
244
+ * @param string $start_type Start types are 'runnow', 'runnowalt', 'cronrun', 'runext', 'runcli'
245
+ * @param array|int $job_id The id of job of a job to start
246
+ */
247
+ private function create( $start_type, $job_id = 0 ) {
248
+ global $wpdb;
249
+ /* @var wpdb $wpdb */
250
+
251
+ //check startype
252
+ if ( ! in_array( $start_type, array( 'runnow', 'runnowalt', 'cronrun', 'runext', 'runcli' ), true ) ) {
253
+ return;
254
+ }
255
+
256
+ if ( $job_id ) {
257
+ $this->job = BackWPup_Option::get_job( $job_id );
258
+ } else {
259
+ return;
260
+ }
261
+
262
+ $this->start_time = current_time( 'timestamp' );
263
+ $this->lastmsg = __( 'Starting job', 'backwpup' );
264
+ //set Logfile
265
+ $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
266
+ $log_folder = BackWPup_File::get_absolute_path( $log_folder );
267
+ $this->logfile = $log_folder . 'backwpup_log_' . BackWPup::get_plugin_data( 'hash' ) . '_' . date( 'Y-m-d_H-i-s', current_time( 'timestamp' ) ) . '.html';
268
+ //write settings to job
269
+ BackWPup_Option::update( $this->job['jobid'], 'lastrun', $this->start_time );
270
+ BackWPup_Option::update( $this->job['jobid'], 'logfile', $this->logfile ); //Set current logfile
271
+ BackWPup_Option::update( $this->job['jobid'], 'lastbackupdownloadurl', '' );
272
+ //Set needed job values
273
+ $this->timestamp_last_update = microtime( true );
274
+ $this->exclude_from_backup = explode( ',', trim( $this->job['fileexclude'] ) );
275
+ $this->exclude_from_backup = array_unique( $this->exclude_from_backup );
276
+ //setup job steps
277
+ $this->steps_data['CREATE']['CALLBACK'] = '';
278
+ $this->steps_data['CREATE']['NAME'] = __( 'Job Start', 'backwpup' );
279
+ $this->steps_data['CREATE']['STEP_TRY'] = 0;
280
+ //ADD Job types file
281
+ /* @var $job_type_class BackWPup_JobTypes */
282
+ $job_need_dest = false;
283
+ if ( $job_types = BackWPup::get_job_types() ) {
284
+ foreach ( $job_types as $id => $job_type_class ) {
285
+ if ( in_array( $id, $this->job['type'], true ) && $job_type_class->creates_file() ) {
286
+ $this->steps_todo[] = 'JOB_' . $id;
287
+ $this->steps_data[ 'JOB_' . $id ]['NAME'] = $job_type_class->info['description'];
288
+ $this->steps_data[ 'JOB_' . $id ]['STEP_TRY'] = 0;
289
+ $this->steps_data[ 'JOB_' . $id ]['SAVE_STEP_TRY'] = 0;
290
+ $job_need_dest = true;
291
+ }
292
+ }
293
+ }
294
+ //add destinations and create archive if a job where files to backup
295
+ if ( $job_need_dest ) {
296
+ //Create manifest file
297
+ $this->steps_todo[] = 'CREATE_MANIFEST';
298
+ $this->steps_data['CREATE_MANIFEST']['NAME'] = __( 'Creates manifest file', 'backwpup' );
299
+ $this->steps_data['CREATE_MANIFEST']['STEP_TRY'] = 0;
300
+ $this->steps_data['CREATE_MANIFEST']['SAVE_STEP_TRY'] = 0;
301
+ //Add archive creation and backup filename on backup type archive
302
+ if ( $this->job['backuptype'] == 'archive' ) {
303
+ //get Backup folder if destination folder set
304
+ if ( in_array( 'FOLDER', $this->job['destinations'], true ) ) {
305
+ $this->backup_folder = $this->job['backupdir'];
306
+ //check backup folder
307
+ if ( ! empty( $this->backup_folder ) ) {
308
+ $this->backup_folder = BackWPup_File::get_absolute_path( $this->backup_folder );
309
+ $this->job['backupdir'] = $this->backup_folder;
310
+ }
311
+ }
312
+ //set temp folder to backup folder if not set because we need one
313
+ if ( ! $this->backup_folder || $this->backup_folder == '/' ) {
314
+ $this->backup_folder = BackWPup::get_plugin_data( 'TEMP' );
315
+ }
316
+ //Create backup archive full file name
317
+ $this->backup_file = $this->generate_filename( $this->job['archivename'], $this->job['archiveformat'] );
318
+ //add archive create
319
+ $this->steps_todo[] = 'CREATE_ARCHIVE';
320
+ $this->steps_data['CREATE_ARCHIVE']['NAME'] = __( 'Creates archive', 'backwpup' );
321
+ $this->steps_data['CREATE_ARCHIVE']['STEP_TRY'] = 0;
322
+ $this->steps_data['CREATE_ARCHIVE']['SAVE_STEP_TRY'] = 0;
323
+ }
324
+ //ADD Destinations
325
+ /* @var BackWPup_Destinations $dest_class */
326
+ foreach ( BackWPup::get_registered_destinations() as $id => $dest ) {
327
+ if ( ! in_array( $id, $this->job['destinations'], true ) || empty( $dest['class'] ) ) {
328
+ continue;
329
+ }
330
+ $dest_class = BackWPup::get_destination( $id );
331
+ if ( $dest_class->can_run( $this->job ) ) {
332
+ if ( $this->job['backuptype'] == 'sync' ) {
333
+ if ( $dest['can_sync'] ) {
334
+ $this->steps_todo[] = 'DEST_SYNC_' . $id;
335
+ $this->steps_data[ 'DEST_SYNC_' . $id ]['NAME'] = $dest['info']['description'];
336
+ $this->steps_data[ 'DEST_SYNC_' . $id ]['STEP_TRY'] = 0;
337
+ $this->steps_data[ 'DEST_SYNC_' . $id ]['SAVE_STEP_TRY'] = 0;
338
+ }
339
+ } else {
340
+ $this->steps_todo[] = 'DEST_' . $id;
341
+ $this->steps_data[ 'DEST_' . $id ]['NAME'] = $dest['info']['description'];
342
+ $this->steps_data[ 'DEST_' . $id ]['STEP_TRY'] = 0;
343
+ $this->steps_data[ 'DEST_' . $id ]['SAVE_STEP_TRY'] = 0;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ //ADD Job type no file
349
+ if ( $job_types = BackWPup::get_job_types() ) {
350
+ foreach ( $job_types as $id => $job_type_class ) {
351
+ if ( in_array( $id, $this->job['type'], true ) && ! $job_type_class->creates_file() ) {
352
+ $this->steps_todo[] = 'JOB_' . $id;
353
+ $this->steps_data[ 'JOB_' . $id ]['NAME'] = $job_type_class->info['description'];
354
+ $this->steps_data[ 'JOB_' . $id ]['STEP_TRY'] = 0;
355
+ $this->steps_data[ 'JOB_' . $id ]['SAVE_STEP_TRY'] = 0;
356
+ }
357
+ }
358
+ }
359
+ $this->steps_todo[] = 'END';
360
+ $this->steps_data['END']['NAME'] = __( 'End of Job', 'backwpup' );
361
+ $this->steps_data['END']['STEP_TRY'] = 1;
362
+ //must write working data
363
+ $this->write_running_file();
364
+
365
+ //set log level
366
+ $this->log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
367
+ if ( ! in_array( $this->log_level, array( 'normal_translated', 'normal', 'debug_translated', 'debug' ), true ) ) {
368
+ $this->log_level = 'normal_translated';
369
+ }
370
+ //create log file
371
+ $head = '';
372
+ $info = '';
373
+ $head .= "<!DOCTYPE html>" . PHP_EOL;
374
+ $head .= "<html lang=\"" . str_replace( '_', '-', get_locale() ) . "\">" . PHP_EOL;
375
+ $head .= "<head>" . PHP_EOL;
376
+ $head .= "<meta charset=\"" . get_bloginfo( 'charset' ) . "\" />" . PHP_EOL;
377
+ $head .= "<title>" . sprintf( __( 'BackWPup log for %1$s from %2$s at %3$s', 'backwpup' ), $this->job['name'], date_i18n( get_option( 'date_format' ) ), date_i18n( get_option( 'time_format' ) ) ) . "</title>" . PHP_EOL;
378
+ $head .= "<meta name=\"robots\" content=\"noindex, nofollow\" />" . PHP_EOL;
379
+ $head .= "<meta name=\"copyright\" content=\"Copyright &copy; 2012 - " . date( 'Y' ) . " Inpsyde GmbH\" />" . PHP_EOL;
380
+ $head .= "<meta name=\"author\" content=\"Inpsyde GmbH\" />" . PHP_EOL;
381
+ $head .= "<meta name=\"generator\" content=\"BackWPup " . BackWPup::get_plugin_data( 'Version' ) . "\" />" . PHP_EOL;
382
+ $head .= "<meta http-equiv=\"cache-control\" content=\"no-cache\" />" . PHP_EOL;
383
+ $head .= "<meta http-equiv=\"pragma\" content=\"no-cache\" />" . PHP_EOL;
384
+ $head .= "<meta name=\"date\" content=\"" . date( 'c' ) . "\" />" . PHP_EOL;
385
+ $head .= str_pad( '<meta name="backwpup_errors" content="0" />', 100 ) . PHP_EOL;
386
+ $head .= str_pad( '<meta name="backwpup_warnings" content="0" />', 100 ) . PHP_EOL;
387
+ $head .= "<meta name=\"backwpup_jobid\" content=\"" . $this->job['jobid'] . "\" />" . PHP_EOL;
388
+ $head .= "<meta name=\"backwpup_jobname\" content=\"" . esc_attr( $this->job['name'] ) . "\" />" . PHP_EOL;
389
+ $head .= "<meta name=\"backwpup_jobtype\" content=\"" . implode( '+', $this->job['type'] ) . "\" />" . PHP_EOL;
390
+ $head .= str_pad( '<meta name="backwpup_backupfilesize" content="0" />', 100 ) . PHP_EOL;
391
+ $head .= str_pad( '<meta name="backwpup_jobruntime" content="0" />', 100 ) . PHP_EOL;
392
+ $head .= '</head>' . PHP_EOL;
393
+ $head .= '<body style="margin:0;padding:3px;font-family:monospace;font-size:12px;line-height:15px;background-color:black;color:#c0c0c0;white-space:nowrap;">' . PHP_EOL;
394
+ $info .= sprintf( _x( '[INFO] %1$s %2$s; A project of Inpsyde GmbH', 'Plugin name; Plugin Version; plugin url', 'backwpup' ), BackWPup::get_plugin_data( 'name' ), BackWPup::get_plugin_data( 'Version' ), __( 'http://backwpup.com', 'backwpup' ) ) . '<br />' . PHP_EOL;
395
+ $info .= sprintf( _x( '[INFO] WordPress %1$s on %2$s', 'WordPress Version; Blog url', 'backwpup' ), BackWPup::get_plugin_data( 'wp_version' ), esc_attr( site_url( '/' ) ) ) . '<br />' . PHP_EOL;
396
+ $level = __( 'Normal', 'backwpup' );
397
+ $translated = '';
398
+ if ( $this->is_debug() ) {
399
+ $level = __( 'Debug', 'backwpup' );
400
+ }
401
+ if ( is_textdomain_loaded( 'backwpup' ) ) {
402
+ $translated = __( '(translated)', 'backwpup' );
403
+ }
404
+ $info .= sprintf( __( '[INFO] Log Level: %1$s %2$s', 'backwpup' ), $level, $translated ) . '<br />' . PHP_EOL;
405
+ $job_name = esc_attr( $this->job['name'] );
406
+ if ( $this->is_debug() ) {
407
+ $job_name .= '; ' . implode( '+', $this->job['type'] );
408
+ }
409
+ $info .= sprintf( __( '[INFO] BackWPup job: %1$s', 'backwpup' ), $job_name ) . '<br />' . PHP_EOL;
410
+ if ( $this->is_debug() ) {
411
+ $current_user = wp_get_current_user();
412
+ $info .= sprintf( __( '[INFO] Runs with user: %1$s (%2$d) ', 'backwpup' ), $current_user->user_login, $current_user->ID ) . '<br />' . PHP_EOL;
413
+ }
414
+ if ( $this->job['activetype'] === 'wpcron' ) {
415
+ //check next run
416
+ $cron_next = wp_next_scheduled( 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
417
+ if ( ! $cron_next || $cron_next < time() ) {
418
+ wp_unschedule_event( $cron_next, 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
419
+ $cron_next = BackWPup_Cron::cron_next( $this->job['cron'] );
420
+ wp_schedule_single_event( $cron_next, 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
421
+ $cron_next = wp_next_scheduled( 'backwpup_cron', array( 'id' => $this->job['jobid'] ) );
422
+ }
423
+ //output scheduling
424
+ if ( $this->is_debug() ) {
425
+ if ( ! $cron_next ) {
426
+ $cron_next = __( 'Not scheduled!', 'backwpup' );
427
+ } else {
428
+ $cron_next = date_i18n( 'D, j M Y @ H:i', $cron_next + ( get_option( 'gmt_offset' ) * 3600 ), true );
429
+ }
430
+ $info .= sprintf( __( '[INFO] Cron: %s; Next: %s ', 'backwpup' ), $this->job['cron'], $cron_next ) . '<br />' . PHP_EOL;
431
+ }
432
+ } elseif ( $this->job['activetype'] == 'link' && $this->is_debug() ) {
433
+ $info .= __( '[INFO] BackWPup job start with link is active', 'backwpup' ) . '<br />' . PHP_EOL;
434
+ } elseif ( $this->job['activetype'] == 'easycron' && $this->is_debug() ) {
435
+ $info .= __( '[INFO] BackWPup job start with EasyCron.com', 'backwpup' ) . '<br />' . PHP_EOL;
436
+ //output scheduling
437
+ if ( $this->is_debug() ) {
438
+ $cron_next = BackWPup_Cron::cron_next( $this->job['cron'] );
439
+ $cron_next = date_i18n( 'D, j M Y @ H:i', $cron_next + ( get_option( 'gmt_offset' ) * 3600 ), true );
440
+ $info .= sprintf( __( '[INFO] Cron: %s; Next: %s ', 'backwpup' ), $this->job['cron'], $cron_next ) . '<br />' . PHP_EOL;
441
+ }
442
+ } elseif ( $this->is_debug() ) {
443
+ $info .= __( '[INFO] BackWPup no automatic job start configured', 'backwpup' ) . '<br />' . PHP_EOL;
444
+ }
445
+ if ( $this->is_debug() ) {
446
+ if ( $start_type == 'cronrun' ) {
447
+ $info .= __( '[INFO] BackWPup job started from wp-cron', 'backwpup' ) . '<br />' . PHP_EOL;
448
+ } elseif ( $start_type == 'runnow' || $start_type == 'runnowalt' ) {
449
+ $info .= __( '[INFO] BackWPup job started manually', 'backwpup' ) . '<br />' . PHP_EOL;
450
+ } elseif ( $start_type == 'runext' ) {
451
+ $info .= __( '[INFO] BackWPup job started from external url', 'backwpup' ) . '<br />' . PHP_EOL;
452
+ } elseif ( $start_type == 'runcli' ) {
453
+ $info .= __( '[INFO] BackWPup job started form commandline interface', 'backwpup' ) . '<br />' . PHP_EOL;
454
+ }
455
+ $bit = '';
456
+ if ( PHP_INT_SIZE === 4 ) {
457
+ $bit = ' (32bit)';
458
+ }
459
+ if ( PHP_INT_SIZE === 8 ) {
460
+ $bit = ' (64bit)';
461
+ }
462
+ $info .= __( '[INFO] PHP ver.:', 'backwpup' ) . ' ' . PHP_VERSION . $bit . '; ' . PHP_SAPI . '; ' . PHP_OS . '<br />' . PHP_EOL;
463
+ $info .= sprintf( __( '[INFO] Maximum PHP script execution time is %1$d seconds', 'backwpup' ), ini_get( 'max_execution_time' ) ) . '<br />' . PHP_EOL;
464
+ if ( php_sapi_name() != 'cli' ) {
465
+ $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
466
+ if ( ! empty( $job_max_execution_time ) ) {
467
+ $info .= sprintf( __( '[INFO] Script restart time is configured to %1$d seconds', 'backwpup' ), $job_max_execution_time ) . '<br />' . PHP_EOL;
468
+ }
469
+ }
470
+ $info .= sprintf( __( '[INFO] MySQL ver.: %s', 'backwpup' ), $wpdb->get_var( "SELECT VERSION() AS version" ) ) . '<br />' . PHP_EOL;
471
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
472
+ $info .= sprintf( __( '[INFO] Web Server: %s', 'backwpup' ), $_SERVER['SERVER_SOFTWARE'] ) . '<br />' . PHP_EOL;
473
+ }
474
+ if ( function_exists( 'curl_init' ) ) {
475
+ $curlversion = curl_version();
476
+ $info .= sprintf( __( '[INFO] curl ver.: %1$s; %2$s', 'backwpup' ), $curlversion['version'], $curlversion['ssl_version'] ) . '<br />' . PHP_EOL;
477
+ }
478
+ $info .= sprintf( __( '[INFO] Temp folder is: %s', 'backwpup' ), BackWPup::get_plugin_data( 'TEMP' ) ) . '<br />' . PHP_EOL;
479
+ }
480
+ if ( $this->is_debug() ) {
481
+ $logfile = $this->logfile;
482
+ } else {
483
+ $logfile = basename( $this->logfile );
484
+ }
485
+ $info .= sprintf( __( '[INFO] Logfile is: %s', 'backwpup' ), $logfile ) . '<br />' . PHP_EOL;
486
+ if ( ! empty( $this->backup_file ) && $this->job['backuptype'] === 'archive' ) {
487
+ if ( $this->is_debug() ) {
488
+ $backupfile = $this->backup_folder . $this->backup_file;
489
+ } else {
490
+ $backupfile = $this->backup_file;
491
+ }
492
+ $info .= sprintf( __( '[INFO] Backup file is: %s', 'backwpup' ), $backupfile ) . '<br />' . PHP_EOL;
493
+ } else {
494
+ $info .= sprintf( __( '[INFO] Backup type is: %s', 'backwpup' ), $this->job['backuptype'] ) . '<br />' . PHP_EOL;
495
+ }
496
+ //output info on cli
497
+ if ( php_sapi_name() == 'cli' && defined( 'STDOUT' ) ) {
498
+ fwrite( STDOUT, strip_tags( $info ) );
499
+ }
500
+ if ( ! file_put_contents( $this->logfile, $head . $info, FILE_APPEND ) ) {
501
+ $this->logfile = '';
502
+ $this->log( __( 'Could not write log file', 'backwpup' ), E_USER_ERROR );
503
+ }
504
+ //test for destinations
505
+ if ( $job_need_dest ) {
506
+ $desttest = false;
507
+ foreach ( $this->steps_todo as $deststeptest ) {
508
+ if ( substr( $deststeptest, 0, 5 ) == 'DEST_' ) {
509
+ $desttest = true;
510
+ break;
511
+ }
512
+ }
513
+ if ( ! $desttest ) {
514
+ $this->log( __( 'No destination correctly defined for backup! Please correct job settings.', 'backwpup' ), E_USER_ERROR );
515
+ $this->steps_todo = array( 'END' );
516
+ }
517
+ }
518
+ //test backup folder
519
+ if ( ! empty( $this->backup_folder ) ) {
520
+ $folder_message = BackWPup_File::check_folder( $this->backup_folder, true );
521
+ if ( ! empty( $folder_message ) ) {
522
+ $this->log( $folder_message, E_USER_ERROR );
523
+ $this->steps_todo = array( 'END' );
524
+ }
525
+ }
526
+
527
+ //Set start as done
528
+ $this->steps_done[] = 'CREATE';
529
+ }
530
+
531
+ /**
532
+ * @param $name
533
+ * @param string $suffix
534
+ * @param bool $delete_temp_file
535
+ *
536
+ * @return string
537
+ */
538
+ public function generate_filename( $name, $suffix = '', $delete_temp_file = true ) {
539
+
540
+ $local_time = current_time( 'timestamp' );
541
+
542
+ $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
543
+ $datevalues = array(
544
+ date( 'd', $local_time ),
545
+ date( 'j', $local_time ),
546
+ date( 'm', $local_time ),
547
+ date( 'n', $local_time ),
548
+ date( 'Y', $local_time ),
549
+ date( 'y', $local_time ),
550
+ date( 'a', $local_time ),
551
+ date( 'A', $local_time ),
552
+ date( 'B', $local_time ),
553
+ date( 'g', $local_time ),
554
+ date( 'G', $local_time ),
555
+ date( 'h', $local_time ),
556
+ date( 'H', $local_time ),
557
+ date( 'i', $local_time ),
558
+ date( 's', $local_time )
559
+ );
560
+
561
+ if ( $suffix ) {
562
+ $suffix = '.' . trim( $suffix, '. ' );
563
+ }
564
+
565
+ $name = str_replace( $datevars, $datevalues, self::sanitize_file_name( $name ) );
566
+ $name .= $suffix;
567
+ if ( $delete_temp_file && is_writeable( BackWPup::get_plugin_data( 'TEMP' ) . $name ) && ! is_dir( BackWPup::get_plugin_data( 'TEMP' ) . $name ) && ! is_link( BackWPup::get_plugin_data( 'TEMP' ) . $name ) ) {
568
+ unlink( BackWPup::get_plugin_data( 'TEMP' ) . $name );
569
+ }
570
+
571
+ return BackWPup_Option::normalize_archive_name( $name, $this->job['jobid'] );
572
+ }
573
+
574
+ /**
575
+ * Sanitizes a filename, replacing whitespace with underscores.
576
+ *
577
+ * @param $filename
578
+ *
579
+ * @return mixed
580
+ */
581
+ public static function sanitize_file_name( $filename ) {
582
+
583
+ $filename = trim( $filename );
584
+
585
+ $special_chars = array(
586
+ "?",
587
+ "[",
588
+ "]",
589
+ "/",
590
+ "\\",
591
+ "=",
592
+ "<",
593
+ ">",
594
+ ":",
595
+ ";",
596
+ ",",
597
+ "'",
598
+ "\"",
599
+ "&",
600
+ "$",
601
+ "#",
602
+ "*",
603
+ "(",
604
+ ")",
605
+ "|",
606
+ "~",
607
+ "`",
608
+ "!",
609
+ "{",
610
+ "}",
611
+ chr( 0 )
612
+ );
613
+
614
+ $filename = str_replace( $special_chars, '', $filename );
615
+
616
+ $filename = str_replace( array( ' ', '%20', '+' ), '_', $filename );
617
+ $filename = str_replace( array( "\n", "\t", "\r" ), '-', $filename );
618
+ $filename = trim( $filename, '.-_' );
619
+
620
+ return $filename;
621
+ }
622
+
623
+ /**
624
+ * Checks if the given archive belongs to this job.
625
+ *
626
+ * @param string $file
627
+ *
628
+ * @return bool
629
+ */
630
+ public function owns_backup_archive( $file ) {
631
+ $prefix = BackWPup_Option::get_archive_name_prefix( $this->job['jobid'] );
632
+ return substr( basename( $file ), 0, strlen( $prefix ) ) == $prefix;
633
+ }
634
+
635
+
636
+ private function write_running_file() {
637
+
638
+ $clone = clone $this;
639
+ $data = '<?php //' . serialize( $clone );
640
+
641
+ $write = file_put_contents( BackWPup::get_plugin_data( 'running_file' ), $data );
642
+ if ( ! $write || $write < strlen( $data ) ) {
643
+ unlink( BackWPup::get_plugin_data( 'running_file' ) );
644
+ $this->log( __( 'Cannot write progress to working file. Job will be aborted.', 'backwpup' ), E_USER_ERROR );
645
+ }
646
+ }
647
+
648
+ /**
649
+ * Write messages to log file
650
+ *
651
+ * @param string $message the error message
652
+ * @param int $type the error number (E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE, ...)
653
+ * @param string $file the full path of file with error (__FILE__)
654
+ * @param int $line the line in that is the error (__LINE__)
655
+ *
656
+ * @return bool true
657
+ */
658
+ public function log( $message, $type = E_USER_NOTICE, $file = '', $line = 0 ) {
659
+
660
+ // if error has been suppressed with an @
661
+ if ( error_reporting() == 0 ) {
662
+ return true;
663
+ }
664
+
665
+ //if first the type an second the message switch it on user errors
666
+ if ( ! is_int( $type ) && is_int( $message ) && in_array( $message, array(
667
+ 1,
668
+ 2,
669
+ 4,
670
+ 8,
671
+ 16,
672
+ 32,
673
+ 64,
674
+ 128,
675
+ 256,
676
+ 512,
677
+ 1024,
678
+ 2048,
679
+ 4096,
680
+ 8192,
681
+ 16384
682
+ ), true )
683
+ ) {
684
+ $temp = $message;
685
+ $message = $type;
686
+ $type = $temp;
687
+ }
688
+
689
+ //json message if array or object
690
+ if ( is_array( $message ) || is_object( $message ) ) {
691
+ $message = json_encode( $message );
692
+ }
693
+
694
+ //if not set line and file get it
695
+ if ( $this->is_debug() ) {
696
+ if ( empty( $file ) || empty( $line ) ) {
697
+ $debug_info = debug_backtrace();
698
+ $file = $debug_info[0]['file'];
699
+ $line = $debug_info[0]['line'];
700
+ }
701
+ }
702
+
703
+ $error = false;
704
+ $warning = false;
705
+
706
+ switch ( $type ) {
707
+ case E_NOTICE:
708
+ case E_USER_NOTICE:
709
+ break;
710
+ case E_WARNING:
711
+ case E_CORE_WARNING:
712
+ case E_COMPILE_WARNING:
713
+ case E_USER_WARNING:
714
+ $this->warnings ++;
715
+ $warning = true;
716
+ $message = __( 'WARNING:', 'backwpup' ) . ' ' . $message;
717
+ break;
718
+ case E_ERROR:
719
+ case E_PARSE:
720
+ case E_CORE_ERROR:
721
+ case E_COMPILE_ERROR:
722
+ case E_USER_ERROR:
723
+ $this->errors ++;
724
+ $error = true;
725
+ $message = __( 'ERROR:', 'backwpup' ) . ' ' . $message;
726
+ break;
727
+ case 8192: //E_DEPRECATED comes with php 5.3
728
+ case 16384: //E_USER_DEPRECATED comes with php 5.3
729
+ $message = __( 'DEPRECATED:', 'backwpup' ) . ' ' . $message;
730
+ break;
731
+ case E_STRICT:
732
+ $message = __( 'STRICT NOTICE:', 'backwpup' ) . ' ' . $message;
733
+ break;
734
+ case E_RECOVERABLE_ERROR:
735
+ $this->errors ++;
736
+ $error = true;
737
+ $message = __( 'RECOVERABLE ERROR:', 'backwpup' ) . ' ' . $message;
738
+ break;
739
+ default:
740
+ $message = $type . ': ' . $message;
741
+ break;
742
+ }
743
+
744
+ $in_file = $this->get_destination_path_replacement( $file );
745
+
746
+ //print message to cli
747
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
748
+ $output_message = str_replace( array( '&hellip;', '&#160;' ), array( '...', ' ' ), esc_html( $message ) );
749
+ if ( ! call_user_func( array( '\cli\Shell', 'isPiped' ) ) ) {
750
+ if ( $error ) {
751
+ $output_message = '%r' . $output_message . '%n';
752
+ }
753
+ if ( $warning ) {
754
+ $output_message = '%y' . $output_message . '%n';
755
+ }
756
+ $output_message = call_user_func( array( '\cli\Colors', 'colorize' ), $output_message, true );
757
+ }
758
+ WP_CLI::line( $output_message );
759
+ } elseif ( php_sapi_name() == 'cli' && defined( 'STDOUT' ) ) {
760
+ $output_message = str_replace( array( '&hellip;', '&#160;' ), array( '...', ' ' ), esc_html( $message ) ) . PHP_EOL;
761
+ fwrite( STDOUT, $output_message );
762
+ }
763
+
764
+ //timestamp for log file
765
+ $debug_info = '';
766
+ if ( $this->is_debug() ) {
767
+ $debug_info = ' title="[Type: ' . $type . '|Line: ' . $line . '|File: ' . $in_file . '|Mem: ' . size_format( @memory_get_usage( true ), 2 ) . '|Mem Max: ' . size_format( @memory_get_peak_usage( true ), 2 ) . '|Mem Limit: ' . ini_get( 'memory_limit' ) . '|PID: ' . self::get_pid() . ' | UniqID: ' . $this->uniqid . '|Queries: ' . get_num_queries() . ']"';
768
+ }
769
+ $timestamp = '<span datetime="' . date( 'c' ) . '" ' . $debug_info . '>[' . date( 'd-M-Y H:i:s', current_time( 'timestamp' ) ) . ']</span> ';
770
+
771
+ //set last Message
772
+ if ( $error ) {
773
+ $output_message = '<span style="background-color:#ff6766;color:black;padding:0 2px;">' . esc_html( $message ) . '</span>';
774
+ $this->lasterrormsg = $output_message;
775
+ }
776
+ elseif ( $warning ) {
777
+ $output_message = '<span style="background-color:#ffc766;color:black;padding:0 2px;">' . esc_html( $message ) . '</span>';
778
+ $this->lasterrormsg = $output_message;
779
+ }
780
+ else {
781
+ $output_message = esc_html( $message );
782
+ $this->lastmsg = $output_message;
783
+ }
784
+ //write log file
785
+ if ( $this->logfile ) {
786
+ if ( ! file_put_contents( $this->logfile, $timestamp . $output_message . '<br />' . PHP_EOL, FILE_APPEND ) ) {
787
+ $this->logfile = '';
788
+ restore_error_handler();
789
+ trigger_error( esc_html( $message ), $type );
790
+ }
791
+
792
+ //write new log header
793
+ if ( ( $error || $warning ) && $this->logfile ) {
794
+ if ( $fd = fopen( $this->logfile, 'r+' ) ) {
795
+ $file_pos = ftell( $fd );
796
+ while ( ! feof( $fd ) ) {
797
+ $line = fgets( $fd );
798
+ if ( $error && stripos( $line, '<meta name="backwpup_errors" content="' ) !== false ) {
799
+ fseek( $fd, $file_pos );
800
+ fwrite( $fd, str_pad( '<meta name="backwpup_errors" content="' . $this->errors . '" />', 100 ) . PHP_EOL );
801
+ break;
802
+ }
803
+ if ( $warning && stripos( $line, '<meta name="backwpup_warnings" content="' ) !== false ) {
804
+ fseek( $fd, $file_pos );
805
+ fwrite( $fd, str_pad( '<meta name="backwpup_warnings" content="' . $this->warnings . '" />', 100 ) . PHP_EOL );
806
+ break;
807
+ }
808
+ $file_pos = ftell( $fd );
809
+ }
810
+ fclose( $fd );
811
+ }
812
+ }
813
+ }
814
+
815
+ //write working data
816
+ $this->update_working_data( $error || $warning );
817
+
818
+ //true for no more php error handling.
819
+ return true;
820
+ }
821
+
822
+ /**
823
+ * Is debug log active
824
+ *
825
+ * @return bool
826
+ */
827
+ public function is_debug() {
828
+
829
+ return strstr( $this->log_level, 'debug' ) ? true : false;
830
+ }
831
+
832
+ /**
833
+ * Change path of a given path
834
+ * for better storing in archives or on sync destinations
835
+ *
836
+ * @param $path string path to change to wp default path
837
+ *
838
+ * @return string
839
+ */
840
+ public function get_destination_path_replacement( $path ) {
841
+
842
+ $path = str_replace( '\\', '/', $path );
843
+
844
+ $abs_path = realpath( BackWPup_Path_Fixer::fix_path( ABSPATH ) );
845
+ if ( $this->job['backupabsfolderup'] ) {
846
+ $abs_path = dirname( $abs_path );
847
+ }
848
+
849
+ $abs_path = trailingslashit( str_replace( '\\', '/', $abs_path ) );
850
+
851
+ $path = str_replace( $abs_path, '/', $path );
852
+
853
+ return $path;
854
+ }
855
+
856
+ /**
857
+ * Get the Process id of working script
858
+ *
859
+ * @return int
860
+ */
861
+ private static function get_pid() {
862
+
863
+ if ( function_exists( 'posix_getpid' ) ) {
864
+
865
+ return posix_getpid();
866
+ } elseif ( function_exists( 'getmypid' ) ) {
867
+
868
+ return getmypid();
869
+ }
870
+
871
+ return - 1;
872
+ }
873
+
874
+ /**
875
+ *
876
+ * Write the Working data to display the process or that i can executes again
877
+ * The write will only done every second
878
+ *
879
+ * @param bool $must
880
+ */
881
+ public function update_working_data( $must = false ) {
882
+ global $wpdb;
883
+
884
+ //to reduce server load
885
+ if ( get_site_option( 'backwpup_cfg_jobwaittimems' ) > 0 && get_site_option( 'backwpup_cfg_jobwaittimems' ) <= 500000 ) {
886
+ usleep( get_site_option( 'backwpup_cfg_jobwaittimems' ) );
887
+ }
888
+
889
+ //check free memory
890
+ $this->need_free_memory( '10M' );
891
+
892
+ //only run every 1 sec.
893
+ $time_to_update = microtime( true ) - $this->timestamp_last_update;
894
+ if ( $time_to_update < 1 && ! $must ) {
895
+ return;
896
+ }
897
+
898
+ //FCGI must have a permanent output so that it not broke
899
+ if ( get_site_option( 'backwpup_cfg_jobdooutput' ) && ! defined( 'STDOUT' ) ) {
900
+ echo str_repeat( ' ', 12 );
901
+ flush();
902
+ }
903
+
904
+ // check WPDB connection. WP will do it after a query that will cause messages.
905
+ $wpdb->check_connection( false );
906
+
907
+ //set execution time again for 5 min
908
+ @set_time_limit( 300 );
909
+
910
+ //calc sub step percent
911
+ if ( $this->substeps_todo > 0 && $this->substeps_done > 0 ) {
912
+ $this->substep_percent = round( $this->substeps_done / $this->substeps_todo * 100 );
913
+ } else {
914
+ $this->substep_percent = 1;
915
+ }
916
+
917
+ //check if job aborted
918
+ if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
919
+ if ( $this->step_working !== 'END' ) {
920
+ $this->end();
921
+ }
922
+ } else {
923
+ $this->timestamp_last_update = microtime( true ); //last update of working file
924
+ $this->write_running_file();
925
+ }
926
+
927
+ if ( $this->signal !== 0 ) {
928
+ $this->do_restart();
929
+ }
930
+ }
931
+
932
+ /**
933
+ *
934
+ * Increase automatically the memory that is needed
935
+ *
936
+ * @param int|string $memneed of the needed memory
937
+ */
938
+ public function need_free_memory( $memneed ) {
939
+
940
+ //need memory
941
+ $needmemory = @memory_get_usage( true ) + self::convert_hr_to_bytes( $memneed );
942
+ // increase Memory
943
+ if ( $needmemory > self::convert_hr_to_bytes( ini_get( 'memory_limit' ) ) ) {
944
+ $newmemory = round( $needmemory / 1024 / 1024 ) + 1 . 'M';
945
+ if ( $needmemory >= 1073741824 ) {
946
+ $newmemory = round( $needmemory / 1024 / 1024 / 1024 ) . 'G';
947
+ }
948
+ @ini_set( 'memory_limit', $newmemory );
949
+ }
950
+ }
951
+
952
+ /**
953
+ *
954
+ * Converts hr to bytes
955
+ *
956
+ * @param $size
957
+ *
958
+ * @return int
959
+ */
960
+ public static function convert_hr_to_bytes( $size ) {
961
+ $size = strtolower( $size );
962
+ $bytes = (int) $size;
963
+ if ( strpos( $size, 'k' ) !== false ) {
964
+ $bytes = intval( $size ) * 1024;
965
+ } elseif ( strpos( $size, 'm' ) !== false ) {
966
+ $bytes = intval( $size ) * 1024 * 1024;
967
+ } elseif ( strpos( $size, 'g' ) !== false ) {
968
+ $bytes = intval( $size ) * 1024 * 1024 * 1024;
969
+ }
970
+
971
+ return $bytes;
972
+ }
973
+
974
+ /**
975
+ *
976
+ * Called on job stop makes cleanup and terminates the script
977
+ *
978
+ */
979
+ private function end() {
980
+
981
+ $this->step_working = 'END';
982
+ $this->substeps_todo = 1;
983
+
984
+ if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
985
+ $this->log( __( 'Aborted by user!', 'backwpup' ), E_USER_ERROR );
986
+ }
987
+
988
+ //delete old logs
989
+ if ( get_site_option( 'backwpup_cfg_maxlogs' ) ) {
990
+ $log_file_list = array();
991
+ $log_folder = trailingslashit( dirname( $this->logfile ) );
992
+ if ( is_readable( $log_folder ) ) { //make file list
993
+ try {
994
+ $dir = new BackWPup_Directory( $log_folder );
995
+
996
+ foreach ( $dir as $file ) {
997
+ if ( ! $file->isDot() && strpos( $file->getFilename(), 'backwpup_log_' ) === 0 && strpos( $file->getFilename(), '.html' ) !== false ) {
998
+ $log_file_list[ $file->getMTime() ] = clone $file;
999
+ }
1000
+ }
1001
+ }
1002
+ catch ( UnexpectedValueException $e ) {
1003
+ $this->log( sprintf( __( "Could not open path: %s" ), $e->getMessage() ), E_USER_WARNING );
1004
+ }
1005
+ }
1006
+ if ( count( $log_file_list ) > 0 ) {
1007
+ krsort( $log_file_list, SORT_NUMERIC );
1008
+ $num_delete_files = 0;
1009
+ $i = - 1;
1010
+ foreach ( $log_file_list as $log_file ) {
1011
+ $i ++;
1012
+ if ( $i < get_site_option( 'backwpup_cfg_maxlogs' ) ) {
1013
+ continue;
1014
+ }
1015
+ unlink( $log_file->getPathname() );
1016
+ $num_delete_files ++;
1017
+ }
1018
+ if ( $num_delete_files > 0 ) {
1019
+ $this->log( sprintf( _n( 'One old log deleted', '%d old logs deleted', $num_delete_files, 'backwpup' ), $num_delete_files ) );
1020
+ }
1021
+ }
1022
+ }
1023
+
1024
+ //Display job working time
1025
+ if ( $this->errors > 0 ) {
1026
+ $this->log( sprintf( __( 'Job has ended with errors in %s seconds. You must resolve the errors for correct execution.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ), E_USER_ERROR );
1027
+ } elseif ( $this->warnings > 0 ) {
1028
+ $this->log( sprintf( __( 'Job finished with warnings in %s seconds. Please resolve them for correct execution.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ), E_USER_WARNING );
1029
+ } else {
1030
+ $this->log( sprintf( __( 'Job done in %s seconds.', 'backwpup' ), current_time( 'timestamp' ) - $this->start_time ) );
1031
+ }
1032
+
1033
+ //Update job options
1034
+ $this->job['lastruntime'] = current_time( 'timestamp' ) - $this->start_time;
1035
+ BackWPup_Option::update( $this->job['jobid'], 'lastruntime', $this->job['lastruntime'] );
1036
+
1037
+
1038
+ //write header info
1039
+ if ( ! empty( $this->logfile ) ) {
1040
+
1041
+ if ( $fd = fopen( $this->logfile, 'r+' ) ) {
1042
+ $filepos = ftell( $fd );
1043
+ $found = 0;
1044
+ while ( ! feof( $fd ) ) {
1045
+ $line = fgets( $fd );
1046
+ if ( stripos( $line, '<meta name="backwpup_jobruntime"' ) !== false ) {
1047
+ fseek( $fd, $filepos );
1048
+ fwrite( $fd, str_pad( '<meta name="backwpup_jobruntime" content="' . $this->job['lastruntime'] . '" />', 100 ) . PHP_EOL );
1049
+ $found ++;
1050
+ }
1051
+ if ( stripos( $line, '<meta name="backwpup_backupfilesize"' ) !== false ) {
1052
+ fseek( $fd, $filepos );
1053
+ fwrite( $fd, str_pad( '<meta name="backwpup_backupfilesize" content="' . $this->backup_filesize . '" />', 100 ) . PHP_EOL );
1054
+ $found ++;
1055
+ }
1056
+ if ( $found >= 2 ) {
1057
+ break;
1058
+ }
1059
+ $filepos = ftell( $fd );
1060
+ }
1061
+ fclose( $fd );
1062
+ }
1063
+
1064
+ //Send mail with log
1065
+ $sendmail = false;
1066
+ if ( $this->job['mailaddresslog'] ) {
1067
+ $sendmail = true;
1068
+ }
1069
+ if ( $this->errors === 0 && $this->job['mailerroronly'] ) {
1070
+ $sendmail = false;
1071
+ }
1072
+ if ( $sendmail ) {
1073
+ //special subject
1074
+ $status = __( 'SUCCESSFUL', 'backwpup' );
1075
+ if ( $this->warnings > 0 ) {
1076
+ $status = __( 'WARNING', 'backwpup' );
1077
+ }
1078
+ if ( $this->errors > 0 ) {
1079
+ $status = __( 'ERROR', 'backwpup' );
1080
+ }
1081
+
1082
+ $subject = sprintf( __( '[%3$s] BackWPup log %1$s: %2$s', 'backwpup' ), date_i18n( 'd-M-Y H:i', $this->start_time, true ), esc_attr( $this->job['name'] ), $status );
1083
+ $headers = array();
1084
+ $headers[] = 'Content-Type: text/html; charset=' . get_bloginfo( 'charset' );
1085
+ if ( $this->job['mailaddresssenderlog'] ) {
1086
+ $this->job['mailaddresssenderlog'] = str_replace( array( '&lt;', '&gt;' ), array( '<', '>' ), $this->job['mailaddresssenderlog'] );
1087
+
1088
+ $bracket_pos = strpos( $this->job['mailaddresssenderlog'], '<' );
1089
+ $at_pos = strpos( $this->job['mailaddresssenderlog'], '@' );
1090
+ if ( $bracket_pos === false || $at_pos === false ) {
1091
+ $this->job['mailaddresssenderlog'] = str_replace( array( '<', '>' ), '', $this->job['mailaddresssenderlog'] ) . ' <' . get_bloginfo( 'admin_email' ) . '>';
1092
+ }
1093
+
1094
+ $headers[] = 'From: ' . $this->job['mailaddresssenderlog'];
1095
+ }
1096
+ wp_mail( $this->job['mailaddresslog'], $subject, file_get_contents( $this->logfile ), $headers );
1097
+ }
1098
+ }
1099
+
1100
+ //set done
1101
+ $this->substeps_done = 1;
1102
+ $this->steps_done[] = 'END';
1103
+
1104
+ //clean up temp
1105
+ self::clean_temp_folder();
1106
+
1107
+ //remove shutdown action
1108
+ remove_action( 'shutdown', array( $this, 'shutdown' ) );
1109
+ restore_exception_handler();
1110
+ restore_error_handler();
1111
+
1112
+ //logfile end
1113
+ file_put_contents( $this->logfile, "</body>" . PHP_EOL . "</html>", FILE_APPEND );
1114
+
1115
+ BackWPup_Cron::check_cleanup();
1116
+
1117
+ exit();
1118
+ }
1119
+
1120
+ /**
1121
+ * Cleanup Temp Folder
1122
+ */
1123
+ public static function clean_temp_folder() {
1124
+
1125
+ $temp_dir = BackWPup::get_plugin_data( 'TEMP' );
1126
+ $do_not_delete_files = array( '.htaccess', 'nginx.conf', 'index.php', '.', '..', '.donotbackup' );
1127
+
1128
+ if ( is_writable( $temp_dir ) ) {
1129
+ try {
1130
+ $dir = new BackWPup_Directory( $temp_dir );
1131
+ foreach ( $dir as $file ) {
1132
+ if ( in_array( $file->getFilename(), $do_not_delete_files, true ) || $file->isDir() || $file->isLink() ) {
1133
+ continue;
1134
+ }
1135
+ if ( $file->isWritable() ) {
1136
+ unlink( $file->getPathname() );
1137
+ }
1138
+ }
1139
+ }
1140
+ catch ( UnexpectedValueException $e ) {
1141
+ $this->log( sprintf( __( "Could not open path: %s" ), $e->getMessage() ), E_USER_WARNING );
1142
+ }
1143
+ }
1144
+ }
1145
+
1146
+ /**
1147
+ * Do a job restart
1148
+ *
1149
+ * @param bool $must Restart must done
1150
+ */
1151
+ public function do_restart( $must = false ) {
1152
+
1153
+ //restart must done if signal
1154
+ if ( $this->signal !== 0 ) {
1155
+ $must = true;
1156
+ }
1157
+
1158
+ //no restart if in end step
1159
+ if ( $this->step_working === 'END' || ( count( $this->steps_done ) + 1 ) >= count( $this->steps_todo ) ) {
1160
+ return;
1161
+ }
1162
+
1163
+ //no restart on cli usage
1164
+ if ( php_sapi_name() == 'cli' ) {
1165
+ return;
1166
+ }
1167
+
1168
+ //no restart if no restart time configured
1169
+ $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1170
+ if ( ! $must && empty( $job_max_execution_time ) ) {
1171
+ return;
1172
+ }
1173
+
1174
+ //no restart when restart was 3 Seconds before
1175
+ $execution_time = microtime( true ) - $this->timestamp_script_start;
1176
+ if ( ! $must && $execution_time < 3 ) {
1177
+ return;
1178
+ }
1179
+
1180
+ //no restart if no working job
1181
+ if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
1182
+ return;
1183
+ }
1184
+
1185
+ //print message
1186
+ if ( $this->is_debug() ) {
1187
+ if ( $execution_time !== 0 ) {
1188
+ $this->log( sprintf( __( 'Restart after %1$d seconds.', 'backwpup' ), ceil( $execution_time ) ) );
1189
+ } elseif ( $this->signal !== 0 ) {
1190
+ $this->log( __( 'Restart after getting signal.', 'backwpup' ) );
1191
+ }
1192
+ }
1193
+
1194
+ //do things for a clean restart
1195
+ $this->pid = 0;
1196
+ $this->uniqid = '';
1197
+ $this->write_running_file();
1198
+ remove_action( 'shutdown', array( $this, 'shutdown' ) );
1199
+ //do restart
1200
+ wp_clear_scheduled_hook( 'backwpup_cron', array( 'id' => 'restart' ) );
1201
+ wp_schedule_single_event( time() + 5, 'backwpup_cron', array( 'id' => 'restart' ) );
1202
+ self::get_jobrun_url( 'restart' );
1203
+
1204
+ exit();
1205
+ }
1206
+
1207
+ /**
1208
+ *
1209
+ * Get a url to run a job of BackWPup
1210
+ *
1211
+ * @param string $starttype Start types are 'runnow', 'runnowlink', 'cronrun', 'runext', 'restart', 'restartalt', 'test'
1212
+ * @param int $jobid The id of job to start else 0
1213
+ *
1214
+ * @return array|object [url] is the job url [header] for auth header or object form wp_remote_get()
1215
+ */
1216
+ public static function get_jobrun_url( $starttype, $jobid = 0 ) {
1217
+
1218
+ $authentication = get_site_option( 'backwpup_cfg_authentication', array(
1219
+ 'method' => '',
1220
+ 'basic_user' => '',
1221
+ 'basic_password' => '',
1222
+ 'user_id' => 0,
1223
+ 'query_arg' => ''
1224
+ ) );
1225
+ $url = site_url( 'wp-cron.php' );
1226
+ $header = array( 'Cache-Control' => 'no-cache' );
1227
+ $authurl = '';
1228
+ $query_args = array(
1229
+ '_nonce' => substr( wp_hash( wp_nonce_tick() . 'backwpup_job_run-' . $starttype, 'nonce' ), - 12, 10 ),
1230
+ 'doing_wp_cron' => sprintf( '%.22F', microtime( true ) )
1231
+ );
1232
+
1233
+ if ( in_array( $starttype, array( 'restart', 'runnow', 'cronrun', 'runext', 'test' ), true ) ) {
1234
+ $query_args['backwpup_run'] = $starttype;
1235
+ }
1236
+
1237
+ if ( in_array( $starttype, array( 'runnowlink', 'runnow', 'cronrun', 'runext' ), true ) && ! empty( $jobid ) ) {
1238
+ $query_args['jobid'] = $jobid;
1239
+ }
1240
+
1241
+ if ( ! empty( $authentication['basic_user'] ) && ! empty( $authentication['basic_password'] ) && $authentication['method'] == 'basic' ) {
1242
+ $header['Authorization'] = 'Basic ' . base64_encode( $authentication['basic_user'] . ':' . BackWPup_Encryption::decrypt( $authentication['basic_password'] ) );
1243
+ $authurl = urlencode( $authentication['basic_user'] ) . ':' . urlencode( BackWPup_Encryption::decrypt( $authentication['basic_password'] ) ) . '@';
1244
+ }
1245
+
1246
+ if ( ! empty( $authentication['query_arg'] ) && $authentication['method'] == 'query_arg' ) {
1247
+ $url .= '?' . $authentication['query_arg'];
1248
+ }
1249
+
1250
+ if ( $starttype === 'runext' ) {
1251
+ $query_args['_nonce'] = get_site_option( 'backwpup_cfg_jobrunauthkey' );
1252
+ $query_args['doing_wp_cron'] = null;
1253
+ if ( ! empty( $authurl ) ) {
1254
+ $url = str_replace( 'https://', 'https://' . $authurl, $url );
1255
+ $url = str_replace( 'http://', 'http://' . $authurl, $url );
1256
+ }
1257
+ }
1258
+
1259
+ if ( $starttype === 'runnowlink' && ( ! defined( 'ALTERNATE_WP_CRON' ) || ! ALTERNATE_WP_CRON ) ) {
1260
+ $url = wp_nonce_url( network_admin_url( 'admin.php' ), 'backwpup_job_run-' . $starttype );
1261
+ $query_args['page'] = 'backwpupjobs';
1262
+ $query_args['action'] = 'runnow';
1263
+ $query_args['doing_wp_cron'] = null;
1264
+ unset( $query_args['_nonce'] );
1265
+ }
1266
+
1267
+ if ( $starttype === 'runnowlink' && defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
1268
+ $query_args['backwpup_run'] = 'runnowalt';
1269
+ $query_args['_nonce'] = substr( wp_hash( wp_nonce_tick() . 'backwpup_job_run-runnowalt', 'nonce' ), - 12, 10 );
1270
+ $query_args['doing_wp_cron'] = null;
1271
+ }
1272
+
1273
+ if ( $starttype === 'restartalt' && defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
1274
+ $query_args['backwpup_run'] = 'restart';
1275
+ $query_args['_nonce'] = null;
1276
+ }
1277
+
1278
+ if ( $starttype === 'restart' || $starttype === 'test' ) {
1279
+ $query_args['_nonce'] = null;
1280
+ }
1281
+
1282
+ if ( ! empty( $authentication['user_id'] ) && $authentication['method'] === 'user' ) {
1283
+ //cache cookies for auth some
1284
+ $cookies = get_site_transient( 'backwpup_cookies' );
1285
+ if ( empty( $cookies ) ) {
1286
+ $wp_admin_user = get_users( array( 'role' => 'administrator', 'number' => 1 ) );
1287
+ if ( empty( $wp_admin_user ) ) {
1288
+ $wp_admin_user = get_users( array( 'role' => 'backwpup_admin', 'number' => 1 ) );
1289
+ }
1290
+ if ( ! empty( $wp_admin_user[0]->ID ) ) {
1291
+ $expiration = time() + ( 356 * DAY_IN_SECONDS );
1292
+ $manager = WP_Session_Tokens::get_instance( $wp_admin_user[0]->ID );
1293
+ $token = $manager->create( $expiration );
1294
+ $cookies[ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $wp_admin_user[0]->ID, $expiration, 'logged_in', $token );
1295
+ }
1296
+ set_site_transient( 'backwpup_cookies', $cookies, HOUR_IN_SECONDS - 30 );
1297
+ }
1298
+ } else {
1299
+ $cookies = '';
1300
+ }
1301
+
1302
+ $cron_request = array(
1303
+ 'url' => add_query_arg( $query_args, $url ),
1304
+ 'key' => $query_args['doing_wp_cron'],
1305
+ 'args' => array(
1306
+ 'blocking' => false,
1307
+ 'sslverify' => false,
1308
+ 'timeout' => 0.01,
1309
+ 'headers' => $header,
1310
+ 'user-agent' => BackWPup::get_plugin_data( 'User-Agent' )
1311
+ )
1312
+ );
1313
+
1314
+ if ( ! empty( $cookies ) ) {
1315
+ foreach ( $cookies as $name => $value ) {
1316
+ $cron_request['args']['cookies'][] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
1317
+ }
1318
+ }
1319
+
1320
+ $cron_request = apply_filters( 'cron_request', $cron_request );
1321
+
1322
+ if ( $starttype === 'test' ) {
1323
+ $cron_request['args']['timeout'] = 15;
1324
+ $cron_request['args']['blocking'] = true;
1325
+ }
1326
+
1327
+ if ( ! in_array( $starttype, array( 'runnowlink', 'runext', 'restartalt' ), true ) ) {
1328
+ delete_transient( 'doing_cron' );
1329
+
1330
+ return wp_remote_post( $cron_request['url'], $cron_request['args'] );
1331
+ }
1332
+
1333
+ return $cron_request;
1334
+ }
1335
+
1336
+ /**
1337
+ * Run baby run
1338
+ */
1339
+ public function run() {
1340
+ global $wpdb;
1341
+ /* @var wpdb $wpdb */
1342
+
1343
+ //disable output buffering
1344
+ if ( $level = ob_get_level() ) {
1345
+ for ( $i = 0; $i < $level; $i ++ ) {
1346
+ ob_end_clean();
1347
+ }
1348
+ }
1349
+
1350
+ // Job can't run it is not created
1351
+ if ( empty( $this->steps_todo ) || empty( $this->logfile ) ) {
1352
+ $running_file = BackWPup::get_plugin_data( 'running_file' );
1353
+ if ( file_exists( $running_file ) ) {
1354
+ unlink( $running_file );
1355
+ }
1356
+
1357
+ return;
1358
+ }
1359
+
1360
+ //Check double running and inactivity
1361
+ $last_update = microtime( true ) - $this->timestamp_last_update;
1362
+ if ( ! empty( $this->pid ) && $last_update > 300 ) {
1363
+ $this->log( __( 'Job restarts due to inactivity for more than 5 minutes.', 'backwpup' ), E_USER_WARNING );
1364
+ } elseif ( ! empty( $this->pid ) ) {
1365
+ return;
1366
+ }
1367
+ // set timestamp of script start
1368
+ $this->timestamp_script_start = microtime( true );
1369
+ //set Pid
1370
+ $this->pid = self::get_pid();
1371
+ $this->uniqid = uniqid( '', true );
1372
+ //Early write new working file
1373
+ $this->write_running_file();
1374
+ if ( $this->is_debug() ) {
1375
+ @ini_set( 'error_log', $this->logfile );
1376
+ error_reporting( - 1 );
1377
+ }
1378
+ @ini_set( 'display_errors', '0' );
1379
+ @ini_set( 'log_errors', '1' );
1380
+ @ini_set( 'html_errors', '0' );
1381
+ @ini_set( 'report_memleaks', '1' );
1382
+ @ini_set( 'zlib.output_compression', '0' );
1383
+ @ini_set( 'implicit_flush', '0' );
1384
+ @putenv( 'TMPDIR=' . BackWPup::get_plugin_data( 'TEMP' ) );
1385
+ //Write Wordpress DB errors to log
1386
+ $wpdb->suppress_errors( false );
1387
+ $wpdb->hide_errors();
1388
+ //set wp max memory limit
1389
+ @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
1390
+ //set error handler
1391
+ if ( ! empty( $this->logfile ) ) {
1392
+ if ( $this->is_debug() ) {
1393
+ set_error_handler( array( $this, 'log' ) );
1394
+ } else {
1395
+ set_error_handler( array( $this, 'log' ), E_ALL ^ E_NOTICE );
1396
+ }
1397
+ }
1398
+ set_exception_handler( array( $this, 'exception_handler' ) );
1399
+ // execute function on job shutdown register_shutdown_function( array( $this, 'shutdown' ) );
1400
+ add_action( 'shutdown', array( $this, 'shutdown' ) );
1401
+
1402
+ if ( function_exists( 'pcntl_signal' ) ) {
1403
+ $signals = array(
1404
+ 'SIGHUP', //Term
1405
+ 'SIGINT', //Term
1406
+ 'SIGQUIT', //Core
1407
+ 'SIGILL', //Core
1408
+ //'SIGTRAP', //Core
1409
+ 'SIGABRT', //Core
1410
+ 'SIGBUS', //Core
1411
+ 'SIGFPE', //Core
1412
+ //'SIGKILL', //Term
1413
+ 'SIGSEGV', //Core
1414
+ //'SIGPIPE', Term
1415
+ //'SIGALRM', Term
1416
+ 'SIGTERM', //Term
1417
+ 'SIGSTKFLT', //Term
1418
+ 'SIGUSR1',//Term
1419
+ 'SIGUSR2', //Term
1420
+ //'SIGCHLD', //Ign
1421
+ //'SIGCONT', //Cont
1422
+ //'SIGSTOP', //Stop
1423
+ //'SIGTSTP', //Stop
1424
+ //'SIGTTIN', //Stop
1425
+ //'SIGTTOU', //Stop
1426
+ //'SIGURG', //Ign
1427
+ 'SIGXCPU', //Core
1428
+ 'SIGXFSZ', //Core
1429
+ //'SIGVTALRM', //Term
1430
+ //'SIGPROF', //Term
1431
+ //'SIGWINCH', //Ign
1432
+ //'SIGIO', //Term
1433
+ 'SIGPWR', //Term
1434
+ 'SIGSYS' //Core
1435
+ );
1436
+ $signals = apply_filters( 'backwpup_job_signals_to_handel', $signals );
1437
+ declare( ticks = 1 );
1438
+ $this->signal = 0;
1439
+ foreach ( $signals as $signal ) {
1440
+ if ( defined( $signal ) ) {
1441
+ pcntl_signal( constant( $signal ), array( $this, 'signal_handler' ), false );
1442
+ }
1443
+ }
1444
+ }
1445
+ $job_types = BackWPup::get_job_types();
1446
+ //go step by step
1447
+ foreach ( $this->steps_todo as $this->step_working ) {
1448
+ //Check if step already done
1449
+ if ( in_array( $this->step_working, $this->steps_done, true ) ) {
1450
+ continue;
1451
+ }
1452
+ //calc step percent
1453
+ if ( count( $this->steps_done ) > 0 ) {
1454
+ $this->step_percent = round( count( $this->steps_done ) / count( $this->steps_todo ) * 100 );
1455
+ } else {
1456
+ $this->step_percent = 1;
1457
+ }
1458
+ // do step tries
1459
+ while ( true ) {
1460
+ if ( $this->steps_data[ $this->step_working ]['STEP_TRY'] >= get_site_option( 'backwpup_cfg_jobstepretry' ) ) {
1461
+ $this->log( __( 'Step aborted: too many attempts!', 'backwpup' ), E_USER_ERROR );
1462
+ $this->temp = array();
1463
+ $this->steps_done[] = $this->step_working;
1464
+ $this->substeps_done = 0;
1465
+ $this->substeps_todo = 0;
1466
+ $this->do_restart();
1467
+ break;
1468
+ }
1469
+
1470
+ $this->steps_data[ $this->step_working ]['STEP_TRY'] ++;
1471
+ $done = false;
1472
+
1473
+ //executes the methods of job process
1474
+ if ( $this->step_working == 'CREATE_ARCHIVE' ) {
1475
+ $done = $this->create_archive();
1476
+ } elseif ( $this->step_working == 'CREATE_MANIFEST' ) {
1477
+ $done = $this->create_manifest();
1478
+ } elseif ( $this->step_working == 'END' ) {
1479
+ $this->end();
1480
+ break 2;
1481
+ } elseif ( strstr( $this->step_working, 'JOB_' ) ) {
1482
+ $done = $job_types[ str_replace( 'JOB_', '', $this->step_working ) ]->job_run( $this );
1483
+ } elseif ( strstr( $this->step_working, 'DEST_SYNC_' ) ) {
1484
+ $done = BackWPup::get_destination( str_replace( 'DEST_SYNC_', '', $this->step_working ) )->job_run_sync( $this );
1485
+ } elseif ( strstr( $this->step_working, 'DEST_' ) ) {
1486
+ $done = BackWPup::get_destination( str_replace( 'DEST_', '', $this->step_working ) )->job_run_archive( $this );
1487
+ } elseif ( ! empty( $this->steps_data[ $this->step_working ]['CALLBACK'] ) ) {
1488
+ $done = $this->steps_data[ $this->step_working ]['CALLBACK']( $this );
1489
+ }
1490
+
1491
+ // set step as done
1492
+ if ( $done === true ) {
1493
+ $this->temp = array();
1494
+ $this->steps_done[] = $this->step_working;
1495
+ $this->substeps_done = 0;
1496
+ $this->substeps_todo = 0;
1497
+ $this->update_working_data( true );
1498
+ }
1499
+ if ( count( $this->steps_done ) < count( $this->steps_todo ) - 1 ) {
1500
+ $this->do_restart();
1501
+ }
1502
+ if ( $done === true ) {
1503
+ break;
1504
+ }
1505
+ }
1506
+ }
1507
+ }
1508
+
1509
+ /**
1510
+ * Creates the backup archive
1511
+ */
1512
+ private function create_archive() {
1513
+
1514
+ //load folders to backup
1515
+ $folders_to_backup = $this->get_folders_to_backup();
1516
+
1517
+ $this->substeps_todo = $this->count_folder + 1;
1518
+
1519
+ //initial settings for restarts in archiving
1520
+ if ( ! isset( $this->steps_data[ $this->step_working ]['on_file'] ) ) {
1521
+ $this->steps_data[ $this->step_working ]['on_file'] = '';
1522
+ }
1523
+ if ( ! isset( $this->steps_data[ $this->step_working ]['on_folder'] ) ) {
1524
+ $this->steps_data[ $this->step_working ]['on_folder'] = '';
1525
+ }
1526
+
1527
+ if ( $this->steps_data[ $this->step_working ]['on_folder'] == '' && $this->steps_data[ $this->step_working ]['on_file'] == '' && is_file( $this->backup_folder . $this->backup_file ) ) {
1528
+ unlink( $this->backup_folder . $this->backup_file );
1529
+ }
1530
+
1531
+ if ( $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] != $this->steps_data[ $this->step_working ]['STEP_TRY'] ) {
1532
+ $this->log( sprintf( __( '%d. Trying to create backup archive &hellip;', 'backwpup' ), $this->steps_data[ $this->step_working ]['STEP_TRY'] ), E_USER_NOTICE );
1533
+ }
1534
+
1535
+ try {
1536
+ $backup_archive = new BackWPup_Create_Archive( $this->backup_folder . $this->backup_file );
1537
+
1538
+ //show method for creation
1539
+ if ( $this->substeps_done == 0 ) {
1540
+ $this->log( sprintf( _x( 'Compressing files as %s. Please be patient, this may take a moment.', 'Archive compression method', 'backwpup' ), $backup_archive->get_method() ) );
1541
+ }
1542
+
1543
+ //add extra files
1544
+ if ( $this->substeps_done == 0 ) {
1545
+ if ( ! empty( $this->additional_files_to_backup ) && $this->substeps_done == 0 ) {
1546
+ if ( $this->is_debug() ) {
1547
+ $this->log( __( 'Adding Extra files to Archive', 'backwpup' ) );
1548
+ }
1549
+ foreach ( $this->additional_files_to_backup as $file ) {
1550
+ if ( $backup_archive->add_file( $file, basename( $file ) ) ) {
1551
+ ;
1552
+ $this->count_files ++;
1553
+ $this->count_files_size = $this->count_files_size + filesize( $file );
1554
+ $this->update_working_data();
1555
+ } else {
1556
+ $backup_archive->close();
1557
+ $this->steps_data[ $this->step_working ]['on_file'] = '';
1558
+ $this->steps_data[ $this->step_working ]['on_folder'] = '';
1559
+ $this->log( __( 'Cannot create backup archive correctly. Aborting creation.', 'backwpup' ), E_USER_ERROR );
1560
+
1561
+ return false;
1562
+ }
1563
+ }
1564
+ }
1565
+ $this->substeps_done ++;
1566
+ }
1567
+
1568
+ //add normal files
1569
+ while ( $folder = array_shift( $folders_to_backup ) ) {
1570
+ //jump over already done folders
1571
+ if ( in_array( $this->steps_data[ $this->step_working ]['on_folder'], $folders_to_backup, true ) ) {
1572
+ continue;
1573
+ }
1574
+ if ( $this->is_debug() ) {
1575
+ $this->log( sprintf( __( 'Archiving Folder: %s', 'backwpup' ), $folder ) );
1576
+ }
1577
+ $this->steps_data[ $this->step_working ]['on_folder'] = $folder;
1578
+ $files_in_folder = $this->get_files_in_folder( $folder );
1579
+ //add empty folders
1580
+ if ( empty( $files_in_folder ) ) {
1581
+ $folder_name_in_archive = trim( ltrim( $this->get_destination_path_replacement( $folder ), '/' ) );
1582
+ if ( ! empty ( $folder_name_in_archive ) ) {
1583
+ $backup_archive->add_empty_folder( $folder, $folder_name_in_archive );
1584
+ }
1585
+ continue;
1586
+ }
1587
+ //add files
1588
+ while ( $file = array_shift( $files_in_folder ) ) {
1589
+ //jump over already done files
1590
+ if ( in_array( $this->steps_data[ $this->step_working ]['on_file'], $files_in_folder, true ) ) {
1591
+ continue;
1592
+ }
1593
+ $this->steps_data[ $this->step_working ]['on_file'] = $file;
1594
+ //restart if needed
1595
+ $restart_time = $this->get_restart_time();
1596
+ if ( $restart_time <= 0 ) {
1597
+ unset( $backup_archive );
1598
+ $this->do_restart_time( true );
1599
+
1600
+ return false;
1601
+ }
1602
+ //generate filename in archive
1603
+ $in_archive_filename = ltrim( $this->get_destination_path_replacement( $file ), '/' );
1604
+ //add file to archive
1605
+ if ( $backup_archive->add_file( $file, $in_archive_filename ) ) {
1606
+ $this->count_files ++;
1607
+ $this->count_files_size = $this->count_files_size + filesize( $file );
1608
+ $this->update_working_data();
1609
+ } else {
1610
+ $backup_archive->close();
1611
+ unset( $backup_archive );
1612
+ $this->steps_data[ $this->step_working ]['on_file'] = '';
1613
+ $this->steps_data[ $this->step_working ]['on_folder'] = '';
1614
+ $this->substeps_done = 0;
1615
+ $this->backup_filesize = filesize( $this->backup_folder . $this->backup_file );
1616
+ if ( $this->backup_filesize === false ) {
1617
+ $this->backup_filesize = PHP_INT_MAX;
1618
+ }
1619
+ $this->log( __( 'Cannot create backup archive correctly. Aborting creation.', 'backwpup' ), E_USER_ERROR );
1620
+
1621
+ return false;
1622
+ }
1623
+ }
1624
+ $this->steps_data[ $this->step_working ]['on_file'] = '';
1625
+ $this->substeps_done ++;
1626
+ }
1627
+ $backup_archive->close();
1628
+ unset( $backup_archive );
1629
+ $this->log( __( 'Backup archive created.', 'backwpup' ), E_USER_NOTICE );
1630
+ } catch ( Exception $e ) {
1631
+ $this->log( $e->getMessage(), E_USER_ERROR, $e->getFile(), $e->getLine() );
1632
+ unset( $backup_archive );
1633
+
1634
+ return false;
1635
+ }
1636
+
1637
+ $this->backup_filesize = filesize( $this->backup_folder . $this->backup_file );
1638
+ if ( $this->backup_filesize === false ) {
1639
+ $this->backup_filesize = PHP_INT_MAX;
1640
+ }
1641
+
1642
+ if ( $this->backup_filesize >= PHP_INT_MAX ) {
1643
+ $this->log( __( 'The Backup archive will be too large for file operations with this PHP Version. You might want to consider splitting the backup job in multiple jobs with less files each.', 'backwpup' ), E_USER_ERROR );
1644
+ $this->end();
1645
+ } else {
1646
+ $this->log( sprintf( __( 'Archive size is %s.', 'backwpup' ), size_format( $this->backup_filesize, 2 ) ), E_USER_NOTICE );
1647
+ }
1648
+
1649
+ $this->log( sprintf( __( '%1$d Files with %2$s in Archive.', 'backwpup' ), $this->count_files, size_format( $this->count_files_size, 2 ) ), E_USER_NOTICE );
1650
+
1651
+ return true;
1652
+ }
1653
+
1654
+ /**
1655
+ * Get list of Folder for backup
1656
+ *
1657
+ * @return array folder list
1658
+ */
1659
+ public function get_folders_to_backup() {
1660
+
1661
+ $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-folder.php';
1662
+
1663
+ if ( ! file_exists( $file ) ) {
1664
+ return array();
1665
+ }
1666
+
1667
+ $folders = array();
1668
+
1669
+ $file_data = file( $file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
1670
+
1671
+ foreach ( $file_data as $folder ) {
1672
+ $folder = trim( str_replace( array( '<?php', '//' ), '', $folder ) );
1673
+ if ( ! empty( $folder ) && is_dir( $folder ) ) {
1674
+ $folders[] = $folder;
1675
+ }
1676
+ }
1677
+ $folders = array_unique( $folders );
1678
+ sort( $folders );
1679
+ $this->count_folder = count( $folders );
1680
+
1681
+ return $folders;
1682
+ }
1683
+
1684
+ /**
1685
+ *
1686
+ * Get back a array of files to backup in the selected folder
1687
+ *
1688
+ * @param string $folder the folder to get the files from
1689
+ *
1690
+ * @return array files to backup
1691
+ */
1692
+ public function get_files_in_folder( $folder ) {
1693
+
1694
+ $files = array();
1695
+ $folder = trailingslashit( $folder );
1696
+
1697
+ if ( ! is_dir( $folder ) ) {
1698
+ $this->log( sprintf( _x( 'Folder %s does not exist', 'Folder name', 'backwpup' ), $folder ), E_USER_WARNING );
1699
+ return $files;
1700
+ }
1701
+
1702
+ if ( ! is_readable( $folder ) ) {
1703
+ $this->log( sprintf( _x( 'Folder %s is not readable', 'Folder name', 'backwpup' ), $folder ), E_USER_WARNING );
1704
+ return $files;
1705
+ }
1706
+
1707
+ try {
1708
+ $dir = new BackWPup_Directory( $folder );
1709
+
1710
+ foreach ( $dir as $file ) {
1711
+ if ( $file->isDir() || $file->isDot() ) {
1712
+ continue;
1713
+ }
1714
+ $path = str_replace( '\\', '/', realpath( $file->getPathname() ) );
1715
+ foreach ( $this->exclude_from_backup as $exclusion ) { //exclude files
1716
+ $exclusion = trim( $exclusion );
1717
+ if ( stripos( $path, $exclusion ) !== false && ! empty( $exclusion ) ) {
1718
+ continue 2;
1719
+ }
1720
+ }
1721
+ if ( $this->job['backupexcludethumbs'] && strpos( $folder, BackWPup_File::get_upload_dir() ) !== false && preg_match( "/\-[0-9]{1,4}x[0-9]{1,4}.+\.(jpg|png|gif)$/i", $file->getFilename() ) ) {
1722
+ continue;
1723
+ }
1724
+ if ( $file->isLink() ) {
1725
+ $this->log( sprintf( __( 'Link "%s" not following.', 'backwpup' ), $file->getPathname() ), E_USER_WARNING );
1726
+ } elseif ( ! $file->isReadable() ) {
1727
+ $this->log( sprintf( __( 'File "%s" is not readable!', 'backwpup' ), $file->getPathname() ), E_USER_WARNING );
1728
+ } else {
1729
+ $file_size = $file->getSize();
1730
+ if ( ! is_int( $file_size ) || $file_size < 0 || $file_size > 2147483647 ) {
1731
+ $this->log( sprintf( __( 'File size of “%s” cannot be retrieved. File might be too large and will not be added to queue.', 'backwpup' ), $file->getPathname() . ' ' . $file_size ), E_USER_WARNING );
1732
+ continue;
1733
+ }
1734
+ $files[] = $path;
1735
+ }
1736
+ }
1737
+
1738
+ }
1739
+ catch ( UnexpectedValueException $e ) {
1740
+ $this->log( sprintf( __( "Could not open path: %s" ), $e->getMessage() ), E_USER_WARNING );
1741
+ }
1742
+
1743
+ return $files;
1744
+ }
1745
+
1746
+ /**
1747
+ * Get job restart time
1748
+ *
1749
+ * @return int remaining time
1750
+ */
1751
+ public function get_restart_time() {
1752
+
1753
+ if ( php_sapi_name() == 'cli' ) {
1754
+ return 300;
1755
+ }
1756
+
1757
+ $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1758
+
1759
+ if ( empty( $job_max_execution_time ) ) {
1760
+ return 300;
1761
+ }
1762
+
1763
+ $execution_time = microtime( true ) - $this->timestamp_script_start;
1764
+
1765
+ return $job_max_execution_time - $execution_time - 3;
1766
+ }
1767
+
1768
+ /**
1769
+ * Do a job restart
1770
+ *
1771
+ * @param bool $do_restart_now should time restart now be done
1772
+ *
1773
+ * @return int remaining time
1774
+ */
1775
+ public function do_restart_time( $do_restart_now = false ) {
1776
+
1777
+ if ( php_sapi_name() == 'cli' ) {
1778
+ return 300;
1779
+ }
1780
+
1781
+ //do restart after signal is send
1782
+ if ( $this->signal !== 0 ) {
1783
+ $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] = $this->steps_data[ $this->step_working ]['STEP_TRY'];
1784
+ $this->steps_data[ $this->step_working ]['STEP_TRY'] -= 1;
1785
+ $this->do_restart( true );
1786
+ }
1787
+
1788
+ $job_max_execution_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
1789
+
1790
+ if ( empty( $job_max_execution_time ) ) {
1791
+ return 300;
1792
+ }
1793
+
1794
+ $execution_time = microtime( true ) - $this->timestamp_script_start;
1795
+
1796
+ // do restart 3 sec. before max. execution time
1797
+ if ( $do_restart_now || $execution_time >= ( $job_max_execution_time - 3 ) ) {
1798
+ $this->steps_data[ $this->step_working ]['SAVE_STEP_TRY'] = $this->steps_data[ $this->step_working ]['STEP_TRY'];
1799
+ $this->steps_data[ $this->step_working ]['STEP_TRY'] -= 1;
1800
+ $this->do_restart( true );
1801
+ }
1802
+
1803
+ return $job_max_execution_time - $execution_time;
1804
+ }
1805
+
1806
+ /**
1807
+ * create manifest file
1808
+ * @return bool
1809
+ */
1810
+ public function create_manifest() {
1811
+
1812
+ $this->substeps_todo = 3;
1813
+
1814
+ $this->log( sprintf( __( '%d. Trying to generate a manifest file&#160;&hellip;', 'backwpup' ), $this->steps_data[ $this->step_working ]['STEP_TRY'] ) );
1815
+
1816
+ //build manifest
1817
+ $manifest = array();
1818
+ // add blog information
1819
+ $manifest['blog_info']['url'] = home_url();
1820
+ $manifest['blog_info']['wpurl'] = site_url();
1821
+ $manifest['blog_info']['prefix'] = $GLOBALS['wpdb']->prefix;
1822
+ $manifest['blog_info']['description'] = get_option( 'blogdescription' );
1823
+ $manifest['blog_info']['stylesheet_directory'] = get_template_directory_uri();
1824
+ $manifest['blog_info']['activate_plugins'] = wp_get_active_and_valid_plugins();
1825
+ $manifest['blog_info']['activate_theme'] = wp_get_theme()->get( 'Name' );
1826
+ $manifest['blog_info']['admin_email'] = get_option( 'admin_email' );
1827
+ $manifest['blog_info']['charset'] = get_bloginfo( 'charset' );
1828
+ $manifest['blog_info']['version'] = BackWPup::get_plugin_data( 'wp_version' );
1829
+ $manifest['blog_info']['backwpup_version'] = BackWPup::get_plugin_data( 'version' );
1830
+ $manifest['blog_info']['language'] = get_bloginfo( 'language' );
1831
+ $manifest['blog_info']['name'] = get_bloginfo( 'name' );
1832
+ $manifest['blog_info']['abspath'] = ABSPATH;
1833
+ $manifest['blog_info']['uploads'] = wp_upload_dir( null, false, true );
1834
+ $manifest['blog_info']['contents']['basedir'] = WP_CONTENT_DIR;
1835
+ $manifest['blog_info']['contents']['baseurl'] = WP_CONTENT_URL;
1836
+ $manifest['blog_info']['plugins']['basedir'] = WP_PLUGIN_DIR;
1837
+ $manifest['blog_info']['plugins']['baseurl'] = WP_PLUGIN_URL;
1838
+ $manifest['blog_info']['themes']['basedir'] = get_theme_root();
1839
+ $manifest['blog_info']['themes']['baseurl'] = get_theme_root_uri();
1840
+ // add job settings
1841
+ $manifest['job_settings'] = $this->job;
1842
+ // add archive info
1843
+ foreach ( $this->additional_files_to_backup as $file ) {
1844
+ $manifest['archive']['extra_files'][] = basename( $file );
1845
+ }
1846
+ if ( isset( $this->steps_data['JOB_FILE'] ) ) {
1847
+ if ( $this->job['backuproot'] ) {
1848
+ $manifest['archive']['abspath'] = trailingslashit( $this->get_destination_path_replacement( ABSPATH ) );
1849
+ }
1850
+ if ( $this->job['backupuploads'] ) {
1851
+ $manifest['archive']['uploads'] = trailingslashit( $this->get_destination_path_replacement( BackWPup_File::get_upload_dir() ) );
1852
+ }
1853
+ if ( $this->job['backupcontent'] ) {
1854
+ $manifest['archive']['contents'] = trailingslashit( $this->get_destination_path_replacement( WP_CONTENT_DIR ) );
1855
+ }
1856
+ if ( $this->job['backupplugins'] ) {
1857
+ $manifest['archive']['plugins'] = trailingslashit( $this->get_destination_path_replacement( WP_PLUGIN_DIR ) );
1858
+ }
1859
+ if ( $this->job['backupthemes'] ) {
1860
+ $manifest['archive']['themes'] = trailingslashit( $this->get_destination_path_replacement( get_theme_root() ) );
1861
+ }
1862
+ }
1863
+
1864
+ if ( ! file_put_contents( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json', json_encode( $manifest ) ) ) {
1865
+ return false;
1866
+ }
1867
+ $this->substeps_done = 1;
1868
+
1869
+ //Create backwpup_readme.txt
1870
+ $readme_text = __( 'You may have noticed the manifest.json file in this archive.', 'backwpup' ) . PHP_EOL;
1871
+ $readme_text .= __( 'manifest.json might be needed for later restoring a backup from this archive.', 'backwpup' ) . PHP_EOL;
1872
+ $readme_text .= __( 'Please leave manifest.json untouched and in place. Otherwise it is safe to be ignored.', 'backwpup' ) . PHP_EOL;
1873
+ if ( ! file_put_contents( BackWPup::get_plugin_data( 'TEMP' ) . 'backwpup_readme.txt', $readme_text ) ) {
1874
+ return false;
1875
+ }
1876
+ $this->substeps_done = 2;
1877
+
1878
+ //add file to backup files
1879
+ if ( is_readable( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json' ) ) {
1880
+ $this->additional_files_to_backup[] = BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json';
1881
+ $this->additional_files_to_backup[] = BackWPup::get_plugin_data( 'TEMP' ) . 'backwpup_readme.txt';
1882
+ $this->log( sprintf( __( 'Added manifest.json file with %1$s to backup file list.', 'backwpup' ), size_format( filesize( BackWPup::get_plugin_data( 'TEMP' ) . 'manifest.json' ), 2 ) ) );
1883
+ }
1884
+ $this->substeps_done = 3;
1885
+
1886
+ return true;
1887
+ }
1888
+
1889
+ /**
1890
+ * @param $jobid
1891
+ */
1892
+ public static function start_cli( $jobid ) {
1893
+
1894
+ if ( php_sapi_name() != 'cli' ) {
1895
+ return;
1896
+ }
1897
+
1898
+ //define DOING_CRON to prevent caching
1899
+ if ( ! defined( 'DOING_CRON' ) ) {
1900
+ define( 'DOING_CRON', true );
1901
+ }
1902
+
1903
+ //load text domain
1904
+ $log_level = get_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
1905
+ if ( strstr( $log_level, 'translated' ) ) {
1906
+ BackWPup::load_text_domain();
1907
+ } else {
1908
+ add_filter( 'override_load_textdomain', '__return_true' );
1909
+ $GLOBALS['l10n'] = array();
1910
+ }
1911
+
1912
+ $jobid = absint( $jobid );
1913
+
1914
+ //Logs Folder
1915
+ $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
1916
+ $log_folder = BackWPup_File::get_absolute_path( $log_folder );
1917
+
1918
+ //check job id exists
1919
+ $jobids = BackWPup_Option::get_job_ids();
1920
+ if ( ! in_array( $jobid, $jobids, true ) ) {
1921
+ die( __( 'Wrong BackWPup JobID', 'backwpup' ) );
1922
+ }
1923
+ //check folders
1924
+ $log_folder_message = BackWPup_File::check_folder( $log_folder );
1925
+ if ( ! empty( $log_folder_message ) ) {
1926
+ die( $log_folder_message );
1927
+ }
1928
+ $log_folder_message = BackWPup_File::check_folder( BackWPup::get_plugin_data( 'TEMP' ), true );
1929
+ if ( ! empty( $log_folder_message ) ) {
1930
+ die( $log_folder_message );
1931
+ }
1932
+ //check running job
1933
+ if ( file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
1934
+ die( __( 'A BackWPup job is already running', 'backwpup' ) );
1935
+ }
1936
+
1937
+ //start class
1938
+ $backwpup_job_object = new self();
1939
+ $backwpup_job_object->create( 'runcli', (int) $jobid );
1940
+ $backwpup_job_object->run();
1941
+ }
1942
+
1943
+ /**
1944
+ * disable caches
1945
+ */
1946
+ public static function disable_caches() {
1947
+
1948
+ //Special settings
1949
+ @putenv( 'nokeepalive=1' );
1950
+ @ini_set( 'zlib.output_compression', 'Off' );
1951
+
1952
+ // deactivate caches
1953
+ if ( ! defined( 'DONOTCACHEDB' ) ) {
1954
+ define( 'DONOTCACHEDB', true );
1955
+ }
1956
+ if ( ! defined( 'DONOTCACHEPAGE' ) ) {
1957
+ define( 'DONOTCACHEPAGE', true );
1958
+ }
1959
+ }
1960
+
1961
+ /**
1962
+ *
1963
+ * Reads a BackWPup logfile header and gives back a array of information
1964
+ *
1965
+ * @param string $logfile full logfile path
1966
+ *
1967
+ * @return array|bool
1968
+ */
1969
+ public static function read_logheader( $logfile ) {
1970
+
1971
+ $usedmetas = array(
1972
+ "date" => "logtime",
1973
+ "backwpup_logtime" => "logtime", //old value of date
1974
+ "backwpup_errors" => "errors",
1975
+ "backwpup_warnings" => "warnings",
1976
+ "backwpup_jobid" => "jobid",
1977
+ "backwpup_jobname" => "name",
1978
+ "backwpup_jobtype" => "type",
1979
+ "backwpup_jobruntime" => "runtime",
1980
+ "backwpup_backupfilesize" => "backupfilesize"
1981
+ );
1982
+
1983
+ //get metadata of logfile
1984
+ $metas = array();
1985
+ if ( is_readable( $logfile ) ) {
1986
+ if ( '.gz' == substr( $logfile, - 3 ) ) {
1987
+ $metas = (array) get_meta_tags( 'compress.zlib://' . $logfile );
1988
+ } else {
1989
+ $metas = (array) get_meta_tags( $logfile );
1990
+ }
1991
+ }
1992
+
1993
+ //only output needed data
1994
+ foreach ( $usedmetas as $keyword => $field ) {
1995
+ if ( isset( $metas[ $keyword ] ) ) {
1996
+ $joddata[ $field ] = $metas[ $keyword ];
1997
+ } else {
1998
+ $joddata[ $field ] = '';
1999
+ }
2000
+ }
2001
+
2002
+ //convert date
2003
+ if ( isset( $metas['date'] ) ) {
2004
+ $joddata['logtime'] = strtotime( $metas['date'] ) + ( get_option( 'gmt_offset' ) * 3600 );
2005
+ }
2006
+
2007
+ //use file create date if none
2008
+ if ( empty( $joddata['logtime'] ) ) {
2009
+ $joddata['logtime'] = filectime( $logfile );
2010
+ }
2011
+
2012
+ return $joddata;
2013
+ }
2014
+
2015
+ public static function user_abort() {
2016
+
2017
+ /* @var $job_object BackWPup_Job */
2018
+ $job_object = BackWPup_Job::get_working_data();
2019
+
2020
+ unlink( BackWPup::get_plugin_data( 'running_file' ) );
2021
+
2022
+ //if job not working currently abort it this way for message
2023
+ $not_worked_time = microtime( true ) - $job_object->timestamp_last_update;
2024
+ $restart_time = get_site_option( 'backwpup_cfg_jobmaxexecutiontime' );
2025
+ if ( empty( $restart_time ) ) {
2026
+ $restart_time = 60;
2027
+ }
2028
+ if ( empty( $job_object->pid ) || $not_worked_time > $restart_time ) {
2029
+ $job_object->user_abort = true;
2030
+ $job_object->update_working_data();
2031
+ }
2032
+
2033
+ }
2034
+
2035
+ /**
2036
+ *
2037
+ * Get the mime type of a file
2038
+ *
2039
+ * @param string $file The full file name
2040
+ *
2041
+ * @return bool|string the mime type or false
2042
+ */
2043
+ public static function get_mime_type( $file ) {
2044
+
2045
+ if ( is_dir( $file ) || is_link( $file ) ) {
2046
+ return 'application/octet-stream';
2047
+ }
2048
+
2049
+ $mime_types = array(
2050
+ 'zip' => 'application/zip',
2051
+ 'gz' => 'application/gzip',
2052
+ 'bz2' => 'application/x-bzip',
2053
+ 'tar' => 'application/x-tar',
2054
+ '3gp' => 'video/3gpp',
2055
+ 'ai' => 'application/postscript',
2056
+ 'aif' => 'audio/x-aiff',
2057
+ 'aifc' => 'audio/x-aiff',
2058
+ 'aiff' => 'audio/x-aiff',
2059
+ 'asc' => 'text/plain',
2060
+ 'atom' => 'application/atom+xml',
2061
+ 'au' => 'audio/basic',
2062
+ 'avi' => 'video/x-msvideo',
2063
+ 'bcpio' => 'application/x-bcpio',
2064
+ 'bin' => 'application/octet-stream',
2065
+ 'bmp' => 'image/bmp',
2066
+ 'cdf' => 'application/x-netcdf',
2067
+ 'cgm' => 'image/cgm',
2068
+ 'class' => 'application/octet-stream',
2069
+ 'cpio' => 'application/x-cpio',
2070
+ 'cpt' => 'application/mac-compactpro',
2071
+ 'csh' => 'application/x-csh',
2072
+ 'css' => 'text/css',
2073
+ 'dcr' => 'application/x-director',
2074
+ 'dif' => 'video/x-dv',
2075
+ 'dir' => 'application/x-director',
2076
+ 'djv' => 'image/vnd.djvu',
2077
+ 'djvu' => 'image/vnd.djvu',
2078
+ 'dll' => 'application/octet-stream',
2079
+ 'dmg' => 'application/octet-stream',
2080
+ 'dms' => 'application/octet-stream',
2081
+ 'doc' => 'application/msword',
2082
+ 'dtd' => 'application/xml-dtd',
2083
+ 'dv' => 'video/x-dv',
2084
+ 'dvi' => 'application/x-dvi',
2085
+ 'dxr' => 'application/x-director',
2086
+ 'eps' => 'application/postscript',
2087
+ 'etx' => 'text/x-setext',
2088
+ 'exe' => 'application/octet-stream',
2089
+ 'ez' => 'application/andrew-inset',
2090
+ 'flv' => 'video/x-flv',
2091
+ 'gif' => 'image/gif',
2092
+ 'gram' => 'application/srgs',
2093
+ 'grxml' => 'application/srgs+xml',
2094
+ 'gtar' => 'application/x-gtar',
2095
+ 'hdf' => 'application/x-hdf',
2096
+ 'hqx' => 'application/mac-binhex40',
2097
+ 'htm' => 'text/html',
2098
+ 'html' => 'text/html',
2099
+ 'ice' => 'x-conference/x-cooltalk',
2100
+ 'ico' => 'image/x-icon',
2101
+ 'ics' => 'text/calendar',
2102
+ 'ief' => 'image/ief',
2103
+ 'ifb' => 'text/calendar',
2104
+ 'iges' => 'model/iges',
2105
+ 'igs' => 'model/iges',
2106
+ 'jnlp' => 'application/x-java-jnlp-file',
2107
+ 'jp2' => 'image/jp2',
2108
+ 'jpe' => 'image/jpeg',
2109
+ 'jpeg' => 'image/jpeg',
2110
+ 'jpg' => 'image/jpeg',
2111
+ 'js' => 'application/x-javascript',
2112
+ 'kar' => 'audio/midi',
2113
+ 'latex' => 'application/x-latex',
2114
+ 'lha' => 'application/octet-stream',
2115
+ 'lzh' => 'application/octet-stream',
2116
+ 'm3u' => 'audio/x-mpegurl',
2117
+ 'm4a' => 'audio/mp4a-latm',
2118
+ 'm4p' => 'audio/mp4a-latm',
2119
+ 'm4u' => 'video/vnd.mpegurl',
2120
+ 'm4v' => 'video/x-m4v',
2121
+ 'mac' => 'image/x-macpaint',
2122
+ 'man' => 'application/x-troff-man',
2123
+ 'mathml' => 'application/mathml+xml',
2124
+ 'me' => 'application/x-troff-me',
2125
+ 'mesh' => 'model/mesh',
2126
+ 'mid' => 'audio/midi',
2127
+ 'midi' => 'audio/midi',
2128
+ 'mif' => 'application/vnd.mif',
2129
+ 'mov' => 'video/quicktime',
2130
+ 'movie' => 'video/x-sgi-movie',
2131
+ 'mp2' => 'audio/mpeg',
2132
+ 'mp3' => 'audio/mpeg',
2133
+ 'mp4' => 'video/mp4',
2134
+ 'mpe' => 'video/mpeg',
2135
+ 'mpeg' => 'video/mpeg',
2136
+ 'mpg' => 'video/mpeg',
2137
+ 'mpga' => 'audio/mpeg',
2138
+ 'ms' => 'application/x-troff-ms',
2139
+ 'msh' => 'model/mesh',
2140
+ 'mxu' => 'video/vnd.mpegurl',
2141
+ 'nc' => 'application/x-netcdf',
2142
+ 'oda' => 'application/oda',
2143
+ 'ogg' => 'application/ogg',
2144
+ 'ogv' => 'video/ogv',
2145
+ 'pbm' => 'image/x-portable-bitmap',
2146
+ 'pct' => 'image/pict',
2147
+ 'pdb' => 'chemical/x-pdb',
2148
+ 'pdf' => 'application/pdf',
2149
+ 'pgm' => 'image/x-portable-graymap',
2150
+ 'pgn' => 'application/x-chess-pgn',
2151
+ 'pic' => 'image/pict',
2152
+ 'pict' => 'image/pict',
2153
+ 'png' => 'image/png',
2154
+ 'pnm' => 'image/x-portable-anymap',
2155
+ 'pnt' => 'image/x-macpaint',
2156
+ 'pntg' => 'image/x-macpaint',
2157
+ 'ppm' => 'image/x-portable-pixmap',
2158
+ 'ppt' => 'application/vnd.ms-powerpoint',
2159
+ 'ps' => 'application/postscript',
2160
+ 'qt' => 'video/quicktime',
2161
+ 'qti' => 'image/x-quicktime',
2162
+ 'qtif' => 'image/x-quicktime',
2163
+ 'ra' => 'audio/x-pn-realaudio',
2164
+ 'ram' => 'audio/x-pn-realaudio',
2165
+ 'ras' => 'image/x-cmu-raster',
2166
+ 'rdf' => 'application/rdf+xml',
2167
+ 'rgb' => 'image/x-rgb',
2168
+ 'rm' => 'application/vnd.rn-realmedia',
2169
+ 'roff' => 'application/x-troff',
2170
+ 'rtf' => 'text/rtf',
2171
+ 'rtx' => 'text/richtext',
2172
+ 'sgm' => 'text/sgml',
2173
+ 'sgml' => 'text/sgml',
2174
+ 'sh' => 'application/x-sh',
2175
+ 'shar' => 'application/x-shar',
2176
+ 'silo' => 'model/mesh',
2177
+ 'sit' => 'application/x-stuffit',
2178
+ 'skd' => 'application/x-koan',
2179
+ 'skm' => 'application/x-koan',
2180
+ 'skp' => 'application/x-koan',
2181
+ 'skt' => 'application/x-koan',
2182
+ 'smi' => 'application/smil',
2183
+ 'smil' => 'application/smil',
2184
+ 'snd' => 'audio/basic',
2185
+ 'so' => 'application/octet-stream',
2186
+ 'spl' => 'application/x-futuresplash',
2187
+ 'src' => 'application/x-wais-source',
2188
+ 'sv4cpio' => 'application/x-sv4cpio',
2189
+ 'sv4crc' => 'application/x-sv4crc',
2190
+ 'svg' => 'image/svg+xml',
2191
+ 'swf' => 'application/x-shockwave-flash',
2192
+ 't' => 'application/x-troff',
2193
+ 'tcl' => 'application/x-tcl',
2194
+ 'tex' => 'application/x-tex',
2195
+ 'texi' => 'application/x-texinfo',
2196
+ 'texinfo' => 'application/x-texinfo',
2197
+ 'tif' => 'image/tiff',
2198
+ 'tiff' => 'image/tiff',
2199
+ 'tr' => 'application/x-troff',
2200
+ 'tsv' => 'text/tab-separated-values',
2201
+ 'txt' => 'text/plain',
2202
+ 'ustar' => 'application/x-ustar',
2203
+ 'vcd' => 'application/x-cdlink',
2204
+ 'vrml' => 'model/vrml',
2205
+ 'vxml' => 'application/voicexml+xml',
2206
+ 'wav' => 'audio/x-wav',
2207
+ 'wbmp' => 'image/vnd.wap.wbmp',
2208
+ 'wbxml' => 'application/vnd.wap.wbxml',
2209
+ 'webm' => 'video/webm',
2210
+ 'wml' => 'text/vnd.wap.wml',
2211
+ 'wmlc' => 'application/vnd.wap.wmlc',
2212
+ 'wmls' => 'text/vnd.wap.wmlscript',
2213
+ 'wmlsc' => 'application/vnd.wap.wmlscriptc',
2214
+ 'wmv' => 'video/x-ms-wmv',
2215
+ 'wrl' => 'model/vrml',
2216
+ 'xbm' => 'image/x-xbitmap',
2217
+ 'xht' => 'application/xhtml+xml',
2218
+ 'xhtml' => 'application/xhtml+xml',
2219
+ 'xls' => 'application/vnd.ms-excel',
2220
+ 'xml' => 'application/xml',
2221
+ 'xpm' => 'image/x-xpixmap',
2222
+ 'xsl' => 'application/xml',
2223
+ 'xslt' => 'application/xslt+xml',
2224
+ 'xul' => 'application/vnd.mozilla.xul+xml',
2225
+ 'xwd' => 'image/x-xwindowdump',
2226
+ 'xyz' => 'chemical/x-xyz',
2227
+ );
2228
+
2229
+ $filesuffix = pathinfo( $file, PATHINFO_EXTENSION );
2230
+ $suffix = strtolower( $filesuffix );
2231
+ if ( isset( $mime_types[ $suffix ] ) ) {
2232
+ return $mime_types[ $suffix ];
2233
+ }
2234
+
2235
+ if ( ! is_readable( $file ) ) {
2236
+ return 'application/octet-stream';
2237
+ }
2238
+
2239
+ if ( function_exists( 'fileinfo' ) ) {
2240
+ $finfo = finfo_open( FILEINFO_MIME_TYPE );
2241
+ $mime = finfo_file( $finfo, $file );
2242
+ }
2243
+
2244
+ if ( empty( $mime ) && function_exists( 'mime_content_type' ) ) {
2245
+ $mime = mime_content_type( $file );
2246
+ }
2247
+
2248
+ if ( ! empty( $mime ) ) {
2249
+ return $mime;
2250
+ }
2251
+
2252
+ return 'application/octet-stream';
2253
+ }
2254
+
2255
+ /**
2256
+ * Check whether exec has been disabled.
2257
+ *
2258
+ * @access public
2259
+ * @static
2260
+ * @return bool
2261
+ */
2262
+ public static function is_exec() {
2263
+
2264
+ // Is function avail
2265
+ if ( ! function_exists( 'exec' ) ) {
2266
+ return false;
2267
+ }
2268
+
2269
+ // Is shell_exec disabled?
2270
+ if ( in_array( 'exec', array_map( 'trim', explode( ',', @ini_get( 'disable_functions' ) ) ), true ) ) {
2271
+ return false;
2272
+ }
2273
+
2274
+ // Can we issue a simple echo command?
2275
+ $output = exec( 'echo backwpupechotest' );
2276
+ if ( $output != 'backwpupechotest' ) {
2277
+ return false;
2278
+ }
2279
+
2280
+ return true;
2281
+
2282
+ }
2283
+
2284
+ /**
2285
+ * Delete some data on cloned objects
2286
+ */
2287
+ public function __clone() {
2288
+
2289
+ $this->temp = array();
2290
+ $this->run = array();
2291
+ }
2292
+
2293
+ /**
2294
+ * Signal handler
2295
+ * @param $signal_send
2296
+ */
2297
+ public function signal_handler( $signal_send ) {
2298
+
2299
+ //known signals
2300
+ $signals = array(
2301
+ 'SIGHUP' => array(
2302
+ 'description' => _x( 'Hangup detected on controlling terminal or death of controlling process', 'SIGHUP: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2303
+ 'error' => E_USER_ERROR
2304
+ ),
2305
+ 'SIGINT' => array(
2306
+ 'description' => _x( 'Interrupt from keyboard', 'SIGINT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2307
+ 'error' => E_USER_ERROR
2308
+ ),
2309
+ 'SIGQUIT' => array(
2310
+ 'description' => _x( 'Quit from keyboard', 'SIGQUIT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2311
+ 'error' => E_USER_ERROR
2312
+ ),
2313
+ 'SIGILL' => array(
2314
+ 'description' => _x( 'Illegal Instruction', 'SIGILL: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2315
+ 'error' => E_USER_ERROR
2316
+ ),
2317
+ 'SIGABRT' => array(
2318
+ 'description' => _x( 'Abort signal from abort(3)', 'SIGABRT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2319
+ 'error' => E_USER_NOTICE
2320
+ ),
2321
+ 'SIGBUS' => array(
2322
+ 'description' => _x( 'Bus error (bad memory access)', 'SIGBUS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2323
+ 'error' => E_USER_ERROR
2324
+ ),
2325
+ 'SIGFPE' => array(
2326
+ 'description' => _x( 'Floating point exception', 'SIGFPE: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2327
+ 'error' => E_USER_ERROR
2328
+ ),
2329
+ 'SIGSEGV' => array(
2330
+ 'description' => _x( 'Invalid memory reference', 'SIGSEGV: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2331
+ 'error' => E_USER_ERROR
2332
+ ),
2333
+ 'SIGTERM' => array(
2334
+ 'description' => _x( 'Termination signal', 'SIGTERM: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2335
+ 'error' => E_USER_WARNING
2336
+ ),
2337
+ 'SIGSTKFLT' => array(
2338
+ 'description' => _x( 'Stack fault on coprocessor', 'SIGSTKFLT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2339
+ 'error' => E_USER_ERROR
2340
+ ),
2341
+ 'SIGUSR1' => array(
2342
+ 'description' => _x( 'User-defined signal 1', 'SIGUSR1: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2343
+ 'error' => E_USER_NOTICE
2344
+ ),
2345
+ 'SIGUSR2' => array(
2346
+ 'description' => _x( 'User-defined signal 2', 'SIGUSR2: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2347
+ 'error' => E_USER_NOTICE
2348
+ ),
2349
+ 'SIGURG' => array(
2350
+ 'description' => _x( 'Urgent condition on socket', 'SIGURG: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2351
+ 'error' => E_USER_NOTICE
2352
+ ),
2353
+ 'SIGXCPU' => array(
2354
+ 'description' => _x( 'CPU time limit exceeded', 'SIGXCPU: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2355
+ 'error' => E_USER_ERROR
2356
+ ),
2357
+ 'SIGXFSZ' => array(
2358
+ 'description' => _x( 'File size limit exceeded', 'SIGXFSZ: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2359
+ 'error' => E_USER_ERROR
2360
+ ),
2361
+ 'SIGPWR' => array(
2362
+ 'description' => _x( 'Power failure', 'SIGPWR: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2363
+ 'error' => E_USER_ERROR
2364
+ ),
2365
+ 'SIGSYS' => array(
2366
+ 'description' => _x( 'Bad argument to routine', 'SIGSYS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details', 'backwpup' ),
2367
+ 'error' => E_USER_ERROR
2368
+ ),
2369
+ );
2370
+
2371
+ foreach ( $signals as $signal => $config ) {
2372
+ if ( defined( $signal ) && $signal_send === constant( $signal ) ) {
2373
+ $this->log( sprintf( __( 'Signal "%1$s" (%2$s) is sent to script!', 'backwpup' ), $signal, $config['description'] ), $config['error'] );
2374
+ $this->signal = $signal_send;
2375
+ break;
2376
+ }
2377
+ }
2378
+
2379
+ }
2380
+
2381
+ /**
2382
+ *
2383
+ * Shutdown function is call if script terminates try to make a restart if needed
2384
+ *
2385
+ * Prepare the job for start
2386
+ *
2387
+ * @internal param int the signal that terminates the job
2388
+ */
2389
+ public function shutdown() {
2390
+
2391
+ //Put last error to log if one
2392
+ $lasterror = error_get_last();
2393
+ if ( $lasterror['type'] === E_ERROR || $lasterror['type'] === E_PARSE || $lasterror['type'] === E_CORE_ERROR || $lasterror['type'] === E_CORE_WARNING || $lasterror['type'] === E_COMPILE_ERROR || $lasterror['type'] === E_COMPILE_WARNING ) {
2394
+ $this->log( $lasterror['type'], $lasterror['message'], $lasterror['file'], $lasterror['line'] );
2395
+ }
2396
+
2397
+ $error = false;
2398
+ if ( function_exists( 'pcntl_get_last_error' ) ) {
2399
+ $error = pcntl_get_last_error();
2400
+ if ( ! empty( $error ) ) {
2401
+ $error_msg = pcntl_strerror( $error );
2402
+ if ( ! empty( $error_msg ) ) {
2403
+ $error = '(' . $error . ') ' . $error_msg;
2404
+ }
2405
+ }
2406
+ if ( ! empty( $error ) ) {
2407
+ $this->log( sprintf( __( 'System: %s', 'backwpup' ), $error ), E_USER_ERROR );
2408
+ }
2409
+ }
2410
+
2411
+ if ( function_exists( 'posix_get_last_error' ) && ! $error ) {
2412
+ $error = posix_get_last_error();
2413
+ if ( ! empty( $error ) ) {
2414
+ $error_msg = posix_strerror( $error );
2415
+ if ( ! empty( $error_msg ) ) {
2416
+ $error = '(' . $error . ') ' . $error_msg;
2417
+ }
2418
+ }
2419
+ if ( ! empty( $error ) ) {
2420
+ $this->log( sprintf( __( 'System: %s', 'backwpup' ), $error ), E_USER_ERROR );
2421
+ }
2422
+ }
2423
+
2424
+ $this->do_restart( true );
2425
+ }
2426
+
2427
+ /**
2428
+ *
2429
+ * The uncouth exception handler
2430
+ *
2431
+ * @param object $exception
2432
+ */
2433
+ public function exception_handler( $exception ) {
2434
+
2435
+ $this->log( sprintf( __( 'Exception caught in %1$s: %2$s', 'backwpup' ), get_class( $exception ), $exception->getMessage() ), E_USER_ERROR, $exception->getFile(), $exception->getLine() );
2436
+ }
2437
+
2438
+ /**
2439
+ *
2440
+ * Callback for the CURLOPT_READFUNCTION that submit the transferred bytes
2441
+ * to build the process bar
2442
+ *
2443
+ * @param $curl_handle
2444
+ * @param $file_handle
2445
+ * @param $read_count
2446
+ *
2447
+ * @return string
2448
+ * @internal param $out
2449
+ */
2450
+ public function curl_read_callback( $curl_handle, $file_handle, $read_count ) {
2451
+
2452
+ $data = null;
2453
+ if ( ! empty( $file_handle ) && is_numeric( $read_count ) ) {
2454
+ $data = fread( $file_handle, $read_count );
2455
+ }
2456
+
2457
+ if ( $this->job['backuptype'] == 'sync' ) {
2458
+ return $data;
2459
+ }
2460
+
2461
+ $length = ( is_numeric( $read_count ) ) ? $read_count : strlen( $read_count );
2462
+ $this->substeps_done = $this->substeps_done + $length;
2463
+ $this->update_working_data();
2464
+
2465
+ return $data;
2466
+ }
2467
+
2468
+ /**
2469
+ * @param $file
2470
+ *
2471
+ * @return bool
2472
+ */
2473
+ public function is_backup_archive( $file ) {
2474
+
2475
+ $extensions = array(
2476
+ '.tar.gz',
2477
+ '.tar.bz2',
2478
+ '.tar',
2479
+ '.zip'
2480
+ );
2481
+
2482
+ $file = trim( basename( $file ) );
2483
+ $filename = '';
2484
+
2485
+ foreach ( $extensions as $extension ) {
2486
+ if ( substr( $file, ( strlen( $extension ) * - 1 ) ) === $extension ) {
2487
+ $filename = substr( $file, 0, ( strlen( $extension ) * - 1 ) );
2488
+ }
2489
+ }
2490
+
2491
+ if ( ! $filename ) {
2492
+ return false;
2493
+ }
2494
+
2495
+ $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
2496
+ $dateregex = array(
2497
+ '(0[1-9]|[12][0-9]|3[01])',
2498
+ '([1-9]|[12][0-9]|3[01])',
2499
+ '(0[1-9]|1[012])',
2500
+ '([1-9]|1[012])',
2501
+ '((19|20|21)[0-9]{2})',
2502
+ '([0-9]{2})',
2503
+ '(am|pm)',
2504
+ '(AM|PM)',
2505
+ '([0-9]{3})',
2506
+ '([1-9]|1[012])',
2507
+ '([0-9]|1[0-9]|2[0-3])',
2508
+ '(0[1-9]|1[012])',
2509
+ '([01][0-9]|2[0-3])',
2510
+ '([0-5][0-9])',
2511
+ '([0-5][0-9])'
2512
+ );
2513
+
2514
+ $regex = "/^" . str_replace( $datevars, $dateregex, preg_quote( self::sanitize_file_name( $this->job['archivename'] ) ) ) . "$/i";
2515
+
2516
+ preg_match( $regex, $filename, $matches );
2517
+ if ( ! empty( $matches[0] ) && $matches[0] === $filename ) {
2518
+ return true;
2519
+ }
2520
+
2521
+ return false;
2522
+ }
2523
+
2524
+ /**
2525
+ * For storing and getting data in/from a extra temp file
2526
+ *
2527
+ * @param string $storage The name of the storage
2528
+ * @param array $data data to save in storage
2529
+ *
2530
+ * @return array|mixed|null data from storage
2531
+ */
2532
+ public function data_storage( $storage = null, $data = null ) {
2533
+
2534
+ if ( empty( $storage ) ) {
2535
+ return $data;
2536
+ }
2537
+
2538
+ $storage = strtolower( $storage );
2539
+
2540
+ $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-' . $storage . '.json';
2541
+
2542
+ if ( ! empty( $data ) ) {
2543
+ file_put_contents( $file, json_encode( $data ) );
2544
+ } elseif ( is_readable( $file ) ) {
2545
+ $json = file_get_contents( $file );
2546
+ $data = json_decode( $json, true );
2547
+ }
2548
+
2549
+ return $data;
2550
+ }
2551
+
2552
+ /**
2553
+ * Add a Folders to Folder list that should be backup
2554
+ *
2555
+ * @param array $folders folder to add
2556
+ * @param bool $new overwrite existing file
2557
+ */
2558
+ public function add_folders_to_backup( $folders = array(), $new = false ) {
2559
+
2560
+ if ( ! is_array( $folders ) ) {
2561
+ $folders = (array) $folders;
2562
+ }
2563
+
2564
+ $file = BackWPup::get_plugin_data( 'temp' ) . 'backwpup-' . BackWPup::get_plugin_data( 'hash' ) . '-folder.php';
2565
+
2566
+ if ( ! file_exists( $file ) || $new ) {
2567
+ file_put_contents( $file, '<?php' . PHP_EOL );
2568
+ }
2569
+
2570
+ $content = '';
2571
+ foreach ( $folders AS $folder ) {
2572
+ $content .= '//' . $folder . PHP_EOL;
2573
+ }
2574
+
2575
+ if ( $content ) {
2576
+ file_put_contents( $file, $content, FILE_APPEND );
2577
+ }
2578
+ }
2579
+
2580
+ }
inc/class-jobtype-file.php CHANGED
@@ -63,7 +63,7 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
63
 
64
  @set_time_limit( 300 );
65
  $abs_folder_up = BackWPup_Option::get( $main, 'backupabsfolderup' );
66
- $abs_path = realpath( ABSPATH );
67
  if ( $abs_folder_up ) {
68
  $abs_path = dirname( $abs_path );
69
  }
@@ -75,185 +75,40 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
75
  <th scope="row"><label for="idbackuproot"><?php esc_html_e( 'Backup WordPress install folder', 'backwpup' ); ?></label></th>
76
  <td>
77
  <?php
78
- $folder = $abs_path;
79
- if ( $folder ) {
80
- $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
81
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
82
- }
83
  ?>
84
- <input class="checkbox"
85
- type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backuproot' ), TRUE, TRUE );?>
86
- name="backuproot" id="idbackuproot" value="1" /> <code title="<?php echo esc_attr(sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), $abs_path )); ?>"><?php echo esc_attr( $folder ); ?></code><?php echo esc_html( $folder_size ); ?>
87
-
88
- <fieldset id="backuprootexcludedirs" style="padding-left:15px; margin:2px;">
89
- <legend><strong><?php esc_html_e( 'Exclude:', 'backwpup' ); ?></strong></legend>
90
- <?php
91
- if ( $folder && $dir = opendir( $folder ) ) {
92
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
93
- $excludes = BackWPup_Option::get( $main, 'backuprootexcludedirs' );
94
- if ( ! in_array( $file, array( '.', '..' ), true ) && is_dir( $folder . '/' . $file ) && ! in_array( trailingslashit( $folder . '/' . $file ), $this->get_exclude_dirs( $folder ), true ) ) {
95
- $donotbackup = file_exists( $folder . '/' . $file . '/.donotbackup' );
96
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder . '/' . $file ), 2 ) . ')' : '';
97
- $title = '';
98
- if ( $donotbackup ) {
99
- $excludes[] = $file;
100
- $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
101
- }
102
- echo '<nobr><label for="idrootexcludedirs-'.sanitize_file_name( $file ).'"><input class="checkbox" type="checkbox"' . checked( in_array( $file, $excludes, true ), TRUE, FALSE ) . ' name="backuprootexcludedirs[]" id="idrootexcludedirs-' . sanitize_file_name( $file ) . '" value="' . esc_attr( $file ) . '"' . disabled( $donotbackup, TRUE, FALSE ) . $title . ' /> ' . esc_html( $file ) . esc_html( $folder_size ) . '</label><br /></nobr>';
103
- }
104
- }
105
- closedir( $dir );
106
- }
107
- ?>
108
- </fieldset>
109
  </td>
110
  </tr>
111
  <tr>
112
  <th scope="row"><label for="idbackupcontent"><?php esc_html_e( 'Backup content folder', 'backwpup' ); ?></label></th>
113
  <td>
114
  <?php
115
- $folder = realpath( WP_CONTENT_DIR );
116
- if ( $folder ) {
117
- $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
118
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
119
- }
120
  ?>
121
- <input class="checkbox"
122
- type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupcontent' ), TRUE, TRUE );?>
123
- name="backupcontent" id="idbackupcontent" value="1" /> <code title="<?php echo esc_attr(sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), WP_CONTENT_DIR )); ?>"><?php echo esc_html( $folder ); ?></code><?php echo esc_html($folder_size); ?>
124
-
125
- <fieldset id="backupcontentexcludedirs" style="padding-left:15px; margin:2px;">
126
- <legend><strong><?php esc_html_e( 'Exclude:', 'backwpup' ); ?></strong></legend>
127
- <?php
128
- if ( $folder && $dir = opendir( $folder ) ) {
129
- $excludes = BackWPup_Option::get( $main, 'backupcontentexcludedirs' );
130
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
131
- if ( ! in_array( $file, array( '.', '..' ), true ) && is_dir( $folder . '/' . $file ) && ! in_array( trailingslashit( $folder . '/' . $file ), $this->get_exclude_dirs( $folder ), true ) ) {
132
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder . '/' . $file ), 2 ) . ')' : '';
133
- $donotbackup = file_exists( $folder . '/' . $file . '/.donotbackup' );
134
- $title = '';
135
- if ( $donotbackup ) {
136
- $excludes[] = $file;
137
- $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
138
- }
139
- echo '<nobr><label for="idcontentexcludedirs-'.sanitize_file_name( $file ).'"><input class="checkbox" type="checkbox"' . checked( in_array( $file, $excludes, true ), TRUE, FALSE ) . ' name="backupcontentexcludedirs[]" id="idcontentexcludedirs-'.sanitize_file_name( $file ).'" value="' . esc_attr($file) . '"' . disabled( $donotbackup, TRUE, FALSE ) . $title . ' /> ' . esc_html( $file ) . esc_html($folder_size) . '</label><br /></nobr>';
140
- }
141
- }
142
- closedir( $dir );
143
- }
144
- ?>
145
- </fieldset>
146
  </td>
147
  </tr>
148
  <tr>
149
  <th scope="row"><label for="idbackupplugins"><?php _e( 'Backup plugins', 'backwpup' ); ?></label></th>
150
  <td>
151
  <?php
152
- $folder = realpath( WP_PLUGIN_DIR );
153
- if ( $folder ) {
154
- $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
155
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
156
- }
157
  ?>
158
- <input class="checkbox"
159
- type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupplugins' ), TRUE, TRUE );?>
160
- name="backupplugins" id="idbackupplugins" value="1" /> <code title="<?php echo sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), esc_attr( WP_PLUGIN_DIR ) ); ?>"><?php echo esc_attr( $folder ); ?></code><?php echo $folder_size; ?>
161
-
162
- <fieldset id="backuppluginsexcludedirs" style="padding-left:15px; margin:2px;">
163
- <legend><strong><?php _e( 'Exclude:', 'backwpup' ); ?></strong></legend>
164
- <?php
165
- if ( $folder && $dir = opendir( $folder ) ) {
166
- $excludes = BackWPup_Option::get( $main, 'backuppluginsexcludedirs' );
167
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
168
- if ( ! in_array( $file, array( '.', '..' ), true ) && is_dir( $folder . '/' . $file ) && ! in_array( trailingslashit( $folder . '/' . $file ), $this->get_exclude_dirs( $folder ), true ) ) {
169
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder . '/' . $file ), 2 ) . ')' : '';
170
- $donotbackup = file_exists( $folder . '/' . $file . '/.donotbackup' );
171
- $title = '';
172
- if ( $donotbackup ) {
173
- $excludes[] = $file;
174
- $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
175
- }
176
- echo '<nobr><label for="idpluginexcludedirs-'.sanitize_file_name( $file ).'"><input class="checkbox" type="checkbox"' . checked( in_array( $file, $excludes, true ), TRUE, FALSE ) . ' name="backuppluginsexcludedirs[]" id="idpluginexcludedirs-'.sanitize_file_name( $file ).'" value="' . esc_attr($file) . '"' . disabled( $donotbackup, TRUE, FALSE ) . $title . ' /> ' . esc_html( $file ) . esc_html($folder_size) . '</label><br /></nobr>';
177
- }
178
- }
179
- closedir( $dir );
180
- }
181
- ?>
182
- </fieldset>
183
  </td>
184
  </tr>
185
  <tr>
186
  <th scope="row"><label for="idbackupthemes"><?php esc_html_e( 'Backup themes', 'backwpup' ); ?></label></th>
187
  <td>
188
  <?php
189
- $folder = realpath( get_theme_root() );
190
- if ( $folder ) {
191
- $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
192
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
193
- }
194
  ?>
195
- <input class="checkbox"
196
- type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupthemes' ), TRUE, TRUE );?>
197
- name="backupthemes" id="idbackupthemes" value="1" /> <code title="<?php echo sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), esc_attr( get_theme_root() ) ); ?>"><?php echo esc_attr( $folder ); ?></code><?php echo $folder_size; ?>
198
-
199
- <fieldset id="backupthemesexcludedirs" style="padding-left:15px; margin:2px;">
200
- <legend><strong><?php _e( 'Exclude:', 'backwpup' ); ?></strong></legend>
201
- <?php
202
- if ( $folder && $dir = opendir( $folder ) ) {
203
- $excludes = BackWPup_Option::get( $main, 'backupthemesexcludedirs' );
204
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
205
- if ( ! in_array( $file, array( '.', '..' ), true ) && is_dir( $folder . '/' . $file ) && ! in_array( trailingslashit( $folder . '/' . $file ), $this->get_exclude_dirs( $folder ), true ) ) {
206
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder . '/' . $file ), 2 ) . ')' : '';
207
- $donotbackup = file_exists( $folder . '/' . $file . '/.donotbackup' );
208
- $title = '';
209
- if ( $donotbackup ) {
210
- $excludes[] = $file;
211
- $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
212
- }
213
- echo '<nobr><label for="idthemesexcludedirs-'.sanitize_file_name( $file ).'"><input class="checkbox" type="checkbox"' . checked( in_array( $file, $excludes, true ), TRUE, FALSE ) . ' name="backupthemesexcludedirs[]" id="idthemesexcludedirs-'.sanitize_file_name( $file ).'" value="' . $file . '"' . disabled( $donotbackup, TRUE, FALSE ) . $title . ' /> ' . esc_attr( $file ) . $folder_size . '</label><br /></nobr>';
214
- }
215
- }
216
- closedir( $dir );
217
- }
218
- ?>
219
- </fieldset>
220
  </td>
221
  </tr>
222
  <tr>
223
  <th scope="row"><label for="idbackupuploads"><?php esc_html_e( 'Backup uploads folder', 'backwpup' ); ?></label></th>
224
  <td>
225
  <?php
226
- $folder = realpath( BackWPup_File::get_upload_dir() );
227
- if ( $folder ) {
228
- $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
229
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
230
- }
231
  ?>
232
- <input class="checkbox"
233
- type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupuploads' ), TRUE, TRUE );?>
234
- name="backupuploads" id="idbackupuploads" value="1" /> <code title="<?php echo sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), esc_attr( BackWPup_File::get_upload_dir() ) ); ?>"><?php echo esc_html( $folder ); ?></code><?php echo $folder_size; ?>
235
-
236
- <fieldset id="backupuploadsexcludedirs" style="padding-left:15px; margin:2px;">
237
- <legend><strong><?php esc_html_e( 'Exclude:', 'backwpup' ); ?></strong></legend>
238
- <?php
239
- if ( $folder && $dir = opendir( $folder ) ) {
240
- $excludes = BackWPup_Option::get( $main, 'backupuploadsexcludedirs' );
241
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
242
- if ( ! in_array( $file, array( '.', '..' ), true ) && is_dir( $folder . '/' . $file ) && ! in_array( trailingslashit( $folder . '/' . $file ), $this->get_exclude_dirs( $folder ), true ) ) {
243
- $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder . '/' . $file ), 2 ) . ')' : '';
244
- $donotbackup = file_exists( $folder . '/' . $file . '/.donotbackup' );
245
- $title = '';
246
- if ( $donotbackup ) {
247
- $excludes[] = $file;
248
- $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
249
- }
250
- echo '<nobr><label for="iduploadexcludedirs-'.sanitize_file_name( $file ).'"><input class="checkbox" type="checkbox"' . checked( in_array( $file, $excludes, true ), TRUE, FALSE ) . ' name="backupuploadsexcludedirs[]" id="iduploadexcludedirs-'.sanitize_file_name( $file ).'" value="' . esc_attr($file) . '"' . disabled( $donotbackup, TRUE, FALSE ) . $title . ' /> ' . esc_html( $file ) . esc_html($folder_size) . '</label><br /></nobr>';
251
- }
252
- }
253
- closedir( $dir );
254
- }
255
- ?>
256
- </fieldset>
257
  </td>
258
  </tr>
259
  <tr>
@@ -289,7 +144,7 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
289
  <tr>
290
  <th scope="row"><?php esc_html_e( 'Include special files', 'backwpup' ); ?></th>
291
  <td>
292
- <label for="idbackupspecialfiles"><input class="checkbox" id="idbackupspecialfiles" type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupspecialfiles' ), TRUE, TRUE ); ?> name="backupspecialfiles" value="1" /> <?php esc_html_e( 'Backup wp-config.php, robots.txt, nginx.conf, .htaccess, .htpasswd and favicon.ico from root if it is not included in backup.', 'backwpup' ); ?></label>
293
  </td>
294
  </tr>
295
  <tr>
@@ -309,69 +164,67 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
309
  */
310
  public function edit_form_post_save( $id ) {
311
 
312
- $fileexclude = explode( ',', sanitize_text_field( stripslashes( str_replace( array( "\r\n", "\r" ), ',', $_POST[ 'fileexclude' ] ) ) ) );
313
-
314
- foreach ( $fileexclude as $key => $value ) {
315
- $fileexclude[ $key ] = str_replace( '//', '/', str_replace( '\\', '/', trim( $value ) ) );
316
- if ( empty( $fileexclude[ $key ] ) ) {
317
- unset( $fileexclude[ $key ] );
318
- }
 
 
319
  }
320
- sort( $fileexclude );
321
- BackWPup_Option::update( $id, 'fileexclude', implode( ',', $fileexclude ) );
322
-
323
- $dirinclude = explode( ',', sanitize_text_field( stripslashes( str_replace( array( "\r\n", "\r" ), ',', $_POST[ 'dirinclude' ] ) ) ) );
324
- foreach ( $dirinclude as $key => $value ) {
325
- $dirinclude[ $key ] = trailingslashit( str_replace( '//', '/', str_replace( '\\', '/', trim( $value ) ) ) );
326
- if ( $dirinclude[ $key ] == '/' || empty( $dirinclude[ $key ] ) || ! is_dir( $dirinclude[ $key ] ) ) {
327
- unset( $dirinclude[ $key ] );
328
- }
 
 
 
 
 
329
  }
330
- sort( $dirinclude );
331
- BackWPup_Option::update( $id, 'dirinclude', implode( ',', $dirinclude ) );
332
-
333
- BackWPup_Option::update( $id, 'backupexcludethumbs', ! empty( $_POST[ 'backupexcludethumbs' ] ) );
334
- BackWPup_Option::update( $id, 'backupspecialfiles', ! empty( $_POST[ 'backupspecialfiles' ] ) );
335
- BackWPup_Option::update( $id, 'backuproot', ! empty( $_POST[ 'backuproot' ] ) );
336
- BackWPup_Option::update( $id, 'backupabsfolderup', ! empty( $_POST[ 'backupabsfolderup' ] ) );
337
-
338
- if ( ! isset( $_POST[ 'backuprootexcludedirs' ] ) || ! is_array( $_POST[ 'backuprootexcludedirs' ] ) ) {
339
- $_POST[ 'backuprootexcludedirs' ] = array();
340
- }
341
- sort( $_POST[ 'backuprootexcludedirs' ] );
342
- BackWPup_Option::update( $id, 'backuprootexcludedirs', $_POST[ 'backuprootexcludedirs' ] );
343
-
344
- BackWPup_Option::update( $id, 'backupcontent', ! empty( $_POST[ 'backupcontent' ] ) );
345
-
346
- if ( ! isset( $_POST[ 'backupcontentexcludedirs' ] ) || ! is_array( $_POST[ 'backupcontentexcludedirs' ] ) ) {
347
- $_POST[ 'backupcontentexcludedirs' ] = array();
348
- }
349
- sort( $_POST[ 'backupcontentexcludedirs' ] );
350
- BackWPup_Option::update( $id, 'backupcontentexcludedirs', $_POST[ 'backupcontentexcludedirs' ] );
351
-
352
- BackWPup_Option::update( $id, 'backupplugins', ! empty( $_POST[ 'backupplugins' ] ) );
353
-
354
- if ( ! isset( $_POST[ 'backuppluginsexcludedirs' ] ) || ! is_array( $_POST[ 'backuppluginsexcludedirs' ] ) ) {
355
- $_POST[ 'backuppluginsexcludedirs' ] = array();
356
- }
357
- sort( $_POST[ 'backuppluginsexcludedirs' ] );
358
- BackWPup_Option::update( $id, 'backuppluginsexcludedirs', $_POST[ 'backuppluginsexcludedirs' ] );
359
-
360
- BackWPup_Option::update( $id, 'backupthemes', ! empty( $_POST[ 'backupthemes' ] ) );
361
-
362
- if ( ! isset( $_POST[ 'backupthemesexcludedirs' ] ) || ! is_array( $_POST[ 'backupthemesexcludedirs' ] ) ) {
363
- $_POST[ 'backupthemesexcludedirs' ] = array();
364
  }
365
- sort( $_POST[ 'backupthemesexcludedirs' ] );
366
- BackWPup_Option::update( $id, 'backupthemesexcludedirs', $_POST[ 'backupthemesexcludedirs' ] );
367
-
368
- BackWPup_Option::update( $id, 'backupuploads', ! empty( $_POST[ 'backupuploads' ] ) );
369
-
370
- if ( ! isset( $_POST[ 'backupuploadsexcludedirs' ] ) || ! is_array( $_POST[ 'backupuploadsexcludedirs' ] ) ) {
371
- $_POST[ 'backupuploadsexcludedirs' ] = array();
 
 
 
 
 
 
 
372
  }
373
- sort( $_POST[ 'backupuploadsexcludedirs' ] );
374
- BackWPup_Option::update( $id, 'backupuploadsexcludedirs', $_POST[ 'backupuploadsexcludedirs' ] );
375
  }
376
 
377
  /**
@@ -385,7 +238,7 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
385
  }
386
  $job_object->substeps_todo = 8;
387
 
388
- $abs_path = realpath( ABSPATH );
389
  if ( $job_object->job['backupabsfolderup'] ) {
390
  $abs_path = dirname( $abs_path );
391
  }
@@ -497,6 +350,8 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
497
 
498
  //add extra files if selected
499
  if ( ! empty( $job_object->job['backupspecialfiles'] ) ) {
 
 
500
  if ( is_readable( ABSPATH . 'wp-config.php' ) ) {
501
  $job_object->additional_files_to_backup[] = str_replace( '\\', '/', ABSPATH . 'wp-config.php' );
502
  $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'wp-config.php' ) );
@@ -506,25 +361,22 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
506
  $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'wp-config.php' ) );
507
  }
508
  }
509
- if ( is_readable( $abs_path . '.htaccess' ) && empty( $job_object->job['backuproot'] ) ) {
510
- $job_object->additional_files_to_backup[] = $abs_path . '.htaccess';
511
- $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), '.htaccess' ) );
512
- }
513
- if ( is_readable( $abs_path . 'nginx.conf' ) && empty( $job_object->job['backuproot'] ) ) {
514
- $job_object->additional_files_to_backup[] = $abs_path . 'nginx.conf';
515
- $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'nginx.conf' ) );
516
- }
517
- if ( is_readable( $abs_path . '.htpasswd' ) && empty( $job_object->job['backuproot'] ) ) {
518
- $job_object->additional_files_to_backup[] = $abs_path . '.htpasswd';
519
- $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), '.htpasswd' ) );
520
- }
521
- if ( is_readable( $abs_path . 'robots.txt' ) && empty( $job_object->job['backuproot'] ) ) {
522
- $job_object->additional_files_to_backup[] = $abs_path . 'robots.txt';
523
- $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'robots.txt' ) );
524
- }
525
- if ( is_readable( $abs_path . 'favicon.ico' ) && empty( $job_object->job['backuproot'] ) ) {
526
- $job_object->additional_files_to_backup[] = $abs_path . 'favicon.ico';
527
- $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'favicon.ico' ) );
528
  }
529
  }
530
 
@@ -555,44 +407,47 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
555
 
556
  $folder = trailingslashit( $folder );
557
 
558
- if ( $dir = opendir( $folder ) ) {
 
559
  //add folder to folder list
560
  $job_object->add_folders_to_backup( $folder );
561
  //scan folder
562
- while ( false !== ( $file = readdir( $dir ) ) ) {
563
- if ( in_array( $file, array( '.', '..' ), true ) ) {
564
  continue;
565
  }
 
566
  foreach ( $job_object->exclude_from_backup as $exclusion ) { //exclude files
567
  $exclusion = trim( $exclusion );
568
- if ( false !== stripos( $folder . $file, trim( $exclusion ) ) && ! empty( $exclusion ) ) {
569
  continue 2;
570
  }
571
  }
572
- if ( is_dir( $folder . $file ) ) {
573
- if ( in_array( trailingslashit( $folder . $file ), $excludedirs, true ) ) {
574
  continue;
575
  }
576
- if ( file_exists( trailingslashit( $folder . $file ) . '.donotbackup' ) ) {
577
  continue;
578
  }
579
- if ( ! is_readable( $folder . $file ) ) {
580
- $job_object->log( sprintf( __( 'Folder "%s" is not readable!', 'backwpup' ), $folder . $file ), E_USER_WARNING );
581
  continue;
582
  }
583
- $this->get_folder_list( $job_object, trailingslashit( $folder . $file ), $excludedirs, false );
584
  }
585
  if ( $first ) {
586
  $job_object->do_restart_time();
587
  }
588
  }
589
- closedir( $dir );
 
 
590
  }
591
 
592
  return true;
593
  }
594
 
595
-
596
  /**
597
  *
598
  * Get folder to exclude from a given folder for file backups
@@ -605,7 +460,7 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
605
  */
606
  private function get_exclude_dirs( $folder, $excludedir = array() ) {
607
 
608
- $folder = trailingslashit( str_replace( '\\', '/', realpath( $folder ) ) );
609
 
610
  if ( false !== strpos( trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) ), $folder ) && trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) ) != $folder ) {
611
  $excludedir[] = trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) );
@@ -622,4 +477,52 @@ class BackWPup_JobType_File extends BackWPup_JobTypes {
622
 
623
  return array_unique( $excludedir );
624
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  }
63
 
64
  @set_time_limit( 300 );
65
  $abs_folder_up = BackWPup_Option::get( $main, 'backupabsfolderup' );
66
+ $abs_path = realpath( BackWPup_Path_Fixer::fix_path( ABSPATH ) );
67
  if ( $abs_folder_up ) {
68
  $abs_path = dirname( $abs_path );
69
  }
75
  <th scope="row"><label for="idbackuproot"><?php esc_html_e( 'Backup WordPress install folder', 'backwpup' ); ?></label></th>
76
  <td>
77
  <?php
78
+ $this->show_folder( 'root', $main, $abs_path );
 
 
 
 
79
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  </td>
81
  </tr>
82
  <tr>
83
  <th scope="row"><label for="idbackupcontent"><?php esc_html_e( 'Backup content folder', 'backwpup' ); ?></label></th>
84
  <td>
85
  <?php
86
+ $this->show_folder( 'content', $main, WP_CONTENT_DIR );
 
 
 
 
87
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  </td>
89
  </tr>
90
  <tr>
91
  <th scope="row"><label for="idbackupplugins"><?php _e( 'Backup plugins', 'backwpup' ); ?></label></th>
92
  <td>
93
  <?php
94
+ $this->show_folder( 'plugins', $main, WP_PLUGIN_DIR );
 
 
 
 
95
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  </td>
97
  </tr>
98
  <tr>
99
  <th scope="row"><label for="idbackupthemes"><?php esc_html_e( 'Backup themes', 'backwpup' ); ?></label></th>
100
  <td>
101
  <?php
102
+ $this->show_folder( 'themes', $main, get_theme_root() );
 
 
 
 
103
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  </td>
105
  </tr>
106
  <tr>
107
  <th scope="row"><label for="idbackupuploads"><?php esc_html_e( 'Backup uploads folder', 'backwpup' ); ?></label></th>
108
  <td>
109
  <?php
110
+ $this->show_folder( 'uploads', $main, BackWPup_File::get_upload_dir() );
 
 
 
 
111
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  </td>
113
  </tr>
114
  <tr>
144
  <tr>
145
  <th scope="row"><?php esc_html_e( 'Include special files', 'backwpup' ); ?></th>
146
  <td>
147
+ <label for="idbackupspecialfiles"><input class="checkbox" id="idbackupspecialfiles" type="checkbox"<?php checked( BackWPup_Option::get( $main, 'backupspecialfiles' ), TRUE, TRUE ); ?> name="backupspecialfiles" value="1" /> <?php esc_html_e( 'Backup wp-config.php, robots.txt, nginx.conf, .htaccess, .htpasswd, favicon.ico, and Web.config from root if it is not included in backup.', 'backwpup' ); ?></label>
148
  </td>
149
  </tr>
150
  <tr>
164
  */
165
  public function edit_form_post_save( $id ) {
166
 
167
+ // Parse and save files to exclude
168
+ $exclude_input = filter_input( INPUT_POST , 'fileexclude' );
169
+ $to_exclude_list = $exclude_input ? str_replace( array( "\r\n", "\r" ), ',', $exclude_input ) : array();
170
+ $to_exclude_list and $to_exclude_list = sanitize_text_field( stripslashes( $to_exclude_list ) );
171
+ $to_exclude = $to_exclude_list ? explode( ',', $to_exclude_list ) : array();
172
+ $to_exclude_parsed = array();
173
+ foreach ( $to_exclude as $key => $value ) {
174
+ $normalized = wp_normalize_path( trim( $value ) );
175
+ $normalized and $to_exclude_parsed[$key] = $normalized;
176
  }
177
+ sort( $to_exclude_parsed );
178
+ BackWPup_Option::update( $id, 'fileexclude', implode( ',', $to_exclude_parsed ) );
179
+ unset( $exclude_input, $to_exclude_list, $to_exclude, $to_exclude_parsed, $normalized );
180
+
181
+ // Parse and save folders to include
182
+ $include_input = filter_input( INPUT_POST , 'dirinclude' );
183
+ $include_list = $include_input ? str_replace( array( "\r\n", "\r" ), ',', $include_input ) : array();
184
+ $to_include = $include_list ? explode( ',', $include_list ) : array();
185
+ $to_include_parsed = array();
186
+ foreach ( $to_include as $key => $value ) {
187
+ $normalized = trailingslashit( wp_normalize_path( trim( $value ) ) );
188
+ $normalized and $normalized = filter_var( $normalized, FILTER_SANITIZE_URL );
189
+ $realpath = $normalized && $normalized !== '/' ? realpath( $normalized ) : false;
190
+ $realpath and $to_include_parsed[$key] = $realpath;
191
  }
192
+ sort( $to_include_parsed );
193
+ BackWPup_Option::update( $id, 'dirinclude', implode( ',', $to_include_parsed ) );
194
+ unset( $include_input, $include_list, $to_include, $to_include_parsed, $normalized, $realpath );
195
+
196
+ // Parse and save boolean fields
197
+ $boolean_fields_def = array(
198
+ 'backupexcludethumbs' => FILTER_VALIDATE_BOOLEAN,
199
+ 'backupspecialfiles' => FILTER_VALIDATE_BOOLEAN,
200
+ 'backuproot' => FILTER_VALIDATE_BOOLEAN,
201
+ 'backupabsfolderup' => FILTER_VALIDATE_BOOLEAN,
202
+ 'backupcontent' => FILTER_VALIDATE_BOOLEAN,
203
+ 'backupplugins' => FILTER_VALIDATE_BOOLEAN,
204
+ 'backupthemes' => FILTER_VALIDATE_BOOLEAN,
205
+ 'backupuploads' => FILTER_VALIDATE_BOOLEAN,
206
+ );
207
+ $boolean_data = filter_input_array( INPUT_POST, $boolean_fields_def );
208
+ $boolean_data or $boolean_data = array();
209
+ foreach( $boolean_fields_def as $key => $value ) {
210
+ BackWPup_Option::update( $id, $key, ! empty( $boolean_data[$key] ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  }
212
+ unset( $boolean_fields_def, $boolean_data );
213
+
214
+ // Parse and save directories to exclude
215
+ $exclude_dirs_def = array(
216
+ 'backuprootexcludedirs' => array( 'filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FORCE_ARRAY ),
217
+ 'backuppluginsexcludedirs' => array( 'filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FORCE_ARRAY ),
218
+ 'backupthemesexcludedirs' => array( 'filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FORCE_ARRAY ),
219
+ 'backupuploadsexcludedirs' => array( 'filter' => FILTER_SANITIZE_URL, 'flags' => FILTER_FORCE_ARRAY ),
220
+ );
221
+ $exclude_dirs = filter_input_array( INPUT_POST, $exclude_dirs_def );
222
+ $exclude_dirs or $exclude_dirs = array();
223
+ foreach( $exclude_dirs_def as $key => $filter ) {
224
+ $value = ! empty( $exclude_dirs[$key] ) && is_array( $exclude_dirs[$key] ) ? $exclude_dirs[$key] : array();
225
+ BackWPup_Option::update( $id, $key, $value );
226
  }
227
+ unset( $exclude_dirs_def, $exclude_dirs );
 
228
  }
229
 
230
  /**
238
  }
239
  $job_object->substeps_todo = 8;
240
 
241
+ $abs_path = realpath( BackWPup_Path_Fixer::fix_path( ABSPATH ) );
242
  if ( $job_object->job['backupabsfolderup'] ) {
243
  $abs_path = dirname( $abs_path );
244
  }
350
 
351
  //add extra files if selected
352
  if ( ! empty( $job_object->job['backupspecialfiles'] ) ) {
353
+
354
+ // Special handling for wp-config.php
355
  if ( is_readable( ABSPATH . 'wp-config.php' ) ) {
356
  $job_object->additional_files_to_backup[] = str_replace( '\\', '/', ABSPATH . 'wp-config.php' );
357
  $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'wp-config.php' ) );
361
  $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), 'wp-config.php' ) );
362
  }
363
  }
364
+
365
+ // Files to include
366
+ $special_files = array(
367
+ '.htaccess',
368
+ 'nginx.conf',
369
+ '.htpasswd',
370
+ 'robots.txt',
371
+ 'favicon.ico',
372
+ 'Web.config',
373
+ );
374
+
375
+ foreach ( $special_files as $file ) {
376
+ if ( is_readable( $abs_path . $file ) && empty( $job_object->job['backuproot'] ) ) {
377
+ $job_object->additional_files_to_backup[] = $abs_path . $file;
378
+ $job_object->log( sprintf( __( 'Added "%s" to backup file list', 'backwpup' ), $file ) );
379
+ }
 
 
 
380
  }
381
  }
382
 
407
 
408
  $folder = trailingslashit( $folder );
409
 
410
+ try {
411
+ $dir = new BackWPup_Directory( $folder );
412
  //add folder to folder list
413
  $job_object->add_folders_to_backup( $folder );
414
  //scan folder
415
+ foreach ( $dir as $file ) {
416
+ if ( $file->isDot() ) {
417
  continue;
418
  }
419
+ $path = str_replace( '\\', '/', realpath( $file->getPathname() ) );
420
  foreach ( $job_object->exclude_from_backup as $exclusion ) { //exclude files
421
  $exclusion = trim( $exclusion );
422
+ if ( stripos( $path, $exclusion ) !== false && ! empty( $exclusion ) ) {
423
  continue 2;
424
  }
425
  }
426
+ if ( $file->isDir() ) {
427
+ if ( in_array( trailingslashit( $path ), $excludedirs, true ) ) {
428
  continue;
429
  }
430
+ if ( file_exists( trailingslashit( $file->getPathname() ) . '.donotbackup' ) ) {
431
  continue;
432
  }
433
+ if ( ! $file->isReadable() ) {
434
+ $job_object->log( sprintf( __( 'Folder "%s" is not readable!', 'backwpup' ), $file->getPathname() ), E_USER_WARNING );
435
  continue;
436
  }
437
+ $this->get_folder_list( $job_object, trailingslashit( $path ), $excludedirs, false );
438
  }
439
  if ( $first ) {
440
  $job_object->do_restart_time();
441
  }
442
  }
443
+ }
444
+ catch ( UnexpectedValueException $e ) {
445
+ $job_object->log( sprintf( __( "Could not open path: %s", 'backwpup' ), $e->getMessage() ), E_USER_WARNING );
446
  }
447
 
448
  return true;
449
  }
450
 
 
451
  /**
452
  *
453
  * Get folder to exclude from a given folder for file backups
460
  */
461
  private function get_exclude_dirs( $folder, $excludedir = array() ) {
462
 
463
+ $folder = trailingslashit( str_replace( '\\', '/', realpath( BackWPup_Path_Fixer::fix_path( $folder ) ) ) );
464
 
465
  if ( false !== strpos( trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) ), $folder ) && trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) ) != $folder ) {
466
  $excludedir[] = trailingslashit( str_replace( '\\', '/', realpath( WP_CONTENT_DIR ) ) );
477
 
478
  return array_unique( $excludedir );
479
  }
480
+
481
+ /**
482
+ * Shows a folder with the options of which files to exclude.
483
+ *
484
+ */
485
+ private function show_folder( $id, $jobid, $path ) {
486
+ $folder = realpath( BackWPup_Path_Fixer::fix_path( $path ) );
487
+ if ( $folder ) {
488
+ $folder = untrailingslashit( str_replace( '\\', '/', $folder ) );
489
+ $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize') ) ? ' (' . size_format( BackWPup_File::get_folder_size( $folder, FALSE ), 2 ) . ')' : '';
490
+ }
491
+ ?>
492
+ <input class="checkbox"
493
+ type="checkbox"<?php checked( BackWPup_Option::get( $jobid, 'backup' . $id ) ) ?>
494
+ name="backup<?php echo esc_attr( $id ) ?>" id="idbackup<?php echo esc_attr( $id ) ?>" value="1" /> <code title="<?php echo esc_attr( sprintf( __( 'Path as set by user (symlink?): %s', 'backwpup' ), $path ) ) ?>"><?php echo esc_attr( $folder ) ?></code><?php echo esc_html( $folder_size ) ?>
495
+
496
+ <fieldset id="backup<?php echo esc_attr( $id ) ?>excludedirs" style="padding-left:15px; margin:2px;">
497
+ <legend><strong><?php esc_html_e( 'Exclude:', 'backwpup' ) ?></strong></legend>
498
+ <?php
499
+ try {
500
+ $dir = new BackWPup_Directory( $folder );
501
+ $excludes = BackWPup_Option::get( $jobid, 'backup' . $id . 'excludedirs' );
502
+ foreach ( $dir as $file ) {
503
+ if ( ! $file->isDot() && $file->isDir() && ! in_array( trailingslashit( $file->getPathname() ), $this->get_exclude_dirs( $folder ), true ) ) {
504
+ $donotbackup = file_exists( $file->getPathname() . '/.donotbackup' );
505
+ $folder_size = ( get_site_option( 'backwpup_cfg_showfoldersize' ) ) ? ' (' . size_format( BackWPup_File::get_folder_size( $file->getPathname() ), 2 ) . ')' : '';
506
+ $title = '';
507
+ if ( $donotbackup ) {
508
+ $excludes[] = $file->getFilename();
509
+ $title = ' title="' . esc_attr__( 'Excluded by .donotbackup file!', 'backwpup' ) . '"';
510
+ }
511
+ echo '<nobr><label for="id' . esc_attr( $id ) . 'excludedirs-' . sanitize_file_name( $file->getFilename() ) . '">' .
512
+ '<input class="checkbox" type="checkbox"' .
513
+ checked( in_array( $file->getFilename(), $excludes, true ), true, false ) . ' name="backup' . esc_attr( $id ) . 'excludedirs[]" ' .
514
+ 'id="id' . esc_attr( $id ) . 'excludedirs-' . sanitize_file_name( $file->getFilename() ) . '" ' .
515
+ 'value="' . esc_attr( $file->getFilename() ) . '"' . disabled( $donotbackup, true, false ) . $title . ' /> ' .
516
+ esc_html( $file->getFilename() ) . esc_html( $folder_size ) . '</label><br /></nobr>';
517
+ }
518
+ }
519
+ }
520
+ catch ( Exception $e ) {
521
+ // Do nothing, just skip
522
+ }
523
+ ?>
524
+ </fieldset>
525
+ <?php
526
+ }
527
+
528
  }
inc/class-jobtype-wpexp.php CHANGED
@@ -117,7 +117,7 @@ class BackWPup_JobType_WPEXP extends BackWPup_JobTypes {
117
  $job_object->substeps_done = 0;
118
  }
119
 
120
- add_filter( 'wxr_export_skip_postmeta', array( $this, 'wxr_filter_postmeta' ), 10, 2 );
121
 
122
  if ( $job_object->steps_data[ $job_object->step_working ]['substep'] == 'header' ) {
123
 
@@ -343,7 +343,7 @@ class BackWPup_JobType_WPEXP extends BackWPup_JobTypes {
343
 
344
  $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
345
  foreach ( $postmeta as $meta ) {
346
- if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
347
  continue;
348
  }
349
  $wxr_post .= "\t\t<wp:postmeta>\n\t\t\t<wp:meta_key>" . $meta->meta_key ."</wp:meta_key>\n\t\t\t<wp:meta_value>" .$this->wxr_cdata( $meta->meta_value ) ."</wp:meta_value>\n\t\t</wp:postmeta>\n";
@@ -394,7 +394,7 @@ class BackWPup_JobType_WPEXP extends BackWPup_JobTypes {
394
  $job_object->do_restart_time();
395
  }
396
 
397
- remove_filter( 'wxr_export_skip_postmeta', array( $this, 'wxr_filter_postmeta' ), 10 );
398
 
399
  if ( $job_object->steps_data[ $job_object->step_working ]['substep'] == 'check' ) {
400
 
117
  $job_object->substeps_done = 0;
118
  }
119
 
120
+ add_filter( 'backwpup_wxr_export_skip_postmeta', array( $this, 'wxr_filter_postmeta' ), 10, 2 );
121
 
122
  if ( $job_object->steps_data[ $job_object->step_working ]['substep'] == 'header' ) {
123
 
343
 
344
  $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) );
345
  foreach ( $postmeta as $meta ) {
346
+ if ( apply_filters( 'backwpup_wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
347
  continue;
348
  }
349
  $wxr_post .= "\t\t<wp:postmeta>\n\t\t\t<wp:meta_key>" . $meta->meta_key ."</wp:meta_key>\n\t\t\t<wp:meta_value>" .$this->wxr_cdata( $meta->meta_value ) ."</wp:meta_value>\n\t\t</wp:postmeta>\n";
394
  $job_object->do_restart_time();
395
  }
396
 
397
+ remove_filter( 'backwpup_wxr_export_skip_postmeta', array( $this, 'wxr_filter_postmeta' ), 10 );
398
 
399
  if ( $job_object->steps_data[ $job_object->step_working ]['substep'] == 'check' ) {
400
 
inc/class-mysqldump.php CHANGED
@@ -518,6 +518,9 @@ class BackWPup_MySQLDump {
518
  $value = "NULL";
519
  } elseif ( in_array( (int) $fieldinfo[ $key ]->type, array( MYSQLI_TYPE_DECIMAL, MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_LONG, MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE, MYSQLI_TYPE_LONGLONG, MYSQLI_TYPE_INT24 ), true ) ) {//is value numeric no esc
520
  $value = empty( $value ) ? 0 : $value;
 
 
 
521
  } else {
522
  $value = "'" . $this->mysqli->real_escape_string( $value ) . "'";
523
  }
518
  $value = "NULL";
519
  } elseif ( in_array( (int) $fieldinfo[ $key ]->type, array( MYSQLI_TYPE_DECIMAL, MYSQLI_TYPE_TINY, MYSQLI_TYPE_SHORT, MYSQLI_TYPE_LONG, MYSQLI_TYPE_FLOAT, MYSQLI_TYPE_DOUBLE, MYSQLI_TYPE_LONGLONG, MYSQLI_TYPE_INT24 ), true ) ) {//is value numeric no esc
520
  $value = empty( $value ) ? 0 : $value;
521
+ } elseif ( $fieldinfo[ $key ]->flags & ( MYSQLI_BLOB_FLAG | MYSQLI_BINARY_FLAG ) ) {//is value binary or blob
522
+ $hex = unpack( 'H*', $value );
523
+ $value = empty( $value ) ? "''" : "0x$hex[1]";
524
  } else {
525
  $value = "'" . $this->mysqli->real_escape_string( $value ) . "'";
526
  }
inc/class-option.php CHANGED
@@ -27,6 +27,7 @@ final class BackWPup_Option {
27
  add_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
28
  add_site_option( 'backwpup_cfg_jobwaittimems', 0 );
29
  add_site_option( 'backwpup_cfg_jobdooutput', 0 );
 
30
  //Logs
31
  add_site_option( 'backwpup_cfg_maxlogs', 30 );
32
  add_site_option( 'backwpup_cfg_gzlogs', 0 );
@@ -136,7 +137,13 @@ final class BackWPup_Option {
136
  } elseif ( ! isset( $jobs_options[ $jobid ][ $option ] ) ) {
137
  return self::defaults_job( $option );
138
  } else {
139
- return $jobs_options[ $jobid ][ $option ];
 
 
 
 
 
 
140
  }
141
  }
142
 
@@ -168,7 +175,7 @@ final class BackWPup_Option {
168
  $default['mailerroronly'] = true;
169
  $default['backuptype'] = 'archive';
170
  $default['archiveformat'] = '.zip';
171
- $default['archivename'] = 'backwpup_' . BackWPup::get_plugin_data( 'hash' ) . '_%Y-%m-%d_%H-%i-%s';
172
  //defaults vor destinations
173
  foreach ( BackWPup::get_registered_destinations() as $dest_key => $dest ) {
174
  if ( ! empty( $dest['class'] ) ) {
@@ -295,4 +302,71 @@ final class BackWPup_Option {
295
 
296
  return $new_option_job_ids;
297
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  }
27
  add_site_option( 'backwpup_cfg_loglevel', 'normal_translated' );
28
  add_site_option( 'backwpup_cfg_jobwaittimems', 0 );
29
  add_site_option( 'backwpup_cfg_jobdooutput', 0 );
30
+ add_site_option( 'backwpup_cfg_windows', 0 );
31
  //Logs
32
  add_site_option( 'backwpup_cfg_maxlogs', 30 );
33
  add_site_option( 'backwpup_cfg_gzlogs', 0 );
137
  } elseif ( ! isset( $jobs_options[ $jobid ][ $option ] ) ) {
138
  return self::defaults_job( $option );
139
  } else {
140
+ // Ensure archive name formatted properly
141
+ if ( $option == 'archivename' ) {
142
+ return self::normalize_archive_name( $jobs_options[ $jobid ][ $option ], $jobid );
143
+ }
144
+ else {
145
+ return $jobs_options[ $jobid ][ $option ];
146
+ }
147
  }
148
  }
149
 
175
  $default['mailerroronly'] = true;
176
  $default['backuptype'] = 'archive';
177
  $default['archiveformat'] = '.zip';
178
+ $default['archivename'] = self::get_archive_name_prefix( self::next_job_id() ) . '%Y-%m-%d_%H-%i-%s';
179
  //defaults vor destinations
180
  foreach ( BackWPup::get_registered_destinations() as $dest_key => $dest ) {
181
  if ( ! empty( $dest['class'] ) ) {
302
 
303
  return $new_option_job_ids;
304
  }
305
+
306
+ /**
307
+ * Gets the next available job id.
308
+ *
309
+ * @return int
310
+ */
311
+ public static function next_job_id() {
312
+ $ids = self::get_job_ids();
313
+ sort( $ids );
314
+ return end( $ids ) + 1;
315
+ }
316
+
317
+ /**
318
+ * Normalizes the archive name.
319
+ *
320
+ * The archive name should include the hash to identify this site, and the job id to identify this job.
321
+ *
322
+ * This allows backup files belonging to this job to be tracked.
323
+ *
324
+ * @param string $archive_name
325
+ * @param int $jobid
326
+ *
327
+ * @return string The normalized archive name
328
+ */
329
+ public static function normalize_archive_name( $archive_name, $jobid ) {
330
+ $hash = BackWPup::get_plugin_data( 'hash' );
331
+
332
+ // If name starts with 'backwpup', then we can try to parse
333
+ if ( substr( $archive_name, 0, 8 ) == 'backwpup' ) {
334
+ $parts = explode( '_', $archive_name );
335
+
336
+ // Format = [hash][jobid]
337
+ if ( preg_match( '/^' . preg_quote( $hash ) . '(\d{2,})?$/', $parts[1], $matches ) ) {
338
+ // Was job id included?
339
+ if ( ! isset( $matches[1] ) ) {
340
+ // Append the job id
341
+ $parts[1] .= sprintf( '%02d', $jobid );
342
+ }
343
+ elseif ( $matches[1] != $jobid ) {
344
+ // This isn't the job ID you're looking for
345
+ // So fix, append the correct one
346
+ $parts[1] = $hash . sprintf( '%02d', $jobid );
347
+ }
348
+ }
349
+ else {
350
+ // Hash not included, so insert
351
+ array_splice( $parts, 1, 0, $hash . sprintf( '%02d', $jobid ) );
352
+ }
353
+ return implode( '_', $parts );
354
+ }
355
+ else {
356
+ // But otherwise, just prepend required format
357
+ return "backwpup_$hash" . sprintf( '%02d', $jobid ) . '_' . $archive_name;
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Get the prefix for an archive name.
363
+ *
364
+ * Format should be backwpup_[hash][jobid]_
365
+ *
366
+ * @return string
367
+ */
368
+ public static function get_archive_name_prefix( $jobid ) {
369
+ return 'backwpup_' . BackWPup::get_plugin_data( 'hash' ) . sprintf( '%02d', $jobid ) . '_';
370
+ }
371
+
372
  }
inc/class-page-about.php CHANGED
@@ -368,7 +368,7 @@ class BackWPup_Page_About {
368
  <img class="backwpup-banner-img" src="<?php echo BackWPup::get_plugin_data( 'URL' );?>/assets/images/backwpupbanner.png" />
369
  <h1><?php esc_html_e( 'Welcome to BackWPup Pro', 'backwpup' ); ?></h1>
370
  <p><?php esc_html_e( 'BackWPup’s job wizards make planning and scheduling your backup jobs a breeze.', 'backwpup' ); echo ' ';
371
- _e( 'Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server. With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href="http://wordpress.org/plugins/adminer/" target="_blank">Adminer</a> to restore your database backup files.', 'backwpup' ); ?></p>
372
  <p><?php echo str_replace( '\"','"', sprintf( __( 'Ready to <a href="%1$s">set up a backup job</a>? You can <a href="%2$s">use the wizards</a> or plan your backup in expert mode.', 'backwpup' ), network_admin_url( 'admin.php').'?page=backwpupeditjob' , network_admin_url( 'admin.php').'?page=backwpupwizard' ) ); ?></p>
373
  </div>
374
  <?php if ( '2016-06-30' > date( 'Y-m-d' ) ) { ?>
@@ -381,7 +381,7 @@ _e( 'Use your backup archives to save your entire WordPress installation includi
381
  <img class="backwpup-banner-img" src="<?php echo esc_attr( BackWPup::get_plugin_data( 'URL' ));?>/assets/images/backwpupbanner.png" />
382
  <h1><?php esc_html_e( 'Welcome to BackWPup', 'backwpup' ); ?></h1>
383
  <p><?php
384
- _e( 'Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server. With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href="http://wordpress.org/plugins/adminer/" target="_blank">Adminer</a> to restore your database backup files.', 'backwpup' ); ?></p>
385
  <p><?php esc_html_e( 'Ready to set up a backup job? Use one of the wizards to plan what you want to save.', 'backwpup' ); ?></p>
386
  </div>
387
  <?php if ( '2016-06-30' > date( 'Y-m-d' ) ) { ?>
368
  <img class="backwpup-banner-img" src="<?php echo BackWPup::get_plugin_data( 'URL' );?>/assets/images/backwpupbanner.png" />
369
  <h1><?php esc_html_e( 'Welcome to BackWPup Pro', 'backwpup' ); ?></h1>
370
  <p><?php esc_html_e( 'BackWPup’s job wizards make planning and scheduling your backup jobs a breeze.', 'backwpup' ); echo ' ';
371
+ _e( 'Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server. With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin to restore your database backup files.', 'backwpup' ); ?></p>
372
  <p><?php echo str_replace( '\"','"', sprintf( __( 'Ready to <a href="%1$s">set up a backup job</a>? You can <a href="%2$s">use the wizards</a> or plan your backup in expert mode.', 'backwpup' ), network_admin_url( 'admin.php').'?page=backwpupeditjob' , network_admin_url( 'admin.php').'?page=backwpupwizard' ) ); ?></p>
373
  </div>
374
  <?php if ( '2016-06-30' > date( 'Y-m-d' ) ) { ?>
381
  <img class="backwpup-banner-img" src="<?php echo esc_attr( BackWPup::get_plugin_data( 'URL' ));?>/assets/images/backwpupbanner.png" />
382
  <h1><?php esc_html_e( 'Welcome to BackWPup', 'backwpup' ); ?></h1>
383
  <p><?php
384
+ _e( 'Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server. With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin to restore your database backup files.', 'backwpup' ); ?></p>
385
  <p><?php esc_html_e( 'Ready to set up a backup job? Use one of the wizards to plan what you want to save.', 'backwpup' ); ?></p>
386
  </div>
387
  <?php if ( '2016-06-30' > date( 'Y-m-d' ) ) { ?>
inc/class-page-backups.php CHANGED
@@ -357,7 +357,6 @@ class BackWPup_Page_Backups extends WP_List_Table {
357
  */
358
  function column_time( $item ) {
359
 
360
- $item[ 'time' ] = $item[ 'time' ] + get_option( 'gmt_offset' ) * 3600;
361
  return sprintf( __( '%1$s at %2$s', 'backwpup' ), date_i18n( get_option( 'date_format' ), $item[ 'time' ], TRUE ), date_i18n( get_option( 'time_format' ), $item[ 'time' ], TRUE ) );
362
  }
363
 
357
  */
358
  function column_time( $item ) {
359
 
 
360
  return sprintf( __( '%1$s at %2$s', 'backwpup' ), date_i18n( get_option( 'date_format' ), $item[ 'time' ], TRUE ), date_i18n( get_option( 'time_format' ), $item[ 'time' ], TRUE ) );
361
  }
362
 
inc/class-page-backwpup.php CHANGED
@@ -74,7 +74,7 @@ class BackWPup_Page_BackWPup {
74
  <h3><?php _ex( 'Planning backups', 'Dashboard heading', 'backwpup' ); ?></h3>
75
  <p><?php _e('BackWPup’s job wizards make planning and scheduling your backup jobs a breeze.','backwpup' ); echo ' '; _e('Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server.','backwpup'); ?></p>
76
  <h3><?php _ex( 'Restoring backups', 'Dashboard heading', 'backwpup' ); ?></h3>
77
- <p><?php _e( 'With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href="http://wordpress.org/plugins/adminer/" target="_blank">Adminer</a> to restore your database backup files.', 'backwpup' ) ?></p>
78
  <h3><?php _ex( 'Ready to set up a backup job?', 'Dashboard heading','backwpup' ); ?></h3>
79
  <p><?php printf( __('Use one of the wizards to plan a backup, or use <a href="%s">expert mode</a> for full control over all options.','backwpup'), network_admin_url( 'admin.php') . '?page=backwpupeditjob' ); echo ' '; _e( '<strong>Please note: You are solely responsible for the security of your data; the authors of this plugin are not.</strong>', 'backwpup' ); ?></p>
80
  </div>
@@ -83,7 +83,7 @@ class BackWPup_Page_BackWPup {
83
  <h3><?php _ex( 'Planning backups', 'Dashboard heading', 'backwpup' ); ?></h3>
84
  <p><?php _e('Use the short links in the <strong>First steps</strong> box to plan and schedule backup jobs.','backwpup' ); echo ' '; _e('Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server.','backwpup'); ?></p>
85
  <h3><?php _ex( 'Restoring backups', 'Dashboard heading', 'backwpup' ); ?></h3>
86
- <p><?php _e( 'With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href="http://wordpress.org/plugins/adminer/" target="_blank">Adminer</a> to restore your database backup files.', 'backwpup' ) ?></p>
87
  <h3><?php _ex( 'Ready to set up a backup job?', 'Dashboard heading','backwpup' ); ?></h3>
88
  <p><?php printf( __('<a href="%s">Add a new backup job</a> and plan what you want to save.','backwpup'), network_admin_url( 'admin.php') . '?page=backwpupeditjob' ); ?>
89
  <br /><?php _e( '<strong>Please note: You are solely responsible for the security of your data; the authors of this plugin are not.</strong>', 'backwpup' ); ?></p>
@@ -254,7 +254,7 @@ class BackWPup_Page_BackWPup {
254
  <p><img class="backwpup-banner-img" src="<?php echo BackWPup::get_plugin_data( 'URL' ) . '/assets/images/backwpupbanner.png'; ?>" alt="BackWPup Banner" /></p>
255
  <h3 class="backwpup-text-center"><?php _ex( 'Get access to:', 'Pro teaser box', 'backwpup' ); ?></h3>
256
  <ul class="backwpup-text-center">
257
- <li><?php _ex( 'First-class <strong>dedicated support</strong> at MarketPress Helpdesk.', 'Pro teaser box', 'backwpup' ); ?></li>
258
  <li><?php echo esc_html_x( 'Differential backups to Google Drive and other cloud storage service.', 'Pro teaser box', 'backwpup' ); ?></li>
259
  <li><?php echo esc_html_x( 'Easy-peasy wizards to create and schedule backup jobs.', 'Pro teaser box', 'backwpup' ); ?></li>
260
  <li><?php printf( '<a href="' . esc_html__( 'http://backwpup.com', 'backwpup' ) .'">%s</a>', _x( 'And more…', 'Pro teaser box, link text', 'backwpup' ) ); ?></li>
@@ -351,39 +351,47 @@ class BackWPup_Page_BackWPup {
351
  $logfiles = array();
352
  $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
353
  $log_folder = BackWPup_File::get_absolute_path( $log_folder );
354
- if ( is_readable( $log_folder ) && $dir = opendir( $log_folder ) ) {
355
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
356
- if ( is_readable( $log_folder . $file ) && is_file( $log_folder . $file ) && FALSE !== strpos( $file, 'backwpup_log_' ) && FALSE !== strpos( $file, '.html' ) ) {
357
- $logfiles[ filemtime( $log_folder . $file ) ] = $file;
 
 
 
358
  }
 
 
 
 
 
 
359
  }
360
- closedir( $dir );
361
- krsort( $logfiles, SORT_NUMERIC );
362
  }
363
 
364
  if ( count( $logfiles ) > 0 ) {
365
  $count = 0;
366
  $alternate = TRUE;
367
  foreach ( $logfiles as $logfile ) {
368
- $logdata = BackWPup_Job::read_logheader( $log_folder . $logfile );
369
  if ( ! $alternate ) {
370
  echo '<tr>';
371
  $alternate = TRUE;
372
- } else {
 
373
  echo '<tr class="alternate">';
374
  $alternate = FALSE;
375
  }
376
  echo '<td>' . sprintf( __( '%1$s at %2$s', 'backwpup' ), date_i18n( get_option( 'date_format' ) , $logdata[ 'logtime' ] ), date_i18n( get_option( 'time_format' ), $logdata[ 'logtime' ] ) ) . '</td>';
377
- $log_name = str_replace( array( '.html', '.gz' ), '', basename( $logfile ) );
378
- echo '<td><a class="thickbox" href="' . admin_url( 'admin-ajax.php' ) . '?&action=backwpup_view_log&log=' . $log_name .'&_ajax_nonce=' . wp_create_nonce( 'view-log_' . $log_name ) . '&amp;TB_iframe=true&amp;width=640&amp;height=440" title="' . esc_attr( basename( $logfile ) ) . '">' . esc_html( $logdata[ 'name' ] ) . '</i></a></td>';
379
  echo '<td>';
380
- if ( $logdata[ 'errors' ] ) {
381
- printf( '<span style="color:red;font-weight:bold;">' . _n( "%d ERROR", "%d ERRORS", $logdata[ 'errors' ], 'backwpup' ) . '</span><br />', $logdata[ 'errors' ] );
382
  }
383
- if ( $logdata[ 'warnings' ] ) {
384
- printf( '<span style="color:#e66f00;font-weight:bold;">' . _n( "%d WARNING", "%d WARNINGS", $logdata[ 'warnings' ], 'backwpup' ) . '</span><br />', $logdata[ 'warnings' ] );
385
  }
386
- if ( ! $logdata[ 'errors' ] && ! $logdata[ 'warnings' ] ) {
387
  echo '<span style="color:green;font-weight:bold;">' . __( 'OK', 'backwpup' ) . '</span>';
388
  }
389
  echo '</td></tr>';
74
  <h3><?php _ex( 'Planning backups', 'Dashboard heading', 'backwpup' ); ?></h3>
75
  <p><?php _e('BackWPup’s job wizards make planning and scheduling your backup jobs a breeze.','backwpup' ); echo ' '; _e('Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server.','backwpup'); ?></p>
76
  <h3><?php _ex( 'Restoring backups', 'Dashboard heading', 'backwpup' ); ?></h3>
77
+ <p><?php _e( 'With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin to restore your database backup files.', 'backwpup' ) ?></p>
78
  <h3><?php _ex( 'Ready to set up a backup job?', 'Dashboard heading','backwpup' ); ?></h3>
79
  <p><?php printf( __('Use one of the wizards to plan a backup, or use <a href="%s">expert mode</a> for full control over all options.','backwpup'), network_admin_url( 'admin.php') . '?page=backwpupeditjob' ); echo ' '; _e( '<strong>Please note: You are solely responsible for the security of your data; the authors of this plugin are not.</strong>', 'backwpup' ); ?></p>
80
  </div>
83
  <h3><?php _ex( 'Planning backups', 'Dashboard heading', 'backwpup' ); ?></h3>
84
  <p><?php _e('Use the short links in the <strong>First steps</strong> box to plan and schedule backup jobs.','backwpup' ); echo ' '; _e('Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you don’t want to save the backups on the same server.','backwpup'); ?></p>
85
  <h3><?php _ex( 'Restoring backups', 'Dashboard heading', 'backwpup' ); ?></h3>
86
+ <p><?php _e( 'With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin to restore your database backup files.', 'backwpup' ) ?></p>
87
  <h3><?php _ex( 'Ready to set up a backup job?', 'Dashboard heading','backwpup' ); ?></h3>
88
  <p><?php printf( __('<a href="%s">Add a new backup job</a> and plan what you want to save.','backwpup'), network_admin_url( 'admin.php') . '?page=backwpupeditjob' ); ?>
89
  <br /><?php _e( '<strong>Please note: You are solely responsible for the security of your data; the authors of this plugin are not.</strong>', 'backwpup' ); ?></p>
254
  <p><img class="backwpup-banner-img" src="<?php echo BackWPup::get_plugin_data( 'URL' ) . '/assets/images/backwpupbanner.png'; ?>" alt="BackWPup Banner" /></p>
255
  <h3 class="backwpup-text-center"><?php _ex( 'Get access to:', 'Pro teaser box', 'backwpup' ); ?></h3>
256
  <ul class="backwpup-text-center">
257
+ <li><?php _ex( 'First-class <strong>dedicated support</strong> at backwpup.com.', 'Pro teaser box', 'backwpup' ); ?></li>
258
  <li><?php echo esc_html_x( 'Differential backups to Google Drive and other cloud storage service.', 'Pro teaser box', 'backwpup' ); ?></li>
259
  <li><?php echo esc_html_x( 'Easy-peasy wizards to create and schedule backup jobs.', 'Pro teaser box', 'backwpup' ); ?></li>
260
  <li><?php printf( '<a href="' . esc_html__( 'http://backwpup.com', 'backwpup' ) .'">%s</a>', _x( 'And more…', 'Pro teaser box, link text', 'backwpup' ) ); ?></li>
351
  $logfiles = array();
352
  $log_folder = get_site_option( 'backwpup_cfg_logfolder' );
353
  $log_folder = BackWPup_File::get_absolute_path( $log_folder );
354
+ if ( is_readable( $log_folder ) ) {
355
+ try {
356
+ $dir = new BackWPup_Directory( $log_folder );
357
+ foreach ( $dir as $file ) {
358
+ if ( $file->isReadable() && $file->isFile() && strpos( $file->getFilename(), 'backwpup_log_' ) !== false && strpos( $file->getFilename(), '.html' ) !== false ) {
359
+ $logfiles[ $file->getMTime() ] = clone $file;
360
+ }
361
  }
362
+ krsort( $logfiles, SORT_NUMERIC );
363
+ }
364
+ catch ( UnexpectedValueException $e ) {
365
+ echo '<tr><td colspan="3"><span style="color:red;font-weight:bold;">' .
366
+ sprintf( __( 'Could not open log folder: %s', 'backwpup' ), $log_folder ) .
367
+ '</td></tr>';
368
  }
 
 
369
  }
370
 
371
  if ( count( $logfiles ) > 0 ) {
372
  $count = 0;
373
  $alternate = TRUE;
374
  foreach ( $logfiles as $logfile ) {
375
+ $logdata = BackWPup_Job::read_logheader( $logfile->getPathname() );
376
  if ( ! $alternate ) {
377
  echo '<tr>';
378
  $alternate = TRUE;
379
+ }
380
+ else {
381
  echo '<tr class="alternate">';
382
  $alternate = FALSE;
383
  }
384
  echo '<td>' . sprintf( __( '%1$s at %2$s', 'backwpup' ), date_i18n( get_option( 'date_format' ) , $logdata[ 'logtime' ] ), date_i18n( get_option( 'time_format' ), $logdata[ 'logtime' ] ) ) . '</td>';
385
+ $log_name = str_replace( array( '.html', '.gz' ), '', $logfile->getBasename() );
386
+ echo '<td><a class="thickbox" href="' . admin_url( 'admin-ajax.php?action=backwpup_view_log&log=' . $log_name .'&_ajax_nonce=' . wp_create_nonce( 'view-log_' . $log_name ) . '&TB_iframe=true&width=640&height=440' ) . '" title="' . esc_attr( $logfile->getBasename() ) . '">' . esc_html( $logdata['name'] ) . '</i></a></td>';
387
  echo '<td>';
388
+ if ( $logdata['errors'] ) {
389
+ printf( '<span style="color:red;font-weight:bold;">' . _n( "%d ERROR", "%d ERRORS", $logdata['errors'], 'backwpup' ) . '</span><br />', $logdata[ 'errors' ] );
390
  }
391
+ if ( $logdata['warnings'] ) {
392
+ printf( '<span style="color:#e66f00;font-weight:bold;">' . _n( "%d WARNING", "%d WARNINGS", $logdata['warnings'], 'backwpup' ) . '</span><br />', $logdata['warnings'] );
393
  }
394
+ if ( ! $logdata['errors'] && ! $logdata['warnings'] ) {
395
  echo '<span style="color:green;font-weight:bold;">' . __( 'OK', 'backwpup' ) . '</span>';
396
  }
397
  echo '</td></tr>';
inc/class-page-editjob.php CHANGED
@@ -122,7 +122,7 @@ class BackWPup_Page_Editjob {
122
  ), true ) ? $_POST['archiveformat'] : '.zip';
123
  BackWPup_Option::update( $jobid, 'archiveformat', $archiveformat );
124
 
125
- BackWPup_Option::update( $jobid, 'archivename', BackWPup_Job::sanitize_file_name( $_POST['archivename'] ) );
126
  break;
127
  case 'cron':
128
  $activetype = in_array( $_POST['activetype'], array(
@@ -313,9 +313,7 @@ class BackWPup_Page_Editjob {
313
  }
314
  else {
315
  //generate jobid if not exists
316
- $newjobid = BackWPup_Option::get_job_ids();
317
- sort( $newjobid );
318
- $jobid = end( $newjobid ) + 1;
319
  }
320
 
321
  $destinations = BackWPup::get_registered_destinations();
@@ -437,7 +435,8 @@ class BackWPup_Page_Editjob {
437
  <tr class="nosync">
438
  <th scope="row"><label for="archivename"><?php esc_html_e( 'Archive name', 'backwpup' ) ?></label></th>
439
  <td>
440
- <input name="archivename" type="text" id="archivename" placeholder="backwpup_%Y-%m-%d_%H-%i-%s" value="<?php echo esc_attr(BackWPup_Option::get( $jobid, 'archivename' ));?>" class="regular-text code" />
 
441
  <?php
442
  $current_time = current_time( 'timestamp' );
443
  $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
122
  ), true ) ? $_POST['archiveformat'] : '.zip';
123
  BackWPup_Option::update( $jobid, 'archiveformat', $archiveformat );
124
 
125
+ BackWPup_Option::update( $jobid, 'archivename', BackWPup_Job::sanitize_file_name( BackWPup_Option::normalize_archive_name( $_POST['archivename'], $jobid ) ) );
126
  break;
127
  case 'cron':
128
  $activetype = in_array( $_POST['activetype'], array(
313
  }
314
  else {
315
  //generate jobid if not exists
316
+ $jobid = BackWPup_Option::next_job_id();
 
 
317
  }
318
 
319
  $destinations = BackWPup::get_registered_destinations();
435
  <tr class="nosync">
436
  <th scope="row"><label for="archivename"><?php esc_html_e( 'Archive name', 'backwpup' ) ?></label></th>
437
  <td>
438
+ <input name="archivename" type="text" id="archivename" placeholder="%Y-%m-%d_%H-%i-%s" value="<?php echo esc_attr( BackWPup_Option::get( $jobid, 'archivename' ) );?>" class="regular-text code" />
439
+ <p><?php printf( __( '<em>Note</em>: In order for backup file tracking to work, the archive name must begin with %s.', 'backwpup' ), BackWPup_Option::get_archive_name_prefix( $jobid ) ) ?></p>
440
  <?php
441
  $current_time = current_time( 'timestamp' );
442
  $datevars = array( '%d', '%j', '%m', '%n', '%Y', '%y', '%a', '%A', '%B', '%g', '%G', '%h', '%H', '%i', '%s' );
inc/class-page-logs.php CHANGED
@@ -46,15 +46,15 @@ class BackWPup_Page_Logs extends WP_List_Table {
46
 
47
  //load logs
48
  $logfiles = array();
49
- if ( is_readable( $this->log_folder ) && $dir = opendir( $this->log_folder ) ) {
50
- while ( ( $file = readdir( $dir ) ) !== FALSE ) {
51
- $log_file = $this->log_folder . '/' . $file;
52
- if ( is_file( $log_file ) && is_readable( $log_file ) && FALSE !== strpos( $file, 'backwpup_log_' ) && FALSE !== strpos( $file, '.html' ) ) {
53
- $logfiles[] = $file;
54
  }
55
  }
56
- closedir( $dir );
57
  }
 
58
  //ordering
59
  $order = isset( $_GET[ 'order' ] ) ? $_GET[ 'order' ] : 'desc';
60
  $orderby = isset( $_GET[ 'orderby' ] ) ? $_GET[ 'orderby' ] : 'time';
46
 
47
  //load logs
48
  $logfiles = array();
49
+ if ( is_readable( $this->log_folder ) ) {
50
+ $dir = new BackWPup_Directory( $this->log_folder );
51
+ foreach ( $dir as $file ) {
52
+ if ( $file->isFile() && $file->isReadable() && strpos( $file->getFilename(), 'backwpup_log_' ) !== false && strpos( $file->getFilename(), '.html' ) !== false ) {
53
+ $logfiles[] = $file->getFilename();
54
  }
55
  }
 
56
  }
57
+
58
  //ordering
59
  $order = isset( $_GET[ 'order' ] ) ? $_GET[ 'order' ] : 'desc';
60
  $orderby = isset( $_GET[ 'orderby' ] ) ? $_GET[ 'orderby' ] : 'time';
inc/class-page-settings.php CHANGED
@@ -41,6 +41,7 @@ class BackWPup_Page_Settings {
41
  delete_site_option( 'backwpup_cfg_jobwaittimems' );
42
  delete_site_option( 'backwpup_cfg_jobrunauthkey' );
43
  delete_site_option( 'backwpup_cfg_jobdooutput' );
 
44
  delete_site_option( 'backwpup_cfg_maxlogs' );
45
  delete_site_option( 'backwpup_cfg_gzlogs' );
46
  delete_site_option( 'backwpup_cfg_protectfolders' );
@@ -74,6 +75,7 @@ class BackWPup_Page_Settings {
74
  update_site_option( 'backwpup_cfg_loglevel', in_array( $_POST[ 'loglevel' ], array( 'normal_translated', 'normal', 'debug_translated', 'debug' ), true ) ? $_POST[ 'loglevel' ] : 'normal_translated' );
75
  update_site_option( 'backwpup_cfg_jobwaittimems', absint( $_POST[ 'jobwaittimems' ] ) );
76
  update_site_option( 'backwpup_cfg_jobdooutput', ! empty( $_POST[ 'jobdooutput' ] ) );
 
77
  update_site_option( 'backwpup_cfg_maxlogs', absint( $_POST[ 'maxlogs' ] ) );
78
  update_site_option( 'backwpup_cfg_gzlogs', ! empty( $_POST[ 'gzlogs' ] ) );
79
  update_site_option( 'backwpup_cfg_protectfolders', ! empty( $_POST[ 'protectfolders' ] ) );
@@ -291,6 +293,19 @@ class BackWPup_Page_Settings {
291
  </fieldset>
292
  </td>
293
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  </table>
295
 
296
  </div>
@@ -339,7 +354,7 @@ class BackWPup_Page_Settings {
339
  <label for="authentication_user_id">
340
  <select name="authentication_user_id" size="1" >
341
  <?php
342
- $users = get_users( array( 'who' => 'administrators', 'number' => 99, 'orderby' => 'display_name' ) );
343
  foreach ( $users as $user ) {
344
  echo '<option value="' . $user->ID . '" '. selected( $authentication[ 'user_id' ], $user->ID, FALSE ) .'>'. esc_attr( $user->display_name ) .'</option>';
345
  }
41
  delete_site_option( 'backwpup_cfg_jobwaittimems' );
42
  delete_site_option( 'backwpup_cfg_jobrunauthkey' );
43
  delete_site_option( 'backwpup_cfg_jobdooutput' );
44
+ delete_site_option( 'backwpup_cfg_windows' );
45
  delete_site_option( 'backwpup_cfg_maxlogs' );
46
  delete_site_option( 'backwpup_cfg_gzlogs' );
47
  delete_site_option( 'backwpup_cfg_protectfolders' );
75
  update_site_option( 'backwpup_cfg_loglevel', in_array( $_POST[ 'loglevel' ], array( 'normal_translated', 'normal', 'debug_translated', 'debug' ), true ) ? $_POST[ 'loglevel' ] : 'normal_translated' );
76
  update_site_option( 'backwpup_cfg_jobwaittimems', absint( $_POST[ 'jobwaittimems' ] ) );
77
  update_site_option( 'backwpup_cfg_jobdooutput', ! empty( $_POST[ 'jobdooutput' ] ) );
78
+ update_site_option( 'backwpup_cfg_windows', ! empty( $_POST[ 'windows' ] ) );
79
  update_site_option( 'backwpup_cfg_maxlogs', absint( $_POST[ 'maxlogs' ] ) );
80
  update_site_option( 'backwpup_cfg_gzlogs', ! empty( $_POST[ 'gzlogs' ] ) );
81
  update_site_option( 'backwpup_cfg_protectfolders', ! empty( $_POST[ 'protectfolders' ] ) );
293
  </fieldset>
294
  </td>
295
  </tr>
296
+ <tr>
297
+ <th scope="row"><?php _e( 'Windows IIS compatibility', 'backwpup' ); ?></th>
298
+ <td>
299
+ <fieldset>
300
+ <legend class="screen-reader-text"><span><?php _e( 'Enable compatibility with IIS on Windows.', 'backwpup' ); ?></span></legend>
301
+ <label for="windows">
302
+ <input name="windows" type="checkbox" id="windows" value="1"<?php checked( get_site_option( 'backwpup_cfg_windows' ), true ) ?> />
303
+ <?php _e( 'Enable compatibility with IIS on Windows.', 'backwpup' ); ?>
304
+ </label>
305
+ <p class="description"><?php _e( 'There is a PHP bug (<a href="https://bugs.php.net/43817">bug #43817</a>), which is triggered on some versions of Windows and IIS. Checking this box will enable a workaround for that bug. Only enable if you are getting errors about &ldquo;Permission denied&rdquo; in your logs.', 'backwpup' ) ?></p>
306
+ </fieldset>
307
+ </td>
308
+ </tr>
309
  </table>
310
 
311
  </div>
354
  <label for="authentication_user_id">
355
  <select name="authentication_user_id" size="1" >
356
  <?php
357
+ $users = get_users( array( 'role' => 'administrator', 'number' => 99, 'orderby' => 'display_name' ) );
358
  foreach ( $users as $user ) {
359
  echo '<option value="' . $user->ID . '" '. selected( $authentication[ 'user_id' ], $user->ID, FALSE ) .'>'. esc_attr( $user->display_name ) .'</option>';
360
  }
inc/class-path-fixer.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Fix paths so they don't trigger an error on Windows.
5
+ *
6
+ * This class is meant to be a workaround for PHP bug #43817.
7
+ *
8
+ * On Windows IIS, if the parent directory is not readable, then the given directory will give access denied.
9
+ *
10
+ * @since 3.4.0
11
+ */
12
+ class BackWPup_Path_Fixer {
13
+
14
+ /**
15
+ * Fix the path if necessary.
16
+ *
17
+ * @param string $path
18
+ *
19
+ * @return string The fixed path.
20
+ */
21
+ public static function fix_path( $path ) {
22
+ if ( get_site_option( 'backwpup_cfg_windows' ) ) {
23
+ $path = trailingslashit( $path );
24
+
25
+ if ( is_dir( $path . 'wp-content' ) ) {
26
+ return $path . 'wp-content/..';
27
+ }
28
+ }
29
+
30
+ return $path;
31
+ }
32
+
33
+ }
inc/class-recursive-directory.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Wraps RecursiveDirectoryIterator to fix paths.
5
+ *
6
+ * @since 3.4.0
7
+ */
8
+ class BackWPup_Recursive_Directory extends RecursiveDirectoryIterator {
9
+
10
+ /**
11
+ * Creates the iterator.
12
+ *
13
+ * Fixes the path before calling the parent constructor.
14
+ *
15
+ * @param string $path
16
+ */
17
+ public function __construct( $path, $flags = null ) {
18
+ if ( $flags === null ) {
19
+ $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO;
20
+ }
21
+ parent::__construct( BackWPup_Path_Fixer::fix_path( $path ), $flags );
22
+ }
23
+
24
+ }
languages/backwpup.pot CHANGED
@@ -1,16 +1,16 @@
1
- # Translation of BackWPup Pro
2
- # Copyright (C) 2017 Inpsyde GmbH
3
- # This file is distributed under the same license BackWPup Pro package.
4
- #
5
- #, fuzzy
6
  msgid ""
7
  msgstr ""
8
- "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 
 
9
  "MIME-Version: 1.0\n"
10
  "Content-Type: text/plain; charset=UTF-8\n"
11
  "Content-Transfer-Encoding: 8bit\n"
12
- "Plural-Forms: nplurals=2; plural=n != 1;\n"
13
- "Project-Id-Version: BackWPup Pro\n"
 
14
 
15
  #: backwpup.php:333 inc/class-page-backups.php:271
16
  msgid "Folder"
@@ -36,7 +36,7 @@ msgstr ""
36
  msgid "Backup to FTP"
37
  msgstr ""
38
 
39
- #: backwpup.php:381 inc/class-destination-dropbox.php:307
40
  msgid "Dropbox"
41
  msgstr ""
42
 
@@ -77,17 +77,14 @@ msgid "Backup to SugarSync"
77
  msgstr ""
78
 
79
  #: backwpup.php:470
80
- #, php-format
81
  msgid "PHP Version %1$s is to low, you need Version %2$s or above."
82
  msgstr ""
83
 
84
  #: backwpup.php:477
85
- #, php-format
86
  msgid "Missing function \"%s\"."
87
  msgstr ""
88
 
89
  #: backwpup.php:486
90
- #, php-format
91
  msgid "Missing class \"%s\"."
92
  msgstr ""
93
 
@@ -108,7 +105,7 @@ msgid "Dashboard"
108
  msgstr ""
109
 
110
  #: inc/class-admin.php:243 inc/class-adminbar.php:82
111
- #: inc/class-page-settings.php:115
112
  msgid "Jobs"
113
  msgstr ""
114
 
@@ -117,7 +114,7 @@ msgid "Add new job"
117
  msgstr ""
118
 
119
  #: inc/class-admin.php:273 inc/class-adminbar.php:98
120
- #: inc/class-page-logs.php:328 inc/class-page-settings.php:115
121
  msgid "Logs"
122
  msgstr ""
123
 
@@ -138,24 +135,24 @@ msgid "Cheating, huh?"
138
  msgstr ""
139
 
140
  #: inc/class-admin.php:381
141
- #, php-format
142
  msgid "<a class=\"backwpup-get-pro\" href=\"%s\">Get BackWPup Pro now.</a>"
143
  msgstr ""
144
 
 
 
145
  #: inc/class-admin.php:381 inc/class-admin.php:401 inc/class-help.php:17
146
  #: inc/class-help.php:22 inc/class-job.php:394
147
  #: inc/class-jobtype-dbcheck.php:15 inc/class-jobtype-dbdump.php:15
148
  #: inc/class-jobtype-file.php:15 inc/class-jobtype-wpexp.php:15
149
  #: inc/class-jobtype-wpplugin.php:15 inc/class-page-about.php:624
150
  #: inc/class-page-backwpup.php:260 inc/class-page-backwpup.php:262
151
- #: inc/class-page-settings.php:376 inc/pro/class-wizard-job.php:17
152
  #: inc/pro/class-wizard-jobimport.php:16
153
  #: inc/pro/class-wizard-systemtest.php:16
154
  msgid "http://backwpup.com"
155
  msgstr ""
156
 
157
  #: inc/class-admin.php:401
158
- #, php-format
159
  msgid "version %s"
160
  msgstr ""
161
 
@@ -192,19 +189,23 @@ msgstr ""
192
  msgid "Make BackWPup better!"
193
  msgstr ""
194
 
195
- #: inc/class-become-inpsyder-widget.php:105
196
- msgid "https://inpsyde.com/en/jobs/?utm_source=BackWPup&utm_medium=Link&utm_campaign=BecomeAnInpsyder"
 
 
197
  msgstr ""
198
 
199
- #: inc/class-become-inpsyder-widget.php:115
200
  msgid "We want to make BackWPup even stronger and its support much faster."
201
  msgstr ""
202
 
203
- #: inc/class-become-inpsyder-widget.php:123
204
- msgid "This is why we are looking for a talented developer who can work remotely and support us in BackWPup"
 
 
205
  msgstr ""
206
 
207
- #: inc/class-become-inpsyder-widget.php:130
208
  msgid "and other exciting WordPress projects at our VIP partner agency."
209
  msgstr ""
210
 
@@ -229,8 +230,10 @@ msgstr ""
229
  msgid "https://backwpup.com/become-backwpup-beta-tester/"
230
  msgstr ""
231
 
232
- #: inc/class-betatester-admin-notice.php:78
233
- msgid "To ensure that our releases are as bug-free as possible, we need you as a beta tester!"
 
 
234
  msgstr ""
235
 
236
  #: inc/class-create-archive.php:64
@@ -238,7 +241,6 @@ msgid "The file name of an archive cannot be empty."
238
  msgstr ""
239
 
240
  #: inc/class-create-archive.php:72
241
- #, php-format
242
  msgctxt "%s = Folder name"
243
  msgid "Folder %s for archive not found"
244
  msgstr ""
@@ -253,13 +255,11 @@ msgid "Functions for bz2 compression not available"
253
  msgstr ""
254
 
255
  #: inc/class-create-archive.php:106
256
- #, php-format
257
  msgctxt "ZipArchive open() result"
258
  msgid "Cannot create zip archive: %d"
259
  msgstr ""
260
 
261
  #: inc/class-create-archive.php:135
262
- #, php-format
263
  msgctxt "%s = file name"
264
  msgid "Method to archive file %s not detected"
265
  msgstr ""
@@ -269,7 +269,6 @@ msgid "Cannot open archive file"
269
  msgstr ""
270
 
271
  #: inc/class-create-archive.php:155 inc/class-create-archive.php:349
272
- #, php-format
273
  msgid "PclZip archive add error: %s"
274
  msgstr ""
275
 
@@ -282,7 +281,6 @@ msgid "File name cannot be empty"
282
  msgstr ""
283
 
284
  #: inc/class-create-archive.php:228
285
- #, php-format
286
  msgctxt "File to add to archive"
287
  msgid "File %s does not exist or is not readable"
288
  msgstr ""
@@ -293,7 +291,6 @@ msgid "This archive method can only add one file"
293
  msgstr ""
294
 
295
  #: inc/class-create-archive.php:247 inc/class-create-archive.php:263
296
- #, php-format
297
  msgid "Cannot open source file %s to archive"
298
  msgstr ""
299
 
@@ -303,7 +300,6 @@ msgstr ""
303
 
304
  #: inc/class-create-archive.php:329 inc/class-create-archive.php:338
305
  #: inc/class-create-archive.php:407
306
- #, php-format
307
  msgid "Cannot add \"%s\" to zip archive!"
308
  msgstr ""
309
 
@@ -312,29 +308,24 @@ msgid "Folder name cannot be empty"
312
  msgstr ""
313
 
314
  #: inc/class-create-archive.php:379
315
- #, php-format
316
  msgctxt "Folder path to add to archive"
317
  msgid "Folder %s does not exist or is not readable"
318
  msgstr ""
319
 
320
  #: inc/class-create-archive.php:429
321
- #, php-format
322
  msgctxt "Text of ZipArchive status Message"
323
  msgid "ZipArchive returns status: %s"
324
  msgstr ""
325
 
326
  #: inc/class-create-archive.php:459
327
- #, php-format
328
  msgid "File name \"%1$s\" is too long to be saved correctly in %2$s archive!"
329
  msgstr ""
330
 
331
  #: inc/class-create-archive.php:462
332
- #, php-format
333
  msgid "File path \"%1$s\" is too long to be saved correctly in %2$s archive!"
334
  msgstr ""
335
 
336
  #: inc/class-create-archive.php:474
337
- #, php-format
338
  msgid "Cannot open source file %s for archiving"
339
  msgstr ""
340
 
@@ -344,204 +335,233 @@ msgid "Unknown"
344
  msgstr ""
345
 
346
  #: inc/class-create-archive.php:570
347
- #, php-format
348
  msgid "Folder name \"%1$s\" is too long to be saved correctly in %2$s archive!"
349
  msgstr ""
350
 
351
  #: inc/class-create-archive.php:573
352
- #, php-format
353
  msgid "Folder path \"%1$s\" is too long to be saved correctly in %2$s archive!"
354
  msgstr ""
355
 
356
  #: inc/class-create-archive.php:654
357
- #, php-format
358
- msgid "If %s will be added to your backup archive, the archive will be too large for operations with this PHP Version. You might want to consider splitting the backup job in multiple jobs with less files each."
 
 
359
  msgstr ""
360
 
361
  #: inc/class-cron.php:69
362
  msgid "Aborted, because no progress for one hour!"
363
  msgstr ""
364
 
365
- #: inc/class-destination-dropbox.php:47 inc/class-destination-dropbox.php:364
366
- #: inc/pro/class-destination-dropbox.php:266
367
- #, php-format
 
 
 
 
 
368
  msgid "Dropbox API: %s"
369
  msgstr ""
370
 
371
- #: inc/class-destination-dropbox.php:62
372
  #: inc/pro/class-destination-gdrive.php:51
373
  msgid "Login"
374
  msgstr ""
375
 
376
- #: inc/class-destination-dropbox.php:66 inc/class-destination-sugarsync.php:28
377
  #: inc/class-destination-sugarsync.php:43
378
  msgid "Authentication"
379
  msgstr ""
380
 
381
- #: inc/class-destination-dropbox.php:68
382
  #: inc/pro/class-destination-gdrive.php:57
383
- #: inc/pro/class-destination-gdrive.php:294
384
  msgid "Not authenticated!"
385
  msgstr ""
386
 
387
- #: inc/class-destination-dropbox.php:70
388
- #: inc/pro/class-destination-dropbox.php:29
389
  msgid "Create Account"
390
  msgstr ""
391
 
392
- #: inc/class-destination-dropbox.php:72 inc/class-destination-sugarsync.php:45
393
- #: inc/pro/class-destination-dropbox.php:35
394
  #: inc/pro/class-destination-gdrive.php:59
395
- #: inc/pro/class-destination-gdrive.php:299
396
  #: inc/pro/class-destination-sugarsync.php:31
397
  msgid "Authenticated!"
398
  msgstr ""
399
 
400
- #: inc/class-destination-dropbox.php:75
401
  msgid "Delete Dropbox Authentication"
402
  msgstr ""
403
 
404
- #: inc/class-destination-dropbox.php:82
405
  msgid "App Access to Dropbox"
406
  msgstr ""
407
 
408
- #: inc/class-destination-dropbox.php:85
409
  msgid "Get Dropbox App auth code"
410
  msgstr ""
411
 
412
- #: inc/class-destination-dropbox.php:86
413
- msgid "A dedicated folder named BackWPup will be created inside of the Apps folder in your Dropbox. BackWPup will get read and write access to that folder only. You can specify a subfolder as your backup destination for this job in the destination field below."
 
 
 
 
414
  msgstr ""
415
 
416
- #: inc/class-destination-dropbox.php:91
417
- msgid " OR "
418
  msgstr ""
419
 
420
- #: inc/class-destination-dropbox.php:94
421
  msgid "Full Access to Dropbox"
422
  msgstr ""
423
 
424
- #: inc/class-destination-dropbox.php:97
425
  msgid "Get full Dropbox auth code "
426
  msgstr ""
427
 
428
- #: inc/class-destination-dropbox.php:98
429
- msgid "BackWPup will have full read and write access to your entire Dropbox. You can specify your backup destination wherever you want, just be aware that ANY files or folders inside of your Dropbox can be overridden or deleted by BackWPup."
 
 
 
 
430
  msgstr ""
431
 
432
- #: inc/class-destination-dropbox.php:105 inc/class-destination-folder.php:29
433
  #: inc/class-destination-ftp.php:49 inc/class-destination-msazure.php:66
434
  #: inc/class-destination-rsc.php:95 inc/class-destination-sugarsync.php:81
435
  #: inc/pro/class-destination-gdrive.php:68
436
  msgid "Backup settings"
437
  msgstr ""
438
 
439
- #: inc/class-destination-dropbox.php:109
440
  msgid "Destination Folder"
441
  msgstr ""
442
 
443
- #: inc/class-destination-dropbox.php:113
444
- msgid "Specify a subfolder where your backup archives will be stored. If you use the App option from above, this folder will be created inside of Apps/BackWPup. Otherwise it will be created at the root of your Dropbox. Already exisiting folders with the same name will not be overriden."
 
 
 
 
445
  msgstr ""
446
 
447
- #: inc/class-destination-dropbox.php:118 inc/class-destination-folder.php:39
448
  #: inc/class-destination-ftp.php:59 inc/class-destination-sugarsync.php:91
449
  #: inc/pro/class-destination-gdrive.php:81
450
  msgid "File Deletion"
451
  msgstr ""
452
 
453
- #: inc/class-destination-dropbox.php:125 inc/class-destination-folder.php:46
454
  #: inc/class-destination-ftp.php:66 inc/class-destination-msazure.php:83
455
- #: inc/class-destination-rsc.php:112 inc/class-destination-s3.php:122
456
  #: inc/class-destination-sugarsync.php:98
457
- #: inc/pro/class-destination-dropbox.php:52
458
  #: inc/pro/class-destination-folder.php:43
459
  #: inc/pro/class-destination-gdrive.php:90
460
- #: inc/pro/class-destination-gdrive.php:322
461
  #: inc/pro/class-destination-msazure.php:45
462
- #: inc/pro/class-destination-rsc.php:65 inc/pro/class-destination-s3.php:74
463
  msgid "Number of files to keep in folder."
464
  msgstr ""
465
 
466
- #: inc/class-destination-dropbox.php:130 inc/class-destination-folder.php:51
467
- #: inc/class-destination-ftp.php:71 inc/class-destination-msazure.php:88
468
- #: inc/class-destination-rsc.php:117 inc/class-destination-s3.php:127
469
- #: inc/class-destination-sugarsync.php:103
470
- #: inc/pro/class-destination-dropbox.php:55
 
 
 
 
 
 
 
 
 
 
 
471
  #: inc/pro/class-destination-folder.php:48
472
  #: inc/pro/class-destination-ftp.php:44
473
- #: inc/pro/class-destination-gdrive.php:97
474
- #: inc/pro/class-destination-gdrive.php:327
475
  #: inc/pro/class-destination-msazure.php:56
476
- #: inc/pro/class-destination-rsc.php:70 inc/pro/class-destination-s3.php:79
477
  #: inc/pro/class-destination-sugarsync.php:65
478
  msgid "Do not delete files while syncing to destination!"
479
  msgstr ""
480
 
481
- #: inc/class-destination-dropbox.php:248
482
- #, php-format
483
  msgid "%d. Try to send backup file to Dropbox&#160;&hellip;"
484
  msgstr ""
485
 
486
- #: inc/class-destination-dropbox.php:275
487
- #: inc/pro/class-destination-dropbox.php:141
488
- #, php-format
489
  msgid "Authenticated with Dropbox of user: %s"
490
  msgstr ""
491
 
492
- #: inc/class-destination-dropbox.php:279
493
- #: inc/pro/class-destination-dropbox.php:145
494
- #, php-format
495
  msgid "%s available on your Dropbox"
496
  msgstr ""
497
 
498
- #: inc/class-destination-dropbox.php:282
499
- #: inc/pro/class-destination-dropbox.php:148
500
  msgid "Not Authenticated with Dropbox!"
501
  msgstr ""
502
 
503
- #: inc/class-destination-dropbox.php:286
504
  msgid "Uploading to Dropbox&#160;&hellip;"
505
  msgstr ""
506
 
507
- #: inc/class-destination-dropbox.php:299 inc/class-destination-msazure.php:285
508
- #: inc/class-destination-sugarsync.php:259
509
- #: inc/pro/class-destination-gdrive.php:662
510
- #, php-format
511
  msgid "Backup transferred to %s"
512
  msgstr ""
513
 
514
- #: inc/class-destination-dropbox.php:302
515
- #: inc/pro/class-destination-gdrive.php:665
516
  msgid "Uploaded file size and local file size don't match."
517
  msgstr ""
518
 
519
- #: inc/class-destination-dropbox.php:306
520
- #: inc/pro/class-destination-gdrive.php:667
521
- #: inc/pro/class-destination-glacier.php:447
522
- #, php-format
523
  msgid "Error transfering backup to %s."
524
  msgstr ""
525
 
526
- #: inc/class-destination-dropbox.php:354
527
- #, php-format
528
- msgid "Error while deleting file from Dropbox: %s"
529
- msgstr ""
530
-
531
- #: inc/class-destination-dropbox.php:358
532
- #, php-format
533
  msgid "One file deleted from Dropbox"
534
  msgid_plural "%d files deleted on Dropbox"
535
  msgstr[0] ""
536
  msgstr[1] ""
537
 
 
 
 
 
 
 
 
 
 
 
 
 
538
  #: inc/class-destination-email.php:38 inc/pro/class-destination-email.php:16
539
  #: inc/pro/class-destination-email.php:18
540
  msgid "Email address"
541
  msgstr ""
542
 
543
  #: inc/class-destination-email.php:41
544
- msgid "To email address"
545
  msgstr ""
546
 
547
  #: inc/class-destination-email.php:47 inc/class-destination-email.php:49
@@ -614,7 +634,7 @@ msgstr ""
614
  #: inc/class-destination-email.php:106 inc/class-jobtype-dbdump.php:67
615
  #: inc/class-jobtype-dbdump.php:103 inc/class-jobtype-wpexp.php:73
616
  #: inc/class-jobtype-wpplugin.php:57 inc/class-page-backwpup.php:329
617
- #: inc/class-page-backwpup.php:396 inc/class-page-settings.php:313
618
  #: inc/pro/class-jobtype-dbdump.php:157 inc/pro/class-jobtype-dbdump.php:205
619
  msgid "none"
620
  msgstr ""
@@ -636,7 +656,6 @@ msgid "SMTP password"
636
  msgstr ""
637
 
638
  #: inc/class-destination-email.php:199
639
- #, php-format
640
  msgid "%d. Try to send backup with email&#160;&hellip;"
641
  msgstr ""
642
 
@@ -645,17 +664,14 @@ msgid "Backup archive too big to be sent by email!"
645
  msgstr ""
646
 
647
  #: inc/class-destination-email.php:211
648
- #, php-format
649
  msgid "Sending email to %s&hellip;"
650
  msgstr ""
651
 
652
  #: inc/class-destination-email.php:289
653
- #, php-format
654
  msgid "BackWPup archive from %1$s: %2$s"
655
  msgstr ""
656
 
657
  #: inc/class-destination-email.php:292
658
- #, php-format
659
  msgid "Backup archive: %s"
660
  msgstr ""
661
 
@@ -672,15 +688,16 @@ msgid "BackWPup archive sending TEST Message"
672
  msgstr ""
673
 
674
  #: inc/class-destination-email.php:417
675
- msgid "If this message reaches your inbox, sending backup archives via email should work for you."
 
 
676
  msgstr ""
677
 
678
  #: inc/class-destination-folder.php:33
679
  msgid "Folder to store backups in"
680
  msgstr ""
681
 
682
- #: inc/class-destination-folder.php:217
683
- #, php-format
684
  msgid "One backup file deleted"
685
  msgid_plural "%d backup files deleted"
686
  msgstr[0] ""
@@ -706,151 +723,137 @@ msgstr ""
706
  msgid "Folder to store files in"
707
  msgstr ""
708
 
709
- #: inc/class-destination-ftp.php:78
710
  msgid "FTP specific settings"
711
  msgstr ""
712
 
713
- #: inc/class-destination-ftp.php:82
714
  msgid "Timeout for FTP connection"
715
  msgstr ""
716
 
717
- #: inc/class-destination-ftp.php:86 inc/class-page-logs.php:257
718
- #: inc/class-page-settings.php:451
719
  msgid "seconds"
720
  msgstr ""
721
 
722
- #: inc/class-destination-ftp.php:90
723
  msgid "SSL-FTP connection"
724
  msgstr ""
725
 
726
- #: inc/class-destination-ftp.php:93
727
  msgid "Use explicit SSL-FTP connection."
728
  msgstr ""
729
 
730
- #: inc/class-destination-ftp.php:98
731
  msgid "FTP Passive Mode"
732
  msgstr ""
733
 
734
- #: inc/class-destination-ftp.php:100
735
  msgid "Use FTP Passive Mode."
736
  msgstr ""
737
 
738
- #: inc/class-destination-ftp.php:178
739
  msgid "FTP: Login failure!"
740
  msgstr ""
741
 
742
- #: inc/class-destination-ftp.php:202
743
- #, php-format
744
  msgid "%d. Try to send backup file to an FTP server&#160;&hellip;"
745
  msgstr ""
746
 
747
- #: inc/class-destination-ftp.php:208
748
- #, php-format
749
  msgid "Connected via explicit SSL-FTP to server: %s"
750
  msgstr ""
751
 
752
- #: inc/class-destination-ftp.php:210
753
- #, php-format
754
  msgid "Cannot connect via explicit SSL-FTP to server: %s"
755
  msgstr ""
756
 
757
- #: inc/class-destination-ftp.php:216
758
  msgid "PHP function to connect with explicit SSL-FTP to server does not exist!"
759
  msgstr ""
760
 
761
- #: inc/class-destination-ftp.php:224
762
- #, php-format
763
  msgid "Connected to FTP server: %s"
764
  msgstr ""
765
 
766
- #: inc/class-destination-ftp.php:226
767
- #, php-format
768
  msgid "Cannot connect to FTP server: %s"
769
  msgstr ""
770
 
771
- #: inc/class-destination-ftp.php:233 inc/class-destination-ftp.php:241
772
- #: inc/class-destination-ftp.php:257 inc/class-destination-ftp.php:304
773
- #, php-format
774
  msgid "FTP client command: %s"
775
  msgstr ""
776
 
777
- #: inc/class-destination-ftp.php:235
778
- #, php-format
779
  msgid "FTP server response: %s"
780
  msgstr ""
781
 
782
- #: inc/class-destination-ftp.php:239 inc/class-destination-ftp.php:244
783
- #: inc/class-destination-ftp.php:247 inc/class-destination-ftp.php:260
784
- #: inc/class-destination-ftp.php:262 inc/class-destination-ftp.php:307
785
- #: inc/class-destination-ftp.php:309 inc/class-destination-ftp.php:313
786
- #: inc/class-destination-ftp.php:315
787
- #, php-format
788
  msgid "FTP server reply: %s"
789
  msgstr ""
790
 
791
- #: inc/class-destination-ftp.php:262
792
  msgid "Error getting SYSTYPE"
793
  msgstr ""
794
 
795
- #: inc/class-destination-ftp.php:280
796
- #, php-format
797
  msgid "FTP Folder \"%s\" created!"
798
  msgstr ""
799
 
800
- #: inc/class-destination-ftp.php:284
801
- #, php-format
802
  msgid "FTP Folder \"%s\" cannot be created!"
803
  msgstr ""
804
 
805
- #: inc/class-destination-ftp.php:295
806
- #, php-format
807
  msgid "FTP current folder is: %s"
808
  msgstr ""
809
 
810
- #: inc/class-destination-ftp.php:307
811
  msgid "Entering passive mode"
812
  msgstr ""
813
 
814
- #: inc/class-destination-ftp.php:309
815
  msgid "Cannot enter passive mode"
816
  msgstr ""
817
 
818
- #: inc/class-destination-ftp.php:313
819
  msgid "Entering normal mode"
820
  msgstr ""
821
 
822
- #: inc/class-destination-ftp.php:315
823
  msgid "Cannot enter normal mode"
824
  msgstr ""
825
 
826
- #: inc/class-destination-ftp.php:319
827
  msgid "Starting upload to FTP &#160;&hellip;"
828
  msgstr ""
829
 
830
- #: inc/class-destination-ftp.php:331
831
  msgid "Cannot transfer backup to FTP server!"
832
  msgstr ""
833
 
834
- #: inc/class-destination-ftp.php:336
835
- #, php-format
836
  msgid "Backup transferred to FTP server: %s"
837
  msgstr ""
838
 
839
- #: inc/class-destination-ftp.php:343 inc/class-destination-msazure.php:270
840
- #: inc/class-destination-rsc.php:288 inc/class-destination-s3.php:500
841
- #: inc/class-destination-s3.php:592 inc/pro/class-destination-gdrive.php:652
842
- #: inc/pro/class-destination-glacier.php:467
843
  #: inc/pro/class-destination-rsc.php:226 inc/pro/class-destination-rsc.php:259
844
  msgid "Can not open source file for transfer."
845
  msgstr ""
846
 
847
- #: inc/class-destination-ftp.php:387
848
- #, php-format
849
  msgid "Cannot delete \"%s\" on FTP server!"
850
  msgstr ""
851
 
852
- #: inc/class-destination-ftp.php:390
853
- #, php-format
854
  msgid "One file deleted on FTP server"
855
  msgid_plural "%d files deleted on FTP server"
856
  msgstr[0] ""
@@ -885,66 +888,59 @@ msgid "Folder in container"
885
  msgstr ""
886
 
887
  #: inc/class-destination-msazure.php:76 inc/class-destination-rsc.php:105
888
- #: inc/class-destination-s3.php:115 inc/pro/class-destination-glacier.php:96
889
  msgid "File deletion"
890
  msgstr ""
891
 
892
- #: inc/class-destination-msazure.php:127
893
  #: inc/pro/class-destination-msazure.php:99
894
- #, php-format
895
  msgid "MS Azure container \"%s\" created."
896
  msgstr ""
897
 
898
- #: inc/class-destination-msazure.php:130
899
  #: inc/pro/class-destination-msazure.php:102
900
- #, php-format
901
  msgid "MS Azure container create: %s"
902
  msgstr ""
903
 
904
- #: inc/class-destination-msazure.php:210
905
- #, php-format
906
  msgid "%d. Try sending backup to a Microsoft Azure (Blob)&#160;&hellip;"
907
  msgstr ""
908
 
909
- #: inc/class-destination-msazure.php:232
910
  #: inc/pro/class-destination-msazure.php:144
911
- #, php-format
912
  msgid "MS Azure container \"%s\" does not exist!"
913
  msgstr ""
914
 
915
- #: inc/class-destination-msazure.php:236
916
  #: inc/pro/class-destination-msazure.php:148
917
- #, php-format
918
  msgid "Connected to MS Azure container \"%s\"."
919
  msgstr ""
920
 
921
- #: inc/class-destination-msazure.php:239
922
  msgid "Starting upload to MS Azure&#160;&hellip;"
923
  msgstr ""
924
 
925
- #: inc/class-destination-msazure.php:291 inc/class-destination-msazure.php:347
926
  #: inc/pro/class-destination-msazure.php:233
927
- #, php-format
928
  msgid "Microsoft Azure API: %s"
929
  msgstr ""
930
 
931
- #: inc/class-destination-msazure.php:340
932
- #, php-format
933
  msgid "One file deleted on Microsoft Azure container."
934
  msgid_plural "%d files deleted on Microsoft Azure container."
935
  msgstr[0] ""
936
  msgstr[1] ""
937
 
938
- #: inc/class-destination-msazure.php:441
939
  msgid "Missing account name!"
940
  msgstr ""
941
 
942
- #: inc/class-destination-msazure.php:443 inc/class-destination-s3.php:221
943
- #: inc/pro/class-destination-glacier.php:165
944
  msgid "Missing access key!"
945
  msgstr ""
946
 
947
- #: inc/class-destination-msazure.php:447
948
  msgid "No container found!"
949
  msgstr ""
950
 
@@ -989,61 +985,56 @@ msgstr ""
989
  msgid "Hong Kong (HKG)"
990
  msgstr ""
991
 
992
- #: inc/class-destination-rsc.php:99 inc/class-destination-s3.php:109
993
  msgid "Folder in bucket"
994
  msgstr ""
995
 
996
- #: inc/class-destination-rsc.php:157 inc/pro/class-destination-rsc.php:116
997
- #, php-format
998
  msgid "Rackspace Cloud container \"%s\" created."
999
  msgstr ""
1000
 
1001
- #: inc/class-destination-rsc.php:161 inc/class-destination-rsc.php:273
1002
- #: inc/class-destination-rsc.php:313 inc/class-destination-rsc.php:358
1003
  #: inc/pro/class-destination-rsc.php:120 inc/pro/class-destination-rsc.php:170
1004
  #: inc/pro/class-destination-rsc.php:290
1005
- #, php-format
1006
  msgid "Rackspace Cloud API: %s"
1007
  msgstr ""
1008
 
1009
- #: inc/class-destination-rsc.php:256
1010
- #, php-format
1011
  msgid "%d. Trying to send backup file to Rackspace cloud &hellip;"
1012
  msgstr ""
1013
 
1014
- #: inc/class-destination-rsc.php:270
1015
- #, php-format
1016
  msgid "Connected to Rackspace cloud files container %s"
1017
  msgstr ""
1018
 
1019
- #: inc/class-destination-rsc.php:282
1020
  msgid "Upload to Rackspace cloud started &hellip;"
1021
  msgstr ""
1022
 
1023
- #: inc/class-destination-rsc.php:301
1024
  msgid "Backup File transferred to RSC://"
1025
  msgstr ""
1026
 
1027
- #: inc/class-destination-rsc.php:307
1028
  msgid "Cannot transfer backup to Rackspace cloud."
1029
  msgstr ""
1030
 
1031
- #: inc/class-destination-rsc.php:352
1032
- #, php-format
1033
  msgid "One file deleted on Rackspace cloud container."
1034
  msgid_plural "%d files deleted on Rackspace cloud container."
1035
  msgstr[0] ""
1036
  msgstr[1] ""
1037
 
1038
- #: inc/class-destination-rsc.php:465
1039
  msgid "Missing username!"
1040
  msgstr ""
1041
 
1042
- #: inc/class-destination-rsc.php:467
1043
  msgid "Missing API Key!"
1044
  msgstr ""
1045
 
1046
- #: inc/class-destination-rsc.php:471
1047
  msgid "A container could not be found!"
1048
  msgstr ""
1049
 
@@ -1119,39 +1110,35 @@ msgstr ""
1119
  msgid "Dream Host Cloud Storage"
1120
  msgstr ""
1121
 
1122
- #: inc/class-destination-s3.php:50 inc/pro/class-destination-s3.php:35
1123
- msgid "GreenQloud Storage Qloud"
1124
- msgstr ""
1125
-
1126
- #: inc/class-destination-s3.php:55
1127
  msgid "Or a S3 Server URL"
1128
  msgstr ""
1129
 
1130
- #: inc/class-destination-s3.php:62
1131
  msgid "S3 Access Keys"
1132
  msgstr ""
1133
 
1134
- #: inc/class-destination-s3.php:66 inc/pro/class-destination-glacier.php:54
1135
  msgid "Access Key"
1136
  msgstr ""
1137
 
1138
- #: inc/class-destination-s3.php:73 inc/pro/class-destination-glacier.php:61
1139
  msgid "Secret Key"
1140
  msgstr ""
1141
 
1142
- #: inc/class-destination-s3.php:81
1143
  msgid "S3 Bucket"
1144
  msgstr ""
1145
 
1146
- #: inc/class-destination-s3.php:85
1147
  msgid "Bucket selection"
1148
  msgstr ""
1149
 
1150
- #: inc/class-destination-s3.php:98
1151
  msgid "Create a new bucket"
1152
  msgstr ""
1153
 
1154
- #: inc/class-destination-s3.php:105
1155
  msgid "S3 Backup settings"
1156
  msgstr ""
1157
 
@@ -1164,7 +1151,10 @@ msgid "Use multipart upload for uploading a file"
1164
  msgstr ""
1165
 
1166
  #: inc/class-destination-s3.php:137
1167
- msgid "Multipart splits file into multiple chunks while uploading. This is necessary for displaying the upload process and to transfer bigger files. Works without a problem on Amazon. Other services might have issues."
 
 
 
1168
  msgstr ""
1169
 
1170
  #: inc/class-destination-s3.php:143
@@ -1195,81 +1185,69 @@ msgstr ""
1195
  msgid "Save files encrypted (AES256) on server."
1196
  msgstr ""
1197
 
1198
- #: inc/class-destination-s3.php:223 inc/pro/class-destination-glacier.php:167
1199
  msgid "Missing secret access key!"
1200
  msgstr ""
1201
 
1202
- #: inc/class-destination-s3.php:229
1203
  msgid "No bucket found!"
1204
  msgstr ""
1205
 
1206
- #: inc/class-destination-s3.php:347
1207
- #, php-format
1208
  msgid "Bucket %1$s created."
1209
  msgstr ""
1210
 
1211
- #: inc/class-destination-s3.php:349 inc/pro/class-destination-s3.php:148
1212
- #, php-format
1213
  msgid " %s is not a valid bucket name."
1214
  msgstr ""
1215
 
1216
- #: inc/class-destination-s3.php:390 inc/class-destination-s3.php:523
1217
- #: inc/class-destination-s3.php:577 inc/class-destination-s3.php:611
1218
- #: inc/class-destination-s3.php:671 inc/pro/class-destination-s3.php:338
1219
- #, php-format
1220
  msgid "S3 Service API: %s"
1221
  msgstr ""
1222
 
1223
- #: inc/class-destination-s3.php:458
1224
- #, php-format
1225
  msgid "%d. Trying to send backup file to S3 Service&#160;&hellip;"
1226
  msgstr ""
1227
 
1228
- #: inc/class-destination-s3.php:471 inc/pro/class-destination-s3.php:189
1229
- #, php-format
1230
  msgid "Connected to S3 Bucket \"%1$s\" in %2$s"
1231
  msgstr ""
1232
 
1233
- #: inc/class-destination-s3.php:474 inc/pro/class-destination-s3.php:192
1234
- #, php-format
1235
  msgid "S3 Bucket \"%s\" does not exist!"
1236
  msgstr ""
1237
 
1238
- #: inc/class-destination-s3.php:481
1239
  msgid "Checking for not aborted multipart Uploads&#160;&hellip;"
1240
  msgstr ""
1241
 
1242
- #: inc/class-destination-s3.php:487
1243
- #, php-format
1244
  msgid "Upload for %s aborted."
1245
  msgstr ""
1246
 
1247
- #: inc/class-destination-s3.php:493
1248
  msgid "Starting upload to S3 Service&#160;&hellip;"
1249
  msgstr ""
1250
 
1251
- #: inc/class-destination-s3.php:602 inc/pro/class-destination-glacier.php:443
1252
- #, php-format
1253
  msgid "Backup transferred to %s."
1254
  msgstr ""
1255
 
1256
- #: inc/class-destination-s3.php:607
1257
- #, php-format
1258
  msgid "Cannot transfer backup to S3! (%1$d) %2$s"
1259
  msgstr ""
1260
 
1261
- #: inc/class-destination-s3.php:635
1262
- #, php-format
1263
  msgid "Storage Class: %s"
1264
  msgstr ""
1265
 
1266
- #: inc/class-destination-s3.php:661
1267
- #, php-format
1268
  msgid "Cannot delete backup from %s."
1269
  msgstr ""
1270
 
1271
- #: inc/class-destination-s3.php:665
1272
- #, php-format
1273
  msgid "One file deleted on S3 Bucket."
1274
  msgid_plural "%d files deleted on S3 Bucket"
1275
  msgstr[0] ""
@@ -1291,19 +1269,19 @@ msgid "Password:"
1291
  msgstr ""
1292
 
1293
  #: inc/class-destination-sugarsync.php:37
1294
- #: inc/class-destination-sugarsync.php:119
1295
  msgid "Authenticate with Sugarsync!"
1296
  msgstr ""
1297
 
1298
  #: inc/class-destination-sugarsync.php:38
1299
- #: inc/class-destination-sugarsync.php:135
1300
  #: inc/pro/class-destination-sugarsync.php:26
1301
  #: inc/pro/class-destination-sugarsync.php:98
1302
  msgid "Create Sugarsync account"
1303
  msgstr ""
1304
 
1305
  #: inc/class-destination-sugarsync.php:46
1306
- #: inc/class-destination-sugarsync.php:131
1307
  #: inc/pro/class-destination-sugarsync.php:32
1308
  #: inc/pro/class-destination-sugarsync.php:94
1309
  msgid "Delete Sugarsync authentication!"
@@ -1326,49 +1304,42 @@ msgstr ""
1326
  msgid "Folder in root"
1327
  msgstr ""
1328
 
1329
- #: inc/class-destination-sugarsync.php:229
1330
- #, php-format
1331
  msgid "%d. Try to send backup to SugarSync&#160;&hellip;"
1332
  msgstr ""
1333
 
1334
- #: inc/class-destination-sugarsync.php:236
1335
- #, php-format
1336
  msgid "Authenticated to SugarSync with nickname %s"
1337
  msgstr ""
1338
 
1339
- #: inc/class-destination-sugarsync.php:239
1340
- #, php-format
1341
  msgctxt "Available space on SugarSync"
1342
  msgid "Not enough disk space available on SugarSync. Available: %s."
1343
  msgstr ""
1344
 
1345
- #: inc/class-destination-sugarsync.php:245
1346
- #, php-format
1347
  msgid "%s available at SugarSync"
1348
  msgstr ""
1349
 
1350
- #: inc/class-destination-sugarsync.php:252
1351
  msgid "Starting upload to SugarSync&#160;&hellip;"
1352
  msgstr ""
1353
 
1354
- #: inc/class-destination-sugarsync.php:262
1355
  msgid "Cannot transfer backup to SugarSync!"
1356
  msgstr ""
1357
 
1358
- #: inc/class-destination-sugarsync.php:301
1359
- #, php-format
1360
  msgid "One file deleted on SugarSync folder"
1361
  msgid_plural "%d files deleted on SugarSync folder"
1362
  msgstr[0] ""
1363
  msgstr[1] ""
1364
 
1365
- #: inc/class-destination-sugarsync.php:307
1366
- #, php-format
1367
  msgid "SugarSync API: %s"
1368
  msgstr ""
1369
 
1370
  #: inc/class-easycron.php:179
1371
- #, php-format
1372
  msgid "EasyCron.com API returns (%s): %s"
1373
  msgstr ""
1374
 
@@ -1377,7 +1348,10 @@ msgid "EasyCron"
1377
  msgstr ""
1378
 
1379
  #: inc/class-easycron.php:189
1380
- msgid "Here you can setup your <a href=\"https://www.easycron.com/user/token?ref=36673\" title=\"Affiliate Link!\">EasyCron.com API key</a> to use this service."
 
 
 
1381
  msgstr ""
1382
 
1383
  #: inc/class-easycron.php:192
@@ -1389,31 +1363,31 @@ msgid "Trigger WordPress Cron:"
1389
  msgstr ""
1390
 
1391
  #: inc/class-easycron.php:203
1392
- msgid "If you check this box, a cron job will be created on EasyCron that all 5 Minutes calls the WordPress cron."
 
 
1393
  msgstr ""
1394
 
1395
- #: inc/class-file.php:154
1396
- #, php-format
1397
  msgid "Folder %1$s not allowed, please use another folder."
1398
  msgstr ""
1399
 
1400
- #: inc/class-file.php:159
1401
- #, php-format
1402
  msgid "Folder %1$s is not in open basedir, please use another folder."
1403
  msgstr ""
1404
 
1405
- #: inc/class-file.php:165
1406
- #, php-format
1407
  msgid "Cannot create folder: %1$s"
1408
  msgstr ""
1409
 
1410
- #: inc/class-file.php:171
1411
- #, php-format
1412
  msgid "Folder \"%1$s\" is not writable"
1413
  msgstr ""
1414
 
1415
- #: inc/class-file.php:200
1416
- msgid "BackWPup will not backup folders and its sub folders when this file is inside."
 
 
1417
  msgstr ""
1418
 
1419
  #: inc/class-help.php:15
@@ -1421,13 +1395,16 @@ msgid "Plugin Info"
1421
  msgstr ""
1422
 
1423
  #: inc/class-help.php:17
1424
- #, php-format
1425
  msgctxt "Plugin name and link; Plugin Version"
1426
- msgid "%1$s version %2$s. A project by <a href=\"http://inpsyde.com\">Inpsyde GmbH</a>."
 
 
1427
  msgstr ""
1428
 
1429
  #: inc/class-help.php:18
1430
- msgid "BackWPup comes with ABSOLUTELY NO WARRANTY. This is a free software, and you are welcome to redistribute it under certain conditions."
 
 
1431
  msgstr ""
1432
 
1433
  #: inc/class-help.php:21
@@ -1475,18 +1452,15 @@ msgid "End of Job"
1475
  msgstr ""
1476
 
1477
  #: inc/class-job.php:377
1478
- #, php-format
1479
  msgid "BackWPup log for %1$s from %2$s at %3$s"
1480
  msgstr ""
1481
 
1482
  #: inc/class-job.php:394
1483
- #, php-format
1484
  msgctxt "Plugin name; Plugin Version; plugin url"
1485
  msgid "[INFO] %1$s %2$s; A project of Inpsyde GmbH"
1486
  msgstr ""
1487
 
1488
  #: inc/class-job.php:395
1489
- #, php-format
1490
  msgctxt "WordPress Version; Blog url"
1491
  msgid "[INFO] WordPress %1$s on %2$s"
1492
  msgstr ""
@@ -1504,17 +1478,14 @@ msgid "(translated)"
1504
  msgstr ""
1505
 
1506
  #: inc/class-job.php:404
1507
- #, php-format
1508
  msgid "[INFO] Log Level: %1$s %2$s"
1509
  msgstr ""
1510
 
1511
  #: inc/class-job.php:409
1512
- #, php-format
1513
  msgid "[INFO] BackWPup job: %1$s"
1514
  msgstr ""
1515
 
1516
  #: inc/class-job.php:412
1517
- #, php-format
1518
  msgid "[INFO] Runs with user: %1$s (%2$d) "
1519
  msgstr ""
1520
 
@@ -1524,7 +1495,6 @@ msgid "Not scheduled!"
1524
  msgstr ""
1525
 
1526
  #: inc/class-job.php:430 inc/class-job.php:440
1527
- #, php-format
1528
  msgid "[INFO] Cron: %s; Next: %s "
1529
  msgstr ""
1530
 
@@ -1561,47 +1531,38 @@ msgid "[INFO] PHP ver.:"
1561
  msgstr ""
1562
 
1563
  #: inc/class-job.php:463
1564
- #, php-format
1565
  msgid "[INFO] Maximum PHP script execution time is %1$d seconds"
1566
  msgstr ""
1567
 
1568
  #: inc/class-job.php:467
1569
- #, php-format
1570
  msgid "[INFO] Script restart time is configured to %1$d seconds"
1571
  msgstr ""
1572
 
1573
  #: inc/class-job.php:470
1574
- #, php-format
1575
  msgid "[INFO] MySQL ver.: %s"
1576
  msgstr ""
1577
 
1578
  #: inc/class-job.php:472
1579
- #, php-format
1580
  msgid "[INFO] Web Server: %s"
1581
  msgstr ""
1582
 
1583
  #: inc/class-job.php:476
1584
- #, php-format
1585
  msgid "[INFO] curl ver.: %1$s; %2$s"
1586
  msgstr ""
1587
 
1588
  #: inc/class-job.php:478
1589
- #, php-format
1590
  msgid "[INFO] Temp folder is: %s"
1591
  msgstr ""
1592
 
1593
  #: inc/class-job.php:485
1594
- #, php-format
1595
  msgid "[INFO] Logfile is: %s"
1596
  msgstr ""
1597
 
1598
  #: inc/class-job.php:492
1599
- #, php-format
1600
  msgid "[INFO] Backup file is: %s"
1601
  msgstr ""
1602
 
1603
  #: inc/class-job.php:494
1604
- #, php-format
1605
  msgid "[INFO] Backup type is: %s"
1606
  msgstr ""
1607
 
@@ -1610,289 +1571,315 @@ msgid "Could not write log file"
1610
  msgstr ""
1611
 
1612
  #: inc/class-job.php:514
1613
- msgid "No destination correctly defined for backup! Please correct job settings."
 
1614
  msgstr ""
1615
 
1616
- #: inc/class-job.php:631
1617
  msgid "Cannot write progress to working file. Job will be aborted."
1618
  msgstr ""
1619
 
1620
- #: inc/class-job.php:703 inc/class-page-jobs.php:786
1621
  msgid "WARNING:"
1622
  msgstr ""
1623
 
1624
- #: inc/class-job.php:712 inc/class-page-jobs.php:784
1625
  msgid "ERROR:"
1626
  msgstr ""
1627
 
1628
- #: inc/class-job.php:716
1629
  msgid "DEPRECATED:"
1630
  msgstr ""
1631
 
1632
- #: inc/class-job.php:719
1633
  msgid "STRICT NOTICE:"
1634
  msgstr ""
1635
 
1636
- #: inc/class-job.php:724
1637
  msgid "RECOVERABLE ERROR:"
1638
  msgstr ""
1639
 
1640
- #: inc/class-job.php:972
1641
  msgid "Aborted by user!"
1642
  msgstr ""
1643
 
1644
- #: inc/class-job.php:1000
1645
- #, php-format
1646
  msgid "One old log deleted"
1647
  msgid_plural "%d old logs deleted"
1648
  msgstr[0] ""
1649
  msgstr[1] ""
1650
 
1651
- #: inc/class-job.php:1007 inc/class-page-jobs.php:784
1652
- #, php-format
1653
- msgid "Job has ended with errors in %s seconds. You must resolve the errors for correct execution."
 
1654
  msgstr ""
1655
 
1656
- #: inc/class-job.php:1009
1657
- #, php-format
1658
- msgid "Job finished with warnings in %s seconds. Please resolve them for correct execution."
 
1659
  msgstr ""
1660
 
1661
- #: inc/class-job.php:1011 inc/class-page-jobs.php:788
1662
- #, php-format
1663
  msgid "Job done in %s seconds."
1664
  msgstr ""
1665
 
1666
- #: inc/class-job.php:1055
1667
  msgid "SUCCESSFUL"
1668
  msgstr ""
1669
 
1670
- #: inc/class-job.php:1057
1671
  msgid "WARNING"
1672
  msgstr ""
1673
 
1674
- #: inc/class-job.php:1060
1675
  msgid "ERROR"
1676
  msgstr ""
1677
 
1678
- #: inc/class-job.php:1063
1679
- #, php-format
1680
  msgid "[%3$s] BackWPup log %1$s: %2$s"
1681
  msgstr ""
1682
 
1683
- #: inc/class-job.php:1164
1684
- #, php-format
1685
  msgid "Restart after %1$d seconds."
1686
  msgstr ""
1687
 
1688
- #: inc/class-job.php:1166
1689
  msgid "Restart after getting signal."
1690
  msgstr ""
1691
 
1692
- #: inc/class-job.php:1339
1693
  msgid "Job restarts due to inactivity for more than 5 minutes."
1694
  msgstr ""
1695
 
1696
- #: inc/class-job.php:1437
1697
  msgid "Step aborted: too many attempts!"
1698
  msgstr ""
1699
 
1700
- #: inc/class-job.php:1508
1701
- #, php-format
1702
  msgid "%d. Trying to create backup archive &hellip;"
1703
  msgstr ""
1704
 
1705
- #: inc/class-job.php:1516
1706
- #, php-format
1707
  msgctxt "Archive compression method"
1708
  msgid "Compressing files as %s. Please be patient, this may take a moment."
1709
  msgstr ""
1710
 
1711
- #: inc/class-job.php:1523
1712
  msgid "Adding Extra files to Archive"
1713
  msgstr ""
1714
 
1715
- #: inc/class-job.php:1535 inc/class-job.php:1595
1716
  msgid "Cannot create backup archive correctly. Aborting creation."
1717
  msgstr ""
1718
 
1719
- #: inc/class-job.php:1551
1720
- #, php-format
1721
  msgid "Archiving Folder: %s"
1722
  msgstr ""
1723
 
1724
- #: inc/class-job.php:1605
1725
  msgid "Backup archive created."
1726
  msgstr ""
1727
 
1728
- #: inc/class-job.php:1619
1729
- msgid "The Backup archive will be too large for file operations with this PHP Version. You might want to consider splitting the backup job in multiple jobs with less files each."
 
 
 
1730
  msgstr ""
1731
 
1732
- #: inc/class-job.php:1622
1733
- #, php-format
1734
  msgid "Archive size is %s."
1735
  msgstr ""
1736
 
1737
- #: inc/class-job.php:1625
1738
- #, php-format
1739
  msgid "%1$d Files with %2$s in Archive."
1740
  msgstr ""
1741
 
1742
- #: inc/class-job.php:1674
1743
- #, php-format
1744
  msgctxt "Folder name"
1745
- msgid "Folder %s not exists"
1746
  msgstr ""
1747
 
1748
- #: inc/class-job.php:1680
1749
- #, php-format
1750
  msgctxt "Folder name"
1751
- msgid "Folder %s not readable"
1752
  msgstr ""
1753
 
1754
- #: inc/class-job.php:1700
1755
- #, php-format
1756
  msgid "Link \"%s\" not following."
1757
  msgstr ""
1758
 
1759
- #: inc/class-job.php:1702
1760
- #, php-format
1761
  msgid "File \"%s\" is not readable!"
1762
  msgstr ""
1763
 
1764
- #: inc/class-job.php:1706
1765
- #, php-format
1766
- msgid "File size of %s cannot be retrieved. File might be too large and will not be added to queue."
 
1767
  msgstr ""
1768
 
1769
- #: inc/class-job.php:1786
1770
- #, php-format
1771
  msgid "%d. Trying to generate a manifest file&#160;&hellip;"
1772
  msgstr ""
1773
 
1774
- #: inc/class-job.php:1842
1775
  msgid "You may have noticed the manifest.json file in this archive."
1776
  msgstr ""
1777
 
1778
- #: inc/class-job.php:1843
1779
- msgid "manifest.json might be needed for later restoring a backup from this archive."
 
1780
  msgstr ""
1781
 
1782
- #: inc/class-job.php:1844
1783
- msgid "Please leave manifest.json untouched and in place. Otherwise it is safe to be ignored."
 
 
1784
  msgstr ""
1785
 
1786
- #: inc/class-job.php:1854
1787
- #, php-format
1788
  msgid "Added manifest.json file with %1$s to backup file list."
1789
  msgstr ""
1790
 
1791
- #: inc/class-job.php:1893
1792
  msgid "Wrong BackWPup JobID"
1793
  msgstr ""
1794
 
1795
- #: inc/class-job.php:1906
1796
  msgid "A BackWPup job is already running"
1797
  msgstr ""
1798
 
1799
- #: inc/class-job.php:2274
1800
- msgctxt "SIGHUP: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1801
  msgid "Hangup detected on controlling terminal or death of controlling process"
1802
  msgstr ""
1803
 
1804
- #: inc/class-job.php:2278
1805
- msgctxt "SIGINT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1806
  msgid "Interrupt from keyboard"
1807
  msgstr ""
1808
 
1809
- #: inc/class-job.php:2282
1810
- msgctxt "SIGQUIT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1811
  msgid "Quit from keyboard"
1812
  msgstr ""
1813
 
1814
- #: inc/class-job.php:2286
1815
- msgctxt "SIGILL: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1816
  msgid "Illegal Instruction"
1817
  msgstr ""
1818
 
1819
- #: inc/class-job.php:2290
1820
- msgctxt "SIGABRT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1821
  msgid "Abort signal from abort(3)"
1822
  msgstr ""
1823
 
1824
- #: inc/class-job.php:2294
1825
- msgctxt "SIGBUS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1826
  msgid "Bus error (bad memory access)"
1827
  msgstr ""
1828
 
1829
- #: inc/class-job.php:2298
1830
- msgctxt "SIGFPE: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1831
  msgid "Floating point exception"
1832
  msgstr ""
1833
 
1834
- #: inc/class-job.php:2302
1835
- msgctxt "SIGSEGV: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1836
  msgid "Invalid memory reference"
1837
  msgstr ""
1838
 
1839
- #: inc/class-job.php:2306
1840
- msgctxt "SIGTERM: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1841
  msgid "Termination signal"
1842
  msgstr ""
1843
 
1844
- #: inc/class-job.php:2310
1845
- msgctxt "SIGSTKFLT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1846
  msgid "Stack fault on coprocessor"
1847
  msgstr ""
1848
 
1849
- #: inc/class-job.php:2314
1850
- msgctxt "SIGUSR1: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1851
  msgid "User-defined signal 1"
1852
  msgstr ""
1853
 
1854
- #: inc/class-job.php:2318
1855
- msgctxt "SIGUSR2: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1856
  msgid "User-defined signal 2"
1857
  msgstr ""
1858
 
1859
- #: inc/class-job.php:2322
1860
- msgctxt "SIGURG: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1861
  msgid "Urgent condition on socket"
1862
  msgstr ""
1863
 
1864
- #: inc/class-job.php:2326
1865
- msgctxt "SIGXCPU: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1866
  msgid "CPU time limit exceeded"
1867
  msgstr ""
1868
 
1869
- #: inc/class-job.php:2330
1870
- msgctxt "SIGXFSZ: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1871
  msgid "File size limit exceeded"
1872
  msgstr ""
1873
 
1874
- #: inc/class-job.php:2334
1875
- msgctxt "SIGPWR: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1876
  msgid "Power failure"
1877
  msgstr ""
1878
 
1879
- #: inc/class-job.php:2338
1880
- msgctxt "SIGSYS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for details"
 
 
1881
  msgid "Bad argument to routine"
1882
  msgstr ""
1883
 
1884
- #: inc/class-job.php:2345
1885
- #, php-format
1886
  msgid "Signal \"%1$s\" (%2$s) is sent to script!"
1887
  msgstr ""
1888
 
1889
- #: inc/class-job.php:2379 inc/class-job.php:2392
1890
- #, php-format
1891
  msgid "System: %s"
1892
  msgstr ""
1893
 
1894
- #: inc/class-job.php:2407
1895
- #, php-format
1896
  msgid "Exception caught in %1$s: %2$s"
1897
  msgstr ""
1898
 
@@ -1904,6 +1891,8 @@ msgstr ""
1904
  msgid "Check database tables"
1905
  msgstr ""
1906
 
 
 
1907
  #: inc/class-jobtype-dbcheck.php:17 inc/class-jobtype-dbdump.php:17
1908
  #: inc/class-jobtype-file.php:17 inc/class-jobtype-wpexp.php:17
1909
  #: inc/class-jobtype-wpplugin.php:17 inc/pro/class-wizard-job.php:19
@@ -1933,29 +1922,24 @@ msgid "Try to repair defect table"
1933
  msgstr ""
1934
 
1935
  #: inc/class-jobtype-dbcheck.php:79
1936
- #, php-format
1937
  msgid "%d. Trying to check database&#160;&hellip;"
1938
  msgstr ""
1939
 
1940
  #: inc/class-jobtype-dbcheck.php:111
1941
- #, php-format
1942
  msgid "Table %1$s is a view. Not checked."
1943
  msgstr ""
1944
 
1945
  #: inc/class-jobtype-dbcheck.php:116
1946
- #, php-format
1947
  msgid "Table %1$s is not a MyISAM/InnoDB table. Not checked."
1948
  msgstr ""
1949
 
1950
  #: inc/class-jobtype-dbcheck.php:124 inc/class-jobtype-dbcheck.php:127
1951
  #: inc/class-jobtype-dbcheck.php:129
1952
- #, php-format
1953
  msgid "Result of table check for %1$s is: %2$s"
1954
  msgstr ""
1955
 
1956
  #: inc/class-jobtype-dbcheck.php:135 inc/class-jobtype-dbcheck.php:137
1957
  #: inc/class-jobtype-dbcheck.php:139
1958
- #, php-format
1959
  msgid "Result of table repair for %1$s is: %2$s"
1960
  msgstr ""
1961
 
@@ -2004,12 +1988,10 @@ msgid "GZip"
2004
  msgstr ""
2005
 
2006
  #: inc/class-jobtype-dbdump.php:159 inc/pro/class-jobtype-dbdump.php:475
2007
- #, php-format
2008
  msgid "%d. Try to backup database&#160;&hellip;"
2009
  msgstr ""
2010
 
2011
  #: inc/class-jobtype-dbdump.php:173 inc/pro/class-jobtype-dbdump.php:495
2012
- #, php-format
2013
  msgid "Connected to database %1$s on %2$s"
2014
  msgstr ""
2015
 
@@ -2018,7 +2000,6 @@ msgid "No tables to backup."
2018
  msgstr ""
2019
 
2020
  #: inc/class-jobtype-dbdump.php:210 inc/pro/class-jobtype-dbdump.php:536
2021
- #, php-format
2022
  msgid "Backup database table \"%s\" with \"%s\" records"
2023
  msgstr ""
2024
 
@@ -2027,7 +2008,6 @@ msgid "MySQL backup file not created"
2027
  msgstr ""
2028
 
2029
  #: inc/class-jobtype-dbdump.php:254 inc/pro/class-jobtype-dbdump.php:763
2030
- #, php-format
2031
  msgid "Added database dump \"%1$s\" with %2$s to backup file list"
2032
  msgstr ""
2033
 
@@ -2052,117 +2032,115 @@ msgstr ""
2052
  msgid "Backup WordPress install folder"
2053
  msgstr ""
2054
 
2055
- #: inc/class-jobtype-file.php:86 inc/class-jobtype-file.php:123
2056
- #: inc/class-jobtype-file.php:160 inc/class-jobtype-file.php:197
2057
- #: inc/class-jobtype-file.php:234
2058
- #, php-format
2059
- msgid "Path as set by user (symlink?): %s"
2060
- msgstr ""
2061
-
2062
- #: inc/class-jobtype-file.php:89 inc/class-jobtype-file.php:126
2063
- #: inc/class-jobtype-file.php:163 inc/class-jobtype-file.php:200
2064
- #: inc/class-jobtype-file.php:237
2065
- msgid "Exclude:"
2066
- msgstr ""
2067
-
2068
- #: inc/class-jobtype-file.php:100 inc/class-jobtype-file.php:137
2069
- #: inc/class-jobtype-file.php:174 inc/class-jobtype-file.php:211
2070
- #: inc/class-jobtype-file.php:248
2071
- msgid "Excluded by .donotbackup file!"
2072
- msgstr ""
2073
-
2074
- #: inc/class-jobtype-file.php:112
2075
  msgid "Backup content folder"
2076
  msgstr ""
2077
 
2078
- #: inc/class-jobtype-file.php:149
2079
  msgid "Backup plugins"
2080
  msgstr ""
2081
 
2082
- #: inc/class-jobtype-file.php:186
2083
  msgid "Backup themes"
2084
  msgstr ""
2085
 
2086
- #: inc/class-jobtype-file.php:223 inc/pro/class-wizard-job.php:745
2087
  #: inc/pro/class-wizard-job.php:746
2088
  msgid "Backup uploads folder"
2089
  msgstr ""
2090
 
2091
- #: inc/class-jobtype-file.php:260
2092
  msgid "Extra folders to backup"
2093
  msgstr ""
2094
 
2095
- #: inc/class-jobtype-file.php:263
2096
- msgid "Separate folder names with a line-break or a comma. Folders must be set with their absolute path!"
 
 
2097
  msgstr ""
2098
 
2099
- #: inc/class-jobtype-file.php:268
2100
  msgid "Exclude from backup"
2101
  msgstr ""
2102
 
2103
- #: inc/class-jobtype-file.php:272
2104
  msgid "Thumbnails in uploads"
2105
  msgstr ""
2106
 
2107
- #: inc/class-jobtype-file.php:274
2108
  msgid "Don't backup thumbnails from the site's uploads folder."
2109
  msgstr ""
2110
 
2111
- #: inc/class-jobtype-file.php:278
2112
  msgid "Exclude files/folders from backup"
2113
  msgstr ""
2114
 
2115
- #: inc/class-jobtype-file.php:281
2116
- msgid "Separate file / folder name parts with a line-break or a comma. For example /logs/,.log,.tmp"
 
 
2117
  msgstr ""
2118
 
2119
- #: inc/class-jobtype-file.php:286
2120
  msgid "Special options"
2121
  msgstr ""
2122
 
2123
- #: inc/class-jobtype-file.php:290
2124
  msgid "Include special files"
2125
  msgstr ""
2126
 
2127
- #: inc/class-jobtype-file.php:292
2128
- msgid "Backup wp-config.php, robots.txt, nginx.conf, .htaccess, .htpasswd and favicon.ico from root if it is not included in backup."
 
 
2129
  msgstr ""
2130
 
2131
- #: inc/class-jobtype-file.php:296
2132
  msgid "Use one folder above as WP install folder"
2133
  msgstr ""
2134
 
2135
- #: inc/class-jobtype-file.php:299
2136
- msgid "Use one folder above as WordPress install folder! That can be helpful, if you would backup files and folder that are not in the WordPress installation folder. Or if you made a \"<a href=\"https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory\">Giving WordPress Its Own Directory</a>\" installation. Excludes must be configured again."
 
 
 
 
 
2137
  msgstr ""
2138
 
2139
- #: inc/class-jobtype-file.php:384
2140
- #, php-format
2141
  msgid "%d. Trying to make a list of folders to back up&#160;&hellip;"
2142
  msgstr ""
2143
 
2144
- #: inc/class-jobtype-file.php:502 inc/class-jobtype-file.php:506
2145
- #: inc/class-jobtype-file.php:511 inc/class-jobtype-file.php:515
2146
- #: inc/class-jobtype-file.php:519 inc/class-jobtype-file.php:523
2147
- #: inc/class-jobtype-file.php:527
2148
- #, php-format
2149
  msgid "Added \"%s\" to backup file list"
2150
  msgstr ""
2151
 
2152
- #: inc/class-jobtype-file.php:532
2153
  msgid "No files/folder for the backup."
2154
  msgstr ""
2155
 
2156
- #: inc/class-jobtype-file.php:534
2157
- #, php-format
2158
  msgid "%1$d folders to backup."
2159
  msgstr ""
2160
 
2161
- #: inc/class-jobtype-file.php:580
2162
- #, php-format
2163
  msgid "Folder \"%s\" is not readable!"
2164
  msgstr ""
2165
 
 
 
 
 
 
 
 
 
 
 
 
 
2166
  #: inc/class-jobtype-wpexp.php:13
2167
  msgid "XML export"
2168
  msgstr ""
@@ -2201,13 +2179,11 @@ msgid "BZip2"
2201
  msgstr ""
2202
 
2203
  #: inc/class-jobtype-wpexp.php:112
2204
- #, php-format
2205
  msgid "%d. Trying to create a WordPress export to XML file&#160;&hellip;"
2206
  msgstr ""
2207
 
2208
  #: inc/class-jobtype-wpexp.php:127
2209
- #, php-format
2210
- msgid "WP Export: Post type %s does not allow export."
2211
  msgstr ""
2212
 
2213
  #: inc/class-jobtype-wpexp.php:172 inc/class-jobtype-wpexp.php:186
@@ -2222,17 +2198,14 @@ msgid "Check WP Export file&#160;&hellip;"
2222
  msgstr ""
2223
 
2224
  #: inc/class-jobtype-wpexp.php:422
2225
- #, php-format
2226
  msgid "XML WARNING (%s): %s"
2227
  msgstr ""
2228
 
2229
  #: inc/class-jobtype-wpexp.php:425
2230
- #, php-format
2231
  msgid "XML RECOVERABLE (%s): %s"
2232
  msgstr ""
2233
 
2234
  #: inc/class-jobtype-wpexp.php:428
2235
- #, php-format
2236
  msgid "XML ERROR (%s): %s"
2237
  msgstr ""
2238
 
@@ -2241,7 +2214,8 @@ msgid "There was an error when reading this WXR file"
2241
  msgstr ""
2242
 
2243
  #: inc/class-jobtype-wpexp.php:444 inc/class-jobtype-wpexp.php:451
2244
- msgid "This does not appear to be a WXR file, missing/invalid WXR version number"
 
2245
  msgstr ""
2246
 
2247
  #: inc/class-jobtype-wpexp.php:460
@@ -2249,7 +2223,9 @@ msgid "WP Export file is a valid WXR file."
2249
  msgstr ""
2250
 
2251
  #: inc/class-jobtype-wpexp.php:462
2252
- msgid "WP Export file can not be checked, because no XML extension is loaded, to ensure the file verification."
 
 
2253
  msgstr ""
2254
 
2255
  #: inc/class-jobtype-wpexp.php:474 inc/pro/class-jobtype-dbdump.php:741
@@ -2261,7 +2237,6 @@ msgid "Compressing done."
2261
  msgstr ""
2262
 
2263
  #: inc/class-jobtype-wpexp.php:500
2264
- #, php-format
2265
  msgid "Added XML export \"%1$s\" with %2$s to backup file list."
2266
  msgstr ""
2267
 
@@ -2278,7 +2253,6 @@ msgid "Plugin list file name"
2278
  msgstr ""
2279
 
2280
  #: inc/class-jobtype-wpplugin.php:96
2281
- #, php-format
2282
  msgid "%d. Trying to generate a file with installed plugin names&#160;&hellip;"
2283
  msgstr ""
2284
 
@@ -2287,7 +2261,6 @@ msgid "All plugin information:"
2287
  msgstr ""
2288
 
2289
  #: inc/class-jobtype-wpplugin.php:126
2290
- #, php-format
2291
  msgid "from %s"
2292
  msgstr ""
2293
 
@@ -2304,7 +2277,6 @@ msgid "Can not open target file for writing."
2304
  msgstr ""
2305
 
2306
  #: inc/class-jobtype-wpplugin.php:148
2307
- #, php-format
2308
  msgid "Added plugin list file \"%1$s\" with %2$s to backup file list."
2309
  msgstr ""
2310
 
@@ -2325,12 +2297,10 @@ msgid "Setting of MySQLi connection timeout failed"
2325
  msgstr ""
2326
 
2327
  #: inc/class-mysqldump.php:110 inc/pro/class-jobtype-dbdump.php:843
2328
- #, php-format
2329
  msgid "Cannot connect to MySQL database %1$d: %2$s"
2330
  msgstr ""
2331
 
2332
  #: inc/class-mysqldump.php:117
2333
- #, php-format
2334
  msgctxt "Database Charset"
2335
  msgid "Cannot set DB charset to %s error: %s"
2336
  msgstr ""
@@ -2348,26 +2318,23 @@ msgstr ""
2348
  #: inc/pro/class-jobtype-dbdump.php:859 inc/pro/class-jobtype-dbdump.php:873
2349
  #: inc/pro/class-jobtype-dbdump.php:921 inc/pro/class-jobtype-dbdump.php:940
2350
  #: inc/pro/class-jobtype-dbdump.php:983
2351
- #, php-format
2352
  msgid "Database error %1$s for query %2$s"
2353
  msgstr ""
2354
 
2355
  #: inc/class-mysqldump.php:479
2356
- #, php-format
2357
  msgid "Start for table backup is not correctly set: %1$s"
2358
  msgstr ""
2359
 
2360
  #: inc/class-mysqldump.php:483
2361
- #, php-format
2362
  msgid "Length for table backup is not correctly set: %1$s"
2363
  msgstr ""
2364
 
2365
- #: inc/class-mysqldump.php:558
2366
  msgid "Error while writing file!"
2367
  msgstr ""
2368
 
2369
- #: inc/class-option.php:160 inc/class-page-editjob.php:97
2370
- #: inc/class-page-editjob.php:385
2371
  msgid "New Job"
2372
  msgstr ""
2373
 
@@ -2376,16 +2343,24 @@ msgid "Welcome to BackWPup Pro"
2376
  msgstr ""
2377
 
2378
  #: inc/class-page-about.php:370 inc/class-page-backwpup.php:75
2379
- msgid "BackWPups job wizards make planning and scheduling your backup jobs a breeze."
 
 
2380
  msgstr ""
2381
 
2382
  #: inc/class-page-about.php:371 inc/class-page-about.php:384
2383
- msgid "Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you dont want to save the backups on the same server. With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href=\"http://wordpress.org/plugins/adminer/\" target=\"_blank\">Adminer</a> to restore your database backup files."
 
 
 
 
 
2384
  msgstr ""
2385
 
2386
  #: inc/class-page-about.php:372
2387
- #, php-format
2388
- msgid "Ready to <a href=\"%1$s\">set up a backup job</a>? You can <a href=\"%2$s\">use the wizards</a> or plan your backup in expert mode."
 
2389
  msgstr ""
2390
 
2391
  #: inc/class-page-about.php:382
@@ -2393,7 +2368,9 @@ msgid "Welcome to BackWPup"
2393
  msgstr ""
2394
 
2395
  #: inc/class-page-about.php:385
2396
- msgid "Ready to set up a backup job? Use one of the wizards to plan what you want to save."
 
 
2397
  msgstr ""
2398
 
2399
  #: inc/class-page-about.php:398
@@ -2405,8 +2382,11 @@ msgid "Save your database regularly"
2405
  msgstr ""
2406
 
2407
  #: inc/class-page-about.php:402
2408
- #, php-format
2409
- msgid "With BackWPup you can schedule the database backup to run automatically. With a single backup file you can restore your database. You should <a href=\"%s\">set up a backup job</a>, so you will never forget it. There is also an option to repair and optimize the database after each backup."
 
 
 
2410
  msgstr ""
2411
 
2412
  #: inc/class-page-about.php:407 inc/class-page-about.php:411
@@ -2414,7 +2394,11 @@ msgid "WordPress XML Export"
2414
  msgstr ""
2415
 
2416
  #: inc/class-page-about.php:408
2417
- msgid "You can choose the built-in WordPress export format in addition or exclusive to save your data. This works in automated backups too of course. The advantage is: you can import these files into a blog with the regular WordPress importer."
 
 
 
 
2418
  msgstr ""
2419
 
2420
  #: inc/class-page-about.php:416
@@ -2426,8 +2410,10 @@ msgid "Save all files"
2426
  msgstr ""
2427
 
2428
  #: inc/class-page-about.php:420
2429
- #, php-format
2430
- msgid "You can backup all your attachments, also all system files, plugins and themes in a single file. You can <a href=\"%s\">create a job</a> to update a backup copy of your file system only when files are changed."
 
 
2431
  msgstr ""
2432
 
2433
  #: inc/class-page-about.php:425 inc/class-page-about.php:429
@@ -2435,7 +2421,9 @@ msgid "Security!"
2435
  msgstr ""
2436
 
2437
  #: inc/class-page-about.php:426
2438
- msgid "By default everything is encrypted: connections to external services, local files and access to directories."
 
 
2439
  msgstr ""
2440
 
2441
  #: inc/class-page-about.php:434 inc/class-page-about.php:437
@@ -2443,7 +2431,9 @@ msgid "Cloud Support"
2443
  msgstr ""
2444
 
2445
  #: inc/class-page-about.php:438
2446
- msgid "BackWPup supports multiple cloud services in parallel. This ensures backups are redundant."
 
 
2447
  msgstr ""
2448
 
2449
  #: inc/class-page-about.php:444
@@ -2629,9 +2619,8 @@ msgid "?"
2629
  msgstr ""
2630
 
2631
  #: inc/class-page-backups.php:361 inc/class-page-backwpup.php:321
2632
- #: inc/class-page-backwpup.php:376 inc/class-page-jobs.php:318
2633
  #: inc/class-page-logs.php:164
2634
- #, php-format
2635
  msgid "%1$s at %2$s"
2636
  msgstr ""
2637
 
@@ -2645,12 +2634,10 @@ msgid "Backup Files"
2645
  msgstr ""
2646
 
2647
  #: inc/class-page-backups.php:489
2648
- #, php-format
2649
  msgid "%s &rsaquo; Manage Backup Archives"
2650
  msgstr ""
2651
 
2652
  #: inc/class-page-backwpup.php:67
2653
- #, php-format
2654
  msgid "%s &rsaquo; Dashboard"
2655
  msgstr ""
2656
 
@@ -2660,7 +2647,10 @@ msgid "Planning backups"
2660
  msgstr ""
2661
 
2662
  #: inc/class-page-backwpup.php:75 inc/class-page-backwpup.php:84
2663
- msgid "Use your backup archives to save your entire WordPress installation including <code>/wp-content/</code>. Push them to an external storage service if you dont want to save the backups on the same server."
 
 
 
2664
  msgstr ""
2665
 
2666
  #: inc/class-page-backwpup.php:76 inc/class-page-backwpup.php:85
@@ -2669,7 +2659,9 @@ msgid "Restoring backups"
2669
  msgstr ""
2670
 
2671
  #: inc/class-page-backwpup.php:77 inc/class-page-backwpup.php:86
2672
- msgid "With a single backup archive you are able to restore an installation. Use a tool like phpMyAdmin or a plugin like <a href=\"http://wordpress.org/plugins/adminer/\" target=\"_blank\">Adminer</a> to restore your database backup files."
 
 
2673
  msgstr ""
2674
 
2675
  #: inc/class-page-backwpup.php:78 inc/class-page-backwpup.php:87
@@ -2678,20 +2670,24 @@ msgid "Ready to set up a backup job?"
2678
  msgstr ""
2679
 
2680
  #: inc/class-page-backwpup.php:79
2681
- #, php-format
2682
- msgid "Use one of the wizards to plan a backup, or use <a href=\"%s\">expert mode</a> for full control over all options."
 
2683
  msgstr ""
2684
 
2685
  #: inc/class-page-backwpup.php:79 inc/class-page-backwpup.php:89
2686
- msgid "<strong>Please note: You are solely responsible for the security of your data; the authors of this plugin are not.</strong>"
 
 
2687
  msgstr ""
2688
 
2689
  #: inc/class-page-backwpup.php:84
2690
- msgid "Use the short links in the <strong>First steps</strong> box to plan and schedule backup jobs."
 
 
2691
  msgstr ""
2692
 
2693
  #: inc/class-page-backwpup.php:88
2694
- #, php-format
2695
  msgid "<a href=\"%s\">Add a new backup job</a> and plan what you want to save."
2696
  msgstr ""
2697
 
@@ -2724,7 +2720,8 @@ msgid "One click backup"
2724
  msgstr ""
2725
 
2726
  #: inc/class-page-backwpup.php:117
2727
- msgid "Generate a database backup of WordPress tables and download it right away!"
 
2728
  msgstr ""
2729
 
2730
  #: inc/class-page-backwpup.php:117
@@ -2741,12 +2738,13 @@ msgid "https://backwpup.com/feed/"
2741
  msgstr ""
2742
 
2743
  #: inc/class-page-backwpup.php:130
2744
- #, php-format
2745
  msgid "<strong>RSS Error</strong>: %s"
2746
  msgstr ""
2747
 
2748
  #: inc/class-page-backwpup.php:132
2749
- msgid "An error has occurred, which probably means the feed is down. Try again later."
 
 
2750
  msgstr ""
2751
 
2752
  #: inc/class-page-backwpup.php:146
@@ -2789,7 +2787,7 @@ msgstr ""
2789
 
2790
  #: inc/class-page-backwpup.php:257
2791
  msgctxt "Pro teaser box"
2792
- msgid "First-class <strong>dedicated support</strong> at MarketPress Helpdesk."
2793
  msgstr ""
2794
 
2795
  #: inc/class-page-backwpup.php:258
@@ -2804,7 +2802,7 @@ msgstr ""
2804
 
2805
  #: inc/class-page-backwpup.php:260
2806
  msgctxt "Pro teaser box, link text"
2807
- msgid "And more"
2808
  msgstr ""
2809
 
2810
  #: inc/class-page-backwpup.php:262
@@ -2827,7 +2825,6 @@ msgid "Job"
2827
  msgstr ""
2828
 
2829
  #: inc/class-page-backwpup.php:307
2830
- #, php-format
2831
  msgid "working since %d seconds"
2832
  msgstr ""
2833
 
@@ -2847,31 +2844,31 @@ msgstr ""
2847
  msgid "Result"
2848
  msgstr ""
2849
 
2850
- #: inc/class-page-backwpup.php:381
2851
- #, php-format
 
 
 
2852
  msgid "%d ERROR"
2853
  msgid_plural "%d ERRORS"
2854
  msgstr[0] ""
2855
  msgstr[1] ""
2856
 
2857
- #: inc/class-page-backwpup.php:384
2858
- #, php-format
2859
  msgid "%d WARNING"
2860
  msgid_plural "%d WARNINGS"
2861
  msgstr[0] ""
2862
  msgstr[1] ""
2863
 
2864
- #: inc/class-page-backwpup.php:387
2865
  msgid "OK"
2866
  msgstr ""
2867
 
2868
  #: inc/class-page-editjob.php:98
2869
- #, php-format
2870
  msgid "Job with ID %d"
2871
  msgstr ""
2872
 
2873
  #: inc/class-page-editjob.php:222
2874
- #, php-format
2875
  msgid "Changes for job <i>%s</i> saved."
2876
  msgstr ""
2877
 
@@ -2883,423 +2880,429 @@ msgstr ""
2883
  msgid "Run now"
2884
  msgstr ""
2885
 
2886
- #: inc/class-page-editjob.php:327
2887
- #, php-format
2888
  msgid "%1$s &rsaquo; Job: %2$s"
2889
  msgstr ""
2890
 
2891
- #: inc/class-page-editjob.php:330 inc/class-page-settings.php:115
2892
  msgid "General"
2893
  msgstr ""
2894
 
2895
- #: inc/class-page-editjob.php:330
2896
  msgid "Schedule"
2897
  msgstr ""
2898
 
2899
- #: inc/class-page-editjob.php:345
2900
- #, php-format
2901
  msgid "To: %s"
2902
  msgstr ""
2903
 
2904
- #: inc/class-page-editjob.php:380 inc/class-page-editjob.php:385
2905
  #: inc/class-page-jobs.php:125
2906
  msgid "Job Name"
2907
  msgstr ""
2908
 
2909
- #: inc/class-page-editjob.php:383
2910
  msgid "Please name this job."
2911
  msgstr ""
2912
 
2913
- #: inc/class-page-editjob.php:390
2914
  msgid "Job Tasks"
2915
  msgstr ""
2916
 
2917
- #: inc/class-page-editjob.php:393 inc/pro/class-wizard-job.php:254
2918
  msgid "This job is a&#160;&hellip;"
2919
  msgstr ""
2920
 
2921
- #: inc/class-page-editjob.php:396 inc/pro/class-wizard-job.php:257
2922
  msgid "Job tasks"
2923
  msgstr ""
2924
 
2925
- #: inc/class-page-editjob.php:414
2926
  msgid "Backup File Creation"
2927
  msgstr ""
2928
 
2929
- #: inc/class-page-editjob.php:419 inc/class-page-editjob.php:422
2930
  #: inc/pro/class-wizard-job.php:402 inc/pro/class-wizard-job.php:405
2931
  msgid "Backup type"
2932
  msgstr ""
2933
 
2934
- #: inc/class-page-editjob.php:425
2935
  msgid "Synchronize file by file to destination"
2936
  msgstr ""
2937
 
2938
- #: inc/class-page-editjob.php:430 inc/pro/class-wizard-job.php:413
2939
  msgid "Create a backup archive"
2940
  msgstr ""
2941
 
2942
- #: inc/class-page-editjob.php:438
2943
  msgid "Archive name"
2944
  msgstr ""
2945
 
2946
- #: inc/class-page-editjob.php:448
 
 
 
 
 
 
2947
  msgid "Replacement patterns:"
2948
  msgstr ""
2949
 
2950
- #: inc/class-page-editjob.php:449
2951
- #, php-format
2952
  msgid "%d = Two digit day of the month, with leading zeros"
2953
  msgstr ""
2954
 
2955
- #: inc/class-page-editjob.php:450
2956
  msgid "%j = Day of the month, without leading zeros"
2957
  msgstr ""
2958
 
2959
- #: inc/class-page-editjob.php:451
2960
  msgid "%m = Day of the month, with leading zeros"
2961
  msgstr ""
2962
 
2963
- #: inc/class-page-editjob.php:452
2964
  msgid "%n = Representation of the month (without leading zeros)"
2965
  msgstr ""
2966
 
2967
- #: inc/class-page-editjob.php:453
2968
  msgid "%Y = Four digit representation for the year"
2969
  msgstr ""
2970
 
2971
- #: inc/class-page-editjob.php:454
2972
  msgid "%y = Two digit representation of the year"
2973
  msgstr ""
2974
 
2975
- #: inc/class-page-editjob.php:455
2976
  msgid "%a = Lowercase ante meridiem (am) and post meridiem (pm)"
2977
  msgstr ""
2978
 
2979
- #: inc/class-page-editjob.php:456
2980
  msgid "%A = Uppercase ante meridiem (AM) and post meridiem (PM)"
2981
  msgstr ""
2982
 
2983
- #: inc/class-page-editjob.php:457
2984
  msgid "%B = Swatch Internet Time"
2985
  msgstr ""
2986
 
2987
- #: inc/class-page-editjob.php:458
2988
  msgid "%g = Hour in 12-hour format, without leading zeros"
2989
  msgstr ""
2990
 
2991
- #: inc/class-page-editjob.php:459
2992
  msgid "%G = Hour in 24-hour format, without leading zeros"
2993
  msgstr ""
2994
 
2995
- #: inc/class-page-editjob.php:460
2996
  msgid "%h = Hour in 12-hour format, with leading zeros"
2997
  msgstr ""
2998
 
2999
- #: inc/class-page-editjob.php:461
3000
  msgid "%H = Hour in 24-hour format, with leading zeros"
3001
  msgstr ""
3002
 
3003
- #: inc/class-page-editjob.php:462
3004
  msgid "%i = Two digit representation of the minute"
3005
  msgstr ""
3006
 
3007
- #: inc/class-page-editjob.php:463
3008
- #, php-format
3009
  msgid "%s = Two digit representation of the second"
3010
  msgstr ""
3011
 
3012
- #: inc/class-page-editjob.php:469 inc/class-page-editjob.php:472
3013
  msgid "Archive Format"
3014
  msgstr ""
3015
 
3016
- #: inc/class-page-editjob.php:475 inc/class-page-editjob.php:477
3017
  #: inc/pro/class-wizard-job.php:426 inc/pro/class-wizard-job.php:429
3018
  msgid "Zip"
3019
  msgstr ""
3020
 
3021
- #: inc/class-page-editjob.php:478
3022
- #, php-format
3023
  msgid "Disabled due to missing %s PHP class."
3024
  msgstr ""
3025
 
3026
- #: inc/class-page-editjob.php:480 inc/pro/class-wizard-job.php:433
3027
  msgid "Tar"
3028
  msgstr ""
3029
 
3030
- #: inc/class-page-editjob.php:482 inc/class-page-editjob.php:484
3031
  #: inc/pro/class-wizard-job.php:437 inc/pro/class-wizard-job.php:440
3032
  msgid "Tar GZip"
3033
  msgstr ""
3034
 
3035
- #: inc/class-page-editjob.php:485 inc/class-page-editjob.php:491
3036
- #, php-format
3037
  msgid "Disabled due to missing %s PHP function."
3038
  msgstr ""
3039
 
3040
- #: inc/class-page-editjob.php:488 inc/class-page-editjob.php:490
3041
  #: inc/pro/class-wizard-job.php:444 inc/pro/class-wizard-job.php:447
3042
  msgid "Tar BZip2"
3043
  msgstr ""
3044
 
3045
- #: inc/class-page-editjob.php:498
3046
  msgid "Job Destination"
3047
  msgstr ""
3048
 
3049
- #: inc/class-page-editjob.php:502 inc/class-page-editjob.php:505
3050
  msgid "Where should your backup file be stored?"
3051
  msgstr ""
3052
 
3053
- #: inc/class-page-editjob.php:523
3054
  msgid "Log Files"
3055
  msgstr ""
3056
 
3057
- #: inc/class-page-editjob.php:527
3058
  msgid "Send log to email address"
3059
  msgstr ""
3060
 
3061
- #: inc/class-page-editjob.php:530
3062
- msgid "Leave empty to not have log sent. Or separate with , for more than one receiver."
 
 
3063
  msgstr ""
3064
 
3065
- #: inc/class-page-editjob.php:534
3066
  msgid "Email FROM field"
3067
  msgstr ""
3068
 
3069
- #: inc/class-page-editjob.php:536
3070
  msgid "Your Name &lt;mail@domain.tld&gt;"
3071
  msgstr ""
3072
 
3073
- #: inc/class-page-editjob.php:540
3074
  msgid "Errors only"
3075
  msgstr ""
3076
 
3077
- #: inc/class-page-editjob.php:545
3078
  msgid "Send email with log only when errors occur during job execution."
3079
  msgstr ""
3080
 
3081
- #: inc/class-page-editjob.php:556
3082
  msgid "Job Schedule"
3083
  msgstr ""
3084
 
3085
- #: inc/class-page-editjob.php:560 inc/class-page-editjob.php:563
3086
  msgid "Start job"
3087
  msgstr ""
3088
 
3089
- #: inc/class-page-editjob.php:567
3090
  msgid "manually only"
3091
  msgstr ""
3092
 
3093
- #: inc/class-page-editjob.php:571
3094
  msgid "with WordPress cron"
3095
  msgstr ""
3096
 
3097
- #: inc/class-page-editjob.php:580
3098
- msgid "with <a href=\"https://www.easycron.com?ref=36673\" title=\"Affiliate Link!\">EasyCron.com</a>"
 
 
3099
  msgstr ""
3100
 
3101
- #: inc/class-page-editjob.php:582
3102
- #, php-format
3103
  msgid "First setup <a href=\"%s\">API Key</a>."
3104
  msgstr ""
3105
 
3106
- #: inc/class-page-editjob.php:591
3107
  msgid "with a link"
3108
  msgstr ""
3109
 
3110
- #: inc/class-page-editjob.php:592
3111
- msgid "Copy the link for an external start. This option has to be activated to make the link work."
 
 
3112
  msgstr ""
3113
 
3114
- #: inc/class-page-editjob.php:599
3115
  msgid "Start job with CLI"
3116
  msgstr ""
3117
 
3118
- #: inc/class-page-editjob.php:602
3119
- msgid "Use <a href=\"http://wp-cli.org/\">WP-CLI</a> to run jobs from commandline."
 
3120
  msgstr ""
3121
 
3122
- #: inc/class-page-editjob.php:607
3123
  msgid "Schedule execution time"
3124
  msgstr ""
3125
 
3126
- #: inc/class-page-editjob.php:611 inc/class-page-editjob.php:614
3127
  msgid "Scheduler type"
3128
  msgstr ""
3129
 
3130
- #: inc/class-page-editjob.php:618
3131
  msgid "basic"
3132
  msgstr ""
3133
 
3134
- #: inc/class-page-editjob.php:622
3135
  msgid "advanced"
3136
  msgstr ""
3137
 
3138
- #: inc/class-page-editjob.php:651 inc/class-page-editjob.php:719
3139
  #: inc/pro/class-wizard-job.php:320
3140
  msgid "Scheduler"
3141
  msgstr ""
3142
 
3143
- #: inc/class-page-editjob.php:656 inc/class-page-jobs.php:126
3144
  #: inc/class-page-logs.php:139 inc/pro/class-wizard-job.php:324
3145
  msgid "Type"
3146
  msgstr ""
3147
 
3148
- #: inc/class-page-editjob.php:661 inc/pro/class-wizard-job.php:330
3149
  msgid "Hour"
3150
  msgstr ""
3151
 
3152
- #: inc/class-page-editjob.php:664 inc/pro/class-wizard-job.php:333
3153
  msgid "Minute"
3154
  msgstr ""
3155
 
3156
- #: inc/class-page-editjob.php:668 inc/pro/class-wizard-job.php:337
3157
  msgid "monthly"
3158
  msgstr ""
3159
 
3160
- #: inc/class-page-editjob.php:670 inc/pro/class-wizard-job.php:339
3161
  msgid "on"
3162
  msgstr ""
3163
 
3164
- #: inc/class-page-editjob.php:680 inc/pro/class-wizard-job.php:349
3165
  msgid "weekly"
3166
  msgstr ""
3167
 
3168
- #: inc/class-page-editjob.php:682 inc/class-page-editjob.php:789
3169
  #: inc/pro/class-wizard-job.php:351
3170
  msgid "Sunday"
3171
  msgstr ""
3172
 
3173
- #: inc/class-page-editjob.php:683 inc/class-page-editjob.php:790
3174
  #: inc/pro/class-wizard-job.php:352
3175
  msgid "Monday"
3176
  msgstr ""
3177
 
3178
- #: inc/class-page-editjob.php:684 inc/class-page-editjob.php:791
3179
  #: inc/pro/class-wizard-job.php:353
3180
  msgid "Tuesday"
3181
  msgstr ""
3182
 
3183
- #: inc/class-page-editjob.php:685 inc/class-page-editjob.php:792
3184
  #: inc/pro/class-wizard-job.php:354
3185
  msgid "Wednesday"
3186
  msgstr ""
3187
 
3188
- #: inc/class-page-editjob.php:686 inc/class-page-editjob.php:793
3189
  #: inc/pro/class-wizard-job.php:355
3190
  msgid "Thursday"
3191
  msgstr ""
3192
 
3193
- #: inc/class-page-editjob.php:687 inc/class-page-editjob.php:794
3194
  #: inc/pro/class-wizard-job.php:356
3195
  msgid "Friday"
3196
  msgstr ""
3197
 
3198
- #: inc/class-page-editjob.php:688 inc/class-page-editjob.php:795
3199
  #: inc/pro/class-wizard-job.php:357
3200
  msgid "Saturday"
3201
  msgstr ""
3202
 
3203
- #: inc/class-page-editjob.php:698 inc/pro/class-wizard-job.php:367
3204
  msgid "daily"
3205
  msgstr ""
3206
 
3207
- #: inc/class-page-editjob.php:708 inc/pro/class-wizard-job.php:377
3208
  msgid "hourly"
3209
  msgstr ""
3210
 
3211
- #: inc/class-page-editjob.php:722
3212
  msgid "Minutes:"
3213
  msgstr ""
3214
 
3215
- #: inc/class-page-editjob.php:724 inc/class-page-editjob.php:737
3216
- #: inc/class-page-editjob.php:749 inc/class-page-editjob.php:763
3217
- #: inc/class-page-editjob.php:785
3218
  msgid "Any (*)"
3219
  msgstr ""
3220
 
3221
- #: inc/class-page-editjob.php:734
3222
  msgid "Hours:"
3223
  msgstr ""
3224
 
3225
- #: inc/class-page-editjob.php:747
3226
  msgid "Day of Month:"
3227
  msgstr ""
3228
 
3229
- #: inc/class-page-editjob.php:761
3230
  msgid "Month:"
3231
  msgstr ""
3232
 
3233
- #: inc/class-page-editjob.php:767
3234
  msgid "January"
3235
  msgstr ""
3236
 
3237
- #: inc/class-page-editjob.php:768
3238
  msgid "February"
3239
  msgstr ""
3240
 
3241
- #: inc/class-page-editjob.php:769
3242
  msgid "March"
3243
  msgstr ""
3244
 
3245
- #: inc/class-page-editjob.php:770
3246
  msgid "April"
3247
  msgstr ""
3248
 
3249
- #: inc/class-page-editjob.php:771
3250
  msgid "May"
3251
  msgstr ""
3252
 
3253
- #: inc/class-page-editjob.php:772
3254
  msgid "June"
3255
  msgstr ""
3256
 
3257
- #: inc/class-page-editjob.php:773
3258
  msgid "July"
3259
  msgstr ""
3260
 
3261
- #: inc/class-page-editjob.php:774
3262
  msgid "August"
3263
  msgstr ""
3264
 
3265
- #: inc/class-page-editjob.php:775
3266
  msgid "September"
3267
  msgstr ""
3268
 
3269
- #: inc/class-page-editjob.php:776
3270
  msgid "October"
3271
  msgstr ""
3272
 
3273
- #: inc/class-page-editjob.php:777
3274
  msgid "November"
3275
  msgstr ""
3276
 
3277
- #: inc/class-page-editjob.php:778
3278
  msgid "December"
3279
  msgstr ""
3280
 
3281
- #: inc/class-page-editjob.php:783
3282
  msgid "Day of Week:"
3283
  msgstr ""
3284
 
3285
- #: inc/class-page-editjob.php:819
3286
  msgid "Save changes"
3287
  msgstr ""
3288
 
3289
- #: inc/class-page-editjob.php:906
3290
- msgid "Working as <a href=\"http://wikipedia.org/wiki/Cron\">Cron</a> schedule:"
 
3291
  msgstr ""
3292
 
3293
- #: inc/class-page-editjob.php:915
3294
- #, php-format
3295
  msgid "ATTENTION: Job runs every %d minutes!"
3296
  msgstr ""
3297
 
3298
- #: inc/class-page-editjob.php:919
3299
  msgid "ATTENTION: Can't calculate cron!"
3300
  msgstr ""
3301
 
3302
- #: inc/class-page-editjob.php:922
3303
  msgid "Next runtime:"
3304
  msgstr ""
3305
 
@@ -3321,7 +3324,6 @@ msgid "Last Run"
3321
  msgstr ""
3322
 
3323
  #: inc/class-page-jobs.php:172 inc/class-page-logs.php:200
3324
- #, php-format
3325
  msgid "Job ID: %d"
3326
  msgstr ""
3327
 
@@ -3342,22 +3344,18 @@ msgid "Not needed or set"
3342
  msgstr ""
3343
 
3344
  #: inc/class-page-jobs.php:273
3345
- #, php-format
3346
  msgid "Running for: %s seconds"
3347
  msgstr ""
3348
 
3349
  #: inc/class-page-jobs.php:280 inc/class-page-jobs.php:289
3350
- #, php-format
3351
  msgid "Cron: %s"
3352
  msgstr ""
3353
 
3354
  #: inc/class-page-jobs.php:280
3355
- #, php-format
3356
  msgid "%1$s at %2$s by WP-Cron"
3357
  msgstr ""
3358
 
3359
  #: inc/class-page-jobs.php:289
3360
- #, php-format
3361
  msgid "%1$s at %2$s by EasyCron"
3362
  msgstr ""
3363
 
@@ -3370,7 +3368,6 @@ msgid "Inactive"
3370
  msgstr ""
3371
 
3372
  #: inc/class-page-jobs.php:320
3373
- #, php-format
3374
  msgid "Runtime: %d seconds"
3375
  msgstr ""
3376
 
@@ -3391,22 +3388,20 @@ msgid "Copy of"
3391
  msgstr ""
3392
 
3393
  #: inc/class-page-jobs.php:431
3394
- #, php-format
3395
  msgid "The job \"%s\" destination \"%s\" is not configured properly"
3396
  msgstr ""
3397
 
3398
  #: inc/class-page-jobs.php:436
3399
- #, php-format
3400
  msgid "The job \"%s\" needs properly configured destinations to run!"
3401
  msgstr ""
3402
 
3403
  #: inc/class-page-jobs.php:454
3404
- #, php-format
3405
- msgid "Job \"%s\" has started, but not responded for 10 seconds. Please check <a href=\"%s\">information</a>."
 
3406
  msgstr ""
3407
 
3408
  #: inc/class-page-jobs.php:459
3409
- #, php-format
3410
  msgid "Job \"%s\" started."
3411
  msgstr ""
3412
 
@@ -3415,12 +3410,10 @@ msgid "Job will be terminated."
3415
  msgstr ""
3416
 
3417
  #: inc/class-page-jobs.php:584
3418
- #, php-format
3419
  msgid "%s &rsaquo; Jobs"
3420
  msgstr ""
3421
 
3422
  #: inc/class-page-jobs.php:604
3423
- #, php-format
3424
  msgid "Job currently running: %s"
3425
  msgstr ""
3426
 
@@ -3453,8 +3446,9 @@ msgid "Job completed"
3453
  msgstr ""
3454
 
3455
  #: inc/class-page-jobs.php:786
3456
- #, php-format
3457
- msgid "Job has done with warnings in %s seconds. Please resolve them for correct execution."
 
3458
  msgstr ""
3459
 
3460
  #: inc/class-page-logs.php:113
@@ -3474,14 +3468,12 @@ msgid "View"
3474
  msgstr ""
3475
 
3476
  #: inc/class-page-logs.php:222
3477
- #, php-format
3478
  msgid "1 ERROR"
3479
  msgid_plural "%d ERRORS"
3480
  msgstr[0] ""
3481
  msgstr[1] ""
3482
 
3483
  #: inc/class-page-logs.php:225
3484
- #, php-format
3485
  msgid "1 WARNING"
3486
  msgid_plural "%d WARNINGS"
3487
  msgstr[0] ""
@@ -3496,7 +3488,6 @@ msgid "Log only"
3496
  msgstr ""
3497
 
3498
  #: inc/class-page-logs.php:391
3499
- #, php-format
3500
  msgid "%s &rsaquo; Logs"
3501
  msgstr ""
3502
 
@@ -3504,451 +3495,477 @@ msgstr ""
3504
  msgid "Logfile not found!"
3505
  msgstr ""
3506
 
3507
- #: inc/class-page-settings.php:60
3508
  msgid "Settings reset to default"
3509
  msgstr ""
3510
 
3511
- #: inc/class-page-settings.php:102
3512
  msgid "Settings saved"
3513
  msgstr ""
3514
 
3515
- #: inc/class-page-settings.php:113
3516
- #, php-format
3517
  msgid "%s &rsaquo; Settings"
3518
  msgstr ""
3519
 
3520
- #: inc/class-page-settings.php:115
3521
  msgid "Network"
3522
  msgstr ""
3523
 
3524
- #: inc/class-page-settings.php:115
3525
  msgid "API Keys"
3526
  msgstr ""
3527
 
3528
- #: inc/class-page-settings.php:115
3529
  msgid "Information"
3530
  msgstr ""
3531
 
3532
- #: inc/class-page-settings.php:133
3533
  msgid "Display Settings"
3534
  msgstr ""
3535
 
3536
- #: inc/class-page-settings.php:134
3537
  msgid "Do you want to see BackWPup in the WordPress admin bar?"
3538
  msgstr ""
3539
 
3540
- #: inc/class-page-settings.php:137
3541
  msgid "Admin bar"
3542
  msgstr ""
3543
 
3544
- #: inc/class-page-settings.php:140
3545
  msgid "Admin Bar"
3546
  msgstr ""
3547
 
3548
- #: inc/class-page-settings.php:143
3549
  msgid "Show BackWPup links in admin bar."
3550
  msgstr ""
3551
 
3552
- #: inc/class-page-settings.php:149 inc/class-page-settings.php:152
3553
  msgid "Folder sizes"
3554
  msgstr ""
3555
 
3556
- #: inc/class-page-settings.php:155
3557
- msgid "Display folder sizes in the files tab when editing a job. (Might increase loading time of files tab.)"
 
 
3558
  msgstr ""
3559
 
3560
- #: inc/class-page-settings.php:161
3561
  msgid "Security"
3562
  msgstr ""
3563
 
3564
- #: inc/class-page-settings.php:162
3565
  msgid "Security option for BackWPup"
3566
  msgstr ""
3567
 
3568
- #: inc/class-page-settings.php:165 inc/class-page-settings.php:168
3569
  msgid "Protect folders"
3570
  msgstr ""
3571
 
3572
- #: inc/class-page-settings.php:171
3573
- msgid "Protect BackWPup folders ( Temp, Log and Backups ) with <code>.htaccess</code> and <code>index.php</code>"
 
 
3574
  msgstr ""
3575
 
3576
- #: inc/class-page-settings.php:184
3577
- msgid "Every time BackWPup runs a backup job, a log file is being generated. Choose where to store your log files and how many of them."
 
 
3578
  msgstr ""
3579
 
3580
- #: inc/class-page-settings.php:187
3581
  msgid "Log file folder"
3582
  msgstr ""
3583
 
3584
- #: inc/class-page-settings.php:190
3585
- #, php-format
3586
  msgid "You can use absolute or relative path! Relative path is relative to %s."
3587
  msgstr ""
3588
 
3589
- #: inc/class-page-settings.php:194
3590
  msgid "Maximum log files"
3591
  msgstr ""
3592
 
3593
- #: inc/class-page-settings.php:197
3594
  msgid "Maximum log files in folder."
3595
  msgstr ""
3596
 
3597
- #: inc/class-page-settings.php:201 inc/class-page-settings.php:204
3598
  msgid "Compression"
3599
  msgstr ""
3600
 
3601
- #: inc/class-page-settings.php:207
3602
  msgid "Compress log files with GZip."
3603
  msgstr ""
3604
 
3605
- #: inc/class-page-settings.php:213 inc/class-page-settings.php:216
3606
  msgid "Logging Level"
3607
  msgstr ""
3608
 
3609
- #: inc/class-page-settings.php:219
3610
  msgid "Normal (translated)"
3611
  msgstr ""
3612
 
3613
- #: inc/class-page-settings.php:220
3614
  msgid "Normal (not translated)"
3615
  msgstr ""
3616
 
3617
- #: inc/class-page-settings.php:221
3618
  msgid "Debug (translated)"
3619
  msgstr ""
3620
 
3621
- #: inc/class-page-settings.php:222
3622
  msgid "Debug (not translated)"
3623
  msgstr ""
3624
 
3625
- #: inc/class-page-settings.php:225
3626
- msgid "Debug log has much more informations than normal logs. It is for support and should be handled carefully. For support is the best to use a not translated log file. Usage of not translated logs can reduce the PHP memory usage too."
 
 
 
3627
  msgstr ""
3628
 
3629
- #: inc/class-page-settings.php:234
3630
  msgid "There are a couple of general options for backup jobs. Set them here."
3631
  msgstr ""
3632
 
3633
- #: inc/class-page-settings.php:237
3634
  msgid "Maximum number of retries for job steps"
3635
  msgstr ""
3636
 
3637
- #: inc/class-page-settings.php:243
3638
  msgid "Maximum script execution time"
3639
  msgstr ""
3640
 
3641
- #: inc/class-page-settings.php:246
3642
  msgid "Maximum PHP Script execution time"
3643
  msgstr ""
3644
 
3645
- #: inc/class-page-settings.php:249
3646
  msgid "seconds."
3647
  msgstr ""
3648
 
3649
- #: inc/class-page-settings.php:250
3650
- msgid "Job will restart before hitting maximum execution time. Restarts will be disabled on CLI usage. If <code>ALTERNATE_WP_CRON</code> has been defined, WordPress Cron will be used for restarts, so it can take a while. 0 means no maximum."
 
 
 
 
3651
  msgstr ""
3652
 
3653
- #: inc/class-page-settings.php:257
3654
  msgid "Key to start jobs externally with an URL"
3655
  msgstr ""
3656
 
3657
- #: inc/class-page-settings.php:261
3658
  msgid "Will be used to protect job starts from unauthorized person."
3659
  msgstr ""
3660
 
3661
- #: inc/class-page-settings.php:265 inc/class-page-settings.php:268
3662
  msgid "Reduce server load"
3663
  msgstr ""
3664
 
3665
- #: inc/class-page-settings.php:271
3666
  msgid "disabled"
3667
  msgstr ""
3668
 
3669
- #: inc/class-page-settings.php:272
3670
  msgid "minimum"
3671
  msgstr ""
3672
 
3673
- #: inc/class-page-settings.php:273
3674
  msgid "medium"
3675
  msgstr ""
3676
 
3677
- #: inc/class-page-settings.php:274
3678
  msgid "maximum"
3679
  msgstr ""
3680
 
3681
- #: inc/class-page-settings.php:277
3682
- msgid "This adds short pauses to the process. Can be used to reduce the CPU load."
 
3683
  msgstr ""
3684
 
3685
- #: inc/class-page-settings.php:282
3686
  msgid "Empty output on working"
3687
  msgstr ""
3688
 
3689
- #: inc/class-page-settings.php:285 inc/class-page-settings.php:288
3690
  msgid "Enable an empty Output on backup working."
3691
  msgstr ""
3692
 
3693
- #: inc/class-page-settings.php:290
3694
- msgid "This do an empty output on job working. This can help in some situations or can brake the working. You must test it."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3695
  msgstr ""
3696
 
3697
- #: inc/class-page-settings.php:300
3698
- #, php-format
3699
  msgid "Authentication for <code>%s</code>"
3700
  msgstr ""
3701
 
3702
- #: inc/class-page-settings.php:301
3703
- msgid "If you protected your blog with HTTP basic authentication (.htaccess), or you use a Plugin to secure wp-cron.php, then use the authentication methods below."
 
 
 
3704
  msgstr ""
3705
 
3706
- #: inc/class-page-settings.php:307 inc/class-page-settings.php:310
3707
  msgid "Authentication method"
3708
  msgstr ""
3709
 
3710
- #: inc/class-page-settings.php:314
3711
  msgid "Basic auth"
3712
  msgstr ""
3713
 
3714
- #: inc/class-page-settings.php:315
3715
  msgid "WordPress User"
3716
  msgstr ""
3717
 
3718
- #: inc/class-page-settings.php:316
3719
  msgid "Query argument"
3720
  msgstr ""
3721
 
3722
- #: inc/class-page-settings.php:323
3723
  msgid "Basic Auth Username:"
3724
  msgstr ""
3725
 
3726
- #: inc/class-page-settings.php:329
3727
  msgid "Basic Auth Password:"
3728
  msgstr ""
3729
 
3730
- #: inc/class-page-settings.php:334 inc/class-page-settings.php:337
3731
  msgid "Select WordPress User"
3732
  msgstr ""
3733
 
3734
- #: inc/class-page-settings.php:353
3735
  msgid "Query arg key=value:"
3736
  msgstr ""
3737
 
3738
- #: inc/class-page-settings.php:372 inc/class-page-settings.php:373
3739
  msgid "Setting"
3740
  msgstr ""
3741
 
3742
- #: inc/class-page-settings.php:372 inc/class-page-settings.php:373
3743
  msgid "Value"
3744
  msgstr ""
3745
 
3746
- #: inc/class-page-settings.php:374
3747
  msgid "WordPress version"
3748
  msgstr ""
3749
 
3750
- #: inc/class-page-settings.php:376
3751
  msgid "BackWPup version"
3752
  msgstr ""
3753
 
3754
- #: inc/class-page-settings.php:376
3755
  msgid "Get pro."
3756
  msgstr ""
3757
 
3758
- #: inc/class-page-settings.php:378
3759
  msgid "BackWPup Pro version"
3760
  msgstr ""
3761
 
3762
- #: inc/class-page-settings.php:386
3763
  msgid "PHP version"
3764
  msgstr ""
3765
 
3766
- #: inc/class-page-settings.php:387
3767
  msgid "MySQL version"
3768
  msgstr ""
3769
 
3770
- #: inc/class-page-settings.php:390 inc/class-page-settings.php:394
3771
  msgid "cURL version"
3772
  msgstr ""
3773
 
3774
- #: inc/class-page-settings.php:391
3775
  msgid "cURL SSL version"
3776
  msgstr ""
3777
 
3778
- #: inc/class-page-settings.php:394
3779
  msgid "unavailable"
3780
  msgstr ""
3781
 
3782
- #: inc/class-page-settings.php:396
3783
  msgid "WP-Cron url:"
3784
  msgstr ""
3785
 
3786
- #: inc/class-page-settings.php:398
3787
  msgid "Server self connect:"
3788
  msgstr ""
3789
 
3790
- #: inc/class-page-settings.php:403
3791
  msgid "<strong>Not expected HTTP response:</strong><br>"
3792
  msgstr ""
3793
 
3794
- #: inc/class-page-settings.php:405
3795
- #, php-format
3796
  msgid "WP Http Error: <code>%s</code>"
3797
  msgstr ""
3798
 
3799
- #: inc/class-page-settings.php:407
3800
- #, php-format
3801
  msgid "Status-Code: <code>%d</code>"
3802
  msgstr ""
3803
 
3804
- #: inc/class-page-settings.php:415
3805
- #, php-format
3806
  msgid "Content: <code>%s</code>"
3807
  msgstr ""
3808
 
3809
- #: inc/class-page-settings.php:419
3810
  msgid "Response Test O.K."
3811
  msgstr ""
3812
 
3813
- #: inc/class-page-settings.php:423
3814
  msgid "Temp folder:"
3815
  msgstr ""
3816
 
3817
- #: inc/class-page-settings.php:425
3818
- #, php-format
3819
  msgid "Temp folder %s doesn't exist."
3820
  msgstr ""
3821
 
3822
- #: inc/class-page-settings.php:427
3823
- #, php-format
3824
  msgid "Temporary folder %s is not writable."
3825
  msgstr ""
3826
 
3827
- #: inc/class-page-settings.php:434
3828
  msgid "Log folder:"
3829
  msgstr ""
3830
 
3831
- #: inc/class-page-settings.php:436
3832
- #, php-format
3833
  msgid "Logs folder %s not exist."
3834
  msgstr ""
3835
 
3836
- #: inc/class-page-settings.php:438
3837
- #, php-format
3838
  msgid "Log folder %s is not writable."
3839
  msgstr ""
3840
 
3841
- #: inc/class-page-settings.php:443
3842
  msgid "Server"
3843
  msgstr ""
3844
 
3845
- #: inc/class-page-settings.php:444
3846
  msgid "Operating System"
3847
  msgstr ""
3848
 
3849
- #: inc/class-page-settings.php:445
3850
  msgid "PHP SAPI"
3851
  msgstr ""
3852
 
3853
- #: inc/class-page-settings.php:446
3854
  msgid "Function Disabled"
3855
  msgstr ""
3856
 
3857
- #: inc/class-page-settings.php:450
3858
  msgid "Current PHP user"
3859
  msgstr ""
3860
 
3861
- #: inc/class-page-settings.php:451
3862
  msgid "Maximum execution time"
3863
  msgstr ""
3864
 
3865
- #: inc/class-page-settings.php:453 inc/class-page-settings.php:455
3866
  msgid "Alternative WP Cron"
3867
  msgstr ""
3868
 
3869
- #: inc/class-page-settings.php:453 inc/class-page-settings.php:457
3870
  msgid "On"
3871
  msgstr ""
3872
 
3873
- #: inc/class-page-settings.php:455 inc/class-page-settings.php:459
3874
  msgid "Off"
3875
  msgstr ""
3876
 
3877
- #: inc/class-page-settings.php:457 inc/class-page-settings.php:459
3878
  msgid "Disabled WP Cron"
3879
  msgstr ""
3880
 
3881
- #: inc/class-page-settings.php:461 inc/class-page-settings.php:463
3882
  msgid "CHMOD Dir"
3883
  msgstr ""
3884
 
3885
- #: inc/class-page-settings.php:465
3886
  msgid "Server Time"
3887
  msgstr ""
3888
 
3889
- #: inc/class-page-settings.php:466
3890
  msgid "Blog Time"
3891
  msgstr ""
3892
 
3893
- #: inc/class-page-settings.php:467
3894
  msgid "Blog Timezone"
3895
  msgstr ""
3896
 
3897
- #: inc/class-page-settings.php:468
3898
  msgid "Blog Time offset"
3899
  msgstr ""
3900
 
3901
- #: inc/class-page-settings.php:468
3902
- #, php-format
3903
  msgid "%s hours"
3904
  msgstr ""
3905
 
3906
- #: inc/class-page-settings.php:469
3907
  msgid "Blog language"
3908
  msgstr ""
3909
 
3910
- #: inc/class-page-settings.php:470
3911
  msgid "MySQL Client encoding"
3912
  msgstr ""
3913
 
3914
- #: inc/class-page-settings.php:473
3915
  msgid "Blog charset"
3916
  msgstr ""
3917
 
3918
- #: inc/class-page-settings.php:474
3919
  msgid "PHP Memory limit"
3920
  msgstr ""
3921
 
3922
- #: inc/class-page-settings.php:475
3923
  msgid "WP memory limit"
3924
  msgstr ""
3925
 
3926
- #: inc/class-page-settings.php:476
3927
  msgid "WP maximum memory limit"
3928
  msgstr ""
3929
 
3930
- #: inc/class-page-settings.php:477
3931
  msgid "Memory in use"
3932
  msgstr ""
3933
 
3934
- #: inc/class-page-settings.php:482
3935
  msgid "Disabled PHP Functions:"
3936
  msgstr ""
3937
 
3938
- #: inc/class-page-settings.php:487
3939
  msgid "Loaded PHP Extensions:"
3940
  msgstr ""
3941
 
3942
- #: inc/class-page-settings.php:499
3943
  msgid "Save Changes"
3944
  msgstr ""
3945
 
3946
- #: inc/class-page-settings.php:501
3947
  msgid "Reset all settings to default"
3948
  msgstr ""
3949
 
3950
  #: inc/class-php-admin-notice.php:123
3951
- msgid "With the upcoming major release, BackWPup will be requiring PHP version 5.3 or higher."
 
 
3952
  msgstr ""
3953
 
3954
  #: inc/class-php-admin-notice.php:125
@@ -3964,23 +3981,33 @@ msgid "Don't show again."
3964
  msgstr ""
3965
 
3966
  #: inc/class-php-admin-notice.php:164
3967
- msgid "BackWPup has determined, your installation is still running on the old PHP 5.2 version."
 
 
3968
  msgstr ""
3969
 
3970
  #: inc/class-php-admin-notice.php:167
3971
- msgid "In order to ensure a fast and secure development for BackWPup, we will most likely not support PHP version 5.2 in our next version."
 
 
3972
  msgstr ""
3973
 
3974
  #: inc/class-php-admin-notice.php:168
3975
- msgid "No need to worry, your host can update your PHP version relatively quickly and without any problems."
 
 
3976
  msgstr ""
3977
 
3978
  #: inc/class-php-admin-notice.php:169
3979
- msgid "Otherwise you can continue to stay on this last version and do not update the plugin in the future!"
 
 
3980
  msgstr ""
3981
 
3982
  #: inc/class-php-admin-notice.php:173
3983
- msgid "If the response from PHP 5.2 users is surprisingly high, we will eventually keep support for PHP 5.2 for a while."
 
 
3984
  msgstr ""
3985
 
3986
  #: inc/class-php-admin-notice.php:176
@@ -3991,14 +4018,15 @@ msgstr ""
3991
  msgid "Your BackWPup Team!"
3992
  msgstr ""
3993
 
3994
- #. Translators: This is the anchor text for an HTML link pointing to BackWPup contact page
 
3995
  #: inc/class-php-admin-notice.php:205
3996
  msgid "contact us"
3997
  msgstr ""
3998
 
3999
- #. Translators: %s is replaced by an HTML link with text "contact us" pointing to BackWPup contact page
 
4000
  #: inc/class-php-admin-notice.php:207
4001
- #, php-format
4002
  msgid "If you would like to have PHP 5.2 supported, please %s."
4003
  msgstr ""
4004
 
@@ -4026,61 +4054,52 @@ msgstr ""
4026
  msgid "No job running"
4027
  msgstr ""
4028
 
4029
- #: inc/pro/class-destination-dropbox.php:24
4030
  msgid "Auth Code:"
4031
  msgstr ""
4032
 
4033
- #: inc/pro/class-destination-dropbox.php:27
4034
  msgid "Get auth code"
4035
  msgstr ""
4036
 
4037
- #: inc/pro/class-destination-dropbox.php:34
4038
- #: inc/pro/class-destination-gdrive.php:293
4039
- #: inc/pro/class-destination-gdrive.php:298
4040
  #: inc/pro/class-destination-sugarsync.php:30
4041
  msgid "Login:"
4042
  msgstr ""
4043
 
4044
- #: inc/pro/class-destination-dropbox.php:38
4045
- #: inc/pro/class-destination-gdrive.php:305
4046
  #: inc/pro/class-destination-sugarsync.php:56
4047
  msgid "Folder:"
4048
  msgstr ""
4049
 
4050
- #: inc/pro/class-destination-dropbox.php:118
4051
- #, php-format
4052
  msgid "%d. Try to sync files to Dropbox&#160;&hellip;"
4053
  msgstr ""
4054
 
4055
- #: inc/pro/class-destination-dropbox.php:158
4056
  msgid "Retrieving file list from Dropbox"
4057
  msgstr ""
4058
 
4059
- #: inc/pro/class-destination-dropbox.php:172
4060
  msgid "Upload changed files to Dropbox"
4061
  msgstr ""
4062
 
4063
- #: inc/pro/class-destination-dropbox.php:194
4064
- #, php-format
4065
  msgid "File %s uploaded to Dropbox"
4066
  msgstr ""
4067
 
4068
- #: inc/pro/class-destination-dropbox.php:221
4069
- #, php-format
4070
  msgid "Extra file %s uploaded to Dropbox"
4071
  msgstr ""
4072
 
4073
- #: inc/pro/class-destination-dropbox.php:230
4074
- msgid "Delete not existing files from Dropbox"
4075
- msgstr ""
4076
-
4077
- #: inc/pro/class-destination-dropbox.php:237
4078
- #, php-format
4079
  msgid "Folder %s deleted from Dropbox"
4080
  msgstr ""
4081
 
4082
- #: inc/pro/class-destination-dropbox.php:255
4083
- #, php-format
4084
  msgid "File %s deleted from Dropbox"
4085
  msgstr ""
4086
 
@@ -4089,12 +4108,11 @@ msgid "Absolute path to folder for backup files:"
4089
  msgstr ""
4090
 
4091
  #: inc/pro/class-destination-folder.php:41
4092
- #: inc/pro/class-destination-gdrive.php:320
4093
  msgid "Oldest files will be deleted first."
4094
  msgstr ""
4095
 
4096
  #: inc/pro/class-destination-folder.php:87
4097
- #, php-format
4098
  msgid "%d. Try to sync files to folder&#160;&hellip;"
4099
  msgstr ""
4100
 
@@ -4102,31 +4120,27 @@ msgstr ""
4102
  msgid "Retrieving file list from folder"
4103
  msgstr ""
4104
 
4105
- #: inc/pro/class-destination-folder.php:97
4106
  msgid "Copy changed files to folder"
4107
  msgstr ""
4108
 
4109
- #: inc/pro/class-destination-folder.php:110
4110
- #, php-format
4111
  msgid "File %s copied"
4112
  msgstr ""
4113
 
4114
- #: inc/pro/class-destination-folder.php:123
4115
  msgid "Delete not existing files from folder"
4116
  msgstr ""
4117
 
4118
- #: inc/pro/class-destination-folder.php:131
4119
- #, php-format
4120
  msgid "Extra file %s copied"
4121
  msgstr ""
4122
 
4123
- #: inc/pro/class-destination-folder.php:147
4124
- #, php-format
4125
  msgid "File %s deleted from folder"
4126
  msgstr ""
4127
 
4128
- #: inc/pro/class-destination-folder.php:204
4129
- #, php-format
4130
  msgid "Empty folder %s deleted"
4131
  msgstr ""
4132
 
@@ -4148,18 +4162,20 @@ msgid "Maximum number of backup files to keep in folder:"
4148
  msgstr ""
4149
 
4150
  #: inc/pro/class-destination-gdrive.php:41
4151
- #: inc/pro/class-destination-gdrive.php:281
4152
- #, php-format
4153
- msgid "Looks like you havent set up any API keys yet. Head over to <a href=\"%s\">Settings | API-Keys</a> and get Google Drive all set up, then come back here."
 
 
4154
  msgstr ""
4155
 
4156
  #: inc/pro/class-destination-gdrive.php:55
4157
- #: inc/pro/class-destination-gdrive.php:296
4158
  msgid "Authenticate"
4159
  msgstr ""
4160
 
4161
  #: inc/pro/class-destination-gdrive.php:62
4162
- #: inc/pro/class-destination-gdrive.php:301
4163
  msgid "Reauthenticate"
4164
  msgstr ""
4165
 
@@ -4167,104 +4183,94 @@ msgstr ""
4167
  msgid "Folder in Google Drive"
4168
  msgstr ""
4169
 
4170
- #: inc/pro/class-destination-gdrive.php:109
4171
- msgid "Consider using trash to delete files. If trash is not enabled, files will be deleted permanently."
 
 
4172
  msgstr ""
4173
 
4174
- #: inc/pro/class-destination-gdrive.php:166
4175
- #: inc/pro/class-destination-gdrive.php:190
4176
  msgid "GDrive: Authenticated."
4177
  msgstr ""
4178
 
4179
- #: inc/pro/class-destination-gdrive.php:170
4180
- #: inc/pro/class-destination-gdrive.php:194
4181
  msgid "GDrive: No refresh token received. Try to Authenticate again!"
4182
  msgstr ""
4183
 
4184
- #: inc/pro/class-destination-gdrive.php:176
4185
- #: inc/pro/class-destination-gdrive.php:198
4186
- #: inc/pro/class-destination-gdrive.php:217
4187
- #: inc/pro/class-destination-gdrive.php:236
4188
- #, php-format
4189
  msgid "GDrive API: %s"
4190
  msgstr ""
4191
 
4192
- #: inc/pro/class-destination-gdrive.php:439
4193
- #, php-format
4194
  msgid "%d. Try to send backup file to Google Drive&#160;&hellip;"
4195
  msgstr ""
4196
 
4197
- #: inc/pro/class-destination-gdrive.php:467
4198
  msgid "Uploading to Google Drive&#160;&hellip;"
4199
  msgstr ""
4200
 
4201
- #: inc/pro/class-destination-gdrive.php:529
4202
  msgid "Could not create resumable file transfer to Google Drive"
4203
  msgstr ""
4204
 
4205
- #: inc/pro/class-destination-gdrive.php:573
4206
  msgid "Can not resume transfer backup to Google Drive!"
4207
  msgstr ""
4208
 
4209
- #: inc/pro/class-destination-gdrive.php:640
4210
- #, php-format
4211
  msgid "Error transfering file chunks to %s."
4212
  msgstr ""
4213
 
4214
- #: inc/pro/class-destination-gdrive.php:641
4215
- #: inc/pro/class-destination-gdrive.php:667
4216
  msgid "Google Drive"
4217
  msgstr ""
4218
 
4219
- #: inc/pro/class-destination-gdrive.php:715
4220
- #, php-format
4221
  msgid "One file deleted from Google Drive"
4222
  msgid_plural "%d files deleted on Google Drive"
4223
  msgstr[0] ""
4224
  msgstr[1] ""
4225
 
4226
- #: inc/pro/class-destination-gdrive.php:721
4227
- #: inc/pro/class-destination-gdrive.php:1027
4228
- #, php-format
4229
  msgid "Google Drive API: %s"
4230
  msgstr ""
4231
 
4232
- #: inc/pro/class-destination-gdrive.php:848
4233
- #, php-format
4234
  msgid "%d. Try to sync files to Google Drive&#160;&hellip;"
4235
  msgstr ""
4236
 
4237
- #: inc/pro/class-destination-gdrive.php:873
4238
  msgid "Syncing changed files to Google Drive"
4239
  msgstr ""
4240
 
4241
- #: inc/pro/class-destination-gdrive.php:903
4242
- #, php-format
4243
  msgid "File %s updated on Google Drive"
4244
  msgstr ""
4245
 
4246
- #: inc/pro/class-destination-gdrive.php:924
4247
- #, php-format
4248
  msgid "File %s uploaded to Google Drive"
4249
  msgstr ""
4250
 
4251
- #: inc/pro/class-destination-gdrive.php:945
4252
- #, php-format
4253
  msgid "File %s moved to trash in Google Drive"
4254
  msgstr ""
4255
 
4256
- #: inc/pro/class-destination-gdrive.php:948
4257
- #, php-format
4258
  msgid "File %s deleted permanently in Google Drive"
4259
  msgstr ""
4260
 
4261
- #: inc/pro/class-destination-gdrive.php:994
4262
- #, php-format
4263
  msgid "Extra file %s updated on Google Drive"
4264
  msgstr ""
4265
 
4266
- #: inc/pro/class-destination-gdrive.php:1016
4267
- #, php-format
4268
  msgid "Extra file %s uploaded to Google Drive"
4269
  msgstr ""
4270
 
@@ -4277,42 +4283,42 @@ msgid "Select a region:"
4277
  msgstr ""
4278
 
4279
  #: inc/pro/class-destination-glacier.php:33
4280
- #: inc/pro/class-destination-glacier.php:234
4281
  msgid "Amazon Glacier Region"
4282
  msgstr ""
4283
 
4284
  #: inc/pro/class-destination-glacier.php:34
4285
- #: inc/pro/class-destination-glacier.php:235
4286
  msgid "US Standard"
4287
  msgstr ""
4288
 
4289
  #: inc/pro/class-destination-glacier.php:35
4290
- #: inc/pro/class-destination-glacier.php:236
4291
  msgid "US West (Northern California)"
4292
  msgstr ""
4293
 
4294
  #: inc/pro/class-destination-glacier.php:36
4295
- #: inc/pro/class-destination-glacier.php:237
4296
  msgid "US West (Oregon)"
4297
  msgstr ""
4298
 
4299
  #: inc/pro/class-destination-glacier.php:37
4300
- #: inc/pro/class-destination-glacier.php:238
4301
  msgid "EU (Ireland)"
4302
  msgstr ""
4303
 
4304
  #: inc/pro/class-destination-glacier.php:38
4305
- #: inc/pro/class-destination-glacier.php:239
4306
  msgid "EU (Germany)"
4307
  msgstr ""
4308
 
4309
  #: inc/pro/class-destination-glacier.php:39
4310
- #: inc/pro/class-destination-glacier.php:240
4311
  msgid "Asia Pacific (Tokyo)"
4312
  msgstr ""
4313
 
4314
  #: inc/pro/class-destination-glacier.php:40
4315
- #: inc/pro/class-destination-glacier.php:241
4316
  msgid "Asia Pacific (Seoul)"
4317
  msgstr ""
4318
 
@@ -4321,17 +4327,17 @@ msgid "Asia Pacific (Mumbai)"
4321
  msgstr ""
4322
 
4323
  #: inc/pro/class-destination-glacier.php:42
4324
- #: inc/pro/class-destination-glacier.php:243
4325
  msgid "Asia Pacific (Sydney)"
4326
  msgstr ""
4327
 
4328
  #: inc/pro/class-destination-glacier.php:43
4329
- #: inc/pro/class-destination-glacier.php:244
4330
  msgid "South America (Sao Paulo)"
4331
  msgstr ""
4332
 
4333
  #: inc/pro/class-destination-glacier.php:44
4334
- #: inc/pro/class-destination-glacier.php:245
4335
  msgid "China (Beijing)"
4336
  msgstr ""
4337
 
@@ -4356,96 +4362,89 @@ msgid "Glacier Backup settings"
4356
  msgstr ""
4357
 
4358
  #: inc/pro/class-destination-glacier.php:108
4359
- #: inc/pro/class-destination-glacier.php:263
4360
- msgid "Number of files to keep in folder. (Archives deleted before 3 months after they have been stored may cause extra costs when deleted.)"
 
 
4361
  msgstr ""
4362
 
4363
- #: inc/pro/class-destination-glacier.php:171
4364
  msgid "No vault found!"
4365
  msgstr ""
4366
 
4367
- #: inc/pro/class-destination-glacier.php:210
4368
- #: inc/pro/class-destination-glacier.php:298
4369
- #, php-format
4370
  msgid "Vault %1$s created."
4371
  msgstr ""
4372
 
4373
- #: inc/pro/class-destination-glacier.php:212
4374
- #: inc/pro/class-destination-glacier.php:300
4375
- #, php-format
4376
  msgid "Vault %s could not be created."
4377
  msgstr ""
4378
 
4379
- #: inc/pro/class-destination-glacier.php:233
4380
  msgid "Select an Amazon Glacier region:"
4381
  msgstr ""
4382
 
4383
- #: inc/pro/class-destination-glacier.php:242
4384
  msgid "Asia Pacific (Singapore)"
4385
  msgstr ""
4386
 
4387
- #: inc/pro/class-destination-glacier.php:247
4388
  #: inc/pro/class-destination-msazure.php:19
4389
- #: inc/pro/class-destination-s3.php:40
4390
  msgid "Access Key:"
4391
  msgstr ""
4392
 
4393
- #: inc/pro/class-destination-glacier.php:249
4394
- #: inc/pro/class-destination-s3.php:43
4395
  msgid "Secret Key:"
4396
  msgstr ""
4397
 
4398
- #: inc/pro/class-destination-glacier.php:251
4399
  msgid "Vault:"
4400
  msgstr ""
4401
 
4402
- #: inc/pro/class-destination-glacier.php:260
4403
  msgid "New Vault:"
4404
  msgstr ""
4405
 
4406
- #: inc/pro/class-destination-glacier.php:340
4407
- #: inc/pro/class-destination-glacier.php:455
4408
- #: inc/pro/class-destination-glacier.php:474
4409
- #: inc/pro/class-destination-glacier.php:514
4410
- #, php-format
4411
  msgid "AWS API: %s"
4412
  msgstr ""
4413
 
4414
- #: inc/pro/class-destination-glacier.php:363
4415
- #, php-format
4416
  msgid "%d. Trying to send backup file to Amazon Glacier&#160;&hellip;"
4417
  msgstr ""
4418
 
4419
- #: inc/pro/class-destination-glacier.php:376
4420
- #, php-format
4421
  msgid "Connected to Glacier vault \"%1$s\" with %2$d archives and size of %3$d"
4422
  msgstr ""
4423
 
4424
- #: inc/pro/class-destination-glacier.php:378
4425
- #, php-format
4426
  msgid "Glacier vault \"%s\" does not exist!"
4427
  msgstr ""
4428
 
4429
- #: inc/pro/class-destination-glacier.php:384
4430
  msgid "Starting upload to Amazon Glacier&#160;&hellip;"
4431
  msgstr ""
4432
 
4433
- #: inc/pro/class-destination-glacier.php:437
4434
- #, php-format
4435
  msgid "Archive ID: %s"
4436
  msgstr ""
4437
 
4438
- #: inc/pro/class-destination-glacier.php:448 inc/pro/class-pro.php:92
4439
  msgid "Glacier"
4440
  msgstr ""
4441
 
4442
- #: inc/pro/class-destination-glacier.php:504
4443
- #, php-format
4444
  msgid "Cannot delete archive from %s."
4445
  msgstr ""
4446
 
4447
- #: inc/pro/class-destination-glacier.php:508
4448
- #, php-format
4449
  msgid "One file deleted on vault."
4450
  msgid_plural "%d files deleted on vault"
4451
  msgstr[0] ""
@@ -4471,7 +4470,6 @@ msgid "Folder in container:"
4471
  msgstr ""
4472
 
4473
  #: inc/pro/class-destination-msazure.php:119
4474
- #, php-format
4475
  msgid "%d. Trying to sync files with Microsoft Azure (Blob) &hellip;"
4476
  msgstr ""
4477
 
@@ -4484,12 +4482,10 @@ msgid "Upload changed files to MS Azure."
4484
  msgstr ""
4485
 
4486
  #: inc/pro/class-destination-msazure.php:182
4487
- #, php-format
4488
  msgid "File %s uploaded to MS Azure."
4489
  msgstr ""
4490
 
4491
  #: inc/pro/class-destination-msazure.php:208
4492
- #, php-format
4493
  msgid "Extra file %s uploaded to MS Azure."
4494
  msgstr ""
4495
 
@@ -4498,7 +4494,6 @@ msgid "Delete nonexistent files on MS Azure."
4498
  msgstr ""
4499
 
4500
  #: inc/pro/class-destination-msazure.php:224
4501
- #, php-format
4502
  msgid "File %s deleted from MS Azure."
4503
  msgstr ""
4504
 
@@ -4511,12 +4506,10 @@ msgid "Select region:"
4511
  msgstr ""
4512
 
4513
  #: inc/pro/class-destination-rsc.php:147
4514
- #, php-format
4515
  msgid "%d. Trying to sync files to Rackspace cloud&#160;&hellip;"
4516
  msgstr ""
4517
 
4518
  #: inc/pro/class-destination-rsc.php:167
4519
- #, php-format
4520
  msgid "Connected to Rackspace cloud files container %s."
4521
  msgstr ""
4522
 
@@ -4529,12 +4522,10 @@ msgid "Upload changed files to Rackspace Cloud."
4529
  msgstr ""
4530
 
4531
  #: inc/pro/class-destination-rsc.php:230
4532
- #, php-format
4533
  msgid "File %s uploaded to Rackspace Cloud."
4534
  msgstr ""
4535
 
4536
  #: inc/pro/class-destination-rsc.php:263
4537
- #, php-format
4538
  msgid "Extra file %s uploaded to Rackspace Cloud."
4539
  msgstr ""
4540
 
@@ -4543,7 +4534,6 @@ msgid "Delete nonexistent files on Rackspace Cloud."
4543
  msgstr ""
4544
 
4545
  #: inc/pro/class-destination-rsc.php:280
4546
- #, php-format
4547
  msgid "File %s deleted from Rackspace Cloud."
4548
  msgstr ""
4549
 
@@ -4551,56 +4541,51 @@ msgstr ""
4551
  msgid "Select a S3 service:"
4552
  msgstr ""
4553
 
4554
- #: inc/pro/class-destination-s3.php:37
4555
  msgid "or set an S3 Server URL:"
4556
  msgstr ""
4557
 
4558
- #: inc/pro/class-destination-s3.php:46
4559
  msgid "Bucket:"
4560
  msgstr ""
4561
 
4562
- #: inc/pro/class-destination-s3.php:56
4563
  msgid "New Bucket:"
4564
  msgstr ""
4565
 
4566
- #: inc/pro/class-destination-s3.php:58
4567
  msgid "Folder in bucket:"
4568
  msgstr ""
4569
 
4570
- #: inc/pro/class-destination-s3.php:146
4571
- #, php-format
4572
  msgid "Bucket %1$s created in %2$s."
4573
  msgstr ""
4574
 
4575
- #: inc/pro/class-destination-s3.php:170
4576
- #, php-format
4577
  msgid "%d. Trying to sync files to S3 Service&#160;&hellip;"
4578
  msgstr ""
4579
 
4580
- #: inc/pro/class-destination-s3.php:204
4581
  msgid "Retrieving file list from S3."
4582
  msgstr ""
4583
 
4584
- #: inc/pro/class-destination-s3.php:266
4585
  msgid "Upload changed files to S3."
4586
  msgstr ""
4587
 
4588
- #: inc/pro/class-destination-s3.php:281
4589
- #, php-format
4590
  msgid "File %s uploaded to S3."
4591
  msgstr ""
4592
 
4593
- #: inc/pro/class-destination-s3.php:309
4594
- #, php-format
4595
  msgid "Extra file %s uploaded to S3."
4596
  msgstr ""
4597
 
4598
- #: inc/pro/class-destination-s3.php:322
4599
  msgid "Delete nonexistent files on S3"
4600
  msgstr ""
4601
 
4602
- #: inc/pro/class-destination-s3.php:329
4603
- #, php-format
4604
  msgid "File %s deleted from S3."
4605
  msgstr ""
4606
 
@@ -4670,35 +4655,34 @@ msgid "Path to <em>mysqldump</em> file"
4670
  msgstr ""
4671
 
4672
  #: inc/pro/class-jobtype-dbdump.php:190
4673
- msgid "Path to mysqldump file, so a backup can be made with it. If it is correct and <em>shell_exec</em> is active, the backup will be generated with a system command. If <em>shell_exec</em> ist not active, this is disabled"
 
 
 
4674
  msgstr ""
4675
 
4676
  #: inc/pro/class-jobtype-dbdump.php:580
4677
- #, php-format
4678
  msgid "Added database backup \"%1$s\" with %2$s to backup file list"
4679
  msgstr ""
4680
 
4681
  #: inc/pro/class-jobtype-dbdump.php:601
4682
- #, php-format
4683
  msgid "%d. Try to backup MySQL system&#160;&hellip;"
4684
  msgstr ""
4685
 
4686
  #: inc/pro/class-jobtype-dbdump.php:608
4687
- msgid "Executing of system commands not allowed. Please use backup with mysqli."
 
4688
  msgstr ""
4689
 
4690
  #: inc/pro/class-jobtype-dbdump.php:613
4691
- #, php-format
4692
  msgid "%s file not in open basedir of PHP."
4693
  msgstr ""
4694
 
4695
  #: inc/pro/class-jobtype-dbdump.php:618
4696
- #, php-format
4697
  msgid "%s file not found. Please correct the path for the mysqldump file."
4698
  msgstr ""
4699
 
4700
  #: inc/pro/class-jobtype-dbdump.php:702
4701
- #, php-format
4702
  msgctxt "Executed exec() command"
4703
  msgid "CLI Exec: %s"
4704
  msgstr ""
@@ -4708,7 +4692,9 @@ msgid "Usage error."
4708
  msgstr ""
4709
 
4710
  #: inc/pro/class-jobtype-dbdump.php:713
4711
- msgid "MySQL Server Error. This could be an issue with permissions. Try using database backup with mysqli."
 
 
4712
  msgstr ""
4713
 
4714
  #: inc/pro/class-jobtype-dbdump.php:714
@@ -4728,7 +4714,6 @@ msgid "Illegal table"
4728
  msgstr ""
4729
 
4730
  #: inc/pro/class-jobtype-dbdump.php:722
4731
- #, php-format
4732
  msgid "mysqldump returned: (%d) %s"
4733
  msgstr ""
4734
 
@@ -4737,12 +4722,10 @@ msgid "Can not create mysql backup with mysqldump command"
4737
  msgstr ""
4738
 
4739
  #: inc/pro/class-jobtype-dbdump.php:781
4740
- #, php-format
4741
  msgid "%d. Try to backup database as XML&#160;&hellip;"
4742
  msgstr ""
4743
 
4744
  #: inc/pro/class-jobtype-dbdump.php:851
4745
- #, php-format
4746
  msgctxt "Database Charset"
4747
  msgid "Cannot set DB charset to %s"
4748
  msgstr ""
@@ -4752,22 +4735,18 @@ msgid "No tables for XML backup"
4752
  msgstr ""
4753
 
4754
  #: inc/pro/class-jobtype-dbdump.php:916
4755
- #, php-format
4756
  msgid "Dump database create view \"%s\""
4757
  msgstr ""
4758
 
4759
  #: inc/pro/class-jobtype-dbdump.php:934
4760
- #, php-format
4761
  msgid "Backup database structure \"%s\" to XML"
4762
  msgstr ""
4763
 
4764
  #: inc/pro/class-jobtype-dbdump.php:976
4765
- #, php-format
4766
  msgid "Backup table \"%s\" data to XML"
4767
  msgstr ""
4768
 
4769
  #: inc/pro/class-jobtype-dbdump.php:1044
4770
- #, php-format
4771
  msgid "Added database XML dump \"%1$s\" with %2$s to backup file list"
4772
  msgstr ""
4773
 
@@ -4813,21 +4792,19 @@ msgstr ""
4813
 
4814
  #: inc/pro/class-page-wizard.php:179 inc/pro/class-page-wizard.php:456
4815
  #: inc/pro/class-page-wizard.php:488
4816
- msgid "Next "
4817
  msgstr ""
4818
 
4819
  #: inc/pro/class-page-wizard.php:196 inc/pro/class-page-wizard.php:452
4820
- msgid " Previous"
4821
  msgstr ""
4822
 
4823
  #: inc/pro/class-page-wizard.php:349
4824
- #, php-format
4825
  msgctxt "Plugin Name"
4826
  msgid "%s &rsaquo; Wizards"
4827
  msgstr ""
4828
 
4829
  #: inc/pro/class-page-wizard.php:388
4830
- #, php-format
4831
  msgctxt "Plugin Name"
4832
  msgid "%s Wizard:"
4833
  msgstr ""
@@ -4849,7 +4826,9 @@ msgid "Hash key"
4849
  msgstr ""
4850
 
4851
  #: inc/pro/class-settings-apikeys.php:46
4852
- msgid "Hash Key for BackWPup. It will be used to have hashes in folder and file names. It must at least 6 chars long."
 
 
4853
  msgstr ""
4854
 
4855
  #: inc/pro/class-settings-apikeys.php:49
@@ -4861,7 +4840,9 @@ msgid "Dropbox API Keys"
4861
  msgstr ""
4862
 
4863
  #: inc/pro/class-settings-apikeys.php:74
4864
- msgid "If you want to set your own Dropbox API Keys, you can do it here. Leave empty for default."
 
 
4865
  msgstr ""
4866
 
4867
  #: inc/pro/class-settings-apikeys.php:77
@@ -4885,7 +4866,9 @@ msgid "SugarSync API Keys"
4885
  msgstr ""
4886
 
4887
  #: inc/pro/class-settings-apikeys.php:132
4888
- msgid "If you want to set your own SugarSync API keys you can do that here. Leave empty for default."
 
 
4889
  msgstr ""
4890
 
4891
  #: inc/pro/class-settings-apikeys.php:135
@@ -4977,7 +4960,9 @@ msgid "Archive compression type"
4977
  msgstr ""
4978
 
4979
  #: inc/pro/class-wizard-job.php:427
4980
- msgid "PHP Zip functions will be used if available (memory lees). Else PCLZip Class will used."
 
 
4981
  msgstr ""
4982
 
4983
  #: inc/pro/class-wizard-job.php:430 inc/pro/class-wizard-job.php:441
@@ -5002,12 +4987,10 @@ msgid "Where to store the files"
5002
  msgstr ""
5003
 
5004
  #: inc/pro/class-wizard-job.php:661
5005
- #, php-format
5006
  msgid "Wizard: %1$s"
5007
  msgstr ""
5008
 
5009
  #: inc/pro/class-wizard-job.php:680
5010
- #, php-format
5011
  msgid "New job %s generated."
5012
  msgstr ""
5013
 
@@ -5069,7 +5052,9 @@ msgid "Select which job should be imported or overwritten."
5069
  msgstr ""
5070
 
5071
  #: inc/pro/class-wizard-jobimport.php:67
5072
- msgid "Please upload your BackWPup job XML export file and we&#8217;ll import the jobs into BackWPup."
 
 
5073
  msgstr ""
5074
 
5075
  #: inc/pro/class-wizard-jobimport.php:69
@@ -5077,7 +5062,6 @@ msgid "Choose a file from your computer:"
5077
  msgstr ""
5078
 
5079
  #: inc/pro/class-wizard-jobimport.php:69
5080
- #, php-format
5081
  msgid "Maximum size: %s"
5082
  msgstr ""
5083
 
@@ -5110,12 +5094,16 @@ msgid "Import BackWPup configuration"
5110
  msgstr ""
5111
 
5112
  #: inc/pro/class-wizard-jobimport.php:142
5113
- msgid "File is empty. Please upload something more substantial. This error could also caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini."
 
 
 
5114
  msgstr ""
5115
 
5116
  #: inc/pro/class-wizard-jobimport.php:157
5117
- #, php-format
5118
- msgid "The export file could not be found at <code>%s</code>. This is likely due to an issue with permissions."
 
5119
  msgstr ""
5120
 
5121
  #: inc/pro/class-wizard-jobimport.php:164
@@ -5123,8 +5111,9 @@ msgid "Sorry, there has been a phrase error."
5123
  msgstr ""
5124
 
5125
  #: inc/pro/class-wizard-jobimport.php:171
5126
- #, php-format
5127
- msgid "This Export file (version %s) may not be supported by this version of the importer."
 
5128
  msgstr ""
5129
 
5130
  #: inc/pro/class-wizard-jobimport.php:177
@@ -5132,7 +5121,6 @@ msgid "This is not a BackWPup XML file"
5132
  msgstr ""
5133
 
5134
  #: inc/pro/class-wizard-jobimport.php:243
5135
- #, php-format
5136
  msgid "Job %1$s with id %2$d imported"
5137
  msgstr ""
5138
 
@@ -5165,53 +5153,58 @@ msgid "Test if BackWPup can work without problems."
5165
  msgstr ""
5166
 
5167
  #: inc/pro/class-wizard-systemtest.php:99
5168
- #, php-format
5169
- msgid "You must run WordPress version 3.4 or higher to use this plugin. You are using version %s now."
 
5170
  msgstr ""
5171
 
5172
  #: inc/pro/class-wizard-systemtest.php:104
5173
- #, php-format
5174
- msgid "You must run PHP version 5.2.6 or higher to use this plugin. You are using version %s now."
 
5175
  msgstr ""
5176
 
5177
  #: inc/pro/class-wizard-systemtest.php:108
5178
- #, php-format
5179
- msgid "We recommend to run a PHP version above 5.3.2 to get the full plugin functionality. You are using version %s now."
 
5180
  msgstr ""
5181
 
5182
  #: inc/pro/class-wizard-systemtest.php:113
5183
- #, php-format
5184
- msgid "You must have the MySQLi extension installed and a MySQL server version of 5.0.7 or higher to use this plugin. You are using version %s now."
 
5185
  msgstr ""
5186
 
5187
  #: inc/pro/class-wizard-systemtest.php:118
5188
- msgid "PHP cURL extension must be installed to use the full plugin functionality."
 
5189
  msgstr ""
5190
 
5191
- #: inc/pro/class-wizard-systemtest.php:123
5192
- #, php-format
5193
  msgctxt "%1 = extension name, %2 = file suffix"
5194
  msgid "We recommend to install the %1$s extension to generate %2$s archives."
5195
  msgstr ""
5196
 
5197
  #: inc/pro/class-wizard-systemtest.php:146
5198
- #, php-format
5199
  msgctxt "Link to PHP manual"
5200
  msgid "Please disable the deprecated <a href=\"%s\">PHP safe mode</a>."
5201
  msgstr ""
5202
 
5203
  #: inc/pro/class-wizard-systemtest.php:154
5204
- msgid "We recommend to install the PHP FTP extension to use the FTP backup destination."
 
 
5205
  msgstr ""
5206
 
5207
  #: inc/pro/class-wizard-systemtest.php:174
5208
- #, php-format
5209
  msgid "The HTTP response test result is an error: \"%s\"."
5210
  msgstr ""
5211
 
5212
  #: inc/pro/class-wizard-systemtest.php:178
5213
- #, php-format
5214
- msgid "The HTTP response test result is a wrong HTTP status: %s. It should be status 200."
 
5215
  msgstr ""
5216
 
5217
  #: inc/pro/class-wizard-systemtest.php:191
@@ -5223,9 +5216,51 @@ msgid "All tests passed without errors."
5223
  msgstr ""
5224
 
5225
  #: inc/pro/class-wizard-systemtest.php:199
5226
- msgid "There is no error, but some warnings. BackWPup will work, but with limitations."
 
 
5227
  msgstr ""
5228
 
5229
  #: inc/pro/class-wizard-systemtest.php:202
5230
  msgid "There are errors. Please correct them, or BackWPup cannot work."
5231
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2017 BackWPup Pro
2
+ # This file is distributed under the same license as the BackWPup Pro package.
 
 
 
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: BackWPup Pro 3.3.7\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/backwpup-pro\n"
7
+ "POT-Creation-Date: 2017-05-22 17:29:48+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
  #: backwpup.php:333 inc/class-page-backups.php:271
16
  msgid "Folder"
36
  msgid "Backup to FTP"
37
  msgstr ""
38
 
39
+ #: backwpup.php:381 inc/class-destination-dropbox.php:293
40
  msgid "Dropbox"
41
  msgstr ""
42
 
77
  msgstr ""
78
 
79
  #: backwpup.php:470
 
80
  msgid "PHP Version %1$s is to low, you need Version %2$s or above."
81
  msgstr ""
82
 
83
  #: backwpup.php:477
 
84
  msgid "Missing function \"%s\"."
85
  msgstr ""
86
 
87
  #: backwpup.php:486
 
88
  msgid "Missing class \"%s\"."
89
  msgstr ""
90
 
105
  msgstr ""
106
 
107
  #: inc/class-admin.php:243 inc/class-adminbar.php:82
108
+ #: inc/class-page-settings.php:117
109
  msgid "Jobs"
110
  msgstr ""
111
 
114
  msgstr ""
115
 
116
  #: inc/class-admin.php:273 inc/class-adminbar.php:98
117
+ #: inc/class-page-logs.php:328 inc/class-page-settings.php:117
118
  msgid "Logs"
119
  msgstr ""
120
 
135
  msgstr ""
136
 
137
  #: inc/class-admin.php:381
 
138
  msgid "<a class=\"backwpup-get-pro\" href=\"%s\">Get BackWPup Pro now.</a>"
139
  msgstr ""
140
 
141
+ #. #-#-#-#-# backwpup.pot (BackWPup Pro 3.3.7) #-#-#-#-#
142
+ #. Plugin URI of the plugin/theme
143
  #: inc/class-admin.php:381 inc/class-admin.php:401 inc/class-help.php:17
144
  #: inc/class-help.php:22 inc/class-job.php:394
145
  #: inc/class-jobtype-dbcheck.php:15 inc/class-jobtype-dbdump.php:15
146
  #: inc/class-jobtype-file.php:15 inc/class-jobtype-wpexp.php:15
147
  #: inc/class-jobtype-wpplugin.php:15 inc/class-page-about.php:624
148
  #: inc/class-page-backwpup.php:260 inc/class-page-backwpup.php:262
149
+ #: inc/class-page-settings.php:391 inc/pro/class-wizard-job.php:17
150
  #: inc/pro/class-wizard-jobimport.php:16
151
  #: inc/pro/class-wizard-systemtest.php:16
152
  msgid "http://backwpup.com"
153
  msgstr ""
154
 
155
  #: inc/class-admin.php:401
 
156
  msgid "version %s"
157
  msgstr ""
158
 
189
  msgid "Make BackWPup better!"
190
  msgstr ""
191
 
192
+ #: inc/class-become-inpsyder-widget.php:104
193
+ msgid ""
194
+ "https://inpsyde.com/en/jobs/?"
195
+ "utm_source=BackWPup&utm_medium=Link&utm_campaign=BecomeAnInpsyder"
196
  msgstr ""
197
 
198
+ #: inc/class-become-inpsyder-widget.php:114
199
  msgid "We want to make BackWPup even stronger and its support much faster."
200
  msgstr ""
201
 
202
+ #: inc/class-become-inpsyder-widget.php:122
203
+ msgid ""
204
+ "This is why we are looking for a talented developer who can work remotely "
205
+ "and support us in BackWPup"
206
  msgstr ""
207
 
208
+ #: inc/class-become-inpsyder-widget.php:129
209
  msgid "and other exciting WordPress projects at our VIP partner agency."
210
  msgstr ""
211
 
230
  msgid "https://backwpup.com/become-backwpup-beta-tester/"
231
  msgstr ""
232
 
233
+ #: inc/class-betatester-admin-notice.php:77
234
+ msgid ""
235
+ "To ensure that our releases are as bug-free as possible, we need you as a "
236
+ "beta tester!"
237
  msgstr ""
238
 
239
  #: inc/class-create-archive.php:64
241
  msgstr ""
242
 
243
  #: inc/class-create-archive.php:72
 
244
  msgctxt "%s = Folder name"
245
  msgid "Folder %s for archive not found"
246
  msgstr ""
255
  msgstr ""
256
 
257
  #: inc/class-create-archive.php:106
 
258
  msgctxt "ZipArchive open() result"
259
  msgid "Cannot create zip archive: %d"
260
  msgstr ""
261
 
262
  #: inc/class-create-archive.php:135
 
263
  msgctxt "%s = file name"
264
  msgid "Method to archive file %s not detected"
265
  msgstr ""
269
  msgstr ""
270
 
271
  #: inc/class-create-archive.php:155 inc/class-create-archive.php:349
 
272
  msgid "PclZip archive add error: %s"
273
  msgstr ""
274
 
281
  msgstr ""
282
 
283
  #: inc/class-create-archive.php:228
 
284
  msgctxt "File to add to archive"
285
  msgid "File %s does not exist or is not readable"
286
  msgstr ""
291
  msgstr ""
292
 
293
  #: inc/class-create-archive.php:247 inc/class-create-archive.php:263
 
294
  msgid "Cannot open source file %s to archive"
295
  msgstr ""
296
 
300
 
301
  #: inc/class-create-archive.php:329 inc/class-create-archive.php:338
302
  #: inc/class-create-archive.php:407
 
303
  msgid "Cannot add \"%s\" to zip archive!"
304
  msgstr ""
305
 
308
  msgstr ""
309
 
310
  #: inc/class-create-archive.php:379
 
311
  msgctxt "Folder path to add to archive"
312
  msgid "Folder %s does not exist or is not readable"
313
  msgstr ""
314
 
315
  #: inc/class-create-archive.php:429
 
316
  msgctxt "Text of ZipArchive status Message"
317
  msgid "ZipArchive returns status: %s"
318
  msgstr ""
319
 
320
  #: inc/class-create-archive.php:459
 
321
  msgid "File name \"%1$s\" is too long to be saved correctly in %2$s archive!"
322
  msgstr ""
323
 
324
  #: inc/class-create-archive.php:462
 
325
  msgid "File path \"%1$s\" is too long to be saved correctly in %2$s archive!"
326
  msgstr ""
327
 
328
  #: inc/class-create-archive.php:474
 
329
  msgid "Cannot open source file %s for archiving"
330
  msgstr ""
331
 
335
  msgstr ""
336
 
337
  #: inc/class-create-archive.php:570
 
338
  msgid "Folder name \"%1$s\" is too long to be saved correctly in %2$s archive!"
339
  msgstr ""
340
 
341
  #: inc/class-create-archive.php:573
 
342
  msgid "Folder path \"%1$s\" is too long to be saved correctly in %2$s archive!"
343
  msgstr ""
344
 
345
  #: inc/class-create-archive.php:654
346
+ msgid ""
347
+ "If %s will be added to your backup archive, the archive will be too large "
348
+ "for operations with this PHP Version. You might want to consider splitting "
349
+ "the backup job in multiple jobs with less files each."
350
  msgstr ""
351
 
352
  #: inc/class-cron.php:69
353
  msgid "Aborted, because no progress for one hour!"
354
  msgstr ""
355
 
356
+ #: inc/class-cron.php:101 inc/class-destination-folder.php:209
357
+ #: inc/class-job.php:1003 inc/class-job.php:1141 inc/class-job.php:1739
358
+ #: inc/class-jobtype-file.php:444
359
+ msgid "Could not open path: %s"
360
+ msgstr ""
361
+
362
+ #: inc/class-destination-dropbox.php:41 inc/class-destination-dropbox.php:344
363
+ #: inc/pro/class-destination-dropbox.php:253
364
  msgid "Dropbox API: %s"
365
  msgstr ""
366
 
367
+ #: inc/class-destination-dropbox.php:55
368
  #: inc/pro/class-destination-gdrive.php:51
369
  msgid "Login"
370
  msgstr ""
371
 
372
+ #: inc/class-destination-dropbox.php:59 inc/class-destination-sugarsync.php:28
373
  #: inc/class-destination-sugarsync.php:43
374
  msgid "Authentication"
375
  msgstr ""
376
 
377
+ #: inc/class-destination-dropbox.php:61
378
  #: inc/pro/class-destination-gdrive.php:57
379
+ #: inc/pro/class-destination-gdrive.php:295
380
  msgid "Not authenticated!"
381
  msgstr ""
382
 
383
+ #: inc/class-destination-dropbox.php:63
384
+ #: inc/pro/class-destination-dropbox.php:28
385
  msgid "Create Account"
386
  msgstr ""
387
 
388
+ #: inc/class-destination-dropbox.php:65 inc/class-destination-sugarsync.php:45
389
+ #: inc/pro/class-destination-dropbox.php:34
390
  #: inc/pro/class-destination-gdrive.php:59
391
+ #: inc/pro/class-destination-gdrive.php:300
392
  #: inc/pro/class-destination-sugarsync.php:31
393
  msgid "Authenticated!"
394
  msgstr ""
395
 
396
+ #: inc/class-destination-dropbox.php:68
397
  msgid "Delete Dropbox Authentication"
398
  msgstr ""
399
 
400
+ #: inc/class-destination-dropbox.php:75
401
  msgid "App Access to Dropbox"
402
  msgstr ""
403
 
404
+ #: inc/class-destination-dropbox.php:78
405
  msgid "Get Dropbox App auth code"
406
  msgstr ""
407
 
408
+ #: inc/class-destination-dropbox.php:79
409
+ msgid ""
410
+ "A dedicated folder named BackWPup will be created inside of the Apps folder "
411
+ "in your Dropbox. BackWPup will get read and write access to that folder "
412
+ "only. You can specify a subfolder as your backup destination for this job in "
413
+ "the destination field below."
414
  msgstr ""
415
 
416
+ #: inc/class-destination-dropbox.php:84
417
+ msgid " OR "
418
  msgstr ""
419
 
420
+ #: inc/class-destination-dropbox.php:87
421
  msgid "Full Access to Dropbox"
422
  msgstr ""
423
 
424
+ #: inc/class-destination-dropbox.php:90
425
  msgid "Get full Dropbox auth code "
426
  msgstr ""
427
 
428
+ #: inc/class-destination-dropbox.php:91
429
+ msgid ""
430
+ "BackWPup will have full read and write access to your entire Dropbox. You "
431
+ "can specify your backup destination wherever you want, just be aware that "
432
+ "ANY files or folders inside of your Dropbox can be overridden or deleted by "
433
+ "BackWPup."
434
  msgstr ""
435
 
436
+ #: inc/class-destination-dropbox.php:98 inc/class-destination-folder.php:29
437
  #: inc/class-destination-ftp.php:49 inc/class-destination-msazure.php:66
438
  #: inc/class-destination-rsc.php:95 inc/class-destination-sugarsync.php:81
439
  #: inc/pro/class-destination-gdrive.php:68
440
  msgid "Backup settings"
441
  msgstr ""
442
 
443
+ #: inc/class-destination-dropbox.php:102
444
  msgid "Destination Folder"
445
  msgstr ""
446
 
447
+ #: inc/class-destination-dropbox.php:106
448
+ msgid ""
449
+ "Specify a subfolder where your backup archives will be stored. If you use "
450
+ "the App option from above, this folder will be created inside of Apps/"
451
+ "BackWPup. Otherwise it will be created at the root of your Dropbox. Already "
452
+ "exisiting folders with the same name will not be overriden."
453
  msgstr ""
454
 
455
+ #: inc/class-destination-dropbox.php:111 inc/class-destination-folder.php:39
456
  #: inc/class-destination-ftp.php:59 inc/class-destination-sugarsync.php:91
457
  #: inc/pro/class-destination-gdrive.php:81
458
  msgid "File Deletion"
459
  msgstr ""
460
 
461
+ #: inc/class-destination-dropbox.php:118 inc/class-destination-folder.php:46
462
  #: inc/class-destination-ftp.php:66 inc/class-destination-msazure.php:83
463
+ #: inc/class-destination-rsc.php:112 inc/class-destination-s3.php:121
464
  #: inc/class-destination-sugarsync.php:98
465
+ #: inc/pro/class-destination-dropbox.php:51
466
  #: inc/pro/class-destination-folder.php:43
467
  #: inc/pro/class-destination-gdrive.php:90
468
+ #: inc/pro/class-destination-gdrive.php:323
469
  #: inc/pro/class-destination-msazure.php:45
470
+ #: inc/pro/class-destination-rsc.php:65 inc/pro/class-destination-s3.php:73
471
  msgid "Number of files to keep in folder."
472
  msgstr ""
473
 
474
+ #: inc/class-destination-dropbox.php:120 inc/class-destination-folder.php:48
475
+ #: inc/class-destination-ftp.php:68 inc/class-destination-msazure.php:85
476
+ #: inc/class-destination-rsc.php:114 inc/class-destination-s3.php:123
477
+ #: inc/class-destination-sugarsync.php:100
478
+ #: inc/pro/class-destination-gdrive.php:92
479
+ #: inc/pro/class-destination-glacier.php:109
480
+ msgid ""
481
+ "<strong>Warning</strong>: Files belonging to this job are now tracked. Old "
482
+ "backup archives which are untracked will not be automatically deleted."
483
+ msgstr ""
484
+
485
+ #: inc/class-destination-dropbox.php:124 inc/class-destination-folder.php:52
486
+ #: inc/class-destination-ftp.php:72 inc/class-destination-msazure.php:89
487
+ #: inc/class-destination-rsc.php:118 inc/class-destination-s3.php:127
488
+ #: inc/class-destination-sugarsync.php:104
489
+ #: inc/pro/class-destination-dropbox.php:54
490
  #: inc/pro/class-destination-folder.php:48
491
  #: inc/pro/class-destination-ftp.php:44
492
+ #: inc/pro/class-destination-gdrive.php:98
493
+ #: inc/pro/class-destination-gdrive.php:328
494
  #: inc/pro/class-destination-msazure.php:56
495
+ #: inc/pro/class-destination-rsc.php:70 inc/pro/class-destination-s3.php:78
496
  #: inc/pro/class-destination-sugarsync.php:65
497
  msgid "Do not delete files while syncing to destination!"
498
  msgstr ""
499
 
500
+ #: inc/class-destination-dropbox.php:239
 
501
  msgid "%d. Try to send backup file to Dropbox&#160;&hellip;"
502
  msgstr ""
503
 
504
+ #: inc/class-destination-dropbox.php:256
505
+ #: inc/pro/class-destination-dropbox.php:127
 
506
  msgid "Authenticated with Dropbox of user: %s"
507
  msgstr ""
508
 
509
+ #: inc/class-destination-dropbox.php:262
510
+ #: inc/pro/class-destination-dropbox.php:133
 
511
  msgid "%s available on your Dropbox"
512
  msgstr ""
513
 
514
+ #: inc/class-destination-dropbox.php:266
515
+ #: inc/pro/class-destination-dropbox.php:137
516
  msgid "Not Authenticated with Dropbox!"
517
  msgstr ""
518
 
519
+ #: inc/class-destination-dropbox.php:270
520
  msgid "Uploading to Dropbox&#160;&hellip;"
521
  msgstr ""
522
 
523
+ #: inc/class-destination-dropbox.php:283 inc/class-destination-msazure.php:286
524
+ #: inc/class-destination-sugarsync.php:260
525
+ #: inc/pro/class-destination-gdrive.php:663
 
526
  msgid "Backup transferred to %s"
527
  msgstr ""
528
 
529
+ #: inc/class-destination-dropbox.php:287
530
+ #: inc/pro/class-destination-gdrive.php:666
531
  msgid "Uploaded file size and local file size don't match."
532
  msgstr ""
533
 
534
+ #: inc/class-destination-dropbox.php:292
535
+ #: inc/pro/class-destination-gdrive.php:668
536
+ #: inc/pro/class-destination-glacier.php:448
 
537
  msgid "Error transfering backup to %s."
538
  msgstr ""
539
 
540
+ #: inc/class-destination-dropbox.php:337
 
 
 
 
 
 
541
  msgid "One file deleted from Dropbox"
542
  msgid_plural "%d files deleted on Dropbox"
543
  msgstr[0] ""
544
  msgstr[1] ""
545
 
546
+ #: inc/class-destination-dropbox.php:520
547
+ msgid "Beginning new file upload session"
548
+ msgstr ""
549
+
550
+ #: inc/class-destination-dropbox.php:539
551
+ msgid "Uploading %s of data"
552
+ msgstr ""
553
+
554
+ #: inc/class-destination-dropbox.php:575
555
+ msgid "Finishing upload session with a total of %s uploaded"
556
+ msgstr ""
557
+
558
  #: inc/class-destination-email.php:38 inc/pro/class-destination-email.php:16
559
  #: inc/pro/class-destination-email.php:18
560
  msgid "Email address"
561
  msgstr ""
562
 
563
  #: inc/class-destination-email.php:41
564
+ msgid "To email address (separate with commas for multiple addresses)"
565
  msgstr ""
566
 
567
  #: inc/class-destination-email.php:47 inc/class-destination-email.php:49
634
  #: inc/class-destination-email.php:106 inc/class-jobtype-dbdump.php:67
635
  #: inc/class-jobtype-dbdump.php:103 inc/class-jobtype-wpexp.php:73
636
  #: inc/class-jobtype-wpplugin.php:57 inc/class-page-backwpup.php:329
637
+ #: inc/class-page-backwpup.php:404 inc/class-page-settings.php:328
638
  #: inc/pro/class-jobtype-dbdump.php:157 inc/pro/class-jobtype-dbdump.php:205
639
  msgid "none"
640
  msgstr ""
656
  msgstr ""
657
 
658
  #: inc/class-destination-email.php:199
 
659
  msgid "%d. Try to send backup with email&#160;&hellip;"
660
  msgstr ""
661
 
664
  msgstr ""
665
 
666
  #: inc/class-destination-email.php:211
 
667
  msgid "Sending email to %s&hellip;"
668
  msgstr ""
669
 
670
  #: inc/class-destination-email.php:289
 
671
  msgid "BackWPup archive from %1$s: %2$s"
672
  msgstr ""
673
 
674
  #: inc/class-destination-email.php:292
 
675
  msgid "Backup archive: %s"
676
  msgstr ""
677
 
688
  msgstr ""
689
 
690
  #: inc/class-destination-email.php:417
691
+ msgid ""
692
+ "If this message reaches your inbox, sending backup archives via email should "
693
+ "work for you."
694
  msgstr ""
695
 
696
  #: inc/class-destination-folder.php:33
697
  msgid "Folder to store backups in"
698
  msgstr ""
699
 
700
+ #: inc/class-destination-folder.php:229
 
701
  msgid "One backup file deleted"
702
  msgid_plural "%d backup files deleted"
703
  msgstr[0] ""
723
  msgid "Folder to store files in"
724
  msgstr ""
725
 
726
+ #: inc/class-destination-ftp.php:79
727
  msgid "FTP specific settings"
728
  msgstr ""
729
 
730
+ #: inc/class-destination-ftp.php:83
731
  msgid "Timeout for FTP connection"
732
  msgstr ""
733
 
734
+ #: inc/class-destination-ftp.php:87 inc/class-page-logs.php:257
735
+ #: inc/class-page-settings.php:466
736
  msgid "seconds"
737
  msgstr ""
738
 
739
+ #: inc/class-destination-ftp.php:91
740
  msgid "SSL-FTP connection"
741
  msgstr ""
742
 
743
+ #: inc/class-destination-ftp.php:94
744
  msgid "Use explicit SSL-FTP connection."
745
  msgstr ""
746
 
747
+ #: inc/class-destination-ftp.php:99
748
  msgid "FTP Passive Mode"
749
  msgstr ""
750
 
751
+ #: inc/class-destination-ftp.php:101
752
  msgid "Use FTP Passive Mode."
753
  msgstr ""
754
 
755
+ #: inc/class-destination-ftp.php:179
756
  msgid "FTP: Login failure!"
757
  msgstr ""
758
 
759
+ #: inc/class-destination-ftp.php:203
 
760
  msgid "%d. Try to send backup file to an FTP server&#160;&hellip;"
761
  msgstr ""
762
 
763
+ #: inc/class-destination-ftp.php:209
 
764
  msgid "Connected via explicit SSL-FTP to server: %s"
765
  msgstr ""
766
 
767
+ #: inc/class-destination-ftp.php:211
 
768
  msgid "Cannot connect via explicit SSL-FTP to server: %s"
769
  msgstr ""
770
 
771
+ #: inc/class-destination-ftp.php:217
772
  msgid "PHP function to connect with explicit SSL-FTP to server does not exist!"
773
  msgstr ""
774
 
775
+ #: inc/class-destination-ftp.php:225
 
776
  msgid "Connected to FTP server: %s"
777
  msgstr ""
778
 
779
+ #: inc/class-destination-ftp.php:227
 
780
  msgid "Cannot connect to FTP server: %s"
781
  msgstr ""
782
 
783
+ #: inc/class-destination-ftp.php:234 inc/class-destination-ftp.php:242
784
+ #: inc/class-destination-ftp.php:258 inc/class-destination-ftp.php:305
 
785
  msgid "FTP client command: %s"
786
  msgstr ""
787
 
788
+ #: inc/class-destination-ftp.php:236
 
789
  msgid "FTP server response: %s"
790
  msgstr ""
791
 
792
+ #: inc/class-destination-ftp.php:240 inc/class-destination-ftp.php:245
793
+ #: inc/class-destination-ftp.php:248 inc/class-destination-ftp.php:261
794
+ #: inc/class-destination-ftp.php:263 inc/class-destination-ftp.php:308
795
+ #: inc/class-destination-ftp.php:310 inc/class-destination-ftp.php:314
796
+ #: inc/class-destination-ftp.php:316
 
797
  msgid "FTP server reply: %s"
798
  msgstr ""
799
 
800
+ #: inc/class-destination-ftp.php:263
801
  msgid "Error getting SYSTYPE"
802
  msgstr ""
803
 
804
+ #: inc/class-destination-ftp.php:281
 
805
  msgid "FTP Folder \"%s\" created!"
806
  msgstr ""
807
 
808
+ #: inc/class-destination-ftp.php:285
 
809
  msgid "FTP Folder \"%s\" cannot be created!"
810
  msgstr ""
811
 
812
+ #: inc/class-destination-ftp.php:296
 
813
  msgid "FTP current folder is: %s"
814
  msgstr ""
815
 
816
+ #: inc/class-destination-ftp.php:308
817
  msgid "Entering passive mode"
818
  msgstr ""
819
 
820
+ #: inc/class-destination-ftp.php:310
821
  msgid "Cannot enter passive mode"
822
  msgstr ""
823
 
824
+ #: inc/class-destination-ftp.php:314
825
  msgid "Entering normal mode"
826
  msgstr ""
827
 
828
+ #: inc/class-destination-ftp.php:316
829
  msgid "Cannot enter normal mode"
830
  msgstr ""
831
 
832
+ #: inc/class-destination-ftp.php:320
833
  msgid "Starting upload to FTP &#160;&hellip;"
834
  msgstr ""
835
 
836
+ #: inc/class-destination-ftp.php:332
837
  msgid "Cannot transfer backup to FTP server!"
838
  msgstr ""
839
 
840
+ #: inc/class-destination-ftp.php:337
 
841
  msgid "Backup transferred to FTP server: %s"
842
  msgstr ""
843
 
844
+ #: inc/class-destination-ftp.php:344 inc/class-destination-msazure.php:271
845
+ #: inc/class-destination-rsc.php:289 inc/class-destination-s3.php:499
846
+ #: inc/class-destination-s3.php:591 inc/pro/class-destination-gdrive.php:653
847
+ #: inc/pro/class-destination-glacier.php:468
848
  #: inc/pro/class-destination-rsc.php:226 inc/pro/class-destination-rsc.php:259
849
  msgid "Can not open source file for transfer."
850
  msgstr ""
851
 
852
+ #: inc/class-destination-ftp.php:388
 
853
  msgid "Cannot delete \"%s\" on FTP server!"
854
  msgstr ""
855
 
856
+ #: inc/class-destination-ftp.php:391
 
857
  msgid "One file deleted on FTP server"
858
  msgid_plural "%d files deleted on FTP server"
859
  msgstr[0] ""
888
  msgstr ""
889
 
890
  #: inc/class-destination-msazure.php:76 inc/class-destination-rsc.php:105
891
+ #: inc/class-destination-s3.php:114 inc/pro/class-destination-glacier.php:96
892
  msgid "File deletion"
893
  msgstr ""
894
 
895
+ #: inc/class-destination-msazure.php:128
896
  #: inc/pro/class-destination-msazure.php:99
 
897
  msgid "MS Azure container \"%s\" created."
898
  msgstr ""
899
 
900
+ #: inc/class-destination-msazure.php:131
901
  #: inc/pro/class-destination-msazure.php:102
 
902
  msgid "MS Azure container create: %s"
903
  msgstr ""
904
 
905
+ #: inc/class-destination-msazure.php:211
 
906
  msgid "%d. Try sending backup to a Microsoft Azure (Blob)&#160;&hellip;"
907
  msgstr ""
908
 
909
+ #: inc/class-destination-msazure.php:233
910
  #: inc/pro/class-destination-msazure.php:144
 
911
  msgid "MS Azure container \"%s\" does not exist!"
912
  msgstr ""
913
 
914
+ #: inc/class-destination-msazure.php:237
915
  #: inc/pro/class-destination-msazure.php:148
 
916
  msgid "Connected to MS Azure container \"%s\"."
917
  msgstr ""
918
 
919
+ #: inc/class-destination-msazure.php:240
920
  msgid "Starting upload to MS Azure&#160;&hellip;"
921
  msgstr ""
922
 
923
+ #: inc/class-destination-msazure.php:292 inc/class-destination-msazure.php:348
924
  #: inc/pro/class-destination-msazure.php:233
 
925
  msgid "Microsoft Azure API: %s"
926
  msgstr ""
927
 
928
+ #: inc/class-destination-msazure.php:341
 
929
  msgid "One file deleted on Microsoft Azure container."
930
  msgid_plural "%d files deleted on Microsoft Azure container."
931
  msgstr[0] ""
932
  msgstr[1] ""
933
 
934
+ #: inc/class-destination-msazure.php:442
935
  msgid "Missing account name!"
936
  msgstr ""
937
 
938
+ #: inc/class-destination-msazure.php:444 inc/class-destination-s3.php:222
939
+ #: inc/pro/class-destination-glacier.php:166
940
  msgid "Missing access key!"
941
  msgstr ""
942
 
943
+ #: inc/class-destination-msazure.php:448
944
  msgid "No container found!"
945
  msgstr ""
946
 
985
  msgid "Hong Kong (HKG)"
986
  msgstr ""
987
 
988
+ #: inc/class-destination-rsc.php:99 inc/class-destination-s3.php:108
989
  msgid "Folder in bucket"
990
  msgstr ""
991
 
992
+ #: inc/class-destination-rsc.php:158 inc/pro/class-destination-rsc.php:116
 
993
  msgid "Rackspace Cloud container \"%s\" created."
994
  msgstr ""
995
 
996
+ #: inc/class-destination-rsc.php:162 inc/class-destination-rsc.php:274
997
+ #: inc/class-destination-rsc.php:314 inc/class-destination-rsc.php:359
998
  #: inc/pro/class-destination-rsc.php:120 inc/pro/class-destination-rsc.php:170
999
  #: inc/pro/class-destination-rsc.php:290
 
1000
  msgid "Rackspace Cloud API: %s"
1001
  msgstr ""
1002
 
1003
+ #: inc/class-destination-rsc.php:257
 
1004
  msgid "%d. Trying to send backup file to Rackspace cloud &hellip;"
1005
  msgstr ""
1006
 
1007
+ #: inc/class-destination-rsc.php:271
 
1008
  msgid "Connected to Rackspace cloud files container %s"
1009
  msgstr ""
1010
 
1011
+ #: inc/class-destination-rsc.php:283
1012
  msgid "Upload to Rackspace cloud started &hellip;"
1013
  msgstr ""
1014
 
1015
+ #: inc/class-destination-rsc.php:302
1016
  msgid "Backup File transferred to RSC://"
1017
  msgstr ""
1018
 
1019
+ #: inc/class-destination-rsc.php:308
1020
  msgid "Cannot transfer backup to Rackspace cloud."
1021
  msgstr ""
1022
 
1023
+ #: inc/class-destination-rsc.php:353
 
1024
  msgid "One file deleted on Rackspace cloud container."
1025
  msgid_plural "%d files deleted on Rackspace cloud container."
1026
  msgstr[0] ""
1027
  msgstr[1] ""
1028
 
1029
+ #: inc/class-destination-rsc.php:466
1030
  msgid "Missing username!"
1031
  msgstr ""
1032
 
1033
+ #: inc/class-destination-rsc.php:468
1034
  msgid "Missing API Key!"
1035
  msgstr ""
1036
 
1037
+ #: inc/class-destination-rsc.php:472
1038
  msgid "A container could not be found!"
1039
  msgstr ""
1040
 
1110
  msgid "Dream Host Cloud Storage"
1111
  msgstr ""
1112
 
1113
+ #: inc/class-destination-s3.php:54
 
 
 
 
1114
  msgid "Or a S3 Server URL"
1115
  msgstr ""
1116
 
1117
+ #: inc/class-destination-s3.php:61
1118
  msgid "S3 Access Keys"
1119
  msgstr ""
1120
 
1121
+ #: inc/class-destination-s3.php:65 inc/pro/class-destination-glacier.php:54
1122
  msgid "Access Key"
1123
  msgstr ""
1124
 
1125
+ #: inc/class-destination-s3.php:72 inc/pro/class-destination-glacier.php:61
1126
  msgid "Secret Key"
1127
  msgstr ""
1128
 
1129
+ #: inc/class-destination-s3.php:80
1130
  msgid "S3 Bucket"
1131
  msgstr ""
1132
 
1133
+ #: inc/class-destination-s3.php:84
1134
  msgid "Bucket selection"
1135
  msgstr ""
1136
 
1137
+ #: inc/class-destination-s3.php:97
1138
  msgid "Create a new bucket"
1139
  msgstr ""
1140
 
1141
+ #: inc/class-destination-s3.php:104
1142
  msgid "S3 Backup settings"
1143
  msgstr ""
1144
 
1151
  msgstr ""
1152
 
1153
  #: inc/class-destination-s3.php:137
1154
+ msgid ""
1155
+ "Multipart splits file into multiple chunks while uploading. This is "
1156
+ "necessary for displaying the upload process and to transfer bigger files. "
1157
+ "Works without a problem on Amazon. Other services might have issues."
1158
  msgstr ""
1159
 
1160
  #: inc/class-destination-s3.php:143
1185
  msgid "Save files encrypted (AES256) on server."
1186
  msgstr ""
1187
 
1188
+ #: inc/class-destination-s3.php:224 inc/pro/class-destination-glacier.php:168
1189
  msgid "Missing secret access key!"
1190
  msgstr ""
1191
 
1192
+ #: inc/class-destination-s3.php:230
1193
  msgid "No bucket found!"
1194
  msgstr ""
1195
 
1196
+ #: inc/class-destination-s3.php:346
 
1197
  msgid "Bucket %1$s created."
1198
  msgstr ""
1199
 
1200
+ #: inc/class-destination-s3.php:348 inc/pro/class-destination-s3.php:147
 
1201
  msgid " %s is not a valid bucket name."
1202
  msgstr ""
1203
 
1204
+ #: inc/class-destination-s3.php:389 inc/class-destination-s3.php:522
1205
+ #: inc/class-destination-s3.php:576 inc/class-destination-s3.php:610
1206
+ #: inc/class-destination-s3.php:670 inc/pro/class-destination-s3.php:337
 
1207
  msgid "S3 Service API: %s"
1208
  msgstr ""
1209
 
1210
+ #: inc/class-destination-s3.php:457
 
1211
  msgid "%d. Trying to send backup file to S3 Service&#160;&hellip;"
1212
  msgstr ""
1213
 
1214
+ #: inc/class-destination-s3.php:470 inc/pro/class-destination-s3.php:188
 
1215
  msgid "Connected to S3 Bucket \"%1$s\" in %2$s"
1216
  msgstr ""
1217
 
1218
+ #: inc/class-destination-s3.php:473 inc/pro/class-destination-s3.php:191
 
1219
  msgid "S3 Bucket \"%s\" does not exist!"
1220
  msgstr ""
1221
 
1222
+ #: inc/class-destination-s3.php:480
1223
  msgid "Checking for not aborted multipart Uploads&#160;&hellip;"
1224
  msgstr ""
1225
 
1226
+ #: inc/class-destination-s3.php:486
 
1227
  msgid "Upload for %s aborted."
1228
  msgstr ""
1229
 
1230
+ #: inc/class-destination-s3.php:492
1231
  msgid "Starting upload to S3 Service&#160;&hellip;"
1232
  msgstr ""
1233
 
1234
+ #: inc/class-destination-s3.php:601 inc/pro/class-destination-glacier.php:444
 
1235
  msgid "Backup transferred to %s."
1236
  msgstr ""
1237
 
1238
+ #: inc/class-destination-s3.php:606
 
1239
  msgid "Cannot transfer backup to S3! (%1$d) %2$s"
1240
  msgstr ""
1241
 
1242
+ #: inc/class-destination-s3.php:634
 
1243
  msgid "Storage Class: %s"
1244
  msgstr ""
1245
 
1246
+ #: inc/class-destination-s3.php:660
 
1247
  msgid "Cannot delete backup from %s."
1248
  msgstr ""
1249
 
1250
+ #: inc/class-destination-s3.php:664
 
1251
  msgid "One file deleted on S3 Bucket."
1252
  msgid_plural "%d files deleted on S3 Bucket"
1253
  msgstr[0] ""
1269
  msgstr ""
1270
 
1271
  #: inc/class-destination-sugarsync.php:37
1272
+ #: inc/class-destination-sugarsync.php:120
1273
  msgid "Authenticate with Sugarsync!"
1274
  msgstr ""
1275
 
1276
  #: inc/class-destination-sugarsync.php:38
1277
+ #: inc/class-destination-sugarsync.php:136
1278
  #: inc/pro/class-destination-sugarsync.php:26
1279
  #: inc/pro/class-destination-sugarsync.php:98
1280
  msgid "Create Sugarsync account"
1281
  msgstr ""
1282
 
1283
  #: inc/class-destination-sugarsync.php:46
1284
+ #: inc/class-destination-sugarsync.php:132
1285
  #: inc/pro/class-destination-sugarsync.php:32
1286
  #: inc/pro/class-destination-sugarsync.php:94
1287
  msgid "Delete Sugarsync authentication!"
1304
  msgid "Folder in root"
1305
  msgstr ""
1306
 
1307
+ #: inc/class-destination-sugarsync.php:230
 
1308
  msgid "%d. Try to send backup to SugarSync&#160;&hellip;"
1309
  msgstr ""
1310
 
1311
+ #: inc/class-destination-sugarsync.php:237
 
1312
  msgid "Authenticated to SugarSync with nickname %s"
1313
  msgstr ""
1314
 
1315
+ #: inc/class-destination-sugarsync.php:240
 
1316
  msgctxt "Available space on SugarSync"
1317
  msgid "Not enough disk space available on SugarSync. Available: %s."
1318
  msgstr ""
1319
 
1320
+ #: inc/class-destination-sugarsync.php:246
 
1321
  msgid "%s available at SugarSync"
1322
  msgstr ""
1323
 
1324
+ #: inc/class-destination-sugarsync.php:253
1325
  msgid "Starting upload to SugarSync&#160;&hellip;"
1326
  msgstr ""
1327
 
1328
+ #: inc/class-destination-sugarsync.php:263
1329
  msgid "Cannot transfer backup to SugarSync!"
1330
  msgstr ""
1331
 
1332
+ #: inc/class-destination-sugarsync.php:302
 
1333
  msgid "One file deleted on SugarSync folder"
1334
  msgid_plural "%d files deleted on SugarSync folder"
1335
  msgstr[0] ""
1336
  msgstr[1] ""
1337
 
1338
+ #: inc/class-destination-sugarsync.php:308
 
1339
  msgid "SugarSync API: %s"
1340
  msgstr ""
1341
 
1342
  #: inc/class-easycron.php:179
 
1343
  msgid "EasyCron.com API returns (%s): %s"
1344
  msgstr ""
1345
 
1348
  msgstr ""
1349
 
1350
  #: inc/class-easycron.php:189
1351
+ msgid ""
1352
+ "Here you can setup your <a href=\"https://www.easycron.com/user/token?"
1353
+ "ref=36673\" title=\"Affiliate Link!\">EasyCron.com API key</a> to use this "
1354
+ "service."
1355
  msgstr ""
1356
 
1357
  #: inc/class-easycron.php:192
1363
  msgstr ""
1364
 
1365
  #: inc/class-easycron.php:203
1366
+ msgid ""
1367
+ "If you check this box, a cron job will be created on EasyCron that all 5 "
1368
+ "Minutes calls the WordPress cron."
1369
  msgstr ""
1370
 
1371
+ #: inc/class-file.php:138
 
1372
  msgid "Folder %1$s not allowed, please use another folder."
1373
  msgstr ""
1374
 
1375
+ #: inc/class-file.php:143
 
1376
  msgid "Folder %1$s is not in open basedir, please use another folder."
1377
  msgstr ""
1378
 
1379
+ #: inc/class-file.php:149
 
1380
  msgid "Cannot create folder: %1$s"
1381
  msgstr ""
1382
 
1383
+ #: inc/class-file.php:155
 
1384
  msgid "Folder \"%1$s\" is not writable"
1385
  msgstr ""
1386
 
1387
+ #: inc/class-file.php:191
1388
+ msgid ""
1389
+ "BackWPup will not backup folders and its sub folders when this file is "
1390
+ "inside."
1391
  msgstr ""
1392
 
1393
  #: inc/class-help.php:15
1395
  msgstr ""
1396
 
1397
  #: inc/class-help.php:17
 
1398
  msgctxt "Plugin name and link; Plugin Version"
1399
+ msgid ""
1400
+ "%1$s version %2$s. A project by <a href=\"http://inpsyde.com\">Inpsyde GmbH</"
1401
+ "a>."
1402
  msgstr ""
1403
 
1404
  #: inc/class-help.php:18
1405
+ msgid ""
1406
+ "BackWPup comes with ABSOLUTELY NO WARRANTY. This is a free software, and you "
1407
+ "are welcome to redistribute it under certain conditions."
1408
  msgstr ""
1409
 
1410
  #: inc/class-help.php:21
1452
  msgstr ""
1453
 
1454
  #: inc/class-job.php:377
 
1455
  msgid "BackWPup log for %1$s from %2$s at %3$s"
1456
  msgstr ""
1457
 
1458
  #: inc/class-job.php:394
 
1459
  msgctxt "Plugin name; Plugin Version; plugin url"
1460
  msgid "[INFO] %1$s %2$s; A project of Inpsyde GmbH"
1461
  msgstr ""
1462
 
1463
  #: inc/class-job.php:395
 
1464
  msgctxt "WordPress Version; Blog url"
1465
  msgid "[INFO] WordPress %1$s on %2$s"
1466
  msgstr ""
1478
  msgstr ""
1479
 
1480
  #: inc/class-job.php:404
 
1481
  msgid "[INFO] Log Level: %1$s %2$s"
1482
  msgstr ""
1483
 
1484
  #: inc/class-job.php:409
 
1485
  msgid "[INFO] BackWPup job: %1$s"
1486
  msgstr ""
1487
 
1488
  #: inc/class-job.php:412
 
1489
  msgid "[INFO] Runs with user: %1$s (%2$d) "
1490
  msgstr ""
1491
 
1495
  msgstr ""
1496
 
1497
  #: inc/class-job.php:430 inc/class-job.php:440
 
1498
  msgid "[INFO] Cron: %s; Next: %s "
1499
  msgstr ""
1500
 
1531
  msgstr ""
1532
 
1533
  #: inc/class-job.php:463
 
1534
  msgid "[INFO] Maximum PHP script execution time is %1$d seconds"
1535
  msgstr ""
1536
 
1537
  #: inc/class-job.php:467
 
1538
  msgid "[INFO] Script restart time is configured to %1$d seconds"
1539
  msgstr ""
1540
 
1541
  #: inc/class-job.php:470
 
1542
  msgid "[INFO] MySQL ver.: %s"
1543
  msgstr ""
1544
 
1545
  #: inc/class-job.php:472
 
1546
  msgid "[INFO] Web Server: %s"
1547
  msgstr ""
1548
 
1549
  #: inc/class-job.php:476
 
1550
  msgid "[INFO] curl ver.: %1$s; %2$s"
1551
  msgstr ""
1552
 
1553
  #: inc/class-job.php:478
 
1554
  msgid "[INFO] Temp folder is: %s"
1555
  msgstr ""
1556
 
1557
  #: inc/class-job.php:485
 
1558
  msgid "[INFO] Logfile is: %s"
1559
  msgstr ""
1560
 
1561
  #: inc/class-job.php:492
 
1562
  msgid "[INFO] Backup file is: %s"
1563
  msgstr ""
1564
 
1565
  #: inc/class-job.php:494
 
1566
  msgid "[INFO] Backup type is: %s"
1567
  msgstr ""
1568
 
1571
  msgstr ""
1572
 
1573
  #: inc/class-job.php:514
1574
+ msgid ""
1575
+ "No destination correctly defined for backup! Please correct job settings."
1576
  msgstr ""
1577
 
1578
+ #: inc/class-job.php:644
1579
  msgid "Cannot write progress to working file. Job will be aborted."
1580
  msgstr ""
1581
 
1582
+ #: inc/class-job.php:716 inc/class-page-jobs.php:786
1583
  msgid "WARNING:"
1584
  msgstr ""
1585
 
1586
+ #: inc/class-job.php:725 inc/class-page-jobs.php:784
1587
  msgid "ERROR:"
1588
  msgstr ""
1589
 
1590
+ #: inc/class-job.php:729
1591
  msgid "DEPRECATED:"
1592
  msgstr ""
1593
 
1594
+ #: inc/class-job.php:732
1595
  msgid "STRICT NOTICE:"
1596
  msgstr ""
1597
 
1598
+ #: inc/class-job.php:737
1599
  msgid "RECOVERABLE ERROR:"
1600
  msgstr ""
1601
 
1602
+ #: inc/class-job.php:985
1603
  msgid "Aborted by user!"
1604
  msgstr ""
1605
 
1606
+ #: inc/class-job.php:1019
 
1607
  msgid "One old log deleted"
1608
  msgid_plural "%d old logs deleted"
1609
  msgstr[0] ""
1610
  msgstr[1] ""
1611
 
1612
+ #: inc/class-job.php:1026 inc/class-page-jobs.php:784
1613
+ msgid ""
1614
+ "Job has ended with errors in %s seconds. You must resolve the errors for "
1615
+ "correct execution."
1616
  msgstr ""
1617
 
1618
+ #: inc/class-job.php:1028
1619
+ msgid ""
1620
+ "Job finished with warnings in %s seconds. Please resolve them for correct "
1621
+ "execution."
1622
  msgstr ""
1623
 
1624
+ #: inc/class-job.php:1030 inc/class-page-jobs.php:788
 
1625
  msgid "Job done in %s seconds."
1626
  msgstr ""
1627
 
1628
+ #: inc/class-job.php:1074
1629
  msgid "SUCCESSFUL"
1630
  msgstr ""
1631
 
1632
+ #: inc/class-job.php:1076
1633
  msgid "WARNING"
1634
  msgstr ""
1635
 
1636
+ #: inc/class-job.php:1079
1637
  msgid "ERROR"
1638
  msgstr ""
1639
 
1640
+ #: inc/class-job.php:1082
 
1641
  msgid "[%3$s] BackWPup log %1$s: %2$s"
1642
  msgstr ""
1643
 
1644
+ #: inc/class-job.php:1188
 
1645
  msgid "Restart after %1$d seconds."
1646
  msgstr ""
1647
 
1648
+ #: inc/class-job.php:1190
1649
  msgid "Restart after getting signal."
1650
  msgstr ""
1651
 
1652
+ #: inc/class-job.php:1363
1653
  msgid "Job restarts due to inactivity for more than 5 minutes."
1654
  msgstr ""
1655
 
1656
+ #: inc/class-job.php:1461
1657
  msgid "Step aborted: too many attempts!"
1658
  msgstr ""
1659
 
1660
+ #: inc/class-job.php:1532
 
1661
  msgid "%d. Trying to create backup archive &hellip;"
1662
  msgstr ""
1663
 
1664
+ #: inc/class-job.php:1540
 
1665
  msgctxt "Archive compression method"
1666
  msgid "Compressing files as %s. Please be patient, this may take a moment."
1667
  msgstr ""
1668
 
1669
+ #: inc/class-job.php:1547
1670
  msgid "Adding Extra files to Archive"
1671
  msgstr ""
1672
 
1673
+ #: inc/class-job.php:1559 inc/class-job.php:1619
1674
  msgid "Cannot create backup archive correctly. Aborting creation."
1675
  msgstr ""
1676
 
1677
+ #: inc/class-job.php:1575
 
1678
  msgid "Archiving Folder: %s"
1679
  msgstr ""
1680
 
1681
+ #: inc/class-job.php:1629
1682
  msgid "Backup archive created."
1683
  msgstr ""
1684
 
1685
+ #: inc/class-job.php:1643
1686
+ msgid ""
1687
+ "The Backup archive will be too large for file operations with this PHP "
1688
+ "Version. You might want to consider splitting the backup job in multiple "
1689
+ "jobs with less files each."
1690
  msgstr ""
1691
 
1692
+ #: inc/class-job.php:1646
 
1693
  msgid "Archive size is %s."
1694
  msgstr ""
1695
 
1696
+ #: inc/class-job.php:1649
 
1697
  msgid "%1$d Files with %2$s in Archive."
1698
  msgstr ""
1699
 
1700
+ #: inc/class-job.php:1698
 
1701
  msgctxt "Folder name"
1702
+ msgid "Folder %s does not exist"
1703
  msgstr ""
1704
 
1705
+ #: inc/class-job.php:1703
 
1706
  msgctxt "Folder name"
1707
+ msgid "Folder %s is not readable"
1708
  msgstr ""
1709
 
1710
+ #: inc/class-job.php:1724
 
1711
  msgid "Link \"%s\" not following."
1712
  msgstr ""
1713
 
1714
+ #: inc/class-job.php:1726
 
1715
  msgid "File \"%s\" is not readable!"
1716
  msgstr ""
1717
 
1718
+ #: inc/class-job.php:1730
1719
+ msgid ""
1720
+ "File size of “%s cannot be retrieved. File might be too large and will not "
1721
+ "be added to queue."
1722
  msgstr ""
1723
 
1724
+ #: inc/class-job.php:1813
 
1725
  msgid "%d. Trying to generate a manifest file&#160;&hellip;"
1726
  msgstr ""
1727
 
1728
+ #: inc/class-job.php:1869
1729
  msgid "You may have noticed the manifest.json file in this archive."
1730
  msgstr ""
1731
 
1732
+ #: inc/class-job.php:1870
1733
+ msgid ""
1734
+ "manifest.json might be needed for later restoring a backup from this archive."
1735
  msgstr ""
1736
 
1737
+ #: inc/class-job.php:1871
1738
+ msgid ""
1739
+ "Please leave manifest.json untouched and in place. Otherwise it is safe to "
1740
+ "be ignored."
1741
  msgstr ""
1742
 
1743
+ #: inc/class-job.php:1881
 
1744
  msgid "Added manifest.json file with %1$s to backup file list."
1745
  msgstr ""
1746
 
1747
+ #: inc/class-job.php:1920
1748
  msgid "Wrong BackWPup JobID"
1749
  msgstr ""
1750
 
1751
+ #: inc/class-job.php:1933
1752
  msgid "A BackWPup job is already running"
1753
  msgstr ""
1754
 
1755
+ #: inc/class-job.php:2301
1756
+ msgctxt ""
1757
+ "SIGHUP: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1758
+ "details"
1759
  msgid "Hangup detected on controlling terminal or death of controlling process"
1760
  msgstr ""
1761
 
1762
+ #: inc/class-job.php:2305
1763
+ msgctxt ""
1764
+ "SIGINT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1765
+ "details"
1766
  msgid "Interrupt from keyboard"
1767
  msgstr ""
1768
 
1769
+ #: inc/class-job.php:2309
1770
+ msgctxt ""
1771
+ "SIGQUIT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1772
+ "details"
1773
  msgid "Quit from keyboard"
1774
  msgstr ""
1775
 
1776
+ #: inc/class-job.php:2313
1777
+ msgctxt ""
1778
+ "SIGILL: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1779
+ "details"
1780
  msgid "Illegal Instruction"
1781
  msgstr ""
1782
 
1783
+ #: inc/class-job.php:2317
1784
+ msgctxt ""
1785
+ "SIGABRT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1786
+ "details"
1787
  msgid "Abort signal from abort(3)"
1788
  msgstr ""
1789
 
1790
+ #: inc/class-job.php:2321
1791
+ msgctxt ""
1792
+ "SIGBUS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1793
+ "details"
1794
  msgid "Bus error (bad memory access)"
1795
  msgstr ""
1796
 
1797
+ #: inc/class-job.php:2325
1798
+ msgctxt ""
1799
+ "SIGFPE: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1800
+ "details"
1801
  msgid "Floating point exception"
1802
  msgstr ""
1803
 
1804
+ #: inc/class-job.php:2329
1805
+ msgctxt ""
1806
+ "SIGSEGV: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1807
+ "details"
1808
  msgid "Invalid memory reference"
1809
  msgstr ""
1810
 
1811
+ #: inc/class-job.php:2333
1812
+ msgctxt ""
1813
+ "SIGTERM: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1814
+ "details"
1815
  msgid "Termination signal"
1816
  msgstr ""
1817
 
1818
+ #: inc/class-job.php:2337
1819
+ msgctxt ""
1820
+ "SIGSTKFLT: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1821
+ "details"
1822
  msgid "Stack fault on coprocessor"
1823
  msgstr ""
1824
 
1825
+ #: inc/class-job.php:2341
1826
+ msgctxt ""
1827
+ "SIGUSR1: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1828
+ "details"
1829
  msgid "User-defined signal 1"
1830
  msgstr ""
1831
 
1832
+ #: inc/class-job.php:2345
1833
+ msgctxt ""
1834
+ "SIGUSR2: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1835
+ "details"
1836
  msgid "User-defined signal 2"
1837
  msgstr ""
1838
 
1839
+ #: inc/class-job.php:2349
1840
+ msgctxt ""
1841
+ "SIGURG: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1842
+ "details"
1843
  msgid "Urgent condition on socket"
1844
  msgstr ""
1845
 
1846
+ #: inc/class-job.php:2353
1847
+ msgctxt ""
1848
+ "SIGXCPU: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1849
+ "details"
1850
  msgid "CPU time limit exceeded"
1851
  msgstr ""
1852
 
1853
+ #: inc/class-job.php:2357
1854
+ msgctxt ""
1855
+ "SIGXFSZ: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1856
+ "details"
1857
  msgid "File size limit exceeded"
1858
  msgstr ""
1859
 
1860
+ #: inc/class-job.php:2361
1861
+ msgctxt ""
1862
+ "SIGPWR: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1863
+ "details"
1864
  msgid "Power failure"
1865
  msgstr ""
1866
 
1867
+ #: inc/class-job.php:2365
1868
+ msgctxt ""
1869
+ "SIGSYS: Please see http://man7.org/linux/man-pages/man7/signal.7.html for "
1870
+ "details"
1871
  msgid "Bad argument to routine"
1872
  msgstr ""
1873
 
1874
+ #: inc/class-job.php:2372
 
1875
  msgid "Signal \"%1$s\" (%2$s) is sent to script!"
1876
  msgstr ""
1877
 
1878
+ #: inc/class-job.php:2406 inc/class-job.php:2419
 
1879
  msgid "System: %s"
1880
  msgstr ""
1881
 
1882
+ #: inc/class-job.php:2434
 
1883
  msgid "Exception caught in %1$s: %2$s"
1884
  msgstr ""
1885
 
1891
  msgid "Check database tables"
1892
  msgstr ""
1893
 
1894
+ #. #-#-#-#-# backwpup.pot (BackWPup Pro 3.3.7) #-#-#-#-#
1895
+ #. Author URI of the plugin/theme
1896
  #: inc/class-jobtype-dbcheck.php:17 inc/class-jobtype-dbdump.php:17
1897
  #: inc/class-jobtype-file.php:17 inc/class-jobtype-wpexp.php:17
1898
  #: inc/class-jobtype-wpplugin.php:17 inc/pro/class-wizard-job.php:19
1922
  msgstr ""
1923
 
1924
  #: inc/class-jobtype-dbcheck.php:79
 
1925
  msgid "%d. Trying to check database&#160;&hellip;"
1926
  msgstr ""
1927
 
1928
  #: inc/class-jobtype-dbcheck.php:111
 
1929
  msgid "Table %1$s is a view. Not checked."
1930
  msgstr ""
1931
 
1932
  #: inc/class-jobtype-dbcheck.php:116
 
1933
  msgid "Table %1$s is not a MyISAM/InnoDB table. Not checked."
1934
  msgstr ""
1935
 
1936
  #: inc/class-jobtype-dbcheck.php:124 inc/class-jobtype-dbcheck.php:127
1937
  #: inc/class-jobtype-dbcheck.php:129
 
1938
  msgid "Result of table check for %1$s is: %2$s"
1939
  msgstr ""
1940
 
1941
  #: inc/class-jobtype-dbcheck.php:135 inc/class-jobtype-dbcheck.php:137
1942
  #: inc/class-jobtype-dbcheck.php:139
 
1943
  msgid "Result of table repair for %1$s is: %2$s"
1944
  msgstr ""
1945
 
1988
  msgstr ""
1989
 
1990
  #: inc/class-jobtype-dbdump.php:159 inc/pro/class-jobtype-dbdump.php:475
 
1991
  msgid "%d. Try to backup database&#160;&hellip;"
1992
  msgstr ""
1993
 
1994
  #: inc/class-jobtype-dbdump.php:173 inc/pro/class-jobtype-dbdump.php:495
 
1995
  msgid "Connected to database %1$s on %2$s"
1996
  msgstr ""
1997
 
2000
  msgstr ""
2001
 
2002
  #: inc/class-jobtype-dbdump.php:210 inc/pro/class-jobtype-dbdump.php:536
 
2003
  msgid "Backup database table \"%s\" with \"%s\" records"
2004
  msgstr ""
2005
 
2008
  msgstr ""
2009
 
2010
  #: inc/class-jobtype-dbdump.php:254 inc/pro/class-jobtype-dbdump.php:763
 
2011
  msgid "Added database dump \"%1$s\" with %2$s to backup file list"
2012
  msgstr ""
2013
 
2032
  msgid "Backup WordPress install folder"
2033
  msgstr ""
2034
 
2035
+ #: inc/class-jobtype-file.php:83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2036
  msgid "Backup content folder"
2037
  msgstr ""
2038
 
2039
+ #: inc/class-jobtype-file.php:91
2040
  msgid "Backup plugins"
2041
  msgstr ""
2042
 
2043
+ #: inc/class-jobtype-file.php:99
2044
  msgid "Backup themes"
2045
  msgstr ""
2046
 
2047
+ #: inc/class-jobtype-file.php:107 inc/pro/class-wizard-job.php:745
2048
  #: inc/pro/class-wizard-job.php:746
2049
  msgid "Backup uploads folder"
2050
  msgstr ""
2051
 
2052
+ #: inc/class-jobtype-file.php:115
2053
  msgid "Extra folders to backup"
2054
  msgstr ""
2055
 
2056
+ #: inc/class-jobtype-file.php:118
2057
+ msgid ""
2058
+ "Separate folder names with a line-break or a comma. Folders must be set with "
2059
+ "their absolute path!"
2060
  msgstr ""
2061
 
2062
+ #: inc/class-jobtype-file.php:123
2063
  msgid "Exclude from backup"
2064
  msgstr ""
2065
 
2066
+ #: inc/class-jobtype-file.php:127
2067
  msgid "Thumbnails in uploads"
2068
  msgstr ""
2069
 
2070
+ #: inc/class-jobtype-file.php:129
2071
  msgid "Don't backup thumbnails from the site's uploads folder."
2072
  msgstr ""
2073
 
2074
+ #: inc/class-jobtype-file.php:133
2075
  msgid "Exclude files/folders from backup"
2076
  msgstr ""
2077
 
2078
+ #: inc/class-jobtype-file.php:136
2079
+ msgid ""
2080
+ "Separate file / folder name parts with a line-break or a comma. For example /"
2081
+ "logs/,.log,.tmp"
2082
  msgstr ""
2083
 
2084
+ #: inc/class-jobtype-file.php:141
2085
  msgid "Special options"
2086
  msgstr ""
2087
 
2088
+ #: inc/class-jobtype-file.php:145
2089
  msgid "Include special files"
2090
  msgstr ""
2091
 
2092
+ #: inc/class-jobtype-file.php:147
2093
+ msgid ""
2094
+ "Backup wp-config.php, robots.txt, nginx.conf, .htaccess, .htpasswd, favicon."
2095
+ "ico, and Web.config from root if it is not included in backup."
2096
  msgstr ""
2097
 
2098
+ #: inc/class-jobtype-file.php:151
2099
  msgid "Use one folder above as WP install folder"
2100
  msgstr ""
2101
 
2102
+ #: inc/class-jobtype-file.php:154
2103
+ msgid ""
2104
+ "Use one folder above as WordPress install folder! That can be helpful, if "
2105
+ "you would backup files and folder that are not in the WordPress installation "
2106
+ "folder. Or if you made a \"<a href=\"https://codex.wordpress.org/"
2107
+ "Giving_WordPress_Its_Own_Directory\">Giving WordPress Its Own Directory</a>"
2108
+ "\" installation. Excludes must be configured again."
2109
  msgstr ""
2110
 
2111
+ #: inc/class-jobtype-file.php:237
 
2112
  msgid "%d. Trying to make a list of folders to back up&#160;&hellip;"
2113
  msgstr ""
2114
 
2115
+ #: inc/class-jobtype-file.php:357 inc/class-jobtype-file.php:361
2116
+ #: inc/class-jobtype-file.php:378
 
 
 
2117
  msgid "Added \"%s\" to backup file list"
2118
  msgstr ""
2119
 
2120
+ #: inc/class-jobtype-file.php:384
2121
  msgid "No files/folder for the backup."
2122
  msgstr ""
2123
 
2124
+ #: inc/class-jobtype-file.php:386
 
2125
  msgid "%1$d folders to backup."
2126
  msgstr ""
2127
 
2128
+ #: inc/class-jobtype-file.php:433
 
2129
  msgid "Folder \"%s\" is not readable!"
2130
  msgstr ""
2131
 
2132
+ #: inc/class-jobtype-file.php:493
2133
+ msgid "Path as set by user (symlink?): %s"
2134
+ msgstr ""
2135
+
2136
+ #: inc/class-jobtype-file.php:496
2137
+ msgid "Exclude:"
2138
+ msgstr ""
2139
+
2140
+ #: inc/class-jobtype-file.php:508
2141
+ msgid "Excluded by .donotbackup file!"
2142
+ msgstr ""
2143
+
2144
  #: inc/class-jobtype-wpexp.php:13
2145
  msgid "XML export"
2146
  msgstr ""
2179
  msgstr ""
2180
 
2181
  #: inc/class-jobtype-wpexp.php:112
 
2182
  msgid "%d. Trying to create a WordPress export to XML file&#160;&hellip;"
2183
  msgstr ""
2184
 
2185
  #: inc/class-jobtype-wpexp.php:127
2186
+ msgid "WP Export: Post type “%s” does not allow export."
 
2187
  msgstr ""
2188
 
2189
  #: inc/class-jobtype-wpexp.php:172 inc/class-jobtype-wpexp.php:186
2198
  msgstr ""
2199
 
2200
  #: inc/class-jobtype-wpexp.php:422
 
2201
  msgid "XML WARNING (%s): %s"
2202
  msgstr ""
2203
 
2204
  #: inc/class-jobtype-wpexp.php:425
 
2205
  msgid "XML RECOVERABLE (%s): %s"
2206
  msgstr ""
2207
 
2208
  #: inc/class-jobtype-wpexp.php:428
 
2209
  msgid "XML ERROR (%s): %s"
2210
  msgstr ""
2211
 
2214
  msgstr ""
2215
 
2216
  #: inc/class-jobtype-wpexp.php:444 inc/class-jobtype-wpexp.php:451
2217
+ msgid ""
2218
+ "This does not appear to be a WXR file, missing/invalid WXR version number"
2219
  msgstr ""
2220
 
2221
  #: inc/class-jobtype-wpexp.php:460
2223
  msgstr ""
2224
 
2225
  #: inc/class-jobtype-wpexp.php:462
2226
+ msgid ""
2227
+ "WP Export file can not be checked, because no XML extension is loaded, to "
2228
+ "ensure the file verification."
2229
  msgstr ""
2230
 
2231
  #: inc/class-jobtype-wpexp.php:474 inc/pro/class-jobtype-dbdump.php:741
2237
  msgstr ""
2238
 
2239
  #: inc/class-jobtype-wpexp.php:500
 
2240
  msgid "Added XML export \"%1$s\" with %2$s to backup file list."
2241
  msgstr ""
2242
 
2253
  msgstr ""
2254
 
2255
  #: inc/class-jobtype-wpplugin.php:96
 
2256
  msgid "%d. Trying to generate a file with installed plugin names&#160;&hellip;"
2257
  msgstr ""
2258
 
2261
  msgstr ""
2262
 
2263
  #: inc/class-jobtype-wpplugin.php:126
 
2264
  msgid "from %s"
2265
  msgstr ""
2266
 
2277
  msgstr ""
2278
 
2279
  #: inc/class-jobtype-wpplugin.php:148
 
2280
  msgid "Added plugin list file \"%1$s\" with %2$s to backup file list."
2281
  msgstr ""
2282
 
2297
  msgstr ""
2298
 
2299
  #: inc/class-mysqldump.php:110 inc/pro/class-jobtype-dbdump.php:843
 
2300
  msgid "Cannot connect to MySQL database %1$d: %2$s"
2301
  msgstr ""
2302
 
2303
  #: inc/class-mysqldump.php:117
 
2304
  msgctxt "Database Charset"
2305
  msgid "Cannot set DB charset to %s error: %s"
2306
  msgstr ""
2318
  #: inc/pro/class-jobtype-dbdump.php:859 inc/pro/class-jobtype-dbdump.php:873
2319
  #: inc/pro/class-jobtype-dbdump.php:921 inc/pro/class-jobtype-dbdump.php:940
2320
  #: inc/pro/class-jobtype-dbdump.php:983
 
2321
  msgid "Database error %1$s for query %2$s"
2322
  msgstr ""
2323
 
2324
  #: inc/class-mysqldump.php:479
 
2325
  msgid "Start for table backup is not correctly set: %1$s"
2326
  msgstr ""
2327
 
2328
  #: inc/class-mysqldump.php:483
 
2329
  msgid "Length for table backup is not correctly set: %1$s"
2330
  msgstr ""
2331
 
2332
+ #: inc/class-mysqldump.php:561
2333
  msgid "Error while writing file!"
2334
  msgstr ""
2335
 
2336
+ #: inc/class-option.php:167 inc/class-page-editjob.php:97
2337
+ #: inc/class-page-editjob.php:383
2338
  msgid "New Job"
2339
  msgstr ""
2340
 
2343
  msgstr ""
2344
 
2345
  #: inc/class-page-about.php:370 inc/class-page-backwpup.php:75
2346
+ msgid ""
2347
+ "BackWPup’s job wizards make planning and scheduling your backup jobs a "
2348
+ "breeze."
2349
  msgstr ""
2350
 
2351
  #: inc/class-page-about.php:371 inc/class-page-about.php:384
2352
+ msgid ""
2353
+ "Use your backup archives to save your entire WordPress installation "
2354
+ "including <code>/wp-content/</code>. Push them to an external storage "
2355
+ "service if you don’t want to save the backups on the same server. With a "
2356
+ "single backup archive you are able to restore an installation. Use a tool "
2357
+ "like phpMyAdmin to restore your database backup files."
2358
  msgstr ""
2359
 
2360
  #: inc/class-page-about.php:372
2361
+ msgid ""
2362
+ "Ready to <a href=\"%1$s\">set up a backup job</a>? You can <a href=\"%2$s"
2363
+ "\">use the wizards</a> or plan your backup in expert mode."
2364
  msgstr ""
2365
 
2366
  #: inc/class-page-about.php:382
2368
  msgstr ""
2369
 
2370
  #: inc/class-page-about.php:385
2371
+ msgid ""
2372
+ "Ready to set up a backup job? Use one of the wizards to plan what you want "
2373
+ "to save."
2374
  msgstr ""
2375
 
2376
  #: inc/class-page-about.php:398
2382
  msgstr ""
2383
 
2384
  #: inc/class-page-about.php:402
2385
+ msgid ""
2386
+ "With BackWPup you can schedule the database backup to run automatically. "
2387
+ "With a single backup file you can restore your database. You should <a href="
2388
+ "\"%s\">set up a backup job</a>, so you will never forget it. There is also "
2389
+ "an option to repair and optimize the database after each backup."
2390
  msgstr ""
2391
 
2392
  #: inc/class-page-about.php:407 inc/class-page-about.php:411
2394
  msgstr ""
2395
 
2396
  #: inc/class-page-about.php:408
2397
+ msgid ""
2398
+ "You can choose the built-in WordPress export format in addition or exclusive "
2399
+ "to save your data. This works in automated backups too of course. The "
2400
+ "advantage is: you can import these files into a blog with the regular "
2401
+ "WordPress importer."
2402
  msgstr ""
2403
 
2404
  #: inc/class-page-about.php:416
2410
  msgstr ""
2411
 
2412
  #: inc/class-page-about.php:420
2413
+ msgid ""
2414
+ "You can backup all your attachments, also all system files, plugins and "
2415
+ "themes in a single file. You can <a href=\"%s\">create a job</a> to update a "
2416
+ "backup copy of your file system only when files are changed."
2417
  msgstr ""
2418
 
2419
  #: inc/class-page-about.php:425 inc/class-page-about.php:429
2421
  msgstr ""
2422
 
2423
  #: inc/class-page-about.php:426
2424
+ msgid ""
2425
+ "By default everything is encrypted: connections to external services, local "
2426
+ "files and access to directories."
2427
  msgstr ""
2428
 
2429
  #: inc/class-page-about.php:434 inc/class-page-about.php:437
2431
  msgstr ""
2432
 
2433
  #: inc/class-page-about.php:438
2434
+ msgid ""
2435
+ "BackWPup supports multiple cloud services in parallel. This ensures backups "
2436
+ "are redundant."
2437
  msgstr ""
2438
 
2439
  #: inc/class-page-about.php:444
2619
  msgstr ""
2620
 
2621
  #: inc/class-page-backups.php:361 inc/class-page-backwpup.php:321
2622
+ #: inc/class-page-backwpup.php:384 inc/class-page-jobs.php:318
2623
  #: inc/class-page-logs.php:164
 
2624
  msgid "%1$s at %2$s"
2625
  msgstr ""
2626
 
2634
  msgstr ""
2635
 
2636
  #: inc/class-page-backups.php:489
 
2637
  msgid "%s &rsaquo; Manage Backup Archives"
2638
  msgstr ""
2639
 
2640
  #: inc/class-page-backwpup.php:67
 
2641
  msgid "%s &rsaquo; Dashboard"
2642
  msgstr ""
2643
 
2647
  msgstr ""
2648
 
2649
  #: inc/class-page-backwpup.php:75 inc/class-page-backwpup.php:84
2650
+ msgid ""
2651
+ "Use your backup archives to save your entire WordPress installation "
2652
+ "including <code>/wp-content/</code>. Push them to an external storage "
2653
+ "service if you don’t want to save the backups on the same server."
2654
  msgstr ""
2655
 
2656
  #: inc/class-page-backwpup.php:76 inc/class-page-backwpup.php:85
2659
  msgstr ""
2660
 
2661
  #: inc/class-page-backwpup.php:77 inc/class-page-backwpup.php:86
2662
+ msgid ""
2663
+ "With a single backup archive you are able to restore an installation. Use a "
2664
+ "tool like phpMyAdmin to restore your database backup files."
2665
  msgstr ""
2666
 
2667
  #: inc/class-page-backwpup.php:78 inc/class-page-backwpup.php:87
2670
  msgstr ""
2671
 
2672
  #: inc/class-page-backwpup.php:79
2673
+ msgid ""
2674
+ "Use one of the wizards to plan a backup, or use <a href=\"%s\">expert mode</"
2675
+ "a> for full control over all options."
2676
  msgstr ""
2677
 
2678
  #: inc/class-page-backwpup.php:79 inc/class-page-backwpup.php:89
2679
+ msgid ""
2680
+ "<strong>Please note: You are solely responsible for the security of your "
2681
+ "data; the authors of this plugin are not.</strong>"
2682
  msgstr ""
2683
 
2684
  #: inc/class-page-backwpup.php:84
2685
+ msgid ""
2686
+ "Use the short links in the <strong>First steps</strong> box to plan and "
2687
+ "schedule backup jobs."
2688
  msgstr ""
2689
 
2690
  #: inc/class-page-backwpup.php:88
 
2691
  msgid "<a href=\"%s\">Add a new backup job</a> and plan what you want to save."
2692
  msgstr ""
2693
 
2720
  msgstr ""
2721
 
2722
  #: inc/class-page-backwpup.php:117
2723
+ msgid ""
2724
+ "Generate a database backup of WordPress tables and download it right away!"
2725
  msgstr ""
2726
 
2727
  #: inc/class-page-backwpup.php:117
2738
  msgstr ""
2739
 
2740
  #: inc/class-page-backwpup.php:130
 
2741
  msgid "<strong>RSS Error</strong>: %s"
2742
  msgstr ""
2743
 
2744
  #: inc/class-page-backwpup.php:132
2745
+ msgid ""
2746
+ "An error has occurred, which probably means the feed is down. Try again "
2747
+ "later."
2748
  msgstr ""
2749
 
2750
  #: inc/class-page-backwpup.php:146
2787
 
2788
  #: inc/class-page-backwpup.php:257
2789
  msgctxt "Pro teaser box"
2790
+ msgid "First-class <strong>dedicated support</strong> at backwpup.com."
2791
  msgstr ""
2792
 
2793
  #: inc/class-page-backwpup.php:258
2802
 
2803
  #: inc/class-page-backwpup.php:260
2804
  msgctxt "Pro teaser box, link text"
2805
+ msgid "And more"
2806
  msgstr ""
2807
 
2808
  #: inc/class-page-backwpup.php:262
2825
  msgstr ""
2826
 
2827
  #: inc/class-page-backwpup.php:307
 
2828
  msgid "working since %d seconds"
2829
  msgstr ""
2830
 
2844
  msgid "Result"
2845
  msgstr ""
2846
 
2847
+ #: inc/class-page-backwpup.php:366
2848
+ msgid "Could not open log folder: %s"
2849
+ msgstr ""
2850
+
2851
+ #: inc/class-page-backwpup.php:389
2852
  msgid "%d ERROR"
2853
  msgid_plural "%d ERRORS"
2854
  msgstr[0] ""
2855
  msgstr[1] ""
2856
 
2857
+ #: inc/class-page-backwpup.php:392
 
2858
  msgid "%d WARNING"
2859
  msgid_plural "%d WARNINGS"
2860
  msgstr[0] ""
2861
  msgstr[1] ""
2862
 
2863
+ #: inc/class-page-backwpup.php:395
2864
  msgid "OK"
2865
  msgstr ""
2866
 
2867
  #: inc/class-page-editjob.php:98
 
2868
  msgid "Job with ID %d"
2869
  msgstr ""
2870
 
2871
  #: inc/class-page-editjob.php:222
 
2872
  msgid "Changes for job <i>%s</i> saved."
2873
  msgstr ""
2874
 
2880
  msgid "Run now"
2881
  msgstr ""
2882
 
2883
+ #: inc/class-page-editjob.php:325
 
2884
  msgid "%1$s &rsaquo; Job: %2$s"
2885
  msgstr ""
2886
 
2887
+ #: inc/class-page-editjob.php:328 inc/class-page-settings.php:117
2888
  msgid "General"
2889
  msgstr ""
2890
 
2891
+ #: inc/class-page-editjob.php:328
2892
  msgid "Schedule"
2893
  msgstr ""
2894
 
2895
+ #: inc/class-page-editjob.php:343
 
2896
  msgid "To: %s"
2897
  msgstr ""
2898
 
2899
+ #: inc/class-page-editjob.php:378 inc/class-page-editjob.php:383
2900
  #: inc/class-page-jobs.php:125
2901
  msgid "Job Name"
2902
  msgstr ""
2903
 
2904
+ #: inc/class-page-editjob.php:381
2905
  msgid "Please name this job."
2906
  msgstr ""
2907
 
2908
+ #: inc/class-page-editjob.php:388
2909
  msgid "Job Tasks"
2910
  msgstr ""
2911
 
2912
+ #: inc/class-page-editjob.php:391 inc/pro/class-wizard-job.php:254
2913
  msgid "This job is a&#160;&hellip;"
2914
  msgstr ""
2915
 
2916
+ #: inc/class-page-editjob.php:394 inc/pro/class-wizard-job.php:257
2917
  msgid "Job tasks"
2918
  msgstr ""
2919
 
2920
+ #: inc/class-page-editjob.php:412
2921
  msgid "Backup File Creation"
2922
  msgstr ""
2923
 
2924
+ #: inc/class-page-editjob.php:417 inc/class-page-editjob.php:420
2925
  #: inc/pro/class-wizard-job.php:402 inc/pro/class-wizard-job.php:405
2926
  msgid "Backup type"
2927
  msgstr ""
2928
 
2929
+ #: inc/class-page-editjob.php:423
2930
  msgid "Synchronize file by file to destination"
2931
  msgstr ""
2932
 
2933
+ #: inc/class-page-editjob.php:428 inc/pro/class-wizard-job.php:413
2934
  msgid "Create a backup archive"
2935
  msgstr ""
2936
 
2937
+ #: inc/class-page-editjob.php:436
2938
  msgid "Archive name"
2939
  msgstr ""
2940
 
2941
+ #: inc/class-page-editjob.php:439
2942
+ msgid ""
2943
+ "<em>Note</em>: In order for backup file tracking to work, the archive name "
2944
+ "must begin with %s."
2945
+ msgstr ""
2946
+
2947
+ #: inc/class-page-editjob.php:447
2948
  msgid "Replacement patterns:"
2949
  msgstr ""
2950
 
2951
+ #: inc/class-page-editjob.php:448
 
2952
  msgid "%d = Two digit day of the month, with leading zeros"
2953
  msgstr ""
2954
 
2955
+ #: inc/class-page-editjob.php:449
2956
  msgid "%j = Day of the month, without leading zeros"
2957
  msgstr ""
2958
 
2959
+ #: inc/class-page-editjob.php:450
2960
  msgid "%m = Day of the month, with leading zeros"
2961
  msgstr ""
2962
 
2963
+ #: inc/class-page-editjob.php:451
2964
  msgid "%n = Representation of the month (without leading zeros)"
2965
  msgstr ""
2966
 
2967
+ #: inc/class-page-editjob.php:452
2968
  msgid "%Y = Four digit representation for the year"
2969
  msgstr ""
2970
 
2971
+ #: inc/class-page-editjob.php:453
2972
  msgid "%y = Two digit representation of the year"
2973
  msgstr ""
2974
 
2975
+ #: inc/class-page-editjob.php:454
2976
  msgid "%a = Lowercase ante meridiem (am) and post meridiem (pm)"
2977
  msgstr ""
2978
 
2979
+ #: inc/class-page-editjob.php:455
2980
  msgid "%A = Uppercase ante meridiem (AM) and post meridiem (PM)"
2981
  msgstr ""
2982
 
2983
+ #: inc/class-page-editjob.php:456
2984
  msgid "%B = Swatch Internet Time"
2985
  msgstr ""
2986
 
2987
+ #: inc/class-page-editjob.php:457
2988
  msgid "%g = Hour in 12-hour format, without leading zeros"
2989
  msgstr ""
2990
 
2991
+ #: inc/class-page-editjob.php:458
2992
  msgid "%G = Hour in 24-hour format, without leading zeros"
2993
  msgstr ""
2994
 
2995
+ #: inc/class-page-editjob.php:459
2996
  msgid "%h = Hour in 12-hour format, with leading zeros"
2997
  msgstr ""
2998
 
2999
+ #: inc/class-page-editjob.php:460
3000
  msgid "%H = Hour in 24-hour format, with leading zeros"
3001
  msgstr ""
3002
 
3003
+ #: inc/class-page-editjob.php:461
3004
  msgid "%i = Two digit representation of the minute"
3005
  msgstr ""
3006
 
3007
+ #: inc/class-page-editjob.php:462
 
3008
  msgid "%s = Two digit representation of the second"
3009
  msgstr ""
3010
 
3011
+ #: inc/class-page-editjob.php:468 inc/class-page-editjob.php:471
3012
  msgid "Archive Format"
3013
  msgstr ""
3014
 
3015
+ #: inc/class-page-editjob.php:474 inc/class-page-editjob.php:476
3016
  #: inc/pro/class-wizard-job.php:426 inc/pro/class-wizard-job.php:429
3017
  msgid "Zip"
3018
  msgstr ""
3019
 
3020
+ #: inc/class-page-editjob.php:477
 
3021
  msgid "Disabled due to missing %s PHP class."
3022
  msgstr ""
3023
 
3024
+ #: inc/class-page-editjob.php:479 inc/pro/class-wizard-job.php:433
3025
  msgid "Tar"
3026
  msgstr ""
3027
 
3028
+ #: inc/class-page-editjob.php:481 inc/class-page-editjob.php:483
3029
  #: inc/pro/class-wizard-job.php:437 inc/pro/class-wizard-job.php:440
3030
  msgid "Tar GZip"
3031
  msgstr ""
3032
 
3033
+ #: inc/class-page-editjob.php:484 inc/class-page-editjob.php:490
 
3034
  msgid "Disabled due to missing %s PHP function."
3035
  msgstr ""
3036
 
3037
+ #: inc/class-page-editjob.php:487 inc/class-page-editjob.php:489
3038
  #: inc/pro/class-wizard-job.php:444 inc/pro/class-wizard-job.php:447
3039
  msgid "Tar BZip2"
3040
  msgstr ""
3041
 
3042
+ #: inc/class-page-editjob.php:497
3043
  msgid "Job Destination"
3044
  msgstr ""
3045
 
3046
+ #: inc/class-page-editjob.php:501 inc/class-page-editjob.php:504
3047
  msgid "Where should your backup file be stored?"
3048
  msgstr ""
3049
 
3050
+ #: inc/class-page-editjob.php:522
3051
  msgid "Log Files"
3052
  msgstr ""
3053
 
3054
+ #: inc/class-page-editjob.php:526
3055
  msgid "Send log to email address"
3056
  msgstr ""
3057
 
3058
+ #: inc/class-page-editjob.php:529
3059
+ msgid ""
3060
+ "Leave empty to not have log sent. Or separate with , for more than one "
3061
+ "receiver."
3062
  msgstr ""
3063
 
3064
+ #: inc/class-page-editjob.php:533
3065
  msgid "Email FROM field"
3066
  msgstr ""
3067
 
3068
+ #: inc/class-page-editjob.php:535
3069
  msgid "Your Name &lt;mail@domain.tld&gt;"
3070
  msgstr ""
3071
 
3072
+ #: inc/class-page-editjob.php:539
3073
  msgid "Errors only"
3074
  msgstr ""
3075
 
3076
+ #: inc/class-page-editjob.php:544
3077
  msgid "Send email with log only when errors occur during job execution."
3078
  msgstr ""
3079
 
3080
+ #: inc/class-page-editjob.php:555
3081
  msgid "Job Schedule"
3082
  msgstr ""
3083
 
3084
+ #: inc/class-page-editjob.php:559 inc/class-page-editjob.php:562
3085
  msgid "Start job"
3086
  msgstr ""
3087
 
3088
+ #: inc/class-page-editjob.php:566
3089
  msgid "manually only"
3090
  msgstr ""
3091
 
3092
+ #: inc/class-page-editjob.php:570
3093
  msgid "with WordPress cron"
3094
  msgstr ""
3095
 
3096
+ #: inc/class-page-editjob.php:579
3097
+ msgid ""
3098
+ "with <a href=\"https://www.easycron.com?ref=36673\" title=\"Affiliate Link!"
3099
+ "\">EasyCron.com</a>"
3100
  msgstr ""
3101
 
3102
+ #: inc/class-page-editjob.php:581
 
3103
  msgid "First setup <a href=\"%s\">API Key</a>."
3104
  msgstr ""
3105
 
3106
+ #: inc/class-page-editjob.php:590
3107
  msgid "with a link"
3108
  msgstr ""
3109
 
3110
+ #: inc/class-page-editjob.php:591
3111
+ msgid ""
3112
+ "Copy the link for an external start. This option has to be activated to make "
3113
+ "the link work."
3114
  msgstr ""
3115
 
3116
+ #: inc/class-page-editjob.php:598
3117
  msgid "Start job with CLI"
3118
  msgstr ""
3119
 
3120
+ #: inc/class-page-editjob.php:601
3121
+ msgid ""
3122
+ "Use <a href=\"http://wp-cli.org/\">WP-CLI</a> to run jobs from commandline."
3123
  msgstr ""
3124
 
3125
+ #: inc/class-page-editjob.php:606
3126
  msgid "Schedule execution time"
3127
  msgstr ""
3128
 
3129
+ #: inc/class-page-editjob.php:610 inc/class-page-editjob.php:613
3130
  msgid "Scheduler type"
3131
  msgstr ""
3132
 
3133
+ #: inc/class-page-editjob.php:617
3134
  msgid "basic"
3135
  msgstr ""
3136
 
3137
+ #: inc/class-page-editjob.php:621
3138
  msgid "advanced"
3139
  msgstr ""
3140
 
3141
+ #: inc/class-page-editjob.php:650 inc/class-page-editjob.php:718
3142
  #: inc/pro/class-wizard-job.php:320
3143
  msgid "Scheduler"
3144
  msgstr ""
3145
 
3146
+ #: inc/class-page-editjob.php:655 inc/class-page-jobs.php:126
3147
  #: inc/class-page-logs.php:139 inc/pro/class-wizard-job.php:324
3148
  msgid "Type"
3149
  msgstr ""
3150
 
3151
+ #: inc/class-page-editjob.php:660 inc/pro/class-wizard-job.php:330
3152
  msgid "Hour"
3153
  msgstr ""
3154
 
3155
+ #: inc/class-page-editjob.php:663 inc/pro/class-wizard-job.php:333
3156
  msgid "Minute"
3157
  msgstr ""
3158
 
3159
+ #: inc/class-page-editjob.php:667 inc/pro/class-wizard-job.php:337
3160
  msgid "monthly"
3161
  msgstr ""
3162
 
3163
+ #: inc/class-page-editjob.php:669 inc/pro/class-wizard-job.php:339
3164
  msgid "on"
3165
  msgstr ""
3166
 
3167
+ #: inc/class-page-editjob.php:679 inc/pro/class-wizard-job.php:349
3168
  msgid "weekly"
3169
  msgstr ""
3170
 
3171
+ #: inc/class-page-editjob.php:681 inc/class-page-editjob.php:788
3172
  #: inc/pro/class-wizard-job.php:351
3173
  msgid "Sunday"
3174
  msgstr ""
3175
 
3176
+ #: inc/class-page-editjob.php:682 inc/class-page-editjob.php:789
3177
  #: inc/pro/class-wizard-job.php:352
3178
  msgid "Monday"
3179
  msgstr ""
3180
 
3181
+ #: inc/class-page-editjob.php:683 inc/class-page-editjob.php:790
3182
  #: inc/pro/class-wizard-job.php:353
3183
  msgid "Tuesday"
3184
  msgstr ""
3185
 
3186
+ #: inc/class-page-editjob.php:684 inc/class-page-editjob.php:791
3187
  #: inc/pro/class-wizard-job.php:354
3188
  msgid "Wednesday"
3189
  msgstr ""
3190
 
3191
+ #: inc/class-page-editjob.php:685 inc/class-page-editjob.php:792
3192
  #: inc/pro/class-wizard-job.php:355
3193
  msgid "Thursday"
3194
  msgstr ""
3195
 
3196
+ #: inc/class-page-editjob.php:686 inc/class-page-editjob.php:793
3197
  #: inc/pro/class-wizard-job.php:356
3198
  msgid "Friday"
3199
  msgstr ""
3200
 
3201
+ #: inc/class-page-editjob.php:687 inc/class-page-editjob.php:794
3202
  #: inc/pro/class-wizard-job.php:357
3203
  msgid "Saturday"
3204
  msgstr ""
3205
 
3206
+ #: inc/class-page-editjob.php:697 inc/pro/class-wizard-job.php:367
3207
  msgid "daily"
3208
  msgstr ""
3209
 
3210
+ #: inc/class-page-editjob.php:707 inc/pro/class-wizard-job.php:377
3211
  msgid "hourly"
3212
  msgstr ""
3213
 
3214
+ #: inc/class-page-editjob.php:721
3215
  msgid "Minutes:"
3216
  msgstr ""
3217
 
3218
+ #: inc/class-page-editjob.php:723 inc/class-page-editjob.php:736
3219
+ #: inc/class-page-editjob.php:748 inc/class-page-editjob.php:762
3220
+ #: inc/class-page-editjob.php:784
3221
  msgid "Any (*)"
3222
  msgstr ""
3223
 
3224
+ #: inc/class-page-editjob.php:733
3225
  msgid "Hours:"
3226
  msgstr ""
3227
 
3228
+ #: inc/class-page-editjob.php:746
3229
  msgid "Day of Month:"
3230
  msgstr ""
3231
 
3232
+ #: inc/class-page-editjob.php:760
3233
  msgid "Month:"
3234
  msgstr ""
3235
 
3236
+ #: inc/class-page-editjob.php:766
3237
  msgid "January"
3238
  msgstr ""
3239
 
3240
+ #: inc/class-page-editjob.php:767
3241
  msgid "February"
3242
  msgstr ""
3243
 
3244
+ #: inc/class-page-editjob.php:768
3245
  msgid "March"
3246
  msgstr ""
3247
 
3248
+ #: inc/class-page-editjob.php:769
3249
  msgid "April"
3250
  msgstr ""
3251
 
3252
+ #: inc/class-page-editjob.php:770
3253
  msgid "May"
3254
  msgstr ""
3255
 
3256
+ #: inc/class-page-editjob.php:771
3257
  msgid "June"
3258
  msgstr ""
3259
 
3260
+ #: inc/class-page-editjob.php:772
3261
  msgid "July"
3262
  msgstr ""
3263
 
3264
+ #: inc/class-page-editjob.php:773
3265
  msgid "August"
3266
  msgstr ""
3267
 
3268
+ #: inc/class-page-editjob.php:774
3269
  msgid "September"
3270
  msgstr ""
3271
 
3272
+ #: inc/class-page-editjob.php:775
3273
  msgid "October"
3274
  msgstr ""
3275
 
3276
+ #: inc/class-page-editjob.php:776
3277
  msgid "November"
3278
  msgstr ""
3279
 
3280
+ #: inc/class-page-editjob.php:777
3281
  msgid "December"
3282
  msgstr ""
3283
 
3284
+ #: inc/class-page-editjob.php:782
3285
  msgid "Day of Week:"
3286
  msgstr ""
3287
 
3288
+ #: inc/class-page-editjob.php:818
3289
  msgid "Save changes"
3290
  msgstr ""
3291
 
3292
+ #: inc/class-page-editjob.php:905
3293
+ msgid ""
3294
+ "Working as <a href=\"http://wikipedia.org/wiki/Cron\">Cron</a> schedule:"
3295
  msgstr ""
3296
 
3297
+ #: inc/class-page-editjob.php:914
 
3298
  msgid "ATTENTION: Job runs every %d minutes!"
3299
  msgstr ""
3300
 
3301
+ #: inc/class-page-editjob.php:918
3302
  msgid "ATTENTION: Can't calculate cron!"
3303
  msgstr ""
3304
 
3305
+ #: inc/class-page-editjob.php:921
3306
  msgid "Next runtime:"
3307
  msgstr ""
3308
 
3324
  msgstr ""
3325
 
3326
  #: inc/class-page-jobs.php:172 inc/class-page-logs.php:200
 
3327
  msgid "Job ID: %d"
3328
  msgstr ""
3329
 
3344
  msgstr ""
3345
 
3346
  #: inc/class-page-jobs.php:273
 
3347
  msgid "Running for: %s seconds"
3348
  msgstr ""
3349
 
3350
  #: inc/class-page-jobs.php:280 inc/class-page-jobs.php:289
 
3351
  msgid "Cron: %s"
3352
  msgstr ""
3353
 
3354
  #: inc/class-page-jobs.php:280
 
3355
  msgid "%1$s at %2$s by WP-Cron"
3356
  msgstr ""
3357
 
3358
  #: inc/class-page-jobs.php:289
 
3359
  msgid "%1$s at %2$s by EasyCron"
3360
  msgstr ""
3361
 
3368
  msgstr ""
3369
 
3370
  #: inc/class-page-jobs.php:320
 
3371
  msgid "Runtime: %d seconds"
3372
  msgstr ""
3373
 
3388
  msgstr ""
3389
 
3390
  #: inc/class-page-jobs.php:431
 
3391
  msgid "The job \"%s\" destination \"%s\" is not configured properly"
3392
  msgstr ""
3393
 
3394
  #: inc/class-page-jobs.php:436
 
3395
  msgid "The job \"%s\" needs properly configured destinations to run!"
3396
  msgstr ""
3397
 
3398
  #: inc/class-page-jobs.php:454
3399
+ msgid ""
3400
+ "Job \"%s\" has started, but not responded for 10 seconds. Please check <a "
3401
+ "href=\"%s\">information</a>."
3402
  msgstr ""
3403
 
3404
  #: inc/class-page-jobs.php:459
 
3405
  msgid "Job \"%s\" started."
3406
  msgstr ""
3407
 
3410
  msgstr ""
3411
 
3412
  #: inc/class-page-jobs.php:584
 
3413
  msgid "%s &rsaquo; Jobs"
3414
  msgstr ""
3415
 
3416
  #: inc/class-page-jobs.php:604
 
3417
  msgid "Job currently running: %s"
3418
  msgstr ""
3419
 
3446
  msgstr ""
3447
 
3448
  #: inc/class-page-jobs.php:786
3449
+ msgid ""
3450
+ "Job has done with warnings in %s seconds. Please resolve them for correct "
3451
+ "execution."
3452
  msgstr ""
3453
 
3454
  #: inc/class-page-logs.php:113
3468
  msgstr ""
3469
 
3470
  #: inc/class-page-logs.php:222
 
3471
  msgid "1 ERROR"
3472
  msgid_plural "%d ERRORS"
3473
  msgstr[0] ""
3474
  msgstr[1] ""
3475
 
3476
  #: inc/class-page-logs.php:225
 
3477
  msgid "1 WARNING"
3478
  msgid_plural "%d WARNINGS"
3479
  msgstr[0] ""
3488
  msgstr ""
3489
 
3490
  #: inc/class-page-logs.php:391
 
3491
  msgid "%s &rsaquo; Logs"
3492
  msgstr ""
3493
 
3495
  msgid "Logfile not found!"
3496
  msgstr ""
3497
 
3498
+ #: inc/class-page-settings.php:61
3499
  msgid "Settings reset to default"
3500
  msgstr ""
3501
 
3502
+ #: inc/class-page-settings.php:104
3503
  msgid "Settings saved"
3504
  msgstr ""
3505
 
3506
+ #: inc/class-page-settings.php:115
 
3507
  msgid "%s &rsaquo; Settings"
3508
  msgstr ""
3509
 
3510
+ #: inc/class-page-settings.php:117
3511
  msgid "Network"
3512
  msgstr ""
3513
 
3514
+ #: inc/class-page-settings.php:117
3515
  msgid "API Keys"
3516
  msgstr ""
3517
 
3518
+ #: inc/class-page-settings.php:117
3519
  msgid "Information"
3520
  msgstr ""
3521
 
3522
+ #: inc/class-page-settings.php:135
3523
  msgid "Display Settings"
3524
  msgstr ""
3525
 
3526
+ #: inc/class-page-settings.php:136
3527
  msgid "Do you want to see BackWPup in the WordPress admin bar?"
3528
  msgstr ""
3529
 
3530
+ #: inc/class-page-settings.php:139
3531
  msgid "Admin bar"
3532
  msgstr ""
3533
 
3534
+ #: inc/class-page-settings.php:142
3535
  msgid "Admin Bar"
3536
  msgstr ""
3537
 
3538
+ #: inc/class-page-settings.php:145
3539
  msgid "Show BackWPup links in admin bar."
3540
  msgstr ""
3541
 
3542
+ #: inc/class-page-settings.php:151 inc/class-page-settings.php:154
3543
  msgid "Folder sizes"
3544
  msgstr ""
3545
 
3546
+ #: inc/class-page-settings.php:157
3547
+ msgid ""
3548
+ "Display folder sizes in the files tab when editing a job. (Might increase "
3549
+ "loading time of files tab.)"
3550
  msgstr ""
3551
 
3552
+ #: inc/class-page-settings.php:163
3553
  msgid "Security"
3554
  msgstr ""
3555
 
3556
+ #: inc/class-page-settings.php:164
3557
  msgid "Security option for BackWPup"
3558
  msgstr ""
3559
 
3560
+ #: inc/class-page-settings.php:167 inc/class-page-settings.php:170
3561
  msgid "Protect folders"
3562
  msgstr ""
3563
 
3564
+ #: inc/class-page-settings.php:173
3565
+ msgid ""
3566
+ "Protect BackWPup folders ( Temp, Log and Backups ) with <code>.htaccess</"
3567
+ "code> and <code>index.php</code>"
3568
  msgstr ""
3569
 
3570
+ #: inc/class-page-settings.php:186
3571
+ msgid ""
3572
+ "Every time BackWPup runs a backup job, a log file is being generated. Choose "
3573
+ "where to store your log files and how many of them."
3574
  msgstr ""
3575
 
3576
+ #: inc/class-page-settings.php:189
3577
  msgid "Log file folder"
3578
  msgstr ""
3579
 
3580
+ #: inc/class-page-settings.php:192
 
3581
  msgid "You can use absolute or relative path! Relative path is relative to %s."
3582
  msgstr ""
3583
 
3584
+ #: inc/class-page-settings.php:196
3585
  msgid "Maximum log files"
3586
  msgstr ""
3587
 
3588
+ #: inc/class-page-settings.php:199
3589
  msgid "Maximum log files in folder."
3590
  msgstr ""
3591
 
3592
+ #: inc/class-page-settings.php:203 inc/class-page-settings.php:206
3593
  msgid "Compression"
3594
  msgstr ""
3595
 
3596
+ #: inc/class-page-settings.php:209
3597
  msgid "Compress log files with GZip."
3598
  msgstr ""
3599
 
3600
+ #: inc/class-page-settings.php:215 inc/class-page-settings.php:218
3601
  msgid "Logging Level"
3602
  msgstr ""
3603
 
3604
+ #: inc/class-page-settings.php:221
3605
  msgid "Normal (translated)"
3606
  msgstr ""
3607
 
3608
+ #: inc/class-page-settings.php:222
3609
  msgid "Normal (not translated)"
3610
  msgstr ""
3611
 
3612
+ #: inc/class-page-settings.php:223
3613
  msgid "Debug (translated)"
3614
  msgstr ""
3615
 
3616
+ #: inc/class-page-settings.php:224
3617
  msgid "Debug (not translated)"
3618
  msgstr ""
3619
 
3620
+ #: inc/class-page-settings.php:227
3621
+ msgid ""
3622
+ "Debug log has much more informations than normal logs. It is for support and "
3623
+ "should be handled carefully. For support is the best to use a not translated "
3624
+ "log file. Usage of not translated logs can reduce the PHP memory usage too."
3625
  msgstr ""
3626
 
3627
+ #: inc/class-page-settings.php:236
3628
  msgid "There are a couple of general options for backup jobs. Set them here."
3629
  msgstr ""
3630
 
3631
+ #: inc/class-page-settings.php:239
3632
  msgid "Maximum number of retries for job steps"
3633
  msgstr ""
3634
 
3635
+ #: inc/class-page-settings.php:245
3636
  msgid "Maximum script execution time"
3637
  msgstr ""
3638
 
3639
+ #: inc/class-page-settings.php:248
3640
  msgid "Maximum PHP Script execution time"
3641
  msgstr ""
3642
 
3643
+ #: inc/class-page-settings.php:251
3644
  msgid "seconds."
3645
  msgstr ""
3646
 
3647
+ #: inc/class-page-settings.php:252
3648
+ msgid ""
3649
+ "Job will restart before hitting maximum execution time. Restarts will be "
3650
+ "disabled on CLI usage. If <code>ALTERNATE_WP_CRON</code> has been defined, "
3651
+ "WordPress Cron will be used for restarts, so it can take a while. 0 means no "
3652
+ "maximum."
3653
  msgstr ""
3654
 
3655
+ #: inc/class-page-settings.php:259
3656
  msgid "Key to start jobs externally with an URL"
3657
  msgstr ""
3658
 
3659
+ #: inc/class-page-settings.php:263
3660
  msgid "Will be used to protect job starts from unauthorized person."
3661
  msgstr ""
3662
 
3663
+ #: inc/class-page-settings.php:267 inc/class-page-settings.php:270
3664
  msgid "Reduce server load"
3665
  msgstr ""
3666
 
3667
+ #: inc/class-page-settings.php:273
3668
  msgid "disabled"
3669
  msgstr ""
3670
 
3671
+ #: inc/class-page-settings.php:274
3672
  msgid "minimum"
3673
  msgstr ""
3674
 
3675
+ #: inc/class-page-settings.php:275
3676
  msgid "medium"
3677
  msgstr ""
3678
 
3679
+ #: inc/class-page-settings.php:276
3680
  msgid "maximum"
3681
  msgstr ""
3682
 
3683
+ #: inc/class-page-settings.php:279
3684
+ msgid ""
3685
+ "This adds short pauses to the process. Can be used to reduce the CPU load."
3686
  msgstr ""
3687
 
3688
+ #: inc/class-page-settings.php:284
3689
  msgid "Empty output on working"
3690
  msgstr ""
3691
 
3692
+ #: inc/class-page-settings.php:287 inc/class-page-settings.php:290
3693
  msgid "Enable an empty Output on backup working."
3694
  msgstr ""
3695
 
3696
+ #: inc/class-page-settings.php:292
3697
+ msgid ""
3698
+ "This do an empty output on job working. This can help in some situations or "
3699
+ "can brake the working. You must test it."
3700
+ msgstr ""
3701
+
3702
+ #: inc/class-page-settings.php:297
3703
+ msgid "Windows IIS compatibility"
3704
+ msgstr ""
3705
+
3706
+ #: inc/class-page-settings.php:300 inc/class-page-settings.php:303
3707
+ msgid "Enable compatibility with IIS on Windows."
3708
+ msgstr ""
3709
+
3710
+ #: inc/class-page-settings.php:305
3711
+ msgid ""
3712
+ "There is a PHP bug (<a href=\"https://bugs.php.net/43817\">bug #43817</a>), "
3713
+ "which is triggered on some versions of Windows and IIS. Checking this box "
3714
+ "will enable a workaround for that bug. Only enable if you are getting errors "
3715
+ "about &ldquo;Permission denied&rdquo; in your logs."
3716
  msgstr ""
3717
 
3718
+ #: inc/class-page-settings.php:315
 
3719
  msgid "Authentication for <code>%s</code>"
3720
  msgstr ""
3721
 
3722
+ #: inc/class-page-settings.php:316
3723
+ msgid ""
3724
+ "If you protected your blog with HTTP basic authentication (.htaccess), or "
3725
+ "you use a Plugin to secure wp-cron.php, then use the authentication methods "
3726
+ "below."
3727
  msgstr ""
3728
 
3729
+ #: inc/class-page-settings.php:322 inc/class-page-settings.php:325
3730
  msgid "Authentication method"
3731
  msgstr ""
3732
 
3733
+ #: inc/class-page-settings.php:329
3734
  msgid "Basic auth"
3735
  msgstr ""
3736
 
3737
+ #: inc/class-page-settings.php:330
3738
  msgid "WordPress User"
3739
  msgstr ""
3740
 
3741
+ #: inc/class-page-settings.php:331
3742
  msgid "Query argument"
3743
  msgstr ""
3744
 
3745
+ #: inc/class-page-settings.php:338
3746
  msgid "Basic Auth Username:"
3747
  msgstr ""
3748
 
3749
+ #: inc/class-page-settings.php:344
3750
  msgid "Basic Auth Password:"
3751
  msgstr ""
3752
 
3753
+ #: inc/class-page-settings.php:349 inc/class-page-settings.php:352
3754
  msgid "Select WordPress User"
3755
  msgstr ""
3756
 
3757
+ #: inc/class-page-settings.php:368
3758
  msgid "Query arg key=value:"
3759
  msgstr ""
3760
 
3761
+ #: inc/class-page-settings.php:387 inc/class-page-settings.php:388
3762
  msgid "Setting"
3763
  msgstr ""
3764
 
3765
+ #: inc/class-page-settings.php:387 inc/class-page-settings.php:388
3766
  msgid "Value"
3767
  msgstr ""
3768
 
3769
+ #: inc/class-page-settings.php:389
3770
  msgid "WordPress version"
3771
  msgstr ""
3772
 
3773
+ #: inc/class-page-settings.php:391
3774
  msgid "BackWPup version"
3775
  msgstr ""
3776
 
3777
+ #: inc/class-page-settings.php:391
3778
  msgid "Get pro."
3779
  msgstr ""
3780
 
3781
+ #: inc/class-page-settings.php:393
3782
  msgid "BackWPup Pro version"
3783
  msgstr ""
3784
 
3785
+ #: inc/class-page-settings.php:401
3786
  msgid "PHP version"
3787
  msgstr ""
3788
 
3789
+ #: inc/class-page-settings.php:402
3790
  msgid "MySQL version"
3791
  msgstr ""
3792
 
3793
+ #: inc/class-page-settings.php:405 inc/class-page-settings.php:409
3794
  msgid "cURL version"
3795
  msgstr ""
3796
 
3797
+ #: inc/class-page-settings.php:406
3798
  msgid "cURL SSL version"
3799
  msgstr ""
3800
 
3801
+ #: inc/class-page-settings.php:409
3802
  msgid "unavailable"
3803
  msgstr ""
3804
 
3805
+ #: inc/class-page-settings.php:411
3806
  msgid "WP-Cron url:"
3807
  msgstr ""
3808
 
3809
+ #: inc/class-page-settings.php:413
3810
  msgid "Server self connect:"
3811
  msgstr ""
3812
 
3813
+ #: inc/class-page-settings.php:418
3814
  msgid "<strong>Not expected HTTP response:</strong><br>"
3815
  msgstr ""
3816
 
3817
+ #: inc/class-page-settings.php:420
 
3818
  msgid "WP Http Error: <code>%s</code>"
3819
  msgstr ""
3820
 
3821
+ #: inc/class-page-settings.php:422
 
3822
  msgid "Status-Code: <code>%d</code>"
3823
  msgstr ""
3824
 
3825
+ #: inc/class-page-settings.php:430
 
3826
  msgid "Content: <code>%s</code>"
3827
  msgstr ""
3828
 
3829
+ #: inc/class-page-settings.php:434
3830
  msgid "Response Test O.K."
3831
  msgstr ""
3832
 
3833
+ #: inc/class-page-settings.php:438
3834
  msgid "Temp folder:"
3835
  msgstr ""
3836
 
3837
+ #: inc/class-page-settings.php:440
 
3838
  msgid "Temp folder %s doesn't exist."
3839
  msgstr ""
3840
 
3841
+ #: inc/class-page-settings.php:442
 
3842
  msgid "Temporary folder %s is not writable."
3843
  msgstr ""
3844
 
3845
+ #: inc/class-page-settings.php:449
3846
  msgid "Log folder:"
3847
  msgstr ""
3848
 
3849
+ #: inc/class-page-settings.php:451
 
3850
  msgid "Logs folder %s not exist."
3851
  msgstr ""
3852
 
3853
+ #: inc/class-page-settings.php:453
 
3854
  msgid "Log folder %s is not writable."
3855
  msgstr ""
3856
 
3857
+ #: inc/class-page-settings.php:458
3858
  msgid "Server"
3859
  msgstr ""
3860
 
3861
+ #: inc/class-page-settings.php:459
3862
  msgid "Operating System"
3863
  msgstr ""
3864
 
3865
+ #: inc/class-page-settings.php:460
3866
  msgid "PHP SAPI"
3867
  msgstr ""
3868
 
3869
+ #: inc/class-page-settings.php:461
3870
  msgid "Function Disabled"
3871
  msgstr ""
3872
 
3873
+ #: inc/class-page-settings.php:465
3874
  msgid "Current PHP user"
3875
  msgstr ""
3876
 
3877
+ #: inc/class-page-settings.php:466
3878
  msgid "Maximum execution time"
3879
  msgstr ""
3880
 
3881
+ #: inc/class-page-settings.php:468 inc/class-page-settings.php:470
3882
  msgid "Alternative WP Cron"
3883
  msgstr ""
3884
 
3885
+ #: inc/class-page-settings.php:468 inc/class-page-settings.php:472
3886
  msgid "On"
3887
  msgstr ""
3888
 
3889
+ #: inc/class-page-settings.php:470 inc/class-page-settings.php:474
3890
  msgid "Off"
3891
  msgstr ""
3892
 
3893
+ #: inc/class-page-settings.php:472 inc/class-page-settings.php:474
3894
  msgid "Disabled WP Cron"
3895
  msgstr ""
3896
 
3897
+ #: inc/class-page-settings.php:476 inc/class-page-settings.php:478
3898
  msgid "CHMOD Dir"
3899
  msgstr ""
3900
 
3901
+ #: inc/class-page-settings.php:480
3902
  msgid "Server Time"
3903
  msgstr ""
3904
 
3905
+ #: inc/class-page-settings.php:481
3906
  msgid "Blog Time"
3907
  msgstr ""
3908
 
3909
+ #: inc/class-page-settings.php:482
3910
  msgid "Blog Timezone"
3911
  msgstr ""
3912
 
3913
+ #: inc/class-page-settings.php:483
3914
  msgid "Blog Time offset"
3915
  msgstr ""
3916
 
3917
+ #: inc/class-page-settings.php:483
 
3918
  msgid "%s hours"
3919
  msgstr ""
3920
 
3921
+ #: inc/class-page-settings.php:484
3922
  msgid "Blog language"
3923
  msgstr ""
3924
 
3925
+ #: inc/class-page-settings.php:485
3926
  msgid "MySQL Client encoding"
3927
  msgstr ""
3928
 
3929
+ #: inc/class-page-settings.php:488
3930
  msgid "Blog charset"
3931
  msgstr ""
3932
 
3933
+ #: inc/class-page-settings.php:489
3934
  msgid "PHP Memory limit"
3935
  msgstr ""
3936
 
3937
+ #: inc/class-page-settings.php:490
3938
  msgid "WP memory limit"
3939
  msgstr ""
3940
 
3941
+ #: inc/class-page-settings.php:491
3942
  msgid "WP maximum memory limit"
3943
  msgstr ""
3944
 
3945
+ #: inc/class-page-settings.php:492
3946
  msgid "Memory in use"
3947
  msgstr ""
3948
 
3949
+ #: inc/class-page-settings.php:497
3950
  msgid "Disabled PHP Functions:"
3951
  msgstr ""
3952
 
3953
+ #: inc/class-page-settings.php:502
3954
  msgid "Loaded PHP Extensions:"
3955
  msgstr ""
3956
 
3957
+ #: inc/class-page-settings.php:514
3958
  msgid "Save Changes"
3959
  msgstr ""
3960
 
3961
+ #: inc/class-page-settings.php:516
3962
  msgid "Reset all settings to default"
3963
  msgstr ""
3964
 
3965
  #: inc/class-php-admin-notice.php:123
3966
+ msgid ""
3967
+ "With the upcoming major release, BackWPup will be requiring PHP version 5.3 "
3968
+ "or higher."
3969
  msgstr ""
3970
 
3971
  #: inc/class-php-admin-notice.php:125
3981
  msgstr ""
3982
 
3983
  #: inc/class-php-admin-notice.php:164
3984
+ msgid ""
3985
+ "BackWPup has determined, your installation is still running on the old PHP "
3986
+ "5.2 version."
3987
  msgstr ""
3988
 
3989
  #: inc/class-php-admin-notice.php:167
3990
+ msgid ""
3991
+ "In order to ensure a fast and secure development for BackWPup, we will most "
3992
+ "likely not support PHP version 5.2 in our next version."
3993
  msgstr ""
3994
 
3995
  #: inc/class-php-admin-notice.php:168
3996
+ msgid ""
3997
+ "No need to worry, your host can update your PHP version relatively quickly "
3998
+ "and without any problems."
3999
  msgstr ""
4000
 
4001
  #: inc/class-php-admin-notice.php:169
4002
+ msgid ""
4003
+ "Otherwise you can continue to stay on this last version and do not update "
4004
+ "the plugin in the future!"
4005
  msgstr ""
4006
 
4007
  #: inc/class-php-admin-notice.php:173
4008
+ msgid ""
4009
+ "If the response from PHP 5.2 users is surprisingly high, we will eventually "
4010
+ "keep support for PHP 5.2 for a while."
4011
  msgstr ""
4012
 
4013
  #: inc/class-php-admin-notice.php:176
4018
  msgid "Your BackWPup Team!"
4019
  msgstr ""
4020
 
4021
+ #. Translators: This is the anchor text for an HTML link pointing to BackWPup
4022
+ #. contact page
4023
  #: inc/class-php-admin-notice.php:205
4024
  msgid "contact us"
4025
  msgstr ""
4026
 
4027
+ #. Translators: %s is replaced by an HTML link with text "contact us" pointing
4028
+ #. to BackWPup contact page
4029
  #: inc/class-php-admin-notice.php:207
 
4030
  msgid "If you would like to have PHP 5.2 supported, please %s."
4031
  msgstr ""
4032
 
4054
  msgid "No job running"
4055
  msgstr ""
4056
 
4057
+ #: inc/pro/class-destination-dropbox.php:23
4058
  msgid "Auth Code:"
4059
  msgstr ""
4060
 
4061
+ #: inc/pro/class-destination-dropbox.php:26
4062
  msgid "Get auth code"
4063
  msgstr ""
4064
 
4065
+ #: inc/pro/class-destination-dropbox.php:33
4066
+ #: inc/pro/class-destination-gdrive.php:294
4067
+ #: inc/pro/class-destination-gdrive.php:299
4068
  #: inc/pro/class-destination-sugarsync.php:30
4069
  msgid "Login:"
4070
  msgstr ""
4071
 
4072
+ #: inc/pro/class-destination-dropbox.php:37
4073
+ #: inc/pro/class-destination-gdrive.php:306
4074
  #: inc/pro/class-destination-sugarsync.php:56
4075
  msgid "Folder:"
4076
  msgstr ""
4077
 
4078
+ #: inc/pro/class-destination-dropbox.php:112
 
4079
  msgid "%d. Try to sync files to Dropbox&#160;&hellip;"
4080
  msgstr ""
4081
 
4082
+ #: inc/pro/class-destination-dropbox.php:147
4083
  msgid "Retrieving file list from Dropbox"
4084
  msgstr ""
4085
 
4086
+ #: inc/pro/class-destination-dropbox.php:161
4087
  msgid "Upload changed files to Dropbox"
4088
  msgstr ""
4089
 
4090
+ #: inc/pro/class-destination-dropbox.php:183
 
4091
  msgid "File %s uploaded to Dropbox"
4092
  msgstr ""
4093
 
4094
+ #: inc/pro/class-destination-dropbox.php:211
 
4095
  msgid "Extra file %s uploaded to Dropbox"
4096
  msgstr ""
4097
 
4098
+ #: inc/pro/class-destination-dropbox.php:226
 
 
 
 
 
4099
  msgid "Folder %s deleted from Dropbox"
4100
  msgstr ""
4101
 
4102
+ #: inc/pro/class-destination-dropbox.php:243
 
4103
  msgid "File %s deleted from Dropbox"
4104
  msgstr ""
4105
 
4108
  msgstr ""
4109
 
4110
  #: inc/pro/class-destination-folder.php:41
4111
+ #: inc/pro/class-destination-gdrive.php:321
4112
  msgid "Oldest files will be deleted first."
4113
  msgstr ""
4114
 
4115
  #: inc/pro/class-destination-folder.php:87
 
4116
  msgid "%d. Try to sync files to folder&#160;&hellip;"
4117
  msgstr ""
4118
 
4120
  msgid "Retrieving file list from folder"
4121
  msgstr ""
4122
 
4123
+ #: inc/pro/class-destination-folder.php:101
4124
  msgid "Copy changed files to folder"
4125
  msgstr ""
4126
 
4127
+ #: inc/pro/class-destination-folder.php:115
 
4128
  msgid "File %s copied"
4129
  msgstr ""
4130
 
4131
+ #: inc/pro/class-destination-folder.php:128
4132
  msgid "Delete not existing files from folder"
4133
  msgstr ""
4134
 
4135
+ #: inc/pro/class-destination-folder.php:136
 
4136
  msgid "Extra file %s copied"
4137
  msgstr ""
4138
 
4139
+ #: inc/pro/class-destination-folder.php:152
 
4140
  msgid "File %s deleted from folder"
4141
  msgstr ""
4142
 
4143
+ #: inc/pro/class-destination-folder.php:210
 
4144
  msgid "Empty folder %s deleted"
4145
  msgstr ""
4146
 
4162
  msgstr ""
4163
 
4164
  #: inc/pro/class-destination-gdrive.php:41
4165
+ #: inc/pro/class-destination-gdrive.php:282
4166
+ msgid ""
4167
+ "Looks like you haven’t set up any API keys yet. Head over to <a href=\"%s"
4168
+ "\">Settings | API-Keys</a> and get Google Drive all set up, then come back "
4169
+ "here."
4170
  msgstr ""
4171
 
4172
  #: inc/pro/class-destination-gdrive.php:55
4173
+ #: inc/pro/class-destination-gdrive.php:297
4174
  msgid "Authenticate"
4175
  msgstr ""
4176
 
4177
  #: inc/pro/class-destination-gdrive.php:62
4178
+ #: inc/pro/class-destination-gdrive.php:302
4179
  msgid "Reauthenticate"
4180
  msgstr ""
4181
 
4183
  msgid "Folder in Google Drive"
4184
  msgstr ""
4185
 
4186
+ #: inc/pro/class-destination-gdrive.php:110
4187
+ msgid ""
4188
+ "Consider using trash to delete files. If trash is not enabled, files will be "
4189
+ "deleted permanently."
4190
  msgstr ""
4191
 
4192
+ #: inc/pro/class-destination-gdrive.php:167
4193
+ #: inc/pro/class-destination-gdrive.php:191
4194
  msgid "GDrive: Authenticated."
4195
  msgstr ""
4196
 
4197
+ #: inc/pro/class-destination-gdrive.php:171
4198
+ #: inc/pro/class-destination-gdrive.php:195
4199
  msgid "GDrive: No refresh token received. Try to Authenticate again!"
4200
  msgstr ""
4201
 
4202
+ #: inc/pro/class-destination-gdrive.php:177
4203
+ #: inc/pro/class-destination-gdrive.php:199
4204
+ #: inc/pro/class-destination-gdrive.php:218
4205
+ #: inc/pro/class-destination-gdrive.php:237
 
4206
  msgid "GDrive API: %s"
4207
  msgstr ""
4208
 
4209
+ #: inc/pro/class-destination-gdrive.php:440
 
4210
  msgid "%d. Try to send backup file to Google Drive&#160;&hellip;"
4211
  msgstr ""
4212
 
4213
+ #: inc/pro/class-destination-gdrive.php:468
4214
  msgid "Uploading to Google Drive&#160;&hellip;"
4215
  msgstr ""
4216
 
4217
+ #: inc/pro/class-destination-gdrive.php:530
4218
  msgid "Could not create resumable file transfer to Google Drive"
4219
  msgstr ""
4220
 
4221
+ #: inc/pro/class-destination-gdrive.php:574
4222
  msgid "Can not resume transfer backup to Google Drive!"
4223
  msgstr ""
4224
 
4225
+ #: inc/pro/class-destination-gdrive.php:641
 
4226
  msgid "Error transfering file chunks to %s."
4227
  msgstr ""
4228
 
4229
+ #: inc/pro/class-destination-gdrive.php:642
4230
+ #: inc/pro/class-destination-gdrive.php:668
4231
  msgid "Google Drive"
4232
  msgstr ""
4233
 
4234
+ #: inc/pro/class-destination-gdrive.php:716
 
4235
  msgid "One file deleted from Google Drive"
4236
  msgid_plural "%d files deleted on Google Drive"
4237
  msgstr[0] ""
4238
  msgstr[1] ""
4239
 
4240
+ #: inc/pro/class-destination-gdrive.php:722
4241
+ #: inc/pro/class-destination-gdrive.php:1028
 
4242
  msgid "Google Drive API: %s"
4243
  msgstr ""
4244
 
4245
+ #: inc/pro/class-destination-gdrive.php:849
 
4246
  msgid "%d. Try to sync files to Google Drive&#160;&hellip;"
4247
  msgstr ""
4248
 
4249
+ #: inc/pro/class-destination-gdrive.php:874
4250
  msgid "Syncing changed files to Google Drive"
4251
  msgstr ""
4252
 
4253
+ #: inc/pro/class-destination-gdrive.php:904
 
4254
  msgid "File %s updated on Google Drive"
4255
  msgstr ""
4256
 
4257
+ #: inc/pro/class-destination-gdrive.php:925
 
4258
  msgid "File %s uploaded to Google Drive"
4259
  msgstr ""
4260
 
4261
+ #: inc/pro/class-destination-gdrive.php:946
 
4262
  msgid "File %s moved to trash in Google Drive"
4263
  msgstr ""
4264
 
4265
+ #: inc/pro/class-destination-gdrive.php:949
 
4266
  msgid "File %s deleted permanently in Google Drive"
4267
  msgstr ""
4268
 
4269
+ #: inc/pro/class-destination-gdrive.php:995
 
4270
  msgid "Extra file %s updated on Google Drive"
4271
  msgstr ""
4272
 
4273
+ #: inc/pro/class-destination-gdrive.php:1017
 
4274
  msgid "Extra file %s uploaded to Google Drive"
4275
  msgstr ""
4276
 
4283
  msgstr ""
4284
 
4285
  #: inc/pro/class-destination-glacier.php:33
4286
+ #: inc/pro/class-destination-glacier.php:235
4287
  msgid "Amazon Glacier Region"
4288
  msgstr ""
4289
 
4290
  #: inc/pro/class-destination-glacier.php:34
4291
+ #: inc/pro/class-destination-glacier.php:236
4292
  msgid "US Standard"
4293
  msgstr ""
4294
 
4295
  #: inc/pro/class-destination-glacier.php:35
4296
+ #: inc/pro/class-destination-glacier.php:237
4297
  msgid "US West (Northern California)"
4298
  msgstr ""
4299
 
4300
  #: inc/pro/class-destination-glacier.php:36
4301
+ #: inc/pro/class-destination-glacier.php:238
4302
  msgid "US West (Oregon)"
4303
  msgstr ""
4304
 
4305
  #: inc/pro/class-destination-glacier.php:37
4306
+ #: inc/pro/class-destination-glacier.php:239
4307
  msgid "EU (Ireland)"
4308
  msgstr ""
4309
 
4310
  #: inc/pro/class-destination-glacier.php:38
4311
+ #: inc/pro/class-destination-glacier.php:240
4312
  msgid "EU (Germany)"
4313
  msgstr ""
4314
 
4315
  #: inc/pro/class-destination-glacier.php:39
4316
+ #: inc/pro/class-destination-glacier.php:241
4317
  msgid "Asia Pacific (Tokyo)"
4318
  msgstr ""
4319
 
4320
  #: inc/pro/class-destination-glacier.php:40
4321
+ #: inc/pro/class-destination-glacier.php:242
4322
  msgid "Asia Pacific (Seoul)"
4323
  msgstr ""
4324
 
4327
  msgstr ""
4328
 
4329
  #: inc/pro/class-destination-glacier.php:42
4330
+ #: inc/pro/class-destination-glacier.php:244
4331
  msgid "Asia Pacific (Sydney)"
4332
  msgstr ""
4333
 
4334
  #: inc/pro/class-destination-glacier.php:43
4335
+ #: inc/pro/class-destination-glacier.php:245
4336
  msgid "South America (Sao Paulo)"
4337
  msgstr ""
4338
 
4339
  #: inc/pro/class-destination-glacier.php:44
4340
+ #: inc/pro/class-destination-glacier.php:246
4341
  msgid "China (Beijing)"
4342
  msgstr ""
4343
 
4362
  msgstr ""
4363
 
4364
  #: inc/pro/class-destination-glacier.php:108
4365
+ #: inc/pro/class-destination-glacier.php:264
4366
+ msgid ""
4367
+ "Number of files to keep in folder. (Archives deleted before 3 months after "
4368
+ "they have been stored may cause extra costs when deleted.)"
4369
  msgstr ""
4370
 
4371
+ #: inc/pro/class-destination-glacier.php:172
4372
  msgid "No vault found!"
4373
  msgstr ""
4374
 
4375
+ #: inc/pro/class-destination-glacier.php:211
4376
+ #: inc/pro/class-destination-glacier.php:299
 
4377
  msgid "Vault %1$s created."
4378
  msgstr ""
4379
 
4380
+ #: inc/pro/class-destination-glacier.php:213
4381
+ #: inc/pro/class-destination-glacier.php:301
 
4382
  msgid "Vault %s could not be created."
4383
  msgstr ""
4384
 
4385
+ #: inc/pro/class-destination-glacier.php:234
4386
  msgid "Select an Amazon Glacier region:"
4387
  msgstr ""
4388
 
4389
+ #: inc/pro/class-destination-glacier.php:243
4390
  msgid "Asia Pacific (Singapore)"
4391
  msgstr ""
4392
 
4393
+ #: inc/pro/class-destination-glacier.php:248
4394
  #: inc/pro/class-destination-msazure.php:19
4395
+ #: inc/pro/class-destination-s3.php:39
4396
  msgid "Access Key:"
4397
  msgstr ""
4398
 
4399
+ #: inc/pro/class-destination-glacier.php:250
4400
+ #: inc/pro/class-destination-s3.php:42
4401
  msgid "Secret Key:"
4402
  msgstr ""
4403
 
4404
+ #: inc/pro/class-destination-glacier.php:252
4405
  msgid "Vault:"
4406
  msgstr ""
4407
 
4408
+ #: inc/pro/class-destination-glacier.php:261
4409
  msgid "New Vault:"
4410
  msgstr ""
4411
 
4412
+ #: inc/pro/class-destination-glacier.php:341
4413
+ #: inc/pro/class-destination-glacier.php:456
4414
+ #: inc/pro/class-destination-glacier.php:475
4415
+ #: inc/pro/class-destination-glacier.php:518
 
4416
  msgid "AWS API: %s"
4417
  msgstr ""
4418
 
4419
+ #: inc/pro/class-destination-glacier.php:364
 
4420
  msgid "%d. Trying to send backup file to Amazon Glacier&#160;&hellip;"
4421
  msgstr ""
4422
 
4423
+ #: inc/pro/class-destination-glacier.php:377
 
4424
  msgid "Connected to Glacier vault \"%1$s\" with %2$d archives and size of %3$d"
4425
  msgstr ""
4426
 
4427
+ #: inc/pro/class-destination-glacier.php:379
 
4428
  msgid "Glacier vault \"%s\" does not exist!"
4429
  msgstr ""
4430
 
4431
+ #: inc/pro/class-destination-glacier.php:385
4432
  msgid "Starting upload to Amazon Glacier&#160;&hellip;"
4433
  msgstr ""
4434
 
4435
+ #: inc/pro/class-destination-glacier.php:438
 
4436
  msgid "Archive ID: %s"
4437
  msgstr ""
4438
 
4439
+ #: inc/pro/class-destination-glacier.php:449 inc/pro/class-pro.php:92
4440
  msgid "Glacier"
4441
  msgstr ""
4442
 
4443
+ #: inc/pro/class-destination-glacier.php:508
 
4444
  msgid "Cannot delete archive from %s."
4445
  msgstr ""
4446
 
4447
+ #: inc/pro/class-destination-glacier.php:512
 
4448
  msgid "One file deleted on vault."
4449
  msgid_plural "%d files deleted on vault"
4450
  msgstr[0] ""
4470
  msgstr ""
4471
 
4472
  #: inc/pro/class-destination-msazure.php:119
 
4473
  msgid "%d. Trying to sync files with Microsoft Azure (Blob) &hellip;"
4474
  msgstr ""
4475
 
4482
  msgstr ""
4483
 
4484
  #: inc/pro/class-destination-msazure.php:182
 
4485
  msgid "File %s uploaded to MS Azure."
4486
  msgstr ""
4487
 
4488
  #: inc/pro/class-destination-msazure.php:208
 
4489
  msgid "Extra file %s uploaded to MS Azure."
4490
  msgstr ""
4491
 
4494
  msgstr ""
4495
 
4496
  #: inc/pro/class-destination-msazure.php:224
 
4497
  msgid "File %s deleted from MS Azure."
4498
  msgstr ""
4499
 
4506
  msgstr ""
4507
 
4508
  #: inc/pro/class-destination-rsc.php:147
 
4509
  msgid "%d. Trying to sync files to Rackspace cloud&#160;&hellip;"
4510
  msgstr ""
4511
 
4512
  #: inc/pro/class-destination-rsc.php:167
 
4513
  msgid "Connected to Rackspace cloud files container %s."
4514
  msgstr ""
4515
 
4522
  msgstr ""
4523
 
4524
  #: inc/pro/class-destination-rsc.php:230
 
4525
  msgid "File %s uploaded to Rackspace Cloud."
4526
  msgstr ""
4527
 
4528
  #: inc/pro/class-destination-rsc.php:263
 
4529
  msgid "Extra file %s uploaded to Rackspace Cloud."
4530
  msgstr ""
4531
 
4534
  msgstr ""
4535
 
4536
  #: inc/pro/class-destination-rsc.php:280
 
4537
  msgid "File %s deleted from Rackspace Cloud."
4538
  msgstr ""
4539
 
4541
  msgid "Select a S3 service:"
4542
  msgstr ""
4543
 
4544
+ #: inc/pro/class-destination-s3.php:36
4545
  msgid "or set an S3 Server URL:"
4546
  msgstr ""
4547
 
4548
+ #: inc/pro/class-destination-s3.php:45
4549
  msgid "Bucket:"
4550
  msgstr ""
4551
 
4552
+ #: inc/pro/class-destination-s3.php:55
4553
  msgid "New Bucket:"
4554
  msgstr ""
4555
 
4556
+ #: inc/pro/class-destination-s3.php:57
4557
  msgid "Folder in bucket:"
4558
  msgstr ""
4559
 
4560
+ #: inc/pro/class-destination-s3.php:145
 
4561
  msgid "Bucket %1$s created in %2$s."
4562
  msgstr ""
4563
 
4564
+ #: inc/pro/class-destination-s3.php:169
 
4565
  msgid "%d. Trying to sync files to S3 Service&#160;&hellip;"
4566
  msgstr ""
4567
 
4568
+ #: inc/pro/class-destination-s3.php:203
4569
  msgid "Retrieving file list from S3."
4570
  msgstr ""
4571
 
4572
+ #: inc/pro/class-destination-s3.php:265
4573
  msgid "Upload changed files to S3."
4574
  msgstr ""
4575
 
4576
+ #: inc/pro/class-destination-s3.php:280
 
4577
  msgid "File %s uploaded to S3."
4578
  msgstr ""
4579
 
4580
+ #: inc/pro/class-destination-s3.php:308
 
4581
  msgid "Extra file %s uploaded to S3."
4582
  msgstr ""
4583
 
4584
+ #: inc/pro/class-destination-s3.php:321
4585
  msgid "Delete nonexistent files on S3"
4586
  msgstr ""
4587
 
4588
+ #: inc/pro/class-destination-s3.php:328
 
4589
  msgid "File %s deleted from S3."
4590
  msgstr ""
4591
 
4655
  msgstr ""
4656
 
4657
  #: inc/pro/class-jobtype-dbdump.php:190
4658
+ msgid ""
4659
+ "Path to mysqldump file, so a backup can be made with it. If it is correct "
4660
+ "and <em>shell_exec</em> is active, the backup will be generated with a "
4661
+ "system command. If <em>shell_exec</em> ist not active, this is disabled"
4662
  msgstr ""
4663
 
4664
  #: inc/pro/class-jobtype-dbdump.php:580
 
4665
  msgid "Added database backup \"%1$s\" with %2$s to backup file list"
4666
  msgstr ""
4667
 
4668
  #: inc/pro/class-jobtype-dbdump.php:601
 
4669
  msgid "%d. Try to backup MySQL system&#160;&hellip;"
4670
  msgstr ""
4671
 
4672
  #: inc/pro/class-jobtype-dbdump.php:608
4673
+ msgid ""
4674
+ "Executing of system commands not allowed. Please use backup with mysqli."
4675
  msgstr ""
4676
 
4677
  #: inc/pro/class-jobtype-dbdump.php:613
 
4678
  msgid "%s file not in open basedir of PHP."
4679
  msgstr ""
4680
 
4681
  #: inc/pro/class-jobtype-dbdump.php:618
 
4682
  msgid "%s file not found. Please correct the path for the mysqldump file."
4683
  msgstr ""
4684
 
4685
  #: inc/pro/class-jobtype-dbdump.php:702
 
4686
  msgctxt "Executed exec() command"
4687
  msgid "CLI Exec: %s"
4688
  msgstr ""
4692
  msgstr ""
4693
 
4694
  #: inc/pro/class-jobtype-dbdump.php:713
4695
+ msgid ""
4696
+ "MySQL Server Error. This could be an issue with permissions. Try using "
4697
+ "database backup with mysqli."
4698
  msgstr ""
4699
 
4700
  #: inc/pro/class-jobtype-dbdump.php:714
4714
  msgstr ""
4715
 
4716
  #: inc/pro/class-jobtype-dbdump.php:722
 
4717
  msgid "mysqldump returned: (%d) %s"
4718
  msgstr ""
4719
 
4722
  msgstr ""
4723
 
4724
  #: inc/pro/class-jobtype-dbdump.php:781
 
4725
  msgid "%d. Try to backup database as XML&#160;&hellip;"
4726
  msgstr ""
4727
 
4728
  #: inc/pro/class-jobtype-dbdump.php:851
 
4729
  msgctxt "Database Charset"
4730
  msgid "Cannot set DB charset to %s"
4731
  msgstr ""
4735
  msgstr ""
4736
 
4737
  #: inc/pro/class-jobtype-dbdump.php:916
 
4738
  msgid "Dump database create view \"%s\""
4739
  msgstr ""
4740
 
4741
  #: inc/pro/class-jobtype-dbdump.php:934
 
4742
  msgid "Backup database structure \"%s\" to XML"
4743
  msgstr ""
4744
 
4745
  #: inc/pro/class-jobtype-dbdump.php:976
 
4746
  msgid "Backup table \"%s\" data to XML"
4747
  msgstr ""
4748
 
4749
  #: inc/pro/class-jobtype-dbdump.php:1044
 
4750
  msgid "Added database XML dump \"%1$s\" with %2$s to backup file list"
4751
  msgstr ""
4752
 
4792
 
4793
  #: inc/pro/class-page-wizard.php:179 inc/pro/class-page-wizard.php:456
4794
  #: inc/pro/class-page-wizard.php:488
4795
+ msgid "Next "
4796
  msgstr ""
4797
 
4798
  #: inc/pro/class-page-wizard.php:196 inc/pro/class-page-wizard.php:452
4799
+ msgid " Previous"
4800
  msgstr ""
4801
 
4802
  #: inc/pro/class-page-wizard.php:349
 
4803
  msgctxt "Plugin Name"
4804
  msgid "%s &rsaquo; Wizards"
4805
  msgstr ""
4806
 
4807
  #: inc/pro/class-page-wizard.php:388
 
4808
  msgctxt "Plugin Name"
4809
  msgid "%s Wizard:"
4810
  msgstr ""
4826
  msgstr ""
4827
 
4828
  #: inc/pro/class-settings-apikeys.php:46
4829
+ msgid ""
4830
+ "Hash Key for BackWPup. It will be used to have hashes in folder and file "
4831
+ "names. It must at least 6 chars long."
4832
  msgstr ""
4833
 
4834
  #: inc/pro/class-settings-apikeys.php:49
4840
  msgstr ""
4841
 
4842
  #: inc/pro/class-settings-apikeys.php:74
4843
+ msgid ""
4844
+ "If you want to set your own Dropbox API Keys, you can do it here. Leave "
4845
+ "empty for default."
4846
  msgstr ""
4847
 
4848
  #: inc/pro/class-settings-apikeys.php:77
4866
  msgstr ""
4867
 
4868
  #: inc/pro/class-settings-apikeys.php:132
4869
+ msgid ""
4870
+ "If you want to set your own SugarSync API keys you can do that here. Leave "
4871
+ "empty for default."
4872
  msgstr ""
4873
 
4874
  #: inc/pro/class-settings-apikeys.php:135
4960
  msgstr ""
4961
 
4962
  #: inc/pro/class-wizard-job.php:427
4963
+ msgid ""
4964
+ "PHP Zip functions will be used if available (memory lees). Else PCLZip Class "
4965
+ "will used."
4966
  msgstr ""
4967
 
4968
  #: inc/pro/class-wizard-job.php:430 inc/pro/class-wizard-job.php:441
4987
  msgstr ""
4988
 
4989
  #: inc/pro/class-wizard-job.php:661
 
4990
  msgid "Wizard: %1$s"
4991
  msgstr ""
4992
 
4993
  #: inc/pro/class-wizard-job.php:680
 
4994
  msgid "New job %s generated."
4995
  msgstr ""
4996
 
5052
  msgstr ""
5053
 
5054
  #: inc/pro/class-wizard-jobimport.php:67
5055
+ msgid ""
5056
+ "Please upload your BackWPup job XML export file and we&#8217;ll import the "
5057
+ "jobs into BackWPup."
5058
  msgstr ""
5059
 
5060
  #: inc/pro/class-wizard-jobimport.php:69
5062
  msgstr ""
5063
 
5064
  #: inc/pro/class-wizard-jobimport.php:69
 
5065
  msgid "Maximum size: %s"
5066
  msgstr ""
5067
 
5094
  msgstr ""
5095
 
5096
  #: inc/pro/class-wizard-jobimport.php:142
5097
+ msgid ""
5098
+ "File is empty. Please upload something more substantial. This error could "
5099
+ "also caused by uploads being disabled in your php.ini or by post_max_size "
5100
+ "being defined as smaller than upload_max_filesize in php.ini."
5101
  msgstr ""
5102
 
5103
  #: inc/pro/class-wizard-jobimport.php:157
5104
+ msgid ""
5105
+ "The export file could not be found at <code>%s</code>. This is likely due to "
5106
+ "an issue with permissions."
5107
  msgstr ""
5108
 
5109
  #: inc/pro/class-wizard-jobimport.php:164
5111
  msgstr ""
5112
 
5113
  #: inc/pro/class-wizard-jobimport.php:171
5114
+ msgid ""
5115
+ "This Export file (version %s) may not be supported by this version of the "
5116
+ "importer."
5117
  msgstr ""
5118
 
5119
  #: inc/pro/class-wizard-jobimport.php:177
5121
  msgstr ""
5122
 
5123
  #: inc/pro/class-wizard-jobimport.php:243
 
5124
  msgid "Job %1$s with id %2$d imported"
5125
  msgstr ""
5126
 
5153
  msgstr ""
5154
 
5155
  #: inc/pro/class-wizard-systemtest.php:99
5156
+ msgid ""
5157
+ "You must run WordPress version 3.4 or higher to use this plugin. You are "
5158
+ "using version %s now."
5159
  msgstr ""
5160
 
5161
  #: inc/pro/class-wizard-systemtest.php:104
5162
+ msgid ""
5163
+ "You must run PHP version 5.2.6 or higher to use this plugin. You are using "
5164
+ "version %s now."
5165
  msgstr ""
5166
 
5167
  #: inc/pro/class-wizard-systemtest.php:108
5168
+ msgid ""
5169
+ "We recommend to run a PHP version above 5.3.2 to get the full plugin "
5170
+ "functionality. You are using version %s now."
5171
  msgstr ""
5172
 
5173
  #: inc/pro/class-wizard-systemtest.php:113
5174
+ msgid ""
5175
+ "You must have the MySQLi extension installed and a MySQL server version of "
5176
+ "5.0.7 or higher to use this plugin. You are using version %s now."
5177
  msgstr ""
5178
 
5179
  #: inc/pro/class-wizard-systemtest.php:118
5180
+ msgid ""
5181
+ "PHP cURL extension must be installed to use the full plugin functionality."
5182
  msgstr ""
5183
 
5184
+ #: inc/pro/class-wizard-systemtest.php:122
 
5185
  msgctxt "%1 = extension name, %2 = file suffix"
5186
  msgid "We recommend to install the %1$s extension to generate %2$s archives."
5187
  msgstr ""
5188
 
5189
  #: inc/pro/class-wizard-systemtest.php:146
 
5190
  msgctxt "Link to PHP manual"
5191
  msgid "Please disable the deprecated <a href=\"%s\">PHP safe mode</a>."
5192
  msgstr ""
5193
 
5194
  #: inc/pro/class-wizard-systemtest.php:154
5195
+ msgid ""
5196
+ "We recommend to install the PHP FTP extension to use the FTP backup "
5197
+ "destination."
5198
  msgstr ""
5199
 
5200
  #: inc/pro/class-wizard-systemtest.php:174
 
5201
  msgid "The HTTP response test result is an error: \"%s\"."
5202
  msgstr ""
5203
 
5204
  #: inc/pro/class-wizard-systemtest.php:178
5205
+ msgid ""
5206
+ "The HTTP response test result is a wrong HTTP status: %s. It should be "
5207
+ "status 200."
5208
  msgstr ""
5209
 
5210
  #: inc/pro/class-wizard-systemtest.php:191
5216
  msgstr ""
5217
 
5218
  #: inc/pro/class-wizard-systemtest.php:199
5219
+ msgid ""
5220
+ "There is no error, but some warnings. BackWPup will work, but with "
5221
+ "limitations."
5222
  msgstr ""
5223
 
5224
  #: inc/pro/class-wizard-systemtest.php:202
5225
  msgid "There are errors. Please correct them, or BackWPup cannot work."
5226
+ msgstr ""
5227
+
5228
+ #: vendor/inpsyde/phone-home-client/src/CronController.php:80
5229
+ msgid "Every %d days"
5230
+ msgstr ""
5231
+
5232
+ #: vendor/inpsyde/phone-home-client/src/FrontController.php:143
5233
+ msgid "%s needs your help"
5234
+ msgstr ""
5235
+
5236
+ #: vendor/inpsyde/phone-home-client/src/FrontController.php:144
5237
+ msgid "Help %s"
5238
+ msgstr ""
5239
+
5240
+ #: vendor/inpsyde/phone-home-client/src/Template/Buttons.php:28
5241
+ msgid "Yes, I agree."
5242
+ msgstr ""
5243
+
5244
+ #: vendor/inpsyde/phone-home-client/src/Template/Buttons.php:45
5245
+ msgid "I have to think about that, ask me later."
5246
+ msgstr ""
5247
+
5248
+ #: vendor/inpsyde/phone-home-client/src/Template/Buttons.php:62
5249
+ msgid "Please no. Don't ask me again."
5250
+ msgstr ""
5251
+
5252
+ #: vendor/inpsyde/phone-home-client/src/Template/Buttons.php:84
5253
+ msgid "More info"
5254
+ msgstr ""
5255
+
5256
+ #. Plugin Name of the plugin/theme
5257
+ msgid "BackWPup Pro"
5258
+ msgstr ""
5259
+
5260
+ #. Description of the plugin/theme
5261
+ msgid "WordPress Backup Plugin"
5262
+ msgstr ""
5263
+
5264
+ #. Author of the plugin/theme
5265
+ msgid "Inpsyde GmbH"
5266
+ msgstr ""
readme.txt CHANGED
@@ -1,15 +1,16 @@
1
  === BackWPup - WordPress Backup Plugin ===
2
- Contributors: inpsyde, danielhuesken, Bueltge, nullbyte
3
  Tags: Amazon, Amazon S3, back up, backup, chinese, cloud, cloud files, database, db backup, dropbox, dump, file, french, ftp, ftps, german, migrate, multisite, russian, schedule, sftp, storage, S3, time, upload, xml
4
  Requires at least: 3.9
5
- Tested up to: 4.7.3
6
- Stable tag: 3.3.7
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
10
  Schedule complete automatic backups of your WordPress installation. Decide which content will be stored (Dropbox, S3…). This is the free version
11
 
12
  == Description ==
 
13
  The **backup plugin** **[BackWPup](http://backwpup.com/)** can be used to save your complete installation including /wp-content/ and push them to an external Backup Service, like **Dropbox**, **S3**, **FTP** and many more, see list below. With a single backup .zip file you are able to easily restore an installation. Please understand: this free version will not be supported as good as the [BackWPup Pro version](http://backwpup.com). With our premium version you get first class support and more features.
14
 
15
 
@@ -34,7 +35,7 @@ The **backup plugin** **[BackWPup](http://backwpup.com/)** can be used to save y
34
  * Pro version and support available - [BackWPup Pro](http://backwpup.com)
35
 
36
  = Requirements =
37
- * WordPress 3.9 and PHP 5.2.7 required!
38
  * To use the Plugin with full functionality PHP 5.3.3 with mysqli, FTP,gz, bz2, ZipArchive and curl is needed.
39
  * Plugin functions that don't work because of your server settings, will not be displayed in admin area.
40
 
@@ -156,6 +157,18 @@ Yes. You need to have writing access to the wp-config.php file (usually residing
156
  [You can find a detailed tutorial in the BackWPup documentation.](http://docs.backwpup.com/article/118-install-backwpup)
157
 
158
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
159
  = Version 3.3.7 =
160
  * Fixed: Services credentials lost after 3.3.6 update
161
  * Fixed: Removed all instances of PHP short echo tags and other minor PHP 5.2 compatibility issues
1
  === BackWPup - WordPress Backup Plugin ===
2
+ Contributors: inpsyde, cocreation, danielhuesken, Bueltge, nullbyte
3
  Tags: Amazon, Amazon S3, back up, backup, chinese, cloud, cloud files, database, db backup, dropbox, dump, file, french, ftp, ftps, german, migrate, multisite, russian, schedule, sftp, storage, S3, time, upload, xml
4
  Requires at least: 3.9
5
+ Tested up to: 4.7.4
6
+ Stable tag: 3.4.0
7
  License: GPLv3
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
10
  Schedule complete automatic backups of your WordPress installation. Decide which content will be stored (Dropbox, S3…). This is the free version
11
 
12
  == Description ==
13
+
14
  The **backup plugin** **[BackWPup](http://backwpup.com/)** can be used to save your complete installation including /wp-content/ and push them to an external Backup Service, like **Dropbox**, **S3**, **FTP** and many more, see list below. With a single backup .zip file you are able to easily restore an installation. Please understand: this free version will not be supported as good as the [BackWPup Pro version](http://backwpup.com). With our premium version you get first class support and more features.
15
 
16
 
35
  * Pro version and support available - [BackWPup Pro](http://backwpup.com)
36
 
37
  = Requirements =
38
+ * WordPress 3.9 and PHP 5.3 required!
39
  * To use the Plugin with full functionality PHP 5.3.3 with mysqli, FTP,gz, bz2, ZipArchive and curl is needed.
40
  * Plugin functions that don't work because of your server settings, will not be displayed in admin area.
41
 
157
  [You can find a detailed tutorial in the BackWPup documentation.](http://docs.backwpup.com/article/118-install-backwpup)
158
 
159
  == Changelog ==
160
+ = Version 3.4.0 =
161
+ * Changed: Dropped support for PHP 5.2.
162
+ * Improved: Migrated to Dropbox API V2.
163
+ * Changed: Removed Adminer link from backend.
164
+ * Added: Backup file tracking so backups from other jobs aren't accidentally deleted.
165
+ * Fixed: Call to get_users was previously incorrect.
166
+ * Added: Ability to have backup file sent to multiple email addresses.
167
+ * Added: Web.config is now included in list of special files to back up.
168
+ * Fixed: error for some users when generating XML export.
169
+ * Fixed: opendir permission denied warning on some versions of IIS.
170
+ * Improved: accuracy of binary column export.
171
+
172
  = Version 3.3.7 =
173
  * Fixed: Services credentials lost after 3.3.6 update
174
  * Fixed: Removed all instances of PHP short echo tags and other minor PHP 5.2 compatibility issues
vendor/PEAR/HTTP/Request2.php CHANGED
@@ -1,1030 +1,1030 @@
1
- <?php
2
- /**
3
- * Class representing a HTTP request message
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * A class representing an URL as per RFC 3986.
23
- */
24
- require_once 'Net/URL2.php';
25
-
26
- /**
27
- * Exception class for HTTP_Request2 package
28
- */
29
- require_once 'HTTP/Request2/Exception.php';
30
-
31
- /**
32
- * Class representing a HTTP request message
33
- *
34
- * @category HTTP
35
- * @package HTTP_Request2
36
- * @author Alexey Borzov <avb@php.net>
37
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
38
- * @version Release: 2.2.1
39
- * @link http://pear.php.net/package/HTTP_Request2
40
- * @link http://tools.ietf.org/html/rfc2616#section-5
41
- */
42
- class HTTP_Request2 implements SplSubject
43
- {
44
- /**#@+
45
- * Constants for HTTP request methods
46
- *
47
- * @link http://tools.ietf.org/html/rfc2616#section-5.1.1
48
- */
49
- const METHOD_OPTIONS = 'OPTIONS';
50
- const METHOD_GET = 'GET';
51
- const METHOD_HEAD = 'HEAD';
52
- const METHOD_POST = 'POST';
53
- const METHOD_PUT = 'PUT';
54
- const METHOD_DELETE = 'DELETE';
55
- const METHOD_TRACE = 'TRACE';
56
- const METHOD_CONNECT = 'CONNECT';
57
- /**#@-*/
58
-
59
- /**#@+
60
- * Constants for HTTP authentication schemes
61
- *
62
- * @link http://tools.ietf.org/html/rfc2617
63
- */
64
- const AUTH_BASIC = 'basic';
65
- const AUTH_DIGEST = 'digest';
66
- /**#@-*/
67
-
68
- /**
69
- * Regular expression used to check for invalid symbols in RFC 2616 tokens
70
- * @link http://pear.php.net/bugs/bug.php?id=15630
71
- */
72
- const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!';
73
-
74
- /**
75
- * Regular expression used to check for invalid symbols in cookie strings
76
- * @link http://pear.php.net/bugs/bug.php?id=15630
77
- * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
78
- */
79
- const REGEXP_INVALID_COOKIE = '/[\s,;]/';
80
-
81
- /**
82
- * Fileinfo magic database resource
83
- * @var resource
84
- * @see detectMimeType()
85
- */
86
- private static $_fileinfoDb;
87
-
88
- /**
89
- * Observers attached to the request (instances of SplObserver)
90
- * @var array
91
- */
92
- protected $observers = array();
93
-
94
- /**
95
- * Request URL
96
- * @var Net_URL2
97
- */
98
- protected $url;
99
-
100
- /**
101
- * Request method
102
- * @var string
103
- */
104
- protected $method = self::METHOD_GET;
105
-
106
- /**
107
- * Authentication data
108
- * @var array
109
- * @see getAuth()
110
- */
111
- protected $auth;
112
-
113
- /**
114
- * Request headers
115
- * @var array
116
- */
117
- protected $headers = array();
118
-
119
- /**
120
- * Configuration parameters
121
- * @var array
122
- * @see setConfig()
123
- */
124
- protected $config = array(
125
- 'adapter' => 'HTTP_Request2_Adapter_Socket',
126
- 'connect_timeout' => 10,
127
- 'timeout' => 0,
128
- 'use_brackets' => true,
129
- 'protocol_version' => '1.1',
130
- 'buffer_size' => 16384,
131
- 'store_body' => true,
132
- 'local_ip' => null,
133
-
134
- 'proxy_host' => '',
135
- 'proxy_port' => '',
136
- 'proxy_user' => '',
137
- 'proxy_password' => '',
138
- 'proxy_auth_scheme' => self::AUTH_BASIC,
139
- 'proxy_type' => 'http',
140
-
141
- 'ssl_verify_peer' => true,
142
- 'ssl_verify_host' => true,
143
- 'ssl_cafile' => null,
144
- 'ssl_capath' => null,
145
- 'ssl_local_cert' => null,
146
- 'ssl_passphrase' => null,
147
-
148
- 'digest_compat_ie' => false,
149
-
150
- 'follow_redirects' => false,
151
- 'max_redirects' => 5,
152
- 'strict_redirects' => false
153
- );
154
-
155
- /**
156
- * Last event in request / response handling, intended for observers
157
- * @var array
158
- * @see getLastEvent()
159
- */
160
- protected $lastEvent = array(
161
- 'name' => 'start',
162
- 'data' => null
163
- );
164
-
165
- /**
166
- * Request body
167
- * @var string|resource
168
- * @see setBody()
169
- */
170
- protected $body = '';
171
-
172
- /**
173
- * Array of POST parameters
174
- * @var array
175
- */
176
- protected $postParams = array();
177
-
178
- /**
179
- * Array of file uploads (for multipart/form-data POST requests)
180
- * @var array
181
- */
182
- protected $uploads = array();
183
-
184
- /**
185
- * Adapter used to perform actual HTTP request
186
- * @var HTTP_Request2_Adapter
187
- */
188
- protected $adapter;
189
-
190
- /**
191
- * Cookie jar to persist cookies between requests
192
- * @var HTTP_Request2_CookieJar
193
- */
194
- protected $cookieJar = null;
195
-
196
- /**
197
- * Constructor. Can set request URL, method and configuration array.
198
- *
199
- * Also sets a default value for User-Agent header.
200
- *
201
- * @param string|Net_Url2 $url Request URL
202
- * @param string $method Request method
203
- * @param array $config Configuration for this Request instance
204
- */
205
- public function __construct(
206
- $url = null, $method = self::METHOD_GET, array $config = array()
207
- ) {
208
- $this->setConfig($config);
209
- if (!empty($url)) {
210
- $this->setUrl($url);
211
- }
212
- if (!empty($method)) {
213
- $this->setMethod($method);
214
- }
215
- $this->setHeader(
216
- 'user-agent', 'HTTP_Request2/2.2.1 ' .
217
- '(http://pear.php.net/package/http_request2) PHP/' . phpversion()
218
- );
219
- }
220
-
221
- /**
222
- * Sets the URL for this request
223
- *
224
- * If the URL has userinfo part (username & password) these will be removed
225
- * and converted to auth data. If the URL does not have a path component,
226
- * that will be set to '/'.
227
- *
228
- * @param string|Net_URL2 $url Request URL
229
- *
230
- * @return HTTP_Request2
231
- * @throws HTTP_Request2_LogicException
232
- */
233
- public function setUrl($url)
234
- {
235
- if (is_string($url)) {
236
- $url = new Net_URL2(
237
- $url, array(Net_URL2::OPTION_USE_BRACKETS => $this->config['use_brackets'])
238
- );
239
- }
240
- if (!$url instanceof Net_URL2) {
241
- throw new HTTP_Request2_LogicException(
242
- 'Parameter is not a valid HTTP URL',
243
- HTTP_Request2_Exception::INVALID_ARGUMENT
244
- );
245
- }
246
- // URL contains username / password?
247
- if ($url->getUserinfo()) {
248
- $username = $url->getUser();
249
- $password = $url->getPassword();
250
- $this->setAuth(rawurldecode($username), $password? rawurldecode($password): '');
251
- $url->setUserinfo('');
252
- }
253
- if ('' == $url->getPath()) {
254
- $url->setPath('/');
255
- }
256
- $this->url = $url;
257
-
258
- return $this;
259
- }
260
-
261
- /**
262
- * Returns the request URL
263
- *
264
- * @return Net_URL2
265
- */
266
- public function getUrl()
267
- {
268
- return $this->url;
269
- }
270
-
271
- /**
272
- * Sets the request method
273
- *
274
- * @param string $method one of the methods defined in RFC 2616
275
- *
276
- * @return HTTP_Request2
277
- * @throws HTTP_Request2_LogicException if the method name is invalid
278
- */
279
- public function setMethod($method)
280
- {
281
- // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1
282
- if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) {
283
- throw new HTTP_Request2_LogicException(
284
- "Invalid request method '{$method}'",
285
- HTTP_Request2_Exception::INVALID_ARGUMENT
286
- );
287
- }
288
- $this->method = $method;
289
-
290
- return $this;
291
- }
292
-
293
- /**
294
- * Returns the request method
295
- *
296
- * @return string
297
- */
298
- public function getMethod()
299
- {
300
- return $this->method;
301
- }
302
-
303
- /**
304
- * Sets the configuration parameter(s)
305
- *
306
- * The following parameters are available:
307
- * <ul>
308
- * <li> 'adapter' - adapter to use (string)</li>
309
- * <li> 'connect_timeout' - Connection timeout in seconds (integer)</li>
310
- * <li> 'timeout' - Total number of seconds a request can take.
311
- * Use 0 for no limit, should be greater than
312
- * 'connect_timeout' if set (integer)</li>
313
- * <li> 'use_brackets' - Whether to append [] to array variable names (bool)</li>
314
- * <li> 'protocol_version' - HTTP Version to use, '1.0' or '1.1' (string)</li>
315
- * <li> 'buffer_size' - Buffer size to use for reading and writing (int)</li>
316
- * <li> 'store_body' - Whether to store response body in response object.
317
- * Set to false if receiving a huge response and
318
- * using an Observer to save it (boolean)</li>
319
- * <li> 'local_ip' - Specifies the IP address that will be used for accessing
320
- * the network (string)</li>
321
- * <li> 'proxy_type' - Proxy type, 'http' or 'socks5' (string)</li>
322
- * <li> 'proxy_host' - Proxy server host (string)</li>
323
- * <li> 'proxy_port' - Proxy server port (integer)</li>
324
- * <li> 'proxy_user' - Proxy auth username (string)</li>
325
- * <li> 'proxy_password' - Proxy auth password (string)</li>
326
- * <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li>
327
- * <li> 'proxy' - Shorthand for proxy_* parameters, proxy given as URL,
328
- * e.g. 'socks5://localhost:1080/' (string)</li>
329
- * <li> 'ssl_verify_peer' - Whether to verify peer's SSL certificate (bool)</li>
330
- * <li> 'ssl_verify_host' - Whether to check that Common Name in SSL
331
- * certificate matches host name (bool)</li>
332
- * <li> 'ssl_cafile' - Cerificate Authority file to verify the peer
333
- * with (use with 'ssl_verify_peer') (string)</li>
334
- * <li> 'ssl_capath' - Directory holding multiple Certificate
335
- * Authority files (string)</li>
336
- * <li> 'ssl_local_cert' - Name of a file containing local cerificate (string)</li>
337
- * <li> 'ssl_passphrase' - Passphrase with which local certificate
338
- * was encoded (string)</li>
339
- * <li> 'digest_compat_ie' - Whether to imitate behaviour of MSIE 5 and 6
340
- * in using URL without query string in digest
341
- * authentication (boolean)</li>
342
- * <li> 'follow_redirects' - Whether to automatically follow HTTP Redirects (boolean)</li>
343
- * <li> 'max_redirects' - Maximum number of redirects to follow (integer)</li>
344
- * <li> 'strict_redirects' - Whether to keep request method on redirects via status 301 and
345
- * 302 (true, needed for compatibility with RFC 2616)
346
- * or switch to GET (false, needed for compatibility with most
347
- * browsers) (boolean)</li>
348
- * </ul>
349
- *
350
- * @param string|array $nameOrConfig configuration parameter name or array
351
- * ('parameter name' => 'parameter value')
352
- * @param mixed $value parameter value if $nameOrConfig is not an array
353
- *
354
- * @return HTTP_Request2
355
- * @throws HTTP_Request2_LogicException If the parameter is unknown
356
- */
357
- public function setConfig($nameOrConfig, $value = null)
358
- {
359
- if (is_array($nameOrConfig)) {
360
- foreach ($nameOrConfig as $name => $value) {
361
- $this->setConfig($name, $value);
362
- }
363
-
364
- } elseif ('proxy' == $nameOrConfig) {
365
- $url = new Net_URL2($value);
366
- $this->setConfig(array(
367
- 'proxy_type' => $url->getScheme(),
368
- 'proxy_host' => $url->getHost(),
369
- 'proxy_port' => $url->getPort(),
370
- 'proxy_user' => rawurldecode($url->getUser()),
371
- 'proxy_password' => rawurldecode($url->getPassword())
372
- ));
373
-
374
- } else {
375
- if (!array_key_exists($nameOrConfig, $this->config)) {
376
- throw new HTTP_Request2_LogicException(
377
- "Unknown configuration parameter '{$nameOrConfig}'",
378
- HTTP_Request2_Exception::INVALID_ARGUMENT
379
- );
380
- }
381
- $this->config[$nameOrConfig] = $value;
382
- }
383
-
384
- return $this;
385
- }
386
-
387
- /**
388
- * Returns the value(s) of the configuration parameter(s)
389
- *
390
- * @param string $name parameter name
391
- *
392
- * @return mixed value of $name parameter, array of all configuration
393
- * parameters if $name is not given
394
- * @throws HTTP_Request2_LogicException If the parameter is unknown
395
- */
396
- public function getConfig($name = null)
397
- {
398
- if (null === $name) {
399
- return $this->config;
400
- } elseif (!array_key_exists($name, $this->config)) {
401
- throw new HTTP_Request2_LogicException(
402
- "Unknown configuration parameter '{$name}'",
403
- HTTP_Request2_Exception::INVALID_ARGUMENT
404
- );
405
- }
406
- return $this->config[$name];
407
- }
408
-
409
- /**
410
- * Sets the autentification data
411
- *
412
- * @param string $user user name
413
- * @param string $password password
414
- * @param string $scheme authentication scheme
415
- *
416
- * @return HTTP_Request2
417
- */
418
- public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC)
419
- {
420
- if (empty($user)) {
421
- $this->auth = null;
422
- } else {
423
- $this->auth = array(
424
- 'user' => (string)$user,
425
- 'password' => (string)$password,
426
- 'scheme' => $scheme
427
- );
428
- }
429
-
430
- return $this;
431
- }
432
-
433
- /**
434
- * Returns the authentication data
435
- *
436
- * The array has the keys 'user', 'password' and 'scheme', where 'scheme'
437
- * is one of the HTTP_Request2::AUTH_* constants.
438
- *
439
- * @return array
440
- */
441
- public function getAuth()
442
- {
443
- return $this->auth;
444
- }
445
-
446
- /**
447
- * Sets request header(s)
448
- *
449
- * The first parameter may be either a full header string 'header: value' or
450
- * header name. In the former case $value parameter is ignored, in the latter
451
- * the header's value will either be set to $value or the header will be
452
- * removed if $value is null. The first parameter can also be an array of
453
- * headers, in that case method will be called recursively.
454
- *
455
- * Note that headers are treated case insensitively as per RFC 2616.
456
- *
457
- * <code>
458
- * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar'
459
- * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz'
460
- * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux'
461
- * $req->setHeader('FOO'); // removes 'Foo' header from request
462
- * </code>
463
- *
464
- * @param string|array $name header name, header string ('Header: value')
465
- * or an array of headers
466
- * @param string|array|null $value header value if $name is not an array,
467
- * header will be removed if value is null
468
- * @param bool $replace whether to replace previous header with the
469
- * same name or append to its value
470
- *
471
- * @return HTTP_Request2
472
- * @throws HTTP_Request2_LogicException
473
- */
474
- public function setHeader($name, $value = null, $replace = true)
475
- {
476
- if (is_array($name)) {
477
- foreach ($name as $k => $v) {
478
- if (is_string($k)) {
479
- $this->setHeader($k, $v, $replace);
480
- } else {
481
- $this->setHeader($v, null, $replace);
482
- }
483
- }
484
- } else {
485
- if (null === $value && strpos($name, ':')) {
486
- list($name, $value) = array_map('trim', explode(':', $name, 2));
487
- }
488
- // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2
489
- if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) {
490
- throw new HTTP_Request2_LogicException(
491
- "Invalid header name '{$name}'",
492
- HTTP_Request2_Exception::INVALID_ARGUMENT
493
- );
494
- }
495
- // Header names are case insensitive anyway
496
- $name = strtolower($name);
497
- if (null === $value) {
498
- unset($this->headers[$name]);
499
-
500
- } else {
501
- if (is_array($value)) {
502
- $value = implode(', ', array_map('trim', $value));
503
- } elseif (is_string($value)) {
504
- $value = trim($value);
505
- }
506
- if (!isset($this->headers[$name]) || $replace) {
507
- $this->headers[$name] = $value;
508
- } else {
509
- $this->headers[$name] .= ', ' . $value;
510
- }
511
- }
512
- }
513
-
514
- return $this;
515
- }
516
-
517
- /**
518
- * Returns the request headers
519
- *
520
- * The array is of the form ('header name' => 'header value'), header names
521
- * are lowercased
522
- *
523
- * @return array
524
- */
525
- public function getHeaders()
526
- {
527
- return $this->headers;
528
- }
529
-
530
- /**
531
- * Adds a cookie to the request
532
- *
533
- * If the request does not have a CookieJar object set, this method simply
534
- * appends a cookie to "Cookie:" header.
535
- *
536
- * If a CookieJar object is available, the cookie is stored in that object.
537
- * Data from request URL will be used for setting its 'domain' and 'path'
538
- * parameters, 'expires' and 'secure' will be set to null and false,
539
- * respectively. If you need further control, use CookieJar's methods.
540
- *
541
- * @param string $name cookie name
542
- * @param string $value cookie value
543
- *
544
- * @return HTTP_Request2
545
- * @throws HTTP_Request2_LogicException
546
- * @see setCookieJar()
547
- */
548
- public function addCookie($name, $value)
549
- {
550
- if (!empty($this->cookieJar)) {
551
- $this->cookieJar->store(
552
- array('name' => $name, 'value' => $value), $this->url
553
- );
554
-
555
- } else {
556
- $cookie = $name . '=' . $value;
557
- if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) {
558
- throw new HTTP_Request2_LogicException(
559
- "Invalid cookie: '{$cookie}'",
560
- HTTP_Request2_Exception::INVALID_ARGUMENT
561
- );
562
- }
563
- $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; ';
564
- $this->setHeader('cookie', $cookies . $cookie);
565
- }
566
-
567
- return $this;
568
- }
569
-
570
- /**
571
- * Sets the request body
572
- *
573
- * If you provide file pointer rather than file name, it should support
574
- * fstat() and rewind() operations.
575
- *
576
- * @param string|resource|HTTP_Request2_MultipartBody $body Either a
577
- * string with the body or filename containing body or
578
- * pointer to an open file or object with multipart body data
579
- * @param bool $isFilename Whether
580
- * first parameter is a filename
581
- *
582
- * @return HTTP_Request2
583
- * @throws HTTP_Request2_LogicException
584
- */
585
- public function setBody($body, $isFilename = false)
586
- {
587
- if (!$isFilename && !is_resource($body)) {
588
- if (!$body instanceof HTTP_Request2_MultipartBody) {
589
- $this->body = (string)$body;
590
- } else {
591
- $this->body = $body;
592
- }
593
- } else {
594
- $fileData = $this->fopenWrapper($body, empty($this->headers['content-type']));
595
- $this->body = $fileData['fp'];
596
- if (empty($this->headers['content-type'])) {
597
- $this->setHeader('content-type', $fileData['type']);
598
- }
599
- }
600
- $this->postParams = $this->uploads = array();
601
-
602
- return $this;
603
- }
604
-
605
- /**
606
- * Returns the request body
607
- *
608
- * @return string|resource|HTTP_Request2_MultipartBody
609
- */
610
- public function getBody()
611
- {
612
- if (self::METHOD_POST == $this->method
613
- && (!empty($this->postParams) || !empty($this->uploads))
614
- ) {
615
- if (0 === strpos($this->headers['content-type'], 'application/x-www-form-urlencoded')) {
616
- $body = http_build_query($this->postParams, '', '&');
617
- if (!$this->getConfig('use_brackets')) {
618
- $body = preg_replace('/%5B\d+%5D=/', '=', $body);
619
- }
620
- // support RFC 3986 by not encoding '~' symbol (request #15368)
621
- return str_replace('%7E', '~', $body);
622
-
623
- } elseif (0 === strpos($this->headers['content-type'], 'multipart/form-data')) {
624
- require_once 'HTTP/Request2/MultipartBody.php';
625
- return new HTTP_Request2_MultipartBody(
626
- $this->postParams, $this->uploads, $this->getConfig('use_brackets')
627
- );
628
- }
629
- }
630
- return $this->body;
631
- }
632
-
633
- /**
634
- * Adds a file to form-based file upload
635
- *
636
- * Used to emulate file upload via a HTML form. The method also sets
637
- * Content-Type of HTTP request to 'multipart/form-data'.
638
- *
639
- * If you just want to send the contents of a file as the body of HTTP
640
- * request you should use setBody() method.
641
- *
642
- * If you provide file pointers rather than file names, they should support
643
- * fstat() and rewind() operations.
644
- *
645
- * @param string $fieldName name of file-upload field
646
- * @param string|resource|array $filename full name of local file,
647
- * pointer to open file or an array of files
648
- * @param string $sendFilename filename to send in the request
649
- * @param string $contentType content-type of file being uploaded
650
- *
651
- * @return HTTP_Request2
652
- * @throws HTTP_Request2_LogicException
653
- */
654
- public function addUpload(
655
- $fieldName, $filename, $sendFilename = null, $contentType = null
656
- ) {
657
- if (!is_array($filename)) {
658
- $fileData = $this->fopenWrapper($filename, empty($contentType));
659
- $this->uploads[$fieldName] = array(
660
- 'fp' => $fileData['fp'],
661
- 'filename' => !empty($sendFilename)? $sendFilename
662
- :(is_string($filename)? basename($filename): 'anonymous.blob') ,
663
- 'size' => $fileData['size'],
664
- 'type' => empty($contentType)? $fileData['type']: $contentType
665
- );
666
- } else {
667
- $fps = $names = $sizes = $types = array();
668
- foreach ($filename as $f) {
669
- if (!is_array($f)) {
670
- $f = array($f);
671
- }
672
- $fileData = $this->fopenWrapper($f[0], empty($f[2]));
673
- $fps[] = $fileData['fp'];
674
- $names[] = !empty($f[1])? $f[1]
675
- :(is_string($f[0])? basename($f[0]): 'anonymous.blob');
676
- $sizes[] = $fileData['size'];
677
- $types[] = empty($f[2])? $fileData['type']: $f[2];
678
- }
679
- $this->uploads[$fieldName] = array(
680
- 'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types
681
- );
682
- }
683
- if (empty($this->headers['content-type'])
684
- || 'application/x-www-form-urlencoded' == $this->headers['content-type']
685
- ) {
686
- $this->setHeader('content-type', 'multipart/form-data');
687
- }
688
-
689
- return $this;
690
- }
691
-
692
- /**
693
- * Adds POST parameter(s) to the request.
694
- *
695
- * @param string|array $name parameter name or array ('name' => 'value')
696
- * @param mixed $value parameter value (can be an array)
697
- *
698
- * @return HTTP_Request2
699
- */
700
- public function addPostParameter($name, $value = null)
701
- {
702
- if (!is_array($name)) {
703
- $this->postParams[$name] = $value;
704
- } else {
705
- foreach ($name as $k => $v) {
706
- $this->addPostParameter($k, $v);
707
- }
708
- }
709
- if (empty($this->headers['content-type'])) {
710
- $this->setHeader('content-type', 'application/x-www-form-urlencoded');
711
- }
712
-
713
- return $this;
714
- }
715
-
716
- /**
717
- * Attaches a new observer
718
- *
719
- * @param SplObserver $observer any object implementing SplObserver
720
- */
721
- public function attach(SplObserver $observer)
722
- {
723
- foreach ($this->observers as $attached) {
724
- if ($attached === $observer) {
725
- return;
726
- }
727
- }
728
- $this->observers[] = $observer;
729
- }
730
-
731
- /**
732
- * Detaches an existing observer
733
- *
734
- * @param SplObserver $observer any object implementing SplObserver
735
- */
736
- public function detach(SplObserver $observer)
737
- {
738
- foreach ($this->observers as $key => $attached) {
739
- if ($attached === $observer) {
740
- unset($this->observers[$key]);
741
- return;
742
- }
743
- }
744
- }
745
-
746
- /**
747
- * Notifies all observers
748
- */
749
- public function notify()
750
- {
751
- foreach ($this->observers as $observer) {
752
- $observer->update($this);
753
- }
754
- }
755
-
756
- /**
757
- * Sets the last event
758
- *
759
- * Adapters should use this method to set the current state of the request
760
- * and notify the observers.
761
- *
762
- * @param string $name event name
763
- * @param mixed $data event data
764
- */
765
- public function setLastEvent($name, $data = null)
766
- {
767
- $this->lastEvent = array(
768
- 'name' => $name,
769
- 'data' => $data
770
- );
771
- $this->notify();
772
- }
773
-
774
- /**
775
- * Returns the last event
776
- *
777
- * Observers should use this method to access the last change in request.
778
- * The following event names are possible:
779
- * <ul>
780
- * <li>'connect' - after connection to remote server,
781
- * data is the destination (string)</li>
782
- * <li>'disconnect' - after disconnection from server</li>
783
- * <li>'sentHeaders' - after sending the request headers,
784
- * data is the headers sent (string)</li>
785
- * <li>'sentBodyPart' - after sending a part of the request body,
786
- * data is the length of that part (int)</li>
787
- * <li>'sentBody' - after sending the whole request body,
788
- * data is request body length (int)</li>
789
- * <li>'receivedHeaders' - after receiving the response headers,
790
- * data is HTTP_Request2_Response object</li>
791
- * <li>'receivedBodyPart' - after receiving a part of the response
792
- * body, data is that part (string)</li>
793
- * <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still
794
- * encoded by Content-Encoding</li>
795
- * <li>'receivedBody' - after receiving the complete response
796
- * body, data is HTTP_Request2_Response object</li>
797
- * </ul>
798
- * Different adapters may not send all the event types. Mock adapter does
799
- * not send any events to the observers.
800
- *
801
- * @return array The array has two keys: 'name' and 'data'
802
- */
803
- public function getLastEvent()
804
- {
805
- return $this->lastEvent;
806
- }
807
-
808
- /**
809
- * Sets the adapter used to actually perform the request
810
- *
811
- * You can pass either an instance of a class implementing HTTP_Request2_Adapter
812
- * or a class name. The method will only try to include a file if the class
813
- * name starts with HTTP_Request2_Adapter_, it will also try to prepend this
814
- * prefix to the class name if it doesn't contain any underscores, so that
815
- * <code>
816
- * $request->setAdapter('curl');
817
- * </code>
818
- * will work.
819
- *
820
- * @param string|HTTP_Request2_Adapter $adapter Adapter to use
821
- *
822
- * @return HTTP_Request2
823
- * @throws HTTP_Request2_LogicException
824
- */
825
- public function setAdapter($adapter)
826
- {
827
- if (is_string($adapter)) {
828
- if (!class_exists($adapter, false)) {
829
- if (false === strpos($adapter, '_')) {
830
- $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter);
831
- }
832
- if (!class_exists($adapter, false)
833
- && preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)
834
- ) {
835
- include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php';
836
- }
837
- if (!class_exists($adapter, false)) {
838
- throw new HTTP_Request2_LogicException(
839
- "Class {$adapter} not found",
840
- HTTP_Request2_Exception::MISSING_VALUE
841
- );
842
- }
843
- }
844
- $adapter = new $adapter;
845
- }
846
- if (!$adapter instanceof HTTP_Request2_Adapter) {
847
- throw new HTTP_Request2_LogicException(
848
- 'Parameter is not a HTTP request adapter',
849
- HTTP_Request2_Exception::INVALID_ARGUMENT
850
- );
851
- }
852
- $this->adapter = $adapter;
853
-
854
- return $this;
855
- }
856
-
857
- /**
858
- * Sets the cookie jar
859
- *
860
- * A cookie jar is used to maintain cookies across HTTP requests and
861
- * responses. Cookies from jar will be automatically added to the request
862
- * headers based on request URL.
863
- *
864
- * @param HTTP_Request2_CookieJar|bool $jar Existing CookieJar object, true to
865
- * create a new one, false to remove
866
- *
867
- * @return HTTP_Request2
868
- * @throws HTTP_Request2_LogicException
869
- */
870
- public function setCookieJar($jar = true)
871
- {
872
- if (!class_exists('HTTP_Request2_CookieJar', false)) {
873
- require_once 'HTTP/Request2/CookieJar.php';
874
- }
875
-
876
- if ($jar instanceof HTTP_Request2_CookieJar) {
877
- $this->cookieJar = $jar;
878
- } elseif (true === $jar) {
879
- $this->cookieJar = new HTTP_Request2_CookieJar();
880
- } elseif (!$jar) {
881
- $this->cookieJar = null;
882
- } else {
883
- throw new HTTP_Request2_LogicException(
884
- 'Invalid parameter passed to setCookieJar()',
885
- HTTP_Request2_Exception::INVALID_ARGUMENT
886
- );
887
- }
888
-
889
- return $this;
890
- }
891
-
892
- /**
893
- * Returns current CookieJar object or null if none
894
- *
895
- * @return HTTP_Request2_CookieJar|null
896
- */
897
- public function getCookieJar()
898
- {
899
- return $this->cookieJar;
900
- }
901
-
902
- /**
903
- * Sends the request and returns the response
904
- *
905
- * @throws HTTP_Request2_Exception
906
- * @return HTTP_Request2_Response
907
- */
908
- public function send()
909
- {
910
- // Sanity check for URL
911
- if (!$this->url instanceof Net_URL2
912
- || !$this->url->isAbsolute()
913
- || !in_array(strtolower($this->url->getScheme()), array('https', 'http'))
914
- ) {
915
- throw new HTTP_Request2_LogicException(
916
- 'HTTP_Request2 needs an absolute HTTP(S) request URL, '
917
- . ($this->url instanceof Net_URL2
918
- ? "'" . $this->url->__toString() . "'" : 'none')
919
- . ' given',
920
- HTTP_Request2_Exception::INVALID_ARGUMENT
921
- );
922
- }
923
- if (empty($this->adapter)) {
924
- $this->setAdapter($this->getConfig('adapter'));
925
- }
926
- // magic_quotes_runtime may break file uploads and chunked response
927
- // processing; see bug #4543. Don't use ini_get() here; see bug #16440.
928
- if ($magicQuotes = get_magic_quotes_runtime()) {
929
- set_magic_quotes_runtime(false);
930
- }
931
- // force using single byte encoding if mbstring extension overloads
932
- // strlen() and substr(); see bug #1781, bug #10605
933
- if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
934
- $oldEncoding = mb_internal_encoding();
935
- mb_internal_encoding('8bit');
936
- }
937
-
938
- try {
939
- $response = $this->adapter->sendRequest($this);
940
- } catch (Exception $e) {
941
- }
942
- // cleanup in either case (poor man's "finally" clause)
943
- if ($magicQuotes) {
944
- set_magic_quotes_runtime(true);
945
- }
946
- if (!empty($oldEncoding)) {
947
- mb_internal_encoding($oldEncoding);
948
- }
949
- // rethrow the exception
950
- if (!empty($e)) {
951
- throw $e;
952
- }
953
- return $response;
954
- }
955
-
956
- /**
957
- * Wrapper around fopen()/fstat() used by setBody() and addUpload()
958
- *
959
- * @param string|resource $file file name or pointer to open file
960
- * @param bool $detectType whether to try autodetecting MIME
961
- * type of file, will only work if $file is a
962
- * filename, not pointer
963
- *
964
- * @return array array('fp' => file pointer, 'size' => file size, 'type' => MIME type)
965
- * @throws HTTP_Request2_LogicException
966
- */
967
- protected function fopenWrapper($file, $detectType = false)
968
- {
969
- if (!is_string($file) && !is_resource($file)) {
970
- throw new HTTP_Request2_LogicException(
971
- "Filename or file pointer resource expected",
972
- HTTP_Request2_Exception::INVALID_ARGUMENT
973
- );
974
- }
975
- $fileData = array(
976
- 'fp' => is_string($file)? null: $file,
977
- 'type' => 'application/octet-stream',
978
- 'size' => 0
979
- );
980
- if (is_string($file)) {
981
- if (!($fileData['fp'] = @fopen($file, 'rb'))) {
982
- $error = error_get_last();
983
- throw new HTTP_Request2_LogicException(
984
- $error['message'], HTTP_Request2_Exception::READ_ERROR
985
- );
986
- }
987
- if ($detectType) {
988
- $fileData['type'] = self::detectMimeType($file);
989
- }
990
- }
991
- if (!($stat = fstat($fileData['fp']))) {
992
- throw new HTTP_Request2_LogicException(
993
- "fstat() call failed", HTTP_Request2_Exception::READ_ERROR
994
- );
995
- }
996
- $fileData['size'] = $stat['size'];
997
-
998
- return $fileData;
999
- }
1000
-
1001
- /**
1002
- * Tries to detect MIME type of a file
1003
- *
1004
- * The method will try to use fileinfo extension if it is available,
1005
- * deprecated mime_content_type() function in the other case. If neither
1006
- * works, default 'application/octet-stream' MIME type is returned
1007
- *
1008
- * @param string $filename file name
1009
- *
1010
- * @return string file MIME type
1011
- */
1012
- protected static function detectMimeType($filename)
1013
- {
1014
- // finfo extension from PECL available
1015
- if (function_exists('finfo_open')) {
1016
- if (!isset(self::$_fileinfoDb)) {
1017
- self::$_fileinfoDb = @finfo_open(FILEINFO_MIME);
1018
- }
1019
- if (self::$_fileinfoDb) {
1020
- $info = finfo_file(self::$_fileinfoDb, $filename);
1021
- }
1022
- }
1023
- // (deprecated) mime_content_type function available
1024
- if (empty($info) && function_exists('mime_content_type')) {
1025
- return mime_content_type($filename);
1026
- }
1027
- return empty($info)? 'application/octet-stream': $info;
1028
- }
1029
- }
1030
- ?>
1
+ <?php
2
+ /**
3
+ * Class representing a HTTP request message
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * A class representing an URL as per RFC 3986.
23
+ */
24
+ require_once 'Net/URL2.php';
25
+
26
+ /**
27
+ * Exception class for HTTP_Request2 package
28
+ */
29
+ require_once 'HTTP/Request2/Exception.php';
30
+
31
+ /**
32
+ * Class representing a HTTP request message
33
+ *
34
+ * @category HTTP
35
+ * @package HTTP_Request2
36
+ * @author Alexey Borzov <avb@php.net>
37
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
38
+ * @version Release: 2.2.1
39
+ * @link http://pear.php.net/package/HTTP_Request2
40
+ * @link http://tools.ietf.org/html/rfc2616#section-5
41
+ */
42
+ class HTTP_Request2 implements SplSubject
43
+ {
44
+ /**#@+
45
+ * Constants for HTTP request methods
46
+ *
47
+ * @link http://tools.ietf.org/html/rfc2616#section-5.1.1
48
+ */
49
+ const METHOD_OPTIONS = 'OPTIONS';
50
+ const METHOD_GET = 'GET';
51
+ const METHOD_HEAD = 'HEAD';
52
+ const METHOD_POST = 'POST';
53
+ const METHOD_PUT = 'PUT';
54
+ const METHOD_DELETE = 'DELETE';
55
+ const METHOD_TRACE = 'TRACE';
56
+ const METHOD_CONNECT = 'CONNECT';
57
+ /**#@-*/
58
+
59
+ /**#@+
60
+ * Constants for HTTP authentication schemes
61
+ *
62
+ * @link http://tools.ietf.org/html/rfc2617
63
+ */
64
+ const AUTH_BASIC = 'basic';
65
+ const AUTH_DIGEST = 'digest';
66
+ /**#@-*/
67
+
68
+ /**
69
+ * Regular expression used to check for invalid symbols in RFC 2616 tokens
70
+ * @link http://pear.php.net/bugs/bug.php?id=15630
71
+ */
72
+ const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!';
73
+
74
+ /**
75
+ * Regular expression used to check for invalid symbols in cookie strings
76
+ * @link http://pear.php.net/bugs/bug.php?id=15630
77
+ * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
78
+ */
79
+ const REGEXP_INVALID_COOKIE = '/[\s,;]/';
80
+
81
+ /**
82
+ * Fileinfo magic database resource
83
+ * @var resource
84
+ * @see detectMimeType()
85
+ */
86
+ private static $_fileinfoDb;
87
+
88
+ /**
89
+ * Observers attached to the request (instances of SplObserver)
90
+ * @var array
91
+ */
92
+ protected $observers = array();
93
+
94
+ /**
95
+ * Request URL
96
+ * @var Net_URL2
97
+ */
98
+ protected $url;
99
+
100
+ /**
101
+ * Request method
102
+ * @var string
103
+ */
104
+ protected $method = self::METHOD_GET;
105
+
106
+ /**
107
+ * Authentication data
108
+ * @var array
109
+ * @see getAuth()
110
+ */
111
+ protected $auth;
112
+
113
+ /**
114
+ * Request headers
115
+ * @var array
116
+ */
117
+ protected $headers = array();
118
+
119
+ /**
120
+ * Configuration parameters
121
+ * @var array
122
+ * @see setConfig()
123
+ */
124
+ protected $config = array(
125
+ 'adapter' => 'HTTP_Request2_Adapter_Socket',
126
+ 'connect_timeout' => 10,
127
+ 'timeout' => 0,
128
+ 'use_brackets' => true,
129
+ 'protocol_version' => '1.1',
130
+ 'buffer_size' => 16384,
131
+ 'store_body' => true,
132
+ 'local_ip' => null,
133
+
134
+ 'proxy_host' => '',
135
+ 'proxy_port' => '',
136
+ 'proxy_user' => '',
137
+ 'proxy_password' => '',
138
+ 'proxy_auth_scheme' => self::AUTH_BASIC,
139
+ 'proxy_type' => 'http',
140
+
141
+ 'ssl_verify_peer' => true,
142
+ 'ssl_verify_host' => true,
143
+ 'ssl_cafile' => null,
144
+ 'ssl_capath' => null,
145
+ 'ssl_local_cert' => null,
146
+ 'ssl_passphrase' => null,
147
+
148
+ 'digest_compat_ie' => false,
149
+
150
+ 'follow_redirects' => false,
151
+ 'max_redirects' => 5,
152
+ 'strict_redirects' => false
153
+ );
154
+
155
+ /**
156
+ * Last event in request / response handling, intended for observers
157
+ * @var array
158
+ * @see getLastEvent()
159
+ */
160
+ protected $lastEvent = array(
161
+ 'name' => 'start',
162
+ 'data' => null
163
+ );
164
+
165
+ /**
166
+ * Request body
167
+ * @var string|resource
168
+ * @see setBody()
169
+ */
170
+ protected $body = '';
171
+
172
+ /**
173
+ * Array of POST parameters
174
+ * @var array
175
+ */
176
+ protected $postParams = array();
177
+
178
+ /**
179
+ * Array of file uploads (for multipart/form-data POST requests)
180
+ * @var array
181
+ */
182
+ protected $uploads = array();
183
+
184
+ /**
185
+ * Adapter used to perform actual HTTP request
186
+ * @var HTTP_Request2_Adapter
187
+ */
188
+ protected $adapter;
189
+
190
+ /**
191
+ * Cookie jar to persist cookies between requests
192
+ * @var HTTP_Request2_CookieJar
193
+ */
194
+ protected $cookieJar = null;
195
+
196
+ /**
197
+ * Constructor. Can set request URL, method and configuration array.
198
+ *
199
+ * Also sets a default value for User-Agent header.
200
+ *
201
+ * @param string|Net_Url2 $url Request URL
202
+ * @param string $method Request method
203
+ * @param array $config Configuration for this Request instance
204
+ */
205
+ public function __construct(
206
+ $url = null, $method = self::METHOD_GET, array $config = array()
207
+ ) {
208
+ $this->setConfig($config);
209
+ if (!empty($url)) {
210
+ $this->setUrl($url);
211
+ }
212
+ if (!empty($method)) {
213
+ $this->setMethod($method);
214
+ }
215
+ $this->setHeader(
216
+ 'user-agent', 'HTTP_Request2/2.2.1 ' .
217
+ '(http://pear.php.net/package/http_request2) PHP/' . phpversion()
218
+ );
219
+ }
220
+
221
+ /**
222
+ * Sets the URL for this request
223
+ *
224
+ * If the URL has userinfo part (username & password) these will be removed
225
+ * and converted to auth data. If the URL does not have a path component,
226
+ * that will be set to '/'.
227
+ *
228
+ * @param string|Net_URL2 $url Request URL
229
+ *
230
+ * @return HTTP_Request2
231
+ * @throws HTTP_Request2_LogicException
232
+ */
233
+ public function setUrl($url)
234
+ {
235
+ if (is_string($url)) {
236
+ $url = new Net_URL2(
237
+ $url, array(Net_URL2::OPTION_USE_BRACKETS => $this->config['use_brackets'])
238
+ );
239
+ }
240
+ if (!$url instanceof Net_URL2) {
241
+ throw new HTTP_Request2_LogicException(
242
+ 'Parameter is not a valid HTTP URL',
243
+ HTTP_Request2_Exception::INVALID_ARGUMENT
244
+ );
245
+ }
246
+ // URL contains username / password?
247
+ if ($url->getUserinfo()) {
248
+ $username = $url->getUser();
249
+ $password = $url->getPassword();
250
+ $this->setAuth(rawurldecode($username), $password? rawurldecode($password): '');
251
+ $url->setUserinfo('');
252
+ }
253
+ if ('' == $url->getPath()) {
254
+ $url->setPath('/');
255
+ }
256
+ $this->url = $url;
257
+
258
+ return $this;
259
+ }
260
+
261
+ /**
262
+ * Returns the request URL
263
+ *
264
+ * @return Net_URL2
265
+ */
266
+ public function getUrl()
267
+ {
268
+ return $this->url;
269
+ }
270
+
271
+ /**
272
+ * Sets the request method
273
+ *
274
+ * @param string $method one of the methods defined in RFC 2616
275
+ *
276
+ * @return HTTP_Request2
277
+ * @throws HTTP_Request2_LogicException if the method name is invalid
278
+ */
279
+ public function setMethod($method)
280
+ {
281
+ // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1
282
+ if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) {
283
+ throw new HTTP_Request2_LogicException(
284
+ "Invalid request method '{$method}'",
285
+ HTTP_Request2_Exception::INVALID_ARGUMENT
286
+ );
287
+ }
288
+ $this->method = $method;
289
+
290
+ return $this;
291
+ }
292
+
293
+ /**
294
+ * Returns the request method
295
+ *
296
+ * @return string
297
+ */
298
+ public function getMethod()
299
+ {
300
+ return $this->method;
301
+ }
302
+
303
+ /**
304
+ * Sets the configuration parameter(s)
305
+ *
306
+ * The following parameters are available:
307
+ * <ul>
308
+ * <li> 'adapter' - adapter to use (string)</li>
309
+ * <li> 'connect_timeout' - Connection timeout in seconds (integer)</li>
310
+ * <li> 'timeout' - Total number of seconds a request can take.
311
+ * Use 0 for no limit, should be greater than
312
+ * 'connect_timeout' if set (integer)</li>
313
+ * <li> 'use_brackets' - Whether to append [] to array variable names (bool)</li>
314
+ * <li> 'protocol_version' - HTTP Version to use, '1.0' or '1.1' (string)</li>
315
+ * <li> 'buffer_size' - Buffer size to use for reading and writing (int)</li>
316
+ * <li> 'store_body' - Whether to store response body in response object.
317
+ * Set to false if receiving a huge response and
318
+ * using an Observer to save it (boolean)</li>
319
+ * <li> 'local_ip' - Specifies the IP address that will be used for accessing
320
+ * the network (string)</li>
321
+ * <li> 'proxy_type' - Proxy type, 'http' or 'socks5' (string)</li>
322
+ * <li> 'proxy_host' - Proxy server host (string)</li>
323
+ * <li> 'proxy_port' - Proxy server port (integer)</li>
324
+ * <li> 'proxy_user' - Proxy auth username (string)</li>
325
+ * <li> 'proxy_password' - Proxy auth password (string)</li>
326
+ * <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li>
327
+ * <li> 'proxy' - Shorthand for proxy_* parameters, proxy given as URL,
328
+ * e.g. 'socks5://localhost:1080/' (string)</li>
329
+ * <li> 'ssl_verify_peer' - Whether to verify peer's SSL certificate (bool)</li>
330
+ * <li> 'ssl_verify_host' - Whether to check that Common Name in SSL
331
+ * certificate matches host name (bool)</li>
332
+ * <li> 'ssl_cafile' - Cerificate Authority file to verify the peer
333
+ * with (use with 'ssl_verify_peer') (string)</li>
334
+ * <li> 'ssl_capath' - Directory holding multiple Certificate
335
+ * Authority files (string)</li>
336
+ * <li> 'ssl_local_cert' - Name of a file containing local cerificate (string)</li>
337
+ * <li> 'ssl_passphrase' - Passphrase with which local certificate
338
+ * was encoded (string)</li>
339
+ * <li> 'digest_compat_ie' - Whether to imitate behaviour of MSIE 5 and 6
340
+ * in using URL without query string in digest
341
+ * authentication (boolean)</li>
342
+ * <li> 'follow_redirects' - Whether to automatically follow HTTP Redirects (boolean)</li>
343
+ * <li> 'max_redirects' - Maximum number of redirects to follow (integer)</li>
344
+ * <li> 'strict_redirects' - Whether to keep request method on redirects via status 301 and
345
+ * 302 (true, needed for compatibility with RFC 2616)
346
+ * or switch to GET (false, needed for compatibility with most
347
+ * browsers) (boolean)</li>
348
+ * </ul>
349
+ *
350
+ * @param string|array $nameOrConfig configuration parameter name or array
351
+ * ('parameter name' => 'parameter value')
352
+ * @param mixed $value parameter value if $nameOrConfig is not an array
353
+ *
354
+ * @return HTTP_Request2
355
+ * @throws HTTP_Request2_LogicException If the parameter is unknown
356
+ */
357
+ public function setConfig($nameOrConfig, $value = null)
358
+ {
359
+ if (is_array($nameOrConfig)) {
360
+ foreach ($nameOrConfig as $name => $value) {
361
+ $this->setConfig($name, $value);
362
+ }
363
+
364
+ } elseif ('proxy' == $nameOrConfig) {
365
+ $url = new Net_URL2($value);
366
+ $this->setConfig(array(
367
+ 'proxy_type' => $url->getScheme(),
368
+ 'proxy_host' => $url->getHost(),
369
+ 'proxy_port' => $url->getPort(),
370
+ 'proxy_user' => rawurldecode($url->getUser()),
371
+ 'proxy_password' => rawurldecode($url->getPassword())
372
+ ));
373
+
374
+ } else {
375
+ if (!array_key_exists($nameOrConfig, $this->config)) {
376
+ throw new HTTP_Request2_LogicException(
377
+ "Unknown configuration parameter '{$nameOrConfig}'",
378
+ HTTP_Request2_Exception::INVALID_ARGUMENT
379
+ );
380
+ }
381
+ $this->config[$nameOrConfig] = $value;
382
+ }
383
+
384
+ return $this;
385
+ }
386
+
387
+ /**
388
+ * Returns the value(s) of the configuration parameter(s)
389
+ *
390
+ * @param string $name parameter name
391
+ *
392
+ * @return mixed value of $name parameter, array of all configuration
393
+ * parameters if $name is not given
394
+ * @throws HTTP_Request2_LogicException If the parameter is unknown
395
+ */
396
+ public function getConfig($name = null)
397
+ {
398
+ if (null === $name) {
399
+ return $this->config;
400
+ } elseif (!array_key_exists($name, $this->config)) {
401
+ throw new HTTP_Request2_LogicException(
402
+ "Unknown configuration parameter '{$name}'",
403
+ HTTP_Request2_Exception::INVALID_ARGUMENT
404
+ );
405
+ }
406
+ return $this->config[$name];
407
+ }
408
+
409
+ /**
410
+ * Sets the autentification data
411
+ *
412
+ * @param string $user user name
413
+ * @param string $password password
414
+ * @param string $scheme authentication scheme
415
+ *
416
+ * @return HTTP_Request2
417
+ */
418
+ public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC)
419
+ {
420
+ if (empty($user)) {
421
+ $this->auth = null;
422
+ } else {
423
+ $this->auth = array(
424
+ 'user' => (string)$user,
425
+ 'password' => (string)$password,
426
+ 'scheme' => $scheme
427
+ );
428
+ }
429
+
430
+ return $this;
431
+ }
432
+
433
+ /**
434
+ * Returns the authentication data
435
+ *
436
+ * The array has the keys 'user', 'password' and 'scheme', where 'scheme'
437
+ * is one of the HTTP_Request2::AUTH_* constants.
438
+ *
439
+ * @return array
440
+ */
441
+ public function getAuth()
442
+ {
443
+ return $this->auth;
444
+ }
445
+
446
+ /**
447
+ * Sets request header(s)
448
+ *
449
+ * The first parameter may be either a full header string 'header: value' or
450
+ * header name. In the former case $value parameter is ignored, in the latter
451
+ * the header's value will either be set to $value or the header will be
452
+ * removed if $value is null. The first parameter can also be an array of
453
+ * headers, in that case method will be called recursively.
454
+ *
455
+ * Note that headers are treated case insensitively as per RFC 2616.
456
+ *
457
+ * <code>
458
+ * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar'
459
+ * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz'
460
+ * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux'
461
+ * $req->setHeader('FOO'); // removes 'Foo' header from request
462
+ * </code>
463
+ *
464
+ * @param string|array $name header name, header string ('Header: value')
465
+ * or an array of headers
466
+ * @param string|array|null $value header value if $name is not an array,
467
+ * header will be removed if value is null
468
+ * @param bool $replace whether to replace previous header with the
469
+ * same name or append to its value
470
+ *
471
+ * @return HTTP_Request2
472
+ * @throws HTTP_Request2_LogicException
473
+ */
474
+ public function setHeader($name, $value = null, $replace = true)
475
+ {
476
+ if (is_array($name)) {
477
+ foreach ($name as $k => $v) {
478
+ if (is_string($k)) {
479
+ $this->setHeader($k, $v, $replace);
480
+ } else {
481
+ $this->setHeader($v, null, $replace);
482
+ }
483
+ }
484
+ } else {
485
+ if (null === $value && strpos($name, ':')) {
486
+ list($name, $value) = array_map('trim', explode(':', $name, 2));
487
+ }
488
+ // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2
489
+ if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) {
490
+ throw new HTTP_Request2_LogicException(
491
+ "Invalid header name '{$name}'",
492
+ HTTP_Request2_Exception::INVALID_ARGUMENT
493
+ );
494
+ }
495
+ // Header names are case insensitive anyway
496
+ $name = strtolower($name);
497
+ if (null === $value) {
498
+ unset($this->headers[$name]);
499
+
500
+ } else {
501
+ if (is_array($value)) {
502
+ $value = implode(', ', array_map('trim', $value));
503
+ } elseif (is_string($value)) {
504
+ $value = trim($value);
505
+ }
506
+ if (!isset($this->headers[$name]) || $replace) {
507
+ $this->headers[$name] = $value;
508
+ } else {
509
+ $this->headers[$name] .= ', ' . $value;
510
+ }
511
+ }
512
+ }
513
+
514
+ return $this;
515
+ }
516
+
517
+ /**
518
+ * Returns the request headers
519
+ *
520
+ * The array is of the form ('header name' => 'header value'), header names
521
+ * are lowercased
522
+ *
523
+ * @return array
524
+ */
525
+ public function getHeaders()
526
+ {
527
+ return $this->headers;
528
+ }
529
+
530
+ /**
531
+ * Adds a cookie to the request
532
+ *
533
+ * If the request does not have a CookieJar object set, this method simply
534
+ * appends a cookie to "Cookie:" header.
535
+ *
536
+ * If a CookieJar object is available, the cookie is stored in that object.
537
+ * Data from request URL will be used for setting its 'domain' and 'path'
538
+ * parameters, 'expires' and 'secure' will be set to null and false,
539
+ * respectively. If you need further control, use CookieJar's methods.
540
+ *
541
+ * @param string $name cookie name
542
+ * @param string $value cookie value
543
+ *
544
+ * @return HTTP_Request2
545
+ * @throws HTTP_Request2_LogicException
546
+ * @see setCookieJar()
547
+ */
548
+ public function addCookie($name, $value)
549
+ {
550
+ if (!empty($this->cookieJar)) {
551
+ $this->cookieJar->store(
552
+ array('name' => $name, 'value' => $value), $this->url
553
+ );
554
+
555
+ } else {
556
+ $cookie = $name . '=' . $value;
557
+ if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) {
558
+ throw new HTTP_Request2_LogicException(
559
+ "Invalid cookie: '{$cookie}'",
560
+ HTTP_Request2_Exception::INVALID_ARGUMENT
561
+ );
562
+ }
563
+ $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; ';
564
+ $this->setHeader('cookie', $cookies . $cookie);
565
+ }
566
+
567
+ return $this;
568
+ }
569
+
570
+ /**
571
+ * Sets the request body
572
+ *
573
+ * If you provide file pointer rather than file name, it should support
574
+ * fstat() and rewind() operations.
575
+ *
576
+ * @param string|resource|HTTP_Request2_MultipartBody $body Either a
577
+ * string with the body or filename containing body or
578
+ * pointer to an open file or object with multipart body data
579
+ * @param bool $isFilename Whether
580
+ * first parameter is a filename
581
+ *
582
+ * @return HTTP_Request2
583
+ * @throws HTTP_Request2_LogicException
584
+ */
585
+ public function setBody($body, $isFilename = false)
586
+ {
587
+ if (!$isFilename && !is_resource($body)) {
588
+ if (!$body instanceof HTTP_Request2_MultipartBody) {
589
+ $this->body = (string)$body;
590
+ } else {
591
+ $this->body = $body;
592
+ }
593
+ } else {
594
+ $fileData = $this->fopenWrapper($body, empty($this->headers['content-type']));
595
+ $this->body = $fileData['fp'];
596
+ if (empty($this->headers['content-type'])) {
597
+ $this->setHeader('content-type', $fileData['type']);
598
+ }
599
+ }
600
+ $this->postParams = $this->uploads = array();
601
+
602
+ return $this;
603
+ }
604
+
605
+ /**
606
+ * Returns the request body
607
+ *
608
+ * @return string|resource|HTTP_Request2_MultipartBody
609
+ */
610
+ public function getBody()
611
+ {
612
+ if (self::METHOD_POST == $this->method
613
+ && (!empty($this->postParams) || !empty($this->uploads))
614
+ ) {
615
+ if (0 === strpos($this->headers['content-type'], 'application/x-www-form-urlencoded')) {
616
+ $body = http_build_query($this->postParams, '', '&');
617
+ if (!$this->getConfig('use_brackets')) {
618
+ $body = preg_replace('/%5B\d+%5D=/', '=', $body);
619
+ }
620
+ // support RFC 3986 by not encoding '~' symbol (request #15368)
621
+ return str_replace('%7E', '~', $body);
622
+
623
+ } elseif (0 === strpos($this->headers['content-type'], 'multipart/form-data')) {
624
+ require_once 'HTTP/Request2/MultipartBody.php';
625
+ return new HTTP_Request2_MultipartBody(
626
+ $this->postParams, $this->uploads, $this->getConfig('use_brackets')
627
+ );
628
+ }
629
+ }
630
+ return $this->body;
631
+ }
632
+
633
+ /**
634
+ * Adds a file to form-based file upload
635
+ *
636
+ * Used to emulate file upload via a HTML form. The method also sets
637
+ * Content-Type of HTTP request to 'multipart/form-data'.
638
+ *
639
+ * If you just want to send the contents of a file as the body of HTTP
640
+ * request you should use setBody() method.
641
+ *
642
+ * If you provide file pointers rather than file names, they should support
643
+ * fstat() and rewind() operations.
644
+ *
645
+ * @param string $fieldName name of file-upload field
646
+ * @param string|resource|array $filename full name of local file,
647
+ * pointer to open file or an array of files
648
+ * @param string $sendFilename filename to send in the request
649
+ * @param string $contentType content-type of file being uploaded
650
+ *
651
+ * @return HTTP_Request2
652
+ * @throws HTTP_Request2_LogicException
653
+ */
654
+ public function addUpload(
655
+ $fieldName, $filename, $sendFilename = null, $contentType = null
656
+ ) {
657
+ if (!is_array($filename)) {
658
+ $fileData = $this->fopenWrapper($filename, empty($contentType));
659
+ $this->uploads[$fieldName] = array(
660
+ 'fp' => $fileData['fp'],
661
+ 'filename' => !empty($sendFilename)? $sendFilename
662
+ :(is_string($filename)? basename($filename): 'anonymous.blob') ,
663
+ 'size' => $fileData['size'],
664
+ 'type' => empty($contentType)? $fileData['type']: $contentType
665
+ );
666
+ } else {
667
+ $fps = $names = $sizes = $types = array();
668
+ foreach ($filename as $f) {
669
+ if (!is_array($f)) {
670
+ $f = array($f);
671
+ }
672
+ $fileData = $this->fopenWrapper($f[0], empty($f[2]));
673
+ $fps[] = $fileData['fp'];
674
+ $names[] = !empty($f[1])? $f[1]
675
+ :(is_string($f[0])? basename($f[0]): 'anonymous.blob');
676
+ $sizes[] = $fileData['size'];
677
+ $types[] = empty($f[2])? $fileData['type']: $f[2];
678
+ }
679
+ $this->uploads[$fieldName] = array(
680
+ 'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types
681
+ );
682
+ }
683
+ if (empty($this->headers['content-type'])
684
+ || 'application/x-www-form-urlencoded' == $this->headers['content-type']
685
+ ) {
686
+ $this->setHeader('content-type', 'multipart/form-data');
687
+ }
688
+
689
+ return $this;
690
+ }
691
+
692
+ /**
693
+ * Adds POST parameter(s) to the request.
694
+ *
695
+ * @param string|array $name parameter name or array ('name' => 'value')
696
+ * @param mixed $value parameter value (can be an array)
697
+ *
698
+ * @return HTTP_Request2
699
+ */
700
+ public function addPostParameter($name, $value = null)
701
+ {
702
+ if (!is_array($name)) {
703
+ $this->postParams[$name] = $value;
704
+ } else {
705
+ foreach ($name as $k => $v) {
706
+ $this->addPostParameter($k, $v);
707
+ }
708
+ }
709
+ if (empty($this->headers['content-type'])) {
710
+ $this->setHeader('content-type', 'application/x-www-form-urlencoded');
711
+ }
712
+
713
+ return $this;
714
+ }
715
+
716
+ /**
717
+ * Attaches a new observer
718
+ *
719
+ * @param SplObserver $observer any object implementing SplObserver
720
+ */
721
+ public function attach(SplObserver $observer)
722
+ {
723
+ foreach ($this->observers as $attached) {
724
+ if ($attached === $observer) {
725
+ return;
726
+ }
727
+ }
728
+ $this->observers[] = $observer;
729
+ }
730
+
731
+ /**
732
+ * Detaches an existing observer
733
+ *
734
+ * @param SplObserver $observer any object implementing SplObserver
735
+ */
736
+ public function detach(SplObserver $observer)
737
+ {
738
+ foreach ($this->observers as $key => $attached) {
739
+ if ($attached === $observer) {
740
+ unset($this->observers[$key]);
741
+ return;
742
+ }
743
+ }
744
+ }
745
+
746
+ /**
747
+ * Notifies all observers
748
+ */
749
+ public function notify()
750
+ {
751
+ foreach ($this->observers as $observer) {
752
+ $observer->update($this);
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Sets the last event
758
+ *
759
+ * Adapters should use this method to set the current state of the request
760
+ * and notify the observers.
761
+ *
762
+ * @param string $name event name
763
+ * @param mixed $data event data
764
+ */
765
+ public function setLastEvent($name, $data = null)
766
+ {
767
+ $this->lastEvent = array(
768
+ 'name' => $name,
769
+ 'data' => $data
770
+ );
771
+ $this->notify();
772
+ }
773
+
774
+ /**
775
+ * Returns the last event
776
+ *
777
+ * Observers should use this method to access the last change in request.
778
+ * The following event names are possible:
779
+ * <ul>
780
+ * <li>'connect' - after connection to remote server,
781
+ * data is the destination (string)</li>
782
+ * <li>'disconnect' - after disconnection from server</li>
783
+ * <li>'sentHeaders' - after sending the request headers,
784
+ * data is the headers sent (string)</li>
785
+ * <li>'sentBodyPart' - after sending a part of the request body,
786
+ * data is the length of that part (int)</li>
787
+ * <li>'sentBody' - after sending the whole request body,
788
+ * data is request body length (int)</li>
789
+ * <li>'receivedHeaders' - after receiving the response headers,
790
+ * data is HTTP_Request2_Response object</li>
791
+ * <li>'receivedBodyPart' - after receiving a part of the response
792
+ * body, data is that part (string)</li>
793
+ * <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still
794
+ * encoded by Content-Encoding</li>
795
+ * <li>'receivedBody' - after receiving the complete response
796
+ * body, data is HTTP_Request2_Response object</li>
797
+ * </ul>
798
+ * Different adapters may not send all the event types. Mock adapter does
799
+ * not send any events to the observers.
800
+ *
801
+ * @return array The array has two keys: 'name' and 'data'
802
+ */
803
+ public function getLastEvent()
804
+ {
805
+ return $this->lastEvent;
806
+ }
807
+
808
+ /**
809
+ * Sets the adapter used to actually perform the request
810
+ *
811
+ * You can pass either an instance of a class implementing HTTP_Request2_Adapter
812
+ * or a class name. The method will only try to include a file if the class
813
+ * name starts with HTTP_Request2_Adapter_, it will also try to prepend this
814
+ * prefix to the class name if it doesn't contain any underscores, so that
815
+ * <code>
816
+ * $request->setAdapter('curl');
817
+ * </code>
818
+ * will work.
819
+ *
820
+ * @param string|HTTP_Request2_Adapter $adapter Adapter to use
821
+ *
822
+ * @return HTTP_Request2
823
+ * @throws HTTP_Request2_LogicException
824
+ */
825
+ public function setAdapter($adapter)
826
+ {
827
+ if (is_string($adapter)) {
828
+ if (!class_exists($adapter, false)) {
829
+ if (false === strpos($adapter, '_')) {
830
+ $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter);
831
+ }
832
+ if (!class_exists($adapter, false)
833
+ && preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)
834
+ ) {
835
+ include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php';
836
+ }
837
+ if (!class_exists($adapter, false)) {
838
+ throw new HTTP_Request2_LogicException(
839
+ "Class {$adapter} not found",
840
+ HTTP_Request2_Exception::MISSING_VALUE
841
+ );
842
+ }
843
+ }
844
+ $adapter = new $adapter;
845
+ }
846
+ if (!$adapter instanceof HTTP_Request2_Adapter) {
847
+ throw new HTTP_Request2_LogicException(
848
+ 'Parameter is not a HTTP request adapter',
849
+ HTTP_Request2_Exception::INVALID_ARGUMENT
850
+ );
851
+ }
852
+ $this->adapter = $adapter;
853
+
854
+ return $this;
855
+ }
856
+
857
+ /**
858
+ * Sets the cookie jar
859
+ *
860
+ * A cookie jar is used to maintain cookies across HTTP requests and
861
+ * responses. Cookies from jar will be automatically added to the request
862
+ * headers based on request URL.
863
+ *
864
+ * @param HTTP_Request2_CookieJar|bool $jar Existing CookieJar object, true to
865
+ * create a new one, false to remove
866
+ *
867
+ * @return HTTP_Request2
868
+ * @throws HTTP_Request2_LogicException
869
+ */
870
+ public function setCookieJar($jar = true)
871
+ {
872
+ if (!class_exists('HTTP_Request2_CookieJar', false)) {
873
+ require_once 'HTTP/Request2/CookieJar.php';
874
+ }
875
+
876
+ if ($jar instanceof HTTP_Request2_CookieJar) {
877
+ $this->cookieJar = $jar;
878
+ } elseif (true === $jar) {
879
+ $this->cookieJar = new HTTP_Request2_CookieJar();
880
+ } elseif (!$jar) {
881
+ $this->cookieJar = null;
882
+ } else {
883
+ throw new HTTP_Request2_LogicException(
884
+ 'Invalid parameter passed to setCookieJar()',
885
+ HTTP_Request2_Exception::INVALID_ARGUMENT
886
+ );
887
+ }
888
+
889
+ return $this;
890
+ }
891
+
892
+ /**
893
+ * Returns current CookieJar object or null if none
894
+ *
895
+ * @return HTTP_Request2_CookieJar|null
896
+ */
897
+ public function getCookieJar()
898
+ {
899
+ return $this->cookieJar;
900
+ }
901
+
902
+ /**
903
+ * Sends the request and returns the response
904
+ *
905
+ * @throws HTTP_Request2_Exception
906
+ * @return HTTP_Request2_Response
907
+ */
908
+ public function send()
909
+ {
910
+ // Sanity check for URL
911
+ if (!$this->url instanceof Net_URL2
912
+ || !$this->url->isAbsolute()
913
+ || !in_array(strtolower($this->url->getScheme()), array('https', 'http'))
914
+ ) {
915
+ throw new HTTP_Request2_LogicException(
916
+ 'HTTP_Request2 needs an absolute HTTP(S) request URL, '
917
+ . ($this->url instanceof Net_URL2
918
+ ? "'" . $this->url->__toString() . "'" : 'none')
919
+ . ' given',
920
+ HTTP_Request2_Exception::INVALID_ARGUMENT
921
+ );
922
+ }
923
+ if (empty($this->adapter)) {
924
+ $this->setAdapter($this->getConfig('adapter'));
925
+ }
926
+ // magic_quotes_runtime may break file uploads and chunked response
927
+ // processing; see bug #4543. Don't use ini_get() here; see bug #16440.
928
+ if ($magicQuotes = get_magic_quotes_runtime()) {
929
+ set_magic_quotes_runtime(false);
930
+ }
931
+ // force using single byte encoding if mbstring extension overloads
932
+ // strlen() and substr(); see bug #1781, bug #10605
933
+ if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
934
+ $oldEncoding = mb_internal_encoding();
935
+ mb_internal_encoding('8bit');
936
+ }
937
+
938
+ try {
939
+ $response = $this->adapter->sendRequest($this);
940
+ } catch (Exception $e) {
941
+ }
942
+ // cleanup in either case (poor man's "finally" clause)
943
+ if ($magicQuotes) {
944
+ set_magic_quotes_runtime(true);
945
+ }
946
+ if (!empty($oldEncoding)) {
947
+ mb_internal_encoding($oldEncoding);
948
+ }
949
+ // rethrow the exception
950
+ if (!empty($e)) {
951
+ throw $e;
952
+ }
953
+ return $response;
954
+ }
955
+
956
+ /**
957
+ * Wrapper around fopen()/fstat() used by setBody() and addUpload()
958
+ *
959
+ * @param string|resource $file file name or pointer to open file
960
+ * @param bool $detectType whether to try autodetecting MIME
961
+ * type of file, will only work if $file is a
962
+ * filename, not pointer
963
+ *
964
+ * @return array array('fp' => file pointer, 'size' => file size, 'type' => MIME type)
965
+ * @throws HTTP_Request2_LogicException
966
+ */
967
+ protected function fopenWrapper($file, $detectType = false)
968
+ {
969
+ if (!is_string($file) && !is_resource($file)) {
970
+ throw new HTTP_Request2_LogicException(
971
+ "Filename or file pointer resource expected",
972
+ HTTP_Request2_Exception::INVALID_ARGUMENT
973
+ );
974
+ }
975
+ $fileData = array(
976
+ 'fp' => is_string($file)? null: $file,
977
+ 'type' => 'application/octet-stream',
978
+ 'size' => 0
979
+ );
980
+ if (is_string($file)) {
981
+ if (!($fileData['fp'] = @fopen($file, 'rb'))) {
982
+ $error = error_get_last();
983
+ throw new HTTP_Request2_LogicException(
984
+ $error['message'], HTTP_Request2_Exception::READ_ERROR
985
+ );
986
+ }
987
+ if ($detectType) {
988
+ $fileData['type'] = self::detectMimeType($file);
989
+ }
990
+ }
991
+ if (!($stat = fstat($fileData['fp']))) {
992
+ throw new HTTP_Request2_LogicException(
993
+ "fstat() call failed", HTTP_Request2_Exception::READ_ERROR
994
+ );
995
+ }
996
+ $fileData['size'] = $stat['size'];
997
+
998
+ return $fileData;
999
+ }
1000
+
1001
+ /**
1002
+ * Tries to detect MIME type of a file
1003
+ *
1004
+ * The method will try to use fileinfo extension if it is available,
1005
+ * deprecated mime_content_type() function in the other case. If neither
1006
+ * works, default 'application/octet-stream' MIME type is returned
1007
+ *
1008
+ * @param string $filename file name
1009
+ *
1010
+ * @return string file MIME type
1011
+ */
1012
+ protected static function detectMimeType($filename)
1013
+ {
1014
+ // finfo extension from PECL available
1015
+ if (function_exists('finfo_open')) {
1016
+ if (!isset(self::$_fileinfoDb)) {
1017
+ self::$_fileinfoDb = @finfo_open(FILEINFO_MIME);
1018
+ }
1019
+ if (self::$_fileinfoDb) {
1020
+ $info = finfo_file(self::$_fileinfoDb, $filename);
1021
+ }
1022
+ }
1023
+ // (deprecated) mime_content_type function available
1024
+ if (empty($info) && function_exists('mime_content_type')) {
1025
+ return mime_content_type($filename);
1026
+ }
1027
+ return empty($info)? 'application/octet-stream': $info;
1028
+ }
1029
+ }
1030
+ ?>
vendor/PEAR/HTTP/Request2/Adapter.php CHANGED
@@ -1,137 +1,137 @@
1
- <?php
2
- /**
3
- * Base class for HTTP_Request2 adapters
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * Class representing a HTTP response
23
- */
24
- require_once 'HTTP/Request2/Response.php';
25
-
26
- /**
27
- * Base class for HTTP_Request2 adapters
28
- *
29
- * HTTP_Request2 class itself only defines methods for aggregating the request
30
- * data, all actual work of sending the request to the remote server and
31
- * receiving its response is performed by adapters.
32
- *
33
- * @category HTTP
34
- * @package HTTP_Request2
35
- * @author Alexey Borzov <avb@php.net>
36
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
37
- * @version Release: 2.2.1
38
- * @link http://pear.php.net/package/HTTP_Request2
39
- */
40
- abstract class HTTP_Request2_Adapter
41
- {
42
- /**
43
- * A list of methods that MUST NOT have a request body, per RFC 2616
44
- * @var array
45
- */
46
- protected static $bodyDisallowed = array('TRACE');
47
-
48
- /**
49
- * Methods having defined semantics for request body
50
- *
51
- * Content-Length header (indicating that the body follows, section 4.3 of
52
- * RFC 2616) will be sent for these methods even if no body was added
53
- *
54
- * @var array
55
- * @link http://pear.php.net/bugs/bug.php?id=12900
56
- * @link http://pear.php.net/bugs/bug.php?id=14740
57
- */
58
- protected static $bodyRequired = array('POST', 'PUT');
59
-
60
- /**
61
- * Request being sent
62
- * @var HTTP_Request2
63
- */
64
- protected $request;
65
-
66
- /**
67
- * Request body
68
- * @var string|resource|HTTP_Request2_MultipartBody
69
- * @see HTTP_Request2::getBody()
70
- */
71
- protected $requestBody;
72
-
73
- /**
74
- * Length of the request body
75
- * @var integer
76
- */
77
- protected $contentLength;
78
-
79
- /**
80
- * Sends request to the remote server and returns its response
81
- *
82
- * @param HTTP_Request2 $request HTTP request message
83
- *
84
- * @return HTTP_Request2_Response
85
- * @throws HTTP_Request2_Exception
86
- */
87
- abstract public function sendRequest(HTTP_Request2 $request);
88
-
89
- /**
90
- * Calculates length of the request body, adds proper headers
91
- *
92
- * @param array &$headers associative array of request headers, this method
93
- * will add proper 'Content-Length' and 'Content-Type'
94
- * headers to this array (or remove them if not needed)
95
- */
96
- protected function calculateRequestLength(&$headers)
97
- {
98
- $this->requestBody = $this->request->getBody();
99
-
100
- if (is_string($this->requestBody)) {
101
- $this->contentLength = strlen($this->requestBody);
102
- } elseif (is_resource($this->requestBody)) {
103
- $stat = fstat($this->requestBody);
104
- $this->contentLength = $stat['size'];
105
- rewind($this->requestBody);
106
- } else {
107
- $this->contentLength = $this->requestBody->getLength();
108
- $headers['content-type'] = 'multipart/form-data; boundary=' .
109
- $this->requestBody->getBoundary();
110
- $this->requestBody->rewind();
111
- }
112
-
113
- if (in_array($this->request->getMethod(), self::$bodyDisallowed)
114
- || 0 == $this->contentLength
115
- ) {
116
- // No body: send a Content-Length header nonetheless (request #12900),
117
- // but do that only for methods that require a body (bug #14740)
118
- if (in_array($this->request->getMethod(), self::$bodyRequired)) {
119
- $headers['content-length'] = 0;
120
- } else {
121
- unset($headers['content-length']);
122
- // if the method doesn't require a body and doesn't have a
123
- // body, don't send a Content-Type header. (request #16799)
124
- unset($headers['content-type']);
125
- }
126
- } else {
127
- if (empty($headers['content-type'])) {
128
- $headers['content-type'] = 'application/x-www-form-urlencoded';
129
- }
130
- // Content-Length should not be sent for chunked Transfer-Encoding (bug #20125)
131
- if (!isset($headers['transfer-encoding'])) {
132
- $headers['content-length'] = $this->contentLength;
133
- }
134
- }
135
- }
136
- }
137
- ?>
1
+ <?php
2
+ /**
3
+ * Base class for HTTP_Request2 adapters
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * Class representing a HTTP response
23
+ */
24
+ require_once 'HTTP/Request2/Response.php';
25
+
26
+ /**
27
+ * Base class for HTTP_Request2 adapters
28
+ *
29
+ * HTTP_Request2 class itself only defines methods for aggregating the request
30
+ * data, all actual work of sending the request to the remote server and
31
+ * receiving its response is performed by adapters.
32
+ *
33
+ * @category HTTP
34
+ * @package HTTP_Request2
35
+ * @author Alexey Borzov <avb@php.net>
36
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
37
+ * @version Release: 2.2.1
38
+ * @link http://pear.php.net/package/HTTP_Request2
39
+ */
40
+ abstract class HTTP_Request2_Adapter
41
+ {
42
+ /**
43
+ * A list of methods that MUST NOT have a request body, per RFC 2616
44
+ * @var array
45
+ */
46
+ protected static $bodyDisallowed = array('TRACE');
47
+
48
+ /**
49
+ * Methods having defined semantics for request body
50
+ *
51
+ * Content-Length header (indicating that the body follows, section 4.3 of
52
+ * RFC 2616) will be sent for these methods even if no body was added
53
+ *
54
+ * @var array
55
+ * @link http://pear.php.net/bugs/bug.php?id=12900
56
+ * @link http://pear.php.net/bugs/bug.php?id=14740
57
+ */
58
+ protected static $bodyRequired = array('POST', 'PUT');
59
+
60
+ /**
61
+ * Request being sent
62
+ * @var HTTP_Request2
63
+ */
64
+ protected $request;
65
+
66
+ /**
67
+ * Request body
68
+ * @var string|resource|HTTP_Request2_MultipartBody
69
+ * @see HTTP_Request2::getBody()
70
+ */
71
+ protected $requestBody;
72
+
73
+ /**
74
+ * Length of the request body
75
+ * @var integer
76
+ */
77
+ protected $contentLength;
78
+
79
+ /**
80
+ * Sends request to the remote server and returns its response
81
+ *
82
+ * @param HTTP_Request2 $request HTTP request message
83
+ *
84
+ * @return HTTP_Request2_Response
85
+ * @throws HTTP_Request2_Exception
86
+ */
87
+ abstract public function sendRequest(HTTP_Request2 $request);
88
+
89
+ /**
90
+ * Calculates length of the request body, adds proper headers
91
+ *
92
+ * @param array &$headers associative array of request headers, this method
93
+ * will add proper 'Content-Length' and 'Content-Type'
94
+ * headers to this array (or remove them if not needed)
95
+ */
96
+ protected function calculateRequestLength(&$headers)
97
+ {
98
+ $this->requestBody = $this->request->getBody();
99
+
100
+ if (is_string($this->requestBody)) {
101
+ $this->contentLength = strlen($this->requestBody);
102
+ } elseif (is_resource($this->requestBody)) {
103
+ $stat = fstat($this->requestBody);
104
+ $this->contentLength = $stat['size'];
105
+ rewind($this->requestBody);
106
+ } else {
107
+ $this->contentLength = $this->requestBody->getLength();
108
+ $headers['content-type'] = 'multipart/form-data; boundary=' .
109
+ $this->requestBody->getBoundary();
110
+ $this->requestBody->rewind();
111
+ }
112
+
113
+ if (in_array($this->request->getMethod(), self::$bodyDisallowed)
114
+ || 0 == $this->contentLength
115
+ ) {
116
+ // No body: send a Content-Length header nonetheless (request #12900),
117
+ // but do that only for methods that require a body (bug #14740)
118
+ if (in_array($this->request->getMethod(), self::$bodyRequired)) {
119
+ $headers['content-length'] = 0;
120
+ } else {
121
+ unset($headers['content-length']);
122
+ // if the method doesn't require a body and doesn't have a
123
+ // body, don't send a Content-Type header. (request #16799)
124
+ unset($headers['content-type']);
125
+ }
126
+ } else {
127
+ if (empty($headers['content-type'])) {
128
+ $headers['content-type'] = 'application/x-www-form-urlencoded';
129
+ }
130
+ // Content-Length should not be sent for chunked Transfer-Encoding (bug #20125)
131
+ if (!isset($headers['transfer-encoding'])) {
132
+ $headers['content-length'] = $this->contentLength;
133
+ }
134
+ }
135
+ }
136
+ }
137
+ ?>
vendor/PEAR/HTTP/Request2/Adapter/Curl.php CHANGED
@@ -1,567 +1,567 @@
1
- <?php
2
- /**
3
- * Adapter for HTTP_Request2 wrapping around cURL extension
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * Base class for HTTP_Request2 adapters
23
- */
24
- require_once 'HTTP/Request2/Adapter.php';
25
-
26
- /**
27
- * Adapter for HTTP_Request2 wrapping around cURL extension
28
- *
29
- * @category HTTP
30
- * @package HTTP_Request2
31
- * @author Alexey Borzov <avb@php.net>
32
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
33
- * @version Release: 2.2.1
34
- * @link http://pear.php.net/package/HTTP_Request2
35
- */
36
- class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
37
- {
38
- /**
39
- * Mapping of header names to cURL options
40
- * @var array
41
- */
42
- protected static $headerMap = array(
43
- 'accept-encoding' => CURLOPT_ENCODING,
44
- 'cookie' => CURLOPT_COOKIE,
45
- 'referer' => CURLOPT_REFERER,
46
- 'user-agent' => CURLOPT_USERAGENT
47
- );
48
-
49
- /**
50
- * Mapping of SSL context options to cURL options
51
- * @var array
52
- */
53
- protected static $sslContextMap = array(
54
- 'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
55
- 'ssl_cafile' => CURLOPT_CAINFO,
56
- 'ssl_capath' => CURLOPT_CAPATH,
57
- 'ssl_local_cert' => CURLOPT_SSLCERT,
58
- 'ssl_passphrase' => CURLOPT_SSLCERTPASSWD
59
- );
60
-
61
- /**
62
- * Mapping of CURLE_* constants to Exception subclasses and error codes
63
- * @var array
64
- */
65
- protected static $errorMap = array(
66
- CURLE_UNSUPPORTED_PROTOCOL => array('HTTP_Request2_MessageException',
67
- HTTP_Request2_Exception::NON_HTTP_REDIRECT),
68
- CURLE_COULDNT_RESOLVE_PROXY => array('HTTP_Request2_ConnectionException'),
69
- CURLE_COULDNT_RESOLVE_HOST => array('HTTP_Request2_ConnectionException'),
70
- CURLE_COULDNT_CONNECT => array('HTTP_Request2_ConnectionException'),
71
- // error returned from write callback
72
- CURLE_WRITE_ERROR => array('HTTP_Request2_MessageException',
73
- HTTP_Request2_Exception::NON_HTTP_REDIRECT),
74
- CURLE_OPERATION_TIMEOUTED => array('HTTP_Request2_MessageException',
75
- HTTP_Request2_Exception::TIMEOUT),
76
- CURLE_HTTP_RANGE_ERROR => array('HTTP_Request2_MessageException'),
77
- CURLE_SSL_CONNECT_ERROR => array('HTTP_Request2_ConnectionException'),
78
- CURLE_LIBRARY_NOT_FOUND => array('HTTP_Request2_LogicException',
79
- HTTP_Request2_Exception::MISCONFIGURATION),
80
- CURLE_FUNCTION_NOT_FOUND => array('HTTP_Request2_LogicException',
81
- HTTP_Request2_Exception::MISCONFIGURATION),
82
- CURLE_ABORTED_BY_CALLBACK => array('HTTP_Request2_MessageException',
83
- HTTP_Request2_Exception::NON_HTTP_REDIRECT),
84
- CURLE_TOO_MANY_REDIRECTS => array('HTTP_Request2_MessageException',
85
- HTTP_Request2_Exception::TOO_MANY_REDIRECTS),
86
- CURLE_SSL_PEER_CERTIFICATE => array('HTTP_Request2_ConnectionException'),
87
- CURLE_GOT_NOTHING => array('HTTP_Request2_MessageException'),
88
- CURLE_SSL_ENGINE_NOTFOUND => array('HTTP_Request2_LogicException',
89
- HTTP_Request2_Exception::MISCONFIGURATION),
90
- CURLE_SSL_ENGINE_SETFAILED => array('HTTP_Request2_LogicException',
91
- HTTP_Request2_Exception::MISCONFIGURATION),
92
- CURLE_SEND_ERROR => array('HTTP_Request2_MessageException'),
93
- CURLE_RECV_ERROR => array('HTTP_Request2_MessageException'),
94
- CURLE_SSL_CERTPROBLEM => array('HTTP_Request2_LogicException',
95
- HTTP_Request2_Exception::INVALID_ARGUMENT),
96
- CURLE_SSL_CIPHER => array('HTTP_Request2_ConnectionException'),
97
- CURLE_SSL_CACERT => array('HTTP_Request2_ConnectionException'),
98
- CURLE_BAD_CONTENT_ENCODING => array('HTTP_Request2_MessageException'),
99
- );
100
-
101
- /**
102
- * Response being received
103
- * @var HTTP_Request2_Response
104
- */
105
- protected $response;
106
-
107
- /**
108
- * Whether 'sentHeaders' event was sent to observers
109
- * @var boolean
110
- */
111
- protected $eventSentHeaders = false;
112
-
113
- /**
114
- * Whether 'receivedHeaders' event was sent to observers
115
- * @var boolean
116
- */
117
- protected $eventReceivedHeaders = false;
118
-
119
- /**
120
- * Position within request body
121
- * @var integer
122
- * @see callbackReadBody()
123
- */
124
- protected $position = 0;
125
-
126
- /**
127
- * Information about last transfer, as returned by curl_getinfo()
128
- * @var array
129
- */
130
- protected $lastInfo;
131
-
132
- /**
133
- * Creates a subclass of HTTP_Request2_Exception from curl error data
134
- *
135
- * @param resource $ch curl handle
136
- *
137
- * @return HTTP_Request2_Exception
138
- */
139
- protected static function wrapCurlError($ch)
140
- {
141
- $nativeCode = curl_errno($ch);
142
- $message = 'Curl error: ' . curl_error($ch);
143
- if (!isset(self::$errorMap[$nativeCode])) {
144
- return new HTTP_Request2_Exception($message, 0, $nativeCode);
145
- } else {
146
- $class = self::$errorMap[$nativeCode][0];
147
- $code = empty(self::$errorMap[$nativeCode][1])
148
- ? 0 : self::$errorMap[$nativeCode][1];
149
- return new $class($message, $code, $nativeCode);
150
- }
151
- }
152
-
153
- /**
154
- * Sends request to the remote server and returns its response
155
- *
156
- * @param HTTP_Request2 $request HTTP request message
157
- *
158
- * @return HTTP_Request2_Response
159
- * @throws HTTP_Request2_Exception
160
- */
161
- public function sendRequest(HTTP_Request2 $request)
162
- {
163
- if (!extension_loaded('curl')) {
164
- throw new HTTP_Request2_LogicException(
165
- 'cURL extension not available', HTTP_Request2_Exception::MISCONFIGURATION
166
- );
167
- }
168
-
169
- $this->request = $request;
170
- $this->response = null;
171
- $this->position = 0;
172
- $this->eventSentHeaders = false;
173
- $this->eventReceivedHeaders = false;
174
-
175
- try {
176
- if (false === curl_exec($ch = $this->createCurlHandle())) {
177
- $e = self::wrapCurlError($ch);
178
- }
179
- } catch (Exception $e) {
180
- }
181
- if (isset($ch)) {
182
- $this->lastInfo = curl_getinfo($ch);
183
- curl_close($ch);
184
- }
185
-
186
- $response = $this->response;
187
- unset($this->request, $this->requestBody, $this->response);
188
-
189
- if (!empty($e)) {
190
- throw $e;
191
- }
192
-
193
- if ($jar = $request->getCookieJar()) {
194
- $jar->addCookiesFromResponse($response, $request->getUrl());
195
- }
196
-
197
- if (0 < $this->lastInfo['size_download']) {
198
- $request->setLastEvent('receivedBody', $response);
199
- }
200
- return $response;
201
- }
202
-
203
- /**
204
- * Returns information about last transfer
205
- *
206
- * @return array associative array as returned by curl_getinfo()
207
- */
208
- public function getInfo()
209
- {
210
- return $this->lastInfo;
211
- }
212
-
213
- /**
214
- * Creates a new cURL handle and populates it with data from the request
215
- *
216
- * @return resource a cURL handle, as created by curl_init()
217
- * @throws HTTP_Request2_LogicException
218
- * @throws HTTP_Request2_NotImplementedException
219
- */
220
- protected function createCurlHandle()
221
- {
222
- $ch = curl_init();
223
-
224
- curl_setopt_array($ch, array(
225
- // setup write callbacks
226
- CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),
227
- CURLOPT_WRITEFUNCTION => array($this, 'callbackWriteBody'),
228
- // buffer size
229
- CURLOPT_BUFFERSIZE => $this->request->getConfig('buffer_size'),
230
- // connection timeout
231
- CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),
232
- // save full outgoing headers, in case someone is interested
233
- CURLINFO_HEADER_OUT => true,
234
- // request url
235
- CURLOPT_URL => $this->request->getUrl()->getUrl()
236
- ));
237
-
238
- // set up redirects
239
- if (!$this->request->getConfig('follow_redirects')) {
240
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
241
- } else {
242
- if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true)) {
243
- throw new HTTP_Request2_LogicException(
244
- 'Redirect support in curl is unavailable due to open_basedir or safe_mode setting',
245
- HTTP_Request2_Exception::MISCONFIGURATION
246
- );
247
- }
248
- curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects'));
249
- // limit redirects to http(s), works in 5.2.10+
250
- if (defined('CURLOPT_REDIR_PROTOCOLS')) {
251
- curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
252
- }
253
- // works in 5.3.2+, http://bugs.php.net/bug.php?id=49571
254
- if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR')) {
255
- curl_setopt($ch, CURLOPT_POSTREDIR, 3);
256
- }
257
- }
258
-
259
- // set local IP via CURLOPT_INTERFACE (request #19515)
260
- if ($ip = $this->request->getConfig('local_ip')) {
261
- curl_setopt($ch, CURLOPT_INTERFACE, $ip);
262
- }
263
-
264
- // request timeout
265
- if ($timeout = $this->request->getConfig('timeout')) {
266
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
267
- }
268
-
269
- // set HTTP version
270
- switch ($this->request->getConfig('protocol_version')) {
271
- case '1.0':
272
- curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
273
- break;
274
- case '1.1':
275
- curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
276
- }
277
-
278
- // set request method
279
- switch ($this->request->getMethod()) {
280
- case HTTP_Request2::METHOD_GET:
281
- curl_setopt($ch, CURLOPT_HTTPGET, true);
282
- break;
283
- case HTTP_Request2::METHOD_POST:
284
- curl_setopt($ch, CURLOPT_POST, true);
285
- break;
286
- case HTTP_Request2::METHOD_HEAD:
287
- curl_setopt($ch, CURLOPT_NOBODY, true);
288
- break;
289
- case HTTP_Request2::METHOD_PUT:
290
- curl_setopt($ch, CURLOPT_UPLOAD, true);
291
- break;
292
- default:
293
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());
294
- }
295
-
296
- // set proxy, if needed
297
- if ($host = $this->request->getConfig('proxy_host')) {
298
- if (!($port = $this->request->getConfig('proxy_port'))) {
299
- throw new HTTP_Request2_LogicException(
300
- 'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE
301
- );
302
- }
303
- curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);
304
- if ($user = $this->request->getConfig('proxy_user')) {
305
- curl_setopt(
306
- $ch, CURLOPT_PROXYUSERPWD,
307
- $user . ':' . $this->request->getConfig('proxy_password')
308
- );
309
- switch ($this->request->getConfig('proxy_auth_scheme')) {
310
- case HTTP_Request2::AUTH_BASIC:
311
- curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
312
- break;
313
- case HTTP_Request2::AUTH_DIGEST:
314
- curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
315
- }
316
- }
317
- if ($type = $this->request->getConfig('proxy_type')) {
318
- switch ($type) {
319
- case 'http':
320
- curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
321
- break;
322
- case 'socks5':
323
- curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
324
- break;
325
- default:
326
- throw new HTTP_Request2_NotImplementedException(
327
- "Proxy type '{$type}' is not supported"
328
- );
329
- }
330
- }
331
- }
332
-
333
- // set authentication data
334
- if ($auth = $this->request->getAuth()) {
335
- curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
336
- switch ($auth['scheme']) {
337
- case HTTP_Request2::AUTH_BASIC:
338
- curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
339
- break;
340
- case HTTP_Request2::AUTH_DIGEST:
341
- curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
342
- }
343
- }
344
-
345
- // set SSL options
346
- foreach ($this->request->getConfig() as $name => $value) {
347
- if ('ssl_verify_host' == $name && null !== $value) {
348
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);
349
- } elseif (isset(self::$sslContextMap[$name]) && null !== $value) {
350
- curl_setopt($ch, self::$sslContextMap[$name], $value);
351
- }
352
- }
353
-
354
- $headers = $this->request->getHeaders();
355
- // make cURL automagically send proper header
356
- if (!isset($headers['accept-encoding'])) {
357
- $headers['accept-encoding'] = '';
358
- }
359
-
360
- if (($jar = $this->request->getCookieJar())
361
- && ($cookies = $jar->getMatching($this->request->getUrl(), true))
362
- ) {
363
- $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies;
364
- }
365
-
366
- // set headers having special cURL keys
367
- foreach (self::$headerMap as $name => $option) {
368
- if (isset($headers[$name])) {
369
- curl_setopt($ch, $option, $headers[$name]);
370
- unset($headers[$name]);
371
- }
372
- }
373
-
374
- $this->calculateRequestLength($headers);
375
- if (isset($headers['content-length']) || isset($headers['transfer-encoding'])) {
376
- $this->workaroundPhpBug47204($ch, $headers);
377
- }
378
-
379
- // set headers not having special keys
380
- $headersFmt = array();
381
- foreach ($headers as $name => $value) {
382
- $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
383
- $headersFmt[] = $canonicalName . ': ' . $value;
384
- }
385
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);
386
-
387
- return $ch;
388
- }
389
-
390
- /**
391
- * Workaround for PHP bug #47204 that prevents rewinding request body
392
- *
393
- * The workaround consists of reading the entire request body into memory
394
- * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large
395
- * file uploads, use Socket adapter instead.
396
- *
397
- * @param resource $ch cURL handle
398
- * @param array &$headers Request headers
399
- */
400
- protected function workaroundPhpBug47204($ch, &$headers)
401
- {
402
- // no redirects, no digest auth -> probably no rewind needed
403
- if (!$this->request->getConfig('follow_redirects')
404
- && (!($auth = $this->request->getAuth())
405
- || HTTP_Request2::AUTH_DIGEST != $auth['scheme'])
406
- ) {
407
- curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));
408
-
409
- } else {
410
- // rewind may be needed, read the whole body into memory
411
- if ($this->requestBody instanceof HTTP_Request2_MultipartBody) {
412
- $this->requestBody = $this->requestBody->__toString();
413
-
414
- } elseif (is_resource($this->requestBody)) {
415
- $fp = $this->requestBody;
416
- $this->requestBody = '';
417
- while (!feof($fp)) {
418
- $this->requestBody .= fread($fp, 16384);
419
- }
420
- }
421
- // curl hangs up if content-length is present
422
- unset($headers['content-length']);
423
- curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
424
- }
425
- }
426
-
427
- /**
428
- * Callback function called by cURL for reading the request body
429
- *
430
- * @param resource $ch cURL handle
431
- * @param resource $fd file descriptor (not used)
432
- * @param integer $length maximum length of data to return
433
- *
434
- * @return string part of the request body, up to $length bytes
435
- */
436
- protected function callbackReadBody($ch, $fd, $length)
437
- {
438
- if (!$this->eventSentHeaders) {
439
- $this->request->setLastEvent(
440
- 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
441
- );
442
- $this->eventSentHeaders = true;
443
- }
444
- if (in_array($this->request->getMethod(), self::$bodyDisallowed)
445
- || 0 == $this->contentLength || $this->position >= $this->contentLength
446
- ) {
447
- return '';
448
- }
449
- if (is_string($this->requestBody)) {
450
- $string = substr($this->requestBody, $this->position, $length);
451
- } elseif (is_resource($this->requestBody)) {
452
- $string = fread($this->requestBody, $length);
453
- } else {
454
- $string = $this->requestBody->read($length);
455
- }
456
- $this->request->setLastEvent('sentBodyPart', strlen($string));
457
- $this->position += strlen($string);
458
- return $string;
459
- }
460
-
461
- /**
462
- * Callback function called by cURL for saving the response headers
463
- *
464
- * @param resource $ch cURL handle
465
- * @param string $string response header (with trailing CRLF)
466
- *
467
- * @return integer number of bytes saved
468
- * @see HTTP_Request2_Response::parseHeaderLine()
469
- */
470
- protected function callbackWriteHeader($ch, $string)
471
- {
472
- // we may receive a second set of headers if doing e.g. digest auth
473
- if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {
474
- // don't bother with 100-Continue responses (bug #15785)
475
- if (!$this->eventSentHeaders
476
- || $this->response->getStatus() >= 200
477
- ) {
478
- $this->request->setLastEvent(
479
- 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
480
- );
481
- }
482
- $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);
483
- // if body wasn't read by a callback, send event with total body size
484
- if ($upload > $this->position) {
485
- $this->request->setLastEvent(
486
- 'sentBodyPart', $upload - $this->position
487
- );
488
- $this->position = $upload;
489
- }
490
- if ($upload && (!$this->eventSentHeaders
491
- || $this->response->getStatus() >= 200)
492
- ) {
493
- $this->request->setLastEvent('sentBody', $upload);
494
- }
495
- $this->eventSentHeaders = true;
496
- // we'll need a new response object
497
- if ($this->eventReceivedHeaders) {
498
- $this->eventReceivedHeaders = false;
499
- $this->response = null;
500
- }
501
- }
502
- if (empty($this->response)) {
503
- $this->response = new HTTP_Request2_Response(
504
- $string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)
505
- );
506
- } else {
507
- $this->response->parseHeaderLine($string);
508
- if ('' == trim($string)) {
509
- // don't bother with 100-Continue responses (bug #15785)
510
- if (200 <= $this->response->getStatus()) {
511
- $this->request->setLastEvent('receivedHeaders', $this->response);
512
- }
513
-
514
- if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) {
515
- $redirectUrl = new Net_URL2($this->response->getHeader('location'));
516
-
517
- // for versions lower than 5.2.10, check the redirection URL protocol
518
- if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute()
519
- && !in_array($redirectUrl->getScheme(), array('http', 'https'))
520
- ) {
521
- return -1;
522
- }
523
-
524
- if ($jar = $this->request->getCookieJar()) {
525
- $jar->addCookiesFromResponse($this->response, $this->request->getUrl());
526
- if (!$redirectUrl->isAbsolute()) {
527
- $redirectUrl = $this->request->getUrl()->resolve($redirectUrl);
528
- }
529
- if ($cookies = $jar->getMatching($redirectUrl, true)) {
530
- curl_setopt($ch, CURLOPT_COOKIE, $cookies);
531
- }
532
- }
533
- }
534
- $this->eventReceivedHeaders = true;
535
- }
536
- }
537
- return strlen($string);
538
- }
539
-
540
- /**
541
- * Callback function called by cURL for saving the response body
542
- *
543
- * @param resource $ch cURL handle (not used)
544
- * @param string $string part of the response body
545
- *
546
- * @return integer number of bytes saved
547
- * @throws HTTP_Request2_MessageException
548
- * @see HTTP_Request2_Response::appendBody()
549
- */
550
- protected function callbackWriteBody($ch, $string)
551
- {
552
- // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if
553
- // response doesn't start with proper HTTP status line (see bug #15716)
554
- if (empty($this->response)) {
555
- throw new HTTP_Request2_MessageException(
556
- "Malformed response: {$string}",
557
- HTTP_Request2_Exception::MALFORMED_RESPONSE
558
- );
559
- }
560
- if ($this->request->getConfig('store_body')) {
561
- $this->response->appendBody($string);
562
- }
563
- $this->request->setLastEvent('receivedBodyPart', $string);
564
- return strlen($string);
565
- }
566
- }
567
- ?>
1
+ <?php
2
+ /**
3
+ * Adapter for HTTP_Request2 wrapping around cURL extension
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * Base class for HTTP_Request2 adapters
23
+ */
24
+ require_once 'HTTP/Request2/Adapter.php';
25
+
26
+ /**
27
+ * Adapter for HTTP_Request2 wrapping around cURL extension
28
+ *
29
+ * @category HTTP
30
+ * @package HTTP_Request2
31
+ * @author Alexey Borzov <avb@php.net>
32
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
33
+ * @version Release: 2.2.1
34
+ * @link http://pear.php.net/package/HTTP_Request2
35
+ */
36
+ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
37
+ {
38
+ /**
39
+ * Mapping of header names to cURL options
40
+ * @var array
41
+ */
42
+ protected static $headerMap = array(
43
+ 'accept-encoding' => CURLOPT_ENCODING,
44
+ 'cookie' => CURLOPT_COOKIE,
45
+ 'referer' => CURLOPT_REFERER,
46
+ 'user-agent' => CURLOPT_USERAGENT
47
+ );
48
+
49
+ /**
50
+ * Mapping of SSL context options to cURL options
51
+ * @var array
52
+ */
53
+ protected static $sslContextMap = array(
54
+ 'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
55
+ 'ssl_cafile' => CURLOPT_CAINFO,
56
+ 'ssl_capath' => CURLOPT_CAPATH,
57
+ 'ssl_local_cert' => CURLOPT_SSLCERT,
58
+ 'ssl_passphrase' => CURLOPT_SSLCERTPASSWD
59
+ );
60
+
61
+ /**
62
+ * Mapping of CURLE_* constants to Exception subclasses and error codes
63
+ * @var array
64
+ */
65
+ protected static $errorMap = array(
66
+ CURLE_UNSUPPORTED_PROTOCOL => array('HTTP_Request2_MessageException',
67
+ HTTP_Request2_Exception::NON_HTTP_REDIRECT),
68
+ CURLE_COULDNT_RESOLVE_PROXY => array('HTTP_Request2_ConnectionException'),
69
+ CURLE_COULDNT_RESOLVE_HOST => array('HTTP_Request2_ConnectionException'),
70
+ CURLE_COULDNT_CONNECT => array('HTTP_Request2_ConnectionException'),
71
+ // error returned from write callback
72
+ CURLE_WRITE_ERROR => array('HTTP_Request2_MessageException',
73
+ HTTP_Request2_Exception::NON_HTTP_REDIRECT),
74
+ CURLE_OPERATION_TIMEOUTED => array('HTTP_Request2_MessageException',
75
+ HTTP_Request2_Exception::TIMEOUT),
76
+ CURLE_HTTP_RANGE_ERROR => array('HTTP_Request2_MessageException'),
77
+ CURLE_SSL_CONNECT_ERROR => array('HTTP_Request2_ConnectionException'),
78
+ CURLE_LIBRARY_NOT_FOUND => array('HTTP_Request2_LogicException',
79
+ HTTP_Request2_Exception::MISCONFIGURATION),
80
+ CURLE_FUNCTION_NOT_FOUND => array('HTTP_Request2_LogicException',
81
+ HTTP_Request2_Exception::MISCONFIGURATION),
82
+ CURLE_ABORTED_BY_CALLBACK => array('HTTP_Request2_MessageException',
83
+ HTTP_Request2_Exception::NON_HTTP_REDIRECT),
84
+ CURLE_TOO_MANY_REDIRECTS => array('HTTP_Request2_MessageException',
85
+ HTTP_Request2_Exception::TOO_MANY_REDIRECTS),
86
+ CURLE_SSL_PEER_CERTIFICATE => array('HTTP_Request2_ConnectionException'),
87
+ CURLE_GOT_NOTHING => array('HTTP_Request2_MessageException'),
88
+ CURLE_SSL_ENGINE_NOTFOUND => array('HTTP_Request2_LogicException',
89
+ HTTP_Request2_Exception::MISCONFIGURATION),
90
+ CURLE_SSL_ENGINE_SETFAILED => array('HTTP_Request2_LogicException',
91
+ HTTP_Request2_Exception::MISCONFIGURATION),
92
+ CURLE_SEND_ERROR => array('HTTP_Request2_MessageException'),
93
+ CURLE_RECV_ERROR => array('HTTP_Request2_MessageException'),
94
+ CURLE_SSL_CERTPROBLEM => array('HTTP_Request2_LogicException',
95
+ HTTP_Request2_Exception::INVALID_ARGUMENT),
96
+ CURLE_SSL_CIPHER => array('HTTP_Request2_ConnectionException'),
97
+ CURLE_SSL_CACERT => array('HTTP_Request2_ConnectionException'),
98
+ CURLE_BAD_CONTENT_ENCODING => array('HTTP_Request2_MessageException'),
99
+ );
100
+
101
+ /**
102
+ * Response being received
103
+ * @var HTTP_Request2_Response
104
+ */
105
+ protected $response;
106
+
107
+ /**
108
+ * Whether 'sentHeaders' event was sent to observers
109
+ * @var boolean
110
+ */
111
+ protected $eventSentHeaders = false;
112
+
113
+ /**
114
+ * Whether 'receivedHeaders' event was sent to observers
115
+ * @var boolean
116
+ */
117
+ protected $eventReceivedHeaders = false;
118
+
119
+ /**
120
+ * Position within request body
121
+ * @var integer
122
+ * @see callbackReadBody()
123
+ */
124
+ protected $position = 0;
125
+
126
+ /**
127
+ * Information about last transfer, as returned by curl_getinfo()
128
+ * @var array
129
+ */
130
+ protected $lastInfo;
131
+
132
+ /**
133
+ * Creates a subclass of HTTP_Request2_Exception from curl error data
134
+ *
135
+ * @param resource $ch curl handle
136
+ *
137
+ * @return HTTP_Request2_Exception
138
+ */
139
+ protected static function wrapCurlError($ch)
140
+ {
141
+ $nativeCode = curl_errno($ch);
142
+ $message = 'Curl error: ' . curl_error($ch);
143
+ if (!isset(self::$errorMap[$nativeCode])) {
144
+ return new HTTP_Request2_Exception($message, 0, $nativeCode);
145
+ } else {
146
+ $class = self::$errorMap[$nativeCode][0];
147
+ $code = empty(self::$errorMap[$nativeCode][1])
148
+ ? 0 : self::$errorMap[$nativeCode][1];
149
+ return new $class($message, $code, $nativeCode);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Sends request to the remote server and returns its response
155
+ *
156
+ * @param HTTP_Request2 $request HTTP request message
157
+ *
158
+ * @return HTTP_Request2_Response
159
+ * @throws HTTP_Request2_Exception
160
+ */
161
+ public function sendRequest(HTTP_Request2 $request)
162
+ {
163
+ if (!extension_loaded('curl')) {
164
+ throw new HTTP_Request2_LogicException(
165
+ 'cURL extension not available', HTTP_Request2_Exception::MISCONFIGURATION
166
+ );
167
+ }
168
+
169
+ $this->request = $request;
170
+ $this->response = null;
171
+ $this->position = 0;
172
+ $this->eventSentHeaders = false;
173
+ $this->eventReceivedHeaders = false;
174
+
175
+ try {
176
+ if (false === curl_exec($ch = $this->createCurlHandle())) {
177
+ $e = self::wrapCurlError($ch);
178
+ }
179
+ } catch (Exception $e) {
180
+ }
181
+ if (isset($ch)) {
182
+ $this->lastInfo = curl_getinfo($ch);
183
+ curl_close($ch);
184
+ }
185
+
186
+ $response = $this->response;
187
+ unset($this->request, $this->requestBody, $this->response);
188
+
189
+ if (!empty($e)) {
190
+ throw $e;
191
+ }
192
+
193
+ if ($jar = $request->getCookieJar()) {
194
+ $jar->addCookiesFromResponse($response, $request->getUrl());
195
+ }
196
+
197
+ if (0 < $this->lastInfo['size_download']) {
198
+ $request->setLastEvent('receivedBody', $response);
199
+ }
200
+ return $response;
201
+ }
202
+
203
+ /**
204
+ * Returns information about last transfer
205
+ *
206
+ * @return array associative array as returned by curl_getinfo()
207
+ */
208
+ public function getInfo()
209
+ {
210
+ return $this->lastInfo;
211
+ }
212
+
213
+ /**
214
+ * Creates a new cURL handle and populates it with data from the request
215
+ *
216
+ * @return resource a cURL handle, as created by curl_init()
217
+ * @throws HTTP_Request2_LogicException
218
+ * @throws HTTP_Request2_NotImplementedException
219
+ */
220
+ protected function createCurlHandle()
221
+ {
222
+ $ch = curl_init();
223
+
224
+ curl_setopt_array($ch, array(
225
+ // setup write callbacks
226
+ CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),
227
+ CURLOPT_WRITEFUNCTION => array($this, 'callbackWriteBody'),
228
+ // buffer size
229
+ CURLOPT_BUFFERSIZE => $this->request->getConfig('buffer_size'),
230
+ // connection timeout
231
+ CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),
232
+ // save full outgoing headers, in case someone is interested
233
+ CURLINFO_HEADER_OUT => true,
234
+ // request url
235
+ CURLOPT_URL => $this->request->getUrl()->getUrl()
236
+ ));
237
+
238
+ // set up redirects
239
+ if (!$this->request->getConfig('follow_redirects')) {
240
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
241
+ } else {
242
+ if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true)) {
243
+ throw new HTTP_Request2_LogicException(
244
+ 'Redirect support in curl is unavailable due to open_basedir or safe_mode setting',
245
+ HTTP_Request2_Exception::MISCONFIGURATION
246
+ );
247
+ }
248
+ curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects'));
249
+ // limit redirects to http(s), works in 5.2.10+
250
+ if (defined('CURLOPT_REDIR_PROTOCOLS')) {
251
+ curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
252
+ }
253
+ // works in 5.3.2+, http://bugs.php.net/bug.php?id=49571
254
+ if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR')) {
255
+ curl_setopt($ch, CURLOPT_POSTREDIR, 3);
256
+ }
257
+ }
258
+
259
+ // set local IP via CURLOPT_INTERFACE (request #19515)
260
+ if ($ip = $this->request->getConfig('local_ip')) {
261
+ curl_setopt($ch, CURLOPT_INTERFACE, $ip);
262
+ }
263
+
264
+ // request timeout
265
+ if ($timeout = $this->request->getConfig('timeout')) {
266
+ curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
267
+ }
268
+
269
+ // set HTTP version
270
+ switch ($this->request->getConfig('protocol_version')) {
271
+ case '1.0':
272
+ curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
273
+ break;
274
+ case '1.1':
275
+ curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
276
+ }
277
+
278
+ // set request method
279
+ switch ($this->request->getMethod()) {
280
+ case HTTP_Request2::METHOD_GET:
281
+ curl_setopt($ch, CURLOPT_HTTPGET, true);
282
+ break;
283
+ case HTTP_Request2::METHOD_POST:
284
+ curl_setopt($ch, CURLOPT_POST, true);
285
+ break;
286
+ case HTTP_Request2::METHOD_HEAD:
287
+ curl_setopt($ch, CURLOPT_NOBODY, true);
288
+ break;
289
+ case HTTP_Request2::METHOD_PUT:
290
+ curl_setopt($ch, CURLOPT_UPLOAD, true);
291
+ break;
292
+ default:
293
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());
294
+ }
295
+
296
+ // set proxy, if needed
297
+ if ($host = $this->request->getConfig('proxy_host')) {
298
+ if (!($port = $this->request->getConfig('proxy_port'))) {
299
+ throw new HTTP_Request2_LogicException(
300
+ 'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE
301
+ );
302
+ }
303
+ curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);
304
+ if ($user = $this->request->getConfig('proxy_user')) {
305
+ curl_setopt(
306
+ $ch, CURLOPT_PROXYUSERPWD,
307
+ $user . ':' . $this->request->getConfig('proxy_password')
308
+ );
309
+ switch ($this->request->getConfig('proxy_auth_scheme')) {
310
+ case HTTP_Request2::AUTH_BASIC:
311
+ curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
312
+ break;
313
+ case HTTP_Request2::AUTH_DIGEST:
314
+ curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
315
+ }
316
+ }
317
+ if ($type = $this->request->getConfig('proxy_type')) {
318
+ switch ($type) {
319
+ case 'http':
320
+ curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
321
+ break;
322
+ case 'socks5':
323
+ curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
324
+ break;
325
+ default:
326
+ throw new HTTP_Request2_NotImplementedException(
327
+ "Proxy type '{$type}' is not supported"
328
+ );
329
+ }
330
+ }
331
+ }
332
+
333
+ // set authentication data
334
+ if ($auth = $this->request->getAuth()) {
335
+ curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
336
+ switch ($auth['scheme']) {
337
+ case HTTP_Request2::AUTH_BASIC:
338
+ curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
339
+ break;
340
+ case HTTP_Request2::AUTH_DIGEST:
341
+ curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
342
+ }
343
+ }
344
+
345
+ // set SSL options
346
+ foreach ($this->request->getConfig() as $name => $value) {
347
+ if ('ssl_verify_host' == $name && null !== $value) {
348
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);
349
+ } elseif (isset(self::$sslContextMap[$name]) && null !== $value) {
350
+ curl_setopt($ch, self::$sslContextMap[$name], $value);
351
+ }
352
+ }
353
+
354
+ $headers = $this->request->getHeaders();
355
+ // make cURL automagically send proper header
356
+ if (!isset($headers['accept-encoding'])) {
357
+ $headers['accept-encoding'] = '';
358
+ }
359
+
360
+ if (($jar = $this->request->getCookieJar())
361
+ && ($cookies = $jar->getMatching($this->request->getUrl(), true))
362
+ ) {
363
+ $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies;
364
+ }
365
+
366
+ // set headers having special cURL keys
367
+ foreach (self::$headerMap as $name => $option) {
368
+ if (isset($headers[$name])) {
369
+ curl_setopt($ch, $option, $headers[$name]);
370
+ unset($headers[$name]);
371
+ }
372
+ }
373
+
374
+ $this->calculateRequestLength($headers);
375
+ if (isset($headers['content-length']) || isset($headers['transfer-encoding'])) {
376
+ $this->workaroundPhpBug47204($ch, $headers);
377
+ }
378
+
379
+ // set headers not having special keys
380
+ $headersFmt = array();
381
+ foreach ($headers as $name => $value) {
382
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
383
+ $headersFmt[] = $canonicalName . ': ' . $value;
384
+ }
385
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);
386
+
387
+ return $ch;
388
+ }
389
+
390
+ /**
391
+ * Workaround for PHP bug #47204 that prevents rewinding request body
392
+ *
393
+ * The workaround consists of reading the entire request body into memory
394
+ * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large
395
+ * file uploads, use Socket adapter instead.
396
+ *
397
+ * @param resource $ch cURL handle
398
+ * @param array &$headers Request headers
399
+ */
400
+ protected function workaroundPhpBug47204($ch, &$headers)
401
+ {
402
+ // no redirects, no digest auth -> probably no rewind needed
403
+ if (!$this->request->getConfig('follow_redirects')
404
+ && (!($auth = $this->request->getAuth())
405
+ || HTTP_Request2::AUTH_DIGEST != $auth['scheme'])
406
+ ) {
407
+ curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));
408
+
409
+ } else {
410
+ // rewind may be needed, read the whole body into memory
411
+ if ($this->requestBody instanceof HTTP_Request2_MultipartBody) {
412
+ $this->requestBody = $this->requestBody->__toString();
413
+
414
+ } elseif (is_resource($this->requestBody)) {
415
+ $fp = $this->requestBody;
416
+ $this->requestBody = '';
417
+ while (!feof($fp)) {
418
+ $this->requestBody .= fread($fp, 16384);
419
+ }
420
+ }
421
+ // curl hangs up if content-length is present
422
+ unset($headers['content-length']);
423
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Callback function called by cURL for reading the request body
429
+ *
430
+ * @param resource $ch cURL handle
431
+ * @param resource $fd file descriptor (not used)
432
+ * @param integer $length maximum length of data to return
433
+ *
434
+ * @return string part of the request body, up to $length bytes
435
+ */
436
+ protected function callbackReadBody($ch, $fd, $length)
437
+ {
438
+ if (!$this->eventSentHeaders) {
439
+ $this->request->setLastEvent(
440
+ 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
441
+ );
442
+ $this->eventSentHeaders = true;
443
+ }
444
+ if (in_array($this->request->getMethod(), self::$bodyDisallowed)
445
+ || 0 == $this->contentLength || $this->position >= $this->contentLength
446
+ ) {
447
+ return '';
448
+ }
449
+ if (is_string($this->requestBody)) {
450
+ $string = substr($this->requestBody, $this->position, $length);
451
+ } elseif (is_resource($this->requestBody)) {
452
+ $string = fread($this->requestBody, $length);
453
+ } else {
454
+ $string = $this->requestBody->read($length);
455
+ }
456
+ $this->request->setLastEvent('sentBodyPart', strlen($string));
457
+ $this->position += strlen($string);
458
+ return $string;
459
+ }
460
+
461
+ /**
462
+ * Callback function called by cURL for saving the response headers
463
+ *
464
+ * @param resource $ch cURL handle
465
+ * @param string $string response header (with trailing CRLF)
466
+ *
467
+ * @return integer number of bytes saved
468
+ * @see HTTP_Request2_Response::parseHeaderLine()
469
+ */
470
+ protected function callbackWriteHeader($ch, $string)
471
+ {
472
+ // we may receive a second set of headers if doing e.g. digest auth
473
+ if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {
474
+ // don't bother with 100-Continue responses (bug #15785)
475
+ if (!$this->eventSentHeaders
476
+ || $this->response->getStatus() >= 200
477
+ ) {
478
+ $this->request->setLastEvent(
479
+ 'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
480
+ );
481
+ }
482
+ $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);
483
+ // if body wasn't read by a callback, send event with total body size
484
+ if ($upload > $this->position) {
485
+ $this->request->setLastEvent(
486
+ 'sentBodyPart', $upload - $this->position
487
+ );
488
+ $this->position = $upload;
489
+ }
490
+ if ($upload && (!$this->eventSentHeaders
491
+ || $this->response->getStatus() >= 200)
492
+ ) {
493
+ $this->request->setLastEvent('sentBody', $upload);
494
+ }
495
+ $this->eventSentHeaders = true;
496
+ // we'll need a new response object
497
+ if ($this->eventReceivedHeaders) {
498
+ $this->eventReceivedHeaders = false;
499
+ $this->response = null;
500
+ }
501
+ }
502
+ if (empty($this->response)) {
503
+ $this->response = new HTTP_Request2_Response(
504
+ $string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)
505
+ );
506
+ } else {
507
+ $this->response->parseHeaderLine($string);
508
+ if ('' == trim($string)) {
509
+ // don't bother with 100-Continue responses (bug #15785)
510
+ if (200 <= $this->response->getStatus()) {
511
+ $this->request->setLastEvent('receivedHeaders', $this->response);
512
+ }
513
+
514
+ if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) {
515
+ $redirectUrl = new Net_URL2($this->response->getHeader('location'));
516
+
517
+ // for versions lower than 5.2.10, check the redirection URL protocol
518
+ if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute()
519
+ && !in_array($redirectUrl->getScheme(), array('http', 'https'))
520
+ ) {
521
+ return -1;
522
+ }
523
+
524
+ if ($jar = $this->request->getCookieJar()) {
525
+ $jar->addCookiesFromResponse($this->response, $this->request->getUrl());
526
+ if (!$redirectUrl->isAbsolute()) {
527
+ $redirectUrl = $this->request->getUrl()->resolve($redirectUrl);
528
+ }
529
+ if ($cookies = $jar->getMatching($redirectUrl, true)) {
530
+ curl_setopt($ch, CURLOPT_COOKIE, $cookies);
531
+ }
532
+ }
533
+ }
534
+ $this->eventReceivedHeaders = true;
535
+ }
536
+ }
537
+ return strlen($string);
538
+ }
539
+
540
+ /**
541
+ * Callback function called by cURL for saving the response body
542
+ *
543
+ * @param resource $ch cURL handle (not used)
544
+ * @param string $string part of the response body
545
+ *
546
+ * @return integer number of bytes saved
547
+ * @throws HTTP_Request2_MessageException
548
+ * @see HTTP_Request2_Response::appendBody()
549
+ */
550
+ protected function callbackWriteBody($ch, $string)
551
+ {
552
+ // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if
553
+ // response doesn't start with proper HTTP status line (see bug #15716)
554
+ if (empty($this->response)) {
555
+ throw new HTTP_Request2_MessageException(
556
+ "Malformed response: {$string}",
557
+ HTTP_Request2_Exception::MALFORMED_RESPONSE
558
+ );
559
+ }
560
+ if ($this->request->getConfig('store_body')) {
561
+ $this->response->appendBody($string);
562
+ }
563
+ $this->request->setLastEvent('receivedBodyPart', $string);
564
+ return strlen($string);
565
+ }
566
+ }
567
+ ?>
vendor/PEAR/HTTP/Request2/Adapter/Mock.php CHANGED
@@ -1,166 +1,166 @@
1
- <?php
2
- /**
3
- * Mock adapter intended for testing
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * Base class for HTTP_Request2 adapters
23
- */
24
- require_once 'HTTP/Request2/Adapter.php';
25
-
26
- /**
27
- * Mock adapter intended for testing
28
- *
29
- * Can be used to test applications depending on HTTP_Request2 package without
30
- * actually performing any HTTP requests. This adapter will return responses
31
- * previously added via addResponse()
32
- * <code>
33
- * $mock = new HTTP_Request2_Adapter_Mock();
34
- * $mock->addResponse("HTTP/1.1 ... ");
35
- *
36
- * $request = new HTTP_Request2();
37
- * $request->setAdapter($mock);
38
- *
39
- * // This will return the response set above
40
- * $response = $req->send();
41
- * </code>
42
- *
43
- * @category HTTP
44
- * @package HTTP_Request2
45
- * @author Alexey Borzov <avb@php.net>
46
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
47
- * @version Release: 2.2.1
48
- * @link http://pear.php.net/package/HTTP_Request2
49
- */
50
- class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
51
- {
52
- /**
53
- * A queue of responses to be returned by sendRequest()
54
- * @var array
55
- */
56
- protected $responses = array();
57
-
58
- /**
59
- * Returns the next response from the queue built by addResponse()
60
- *
61
- * Only responses without explicit URLs or with URLs equal to request URL
62
- * will be considered. If matching response is not found or the queue is
63
- * empty then default empty response with status 400 will be returned,
64
- * if an Exception object was added to the queue it will be thrown.
65
- *
66
- * @param HTTP_Request2 $request HTTP request message
67
- *
68
- * @return HTTP_Request2_Response
69
- * @throws Exception
70
- */
71
- public function sendRequest(HTTP_Request2 $request)
72
- {
73
- $requestUrl = (string)$request->getUrl();
74
- $response = null;
75
- foreach ($this->responses as $k => $v) {
76
- if (!$v[1] || $requestUrl == $v[1]) {
77
- $response = $v[0];
78
- array_splice($this->responses, $k, 1);
79
- break;
80
- }
81
- }
82
- if (!$response) {
83
- return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n");
84
-
85
- } elseif ($response instanceof HTTP_Request2_Response) {
86
- return $response;
87
-
88
- } else {
89
- // rethrow the exception
90
- $class = get_class($response);
91
- $message = $response->getMessage();
92
- $code = $response->getCode();
93
- throw new $class($message, $code);
94
- }
95
- }
96
-
97
- /**
98
- * Adds response to the queue
99
- *
100
- * @param mixed $response either a string, a pointer to an open file,
101
- * an instance of HTTP_Request2_Response or Exception
102
- * @param string $url A request URL this response should be valid for
103
- * (see {@link http://pear.php.net/bugs/bug.php?id=19276})
104
- *
105
- * @throws HTTP_Request2_Exception
106
- */
107
- public function addResponse($response, $url = null)
108
- {
109
- if (is_string($response)) {
110
- $response = self::createResponseFromString($response);
111
- } elseif (is_resource($response)) {
112
- $response = self::createResponseFromFile($response);
113
- } elseif (!$response instanceof HTTP_Request2_Response &&
114
- !$response instanceof Exception
115
- ) {
116
- throw new HTTP_Request2_Exception('Parameter is not a valid response');
117
- }
118
- $this->responses[] = array($response, $url);
119
- }
120
-
121
- /**
122
- * Creates a new HTTP_Request2_Response object from a string
123
- *
124
- * @param string $str string containing HTTP response message
125
- *
126
- * @return HTTP_Request2_Response
127
- * @throws HTTP_Request2_Exception
128
- */
129
- public static function createResponseFromString($str)
130
- {
131
- $parts = preg_split('!(\r?\n){2}!m', $str, 2);
132
- $headerLines = explode("\n", $parts[0]);
133
- $response = new HTTP_Request2_Response(array_shift($headerLines));
134
- foreach ($headerLines as $headerLine) {
135
- $response->parseHeaderLine($headerLine);
136
- }
137
- $response->parseHeaderLine('');
138
- if (isset($parts[1])) {
139
- $response->appendBody($parts[1]);
140
- }
141
- return $response;
142
- }
143
-
144
- /**
145
- * Creates a new HTTP_Request2_Response object from a file
146
- *
147
- * @param resource $fp file pointer returned by fopen()
148
- *
149
- * @return HTTP_Request2_Response
150
- * @throws HTTP_Request2_Exception
151
- */
152
- public static function createResponseFromFile($fp)
153
- {
154
- $response = new HTTP_Request2_Response(fgets($fp));
155
- do {
156
- $headerLine = fgets($fp);
157
- $response->parseHeaderLine($headerLine);
158
- } while ('' != trim($headerLine));
159
-
160
- while (!feof($fp)) {
161
- $response->appendBody(fread($fp, 8192));
162
- }
163
- return $response;
164
- }
165
- }
166
  ?>
1
+ <?php
2
+ /**
3
+ * Mock adapter intended for testing
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * Base class for HTTP_Request2 adapters
23
+ */
24
+ require_once 'HTTP/Request2/Adapter.php';
25
+
26
+ /**
27
+ * Mock adapter intended for testing
28
+ *
29
+ * Can be used to test applications depending on HTTP_Request2 package without
30
+ * actually performing any HTTP requests. This adapter will return responses
31
+ * previously added via addResponse()
32
+ * <code>
33
+ * $mock = new HTTP_Request2_Adapter_Mock();
34
+ * $mock->addResponse("HTTP/1.1 ... ");
35
+ *
36
+ * $request = new HTTP_Request2();
37
+ * $request->setAdapter($mock);
38
+ *
39
+ * // This will return the response set above
40
+ * $response = $req->send();
41
+ * </code>
42
+ *
43
+ * @category HTTP
44
+ * @package HTTP_Request2
45
+ * @author Alexey Borzov <avb@php.net>
46
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
47
+ * @version Release: 2.2.1
48
+ * @link http://pear.php.net/package/HTTP_Request2
49
+ */
50
+ class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
51
+ {
52
+ /**
53
+ * A queue of responses to be returned by sendRequest()
54
+ * @var array
55
+ */
56
+ protected $responses = array();
57
+
58
+ /**
59
+ * Returns the next response from the queue built by addResponse()
60
+ *
61
+ * Only responses without explicit URLs or with URLs equal to request URL
62
+ * will be considered. If matching response is not found or the queue is
63
+ * empty then default empty response with status 400 will be returned,
64
+ * if an Exception object was added to the queue it will be thrown.
65
+ *
66
+ * @param HTTP_Request2 $request HTTP request message
67
+ *
68
+ * @return HTTP_Request2_Response
69
+ * @throws Exception
70
+ */
71
+ public function sendRequest(HTTP_Request2 $request)
72
+ {
73
+ $requestUrl = (string)$request->getUrl();
74
+ $response = null;
75
+ foreach ($this->responses as $k => $v) {
76
+ if (!$v[1] || $requestUrl == $v[1]) {
77
+ $response = $v[0];
78
+ array_splice($this->responses, $k, 1);
79
+ break;
80
+ }
81
+ }
82
+ if (!$response) {
83
+ return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n");
84
+
85
+ } elseif ($response instanceof HTTP_Request2_Response) {
86
+ return $response;
87
+
88
+ } else {
89
+ // rethrow the exception
90
+ $class = get_class($response);
91
+ $message = $response->getMessage();
92
+ $code = $response->getCode();
93
+ throw new $class($message, $code);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Adds response to the queue
99
+ *
100
+ * @param mixed $response either a string, a pointer to an open file,
101
+ * an instance of HTTP_Request2_Response or Exception
102
+ * @param string $url A request URL this response should be valid for
103
+ * (see {@link http://pear.php.net/bugs/bug.php?id=19276})
104
+ *
105
+ * @throws HTTP_Request2_Exception
106
+ */
107
+ public function addResponse($response, $url = null)
108
+ {
109
+ if (is_string($response)) {
110
+ $response = self::createResponseFromString($response);
111
+ } elseif (is_resource($response)) {
112
+ $response = self::createResponseFromFile($response);
113
+ } elseif (!$response instanceof HTTP_Request2_Response &&
114
+ !$response instanceof Exception
115
+ ) {
116
+ throw new HTTP_Request2_Exception('Parameter is not a valid response');
117
+ }
118
+ $this->responses[] = array($response, $url);
119
+ }
120
+
121
+ /**
122
+ * Creates a new HTTP_Request2_Response object from a string
123
+ *
124
+ * @param string $str string containing HTTP response message
125
+ *
126
+ * @return HTTP_Request2_Response
127
+ * @throws HTTP_Request2_Exception
128
+ */
129
+ public static function createResponseFromString($str)
130
+ {
131
+ $parts = preg_split('!(\r?\n){2}!m', $str, 2);
132
+ $headerLines = explode("\n", $parts[0]);
133
+ $response = new HTTP_Request2_Response(array_shift($headerLines));
134
+ foreach ($headerLines as $headerLine) {
135
+ $response->parseHeaderLine($headerLine);
136
+ }
137
+ $response->parseHeaderLine('');
138
+ if (isset($parts[1])) {
139
+ $response->appendBody($parts[1]);
140
+ }
141
+ return $response;
142
+ }
143
+
144
+ /**
145
+ * Creates a new HTTP_Request2_Response object from a file
146
+ *
147
+ * @param resource $fp file pointer returned by fopen()
148
+ *
149
+ * @return HTTP_Request2_Response
150
+ * @throws HTTP_Request2_Exception
151
+ */
152
+ public static function createResponseFromFile($fp)
153
+ {
154
+ $response = new HTTP_Request2_Response(fgets($fp));
155
+ do {
156
+ $headerLine = fgets($fp);
157
+ $response->parseHeaderLine($headerLine);
158
+ } while ('' != trim($headerLine));
159
+
160
+ while (!feof($fp)) {
161
+ $response->appendBody(fread($fp, 8192));
162
+ }
163
+ return $response;
164
+ }
165
+ }
166
  ?>
vendor/PEAR/HTTP/Request2/Adapter/Socket.php CHANGED
@@ -1,1121 +1,1121 @@
1
- <?php
2
- /**
3
- * Socket-based adapter for HTTP_Request2
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /** Base class for HTTP_Request2 adapters */
22
- require_once 'HTTP/Request2/Adapter.php';
23
-
24
- /** Socket wrapper class */
25
- require_once 'HTTP/Request2/SocketWrapper.php';
26
-
27
- /**
28
- * Socket-based adapter for HTTP_Request2
29
- *
30
- * This adapter uses only PHP sockets and will work on almost any PHP
31
- * environment. Code is based on original HTTP_Request PEAR package.
32
- *
33
- * @category HTTP
34
- * @package HTTP_Request2
35
- * @author Alexey Borzov <avb@php.net>
36
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
37
- * @version Release: 2.2.1
38
- * @link http://pear.php.net/package/HTTP_Request2
39
- */
40
- class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
41
- {
42
- /**
43
- * Regular expression for 'token' rule from RFC 2616
44
- */
45
- const REGEXP_TOKEN = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+';
46
-
47
- /**
48
- * Regular expression for 'quoted-string' rule from RFC 2616
49
- */
50
- const REGEXP_QUOTED_STRING = '"(?>[^"\\\\]+|\\\\.)*"';
51
-
52
- /**
53
- * Connected sockets, needed for Keep-Alive support
54
- * @var array
55
- * @see connect()
56
- */
57
- protected static $sockets = array();
58
-
59
- /**
60
- * Data for digest authentication scheme
61
- *
62
- * The keys for the array are URL prefixes.
63
- *
64
- * The values are associative arrays with data (realm, nonce, nonce-count,
65
- * opaque...) needed for digest authentication. Stored here to prevent making
66
- * duplicate requests to digest-protected resources after we have already
67
- * received the challenge.
68
- *
69
- * @var array
70
- */
71
- protected static $challenges = array();
72
-
73
- /**
74
- * Connected socket
75
- * @var HTTP_Request2_SocketWrapper
76
- * @see connect()
77
- */
78
- protected $socket;
79
-
80
- /**
81
- * Challenge used for server digest authentication
82
- * @var array
83
- */
84
- protected $serverChallenge;
85
-
86
- /**
87
- * Challenge used for proxy digest authentication
88
- * @var array
89
- */
90
- protected $proxyChallenge;
91
-
92
- /**
93
- * Remaining length of the current chunk, when reading chunked response
94
- * @var integer
95
- * @see readChunked()
96
- */
97
- protected $chunkLength = 0;
98
-
99
- /**
100
- * Remaining amount of redirections to follow
101
- *
102
- * Starts at 'max_redirects' configuration parameter and is reduced on each
103
- * subsequent redirect. An Exception will be thrown once it reaches zero.
104
- *
105
- * @var integer
106
- */
107
- protected $redirectCountdown = null;
108
-
109
- /**
110
- * Whether to wait for "100 Continue" response before sending request body
111
- * @var bool
112
- */
113
- protected $expect100Continue = false;
114
-
115
- /**
116
- * Sends request to the remote server and returns its response
117
- *
118
- * @param HTTP_Request2 $request HTTP request message
119
- *
120
- * @return HTTP_Request2_Response
121
- * @throws HTTP_Request2_Exception
122
- */
123
- public function sendRequest(HTTP_Request2 $request)
124
- {
125
- $this->request = $request;
126
-
127
- try {
128
- $keepAlive = $this->connect();
129
- $headers = $this->prepareHeaders();
130
- $this->socket->write($headers);
131
- // provide request headers to the observer, see request #7633
132
- $this->request->setLastEvent('sentHeaders', $headers);
133
-
134
- if (!$this->expect100Continue) {
135
- $this->writeBody();
136
- $response = $this->readResponse();
137
-
138
- } else {
139
- $response = $this->readResponse();
140
- if (!$response || 100 == $response->getStatus()) {
141
- $this->expect100Continue = false;
142
- // either got "100 Continue" or timed out -> send body
143
- $this->writeBody();
144
- $response = $this->readResponse();
145
- }
146
- }
147
-
148
-
149
- if ($jar = $request->getCookieJar()) {
150
- $jar->addCookiesFromResponse($response, $request->getUrl());
151
- }
152
-
153
- if (!$this->canKeepAlive($keepAlive, $response)) {
154
- $this->disconnect();
155
- }
156
-
157
- if ($this->shouldUseProxyDigestAuth($response)) {
158
- return $this->sendRequest($request);
159
- }
160
- if ($this->shouldUseServerDigestAuth($response)) {
161
- return $this->sendRequest($request);
162
- }
163
- if ($authInfo = $response->getHeader('authentication-info')) {
164
- $this->updateChallenge($this->serverChallenge, $authInfo);
165
- }
166
- if ($proxyInfo = $response->getHeader('proxy-authentication-info')) {
167
- $this->updateChallenge($this->proxyChallenge, $proxyInfo);
168
- }
169
-
170
- } catch (Exception $e) {
171
- $this->disconnect();
172
- }
173
-
174
- unset($this->request, $this->requestBody);
175
-
176
- if (!empty($e)) {
177
- $this->redirectCountdown = null;
178
- throw $e;
179
- }
180
-
181
- if (!$request->getConfig('follow_redirects') || !$response->isRedirect()) {
182
- $this->redirectCountdown = null;
183
- return $response;
184
- } else {
185
- return $this->handleRedirect($request, $response);
186
- }
187
- }
188
-
189
- /**
190
- * Connects to the remote server
191
- *
192
- * @return bool whether the connection can be persistent
193
- * @throws HTTP_Request2_Exception
194
- */
195
- protected function connect()
196
- {
197
- $secure = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https');
198
- $tunnel = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
199
- $headers = $this->request->getHeaders();
200
- $reqHost = $this->request->getUrl()->getHost();
201
- if (!($reqPort = $this->request->getUrl()->getPort())) {
202
- $reqPort = $secure? 443: 80;
203
- }
204
-
205
- $httpProxy = $socksProxy = false;
206
- if (!($host = $this->request->getConfig('proxy_host'))) {
207
- $host = $reqHost;
208
- $port = $reqPort;
209
- } else {
210
- if (!($port = $this->request->getConfig('proxy_port'))) {
211
- throw new HTTP_Request2_LogicException(
212
- 'Proxy port not provided',
213
- HTTP_Request2_Exception::MISSING_VALUE
214
- );
215
- }
216
- if ('http' == ($type = $this->request->getConfig('proxy_type'))) {
217
- $httpProxy = true;
218
- } elseif ('socks5' == $type) {
219
- $socksProxy = true;
220
- } else {
221
- throw new HTTP_Request2_NotImplementedException(
222
- "Proxy type '{$type}' is not supported"
223
- );
224
- }
225
- }
226
-
227
- if ($tunnel && !$httpProxy) {
228
- throw new HTTP_Request2_LogicException(
229
- "Trying to perform CONNECT request without proxy",
230
- HTTP_Request2_Exception::MISSING_VALUE
231
- );
232
- }
233
- if ($secure && !in_array('ssl', stream_get_transports())) {
234
- throw new HTTP_Request2_LogicException(
235
- 'Need OpenSSL support for https:// requests',
236
- HTTP_Request2_Exception::MISCONFIGURATION
237
- );
238
- }
239
-
240
- // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
241
- // connection token to a proxy server...
242
- if ($httpProxy && !$secure && !empty($headers['connection'])
243
- && 'Keep-Alive' == $headers['connection']
244
- ) {
245
- $this->request->setHeader('connection');
246
- }
247
-
248
- $keepAlive = ('1.1' == $this->request->getConfig('protocol_version') &&
249
- empty($headers['connection'])) ||
250
- (!empty($headers['connection']) &&
251
- 'Keep-Alive' == $headers['connection']);
252
-
253
- $options = array();
254
- if ($ip = $this->request->getConfig('local_ip')) {
255
- $options['socket'] = array(
256
- 'bindto' => (false === strpos($ip, ':') ? $ip : '[' . $ip . ']') . ':0'
257
- );
258
- }
259
- if ($secure || $tunnel) {
260
- $options['ssl'] = array();
261
- foreach ($this->request->getConfig() as $name => $value) {
262
- if ('ssl_' == substr($name, 0, 4) && null !== $value) {
263
- if ('ssl_verify_host' == $name) {
264
- if ($value) {
265
- $options['ssl']['CN_match'] = $reqHost;
266
- }
267
- } else {
268
- $options['ssl'][substr($name, 4)] = $value;
269
- }
270
- }
271
- }
272
- ksort($options['ssl']);
273
- }
274
-
275
- // Use global request timeout if given, see feature requests #5735, #8964
276
- if ($timeout = $this->request->getConfig('timeout')) {
277
- $deadline = time() + $timeout;
278
- } else {
279
- $deadline = null;
280
- }
281
-
282
- // Changing SSL context options after connection is established does *not*
283
- // work, we need a new connection if options change
284
- $remote = ((!$secure || $httpProxy || $socksProxy)? 'tcp://': 'ssl://')
285
- . $host . ':' . $port;
286
- $socketKey = $remote . (
287
- ($secure && $httpProxy || $socksProxy)
288
- ? "->{$reqHost}:{$reqPort}" : ''
289
- ) . (empty($options)? '': ':' . serialize($options));
290
- unset($this->socket);
291
-
292
- // We use persistent connections and have a connected socket?
293
- // Ensure that the socket is still connected, see bug #16149
294
- if ($keepAlive && !empty(self::$sockets[$socketKey])
295
- && !self::$sockets[$socketKey]->eof()
296
- ) {
297
- $this->socket =& self::$sockets[$socketKey];
298
-
299
- } else {
300
- if ($socksProxy) {
301
- require_once 'HTTP/Request2/SOCKS5.php';
302
-
303
- $this->socket = new HTTP_Request2_SOCKS5(
304
- $remote, $this->request->getConfig('connect_timeout'),
305
- $options, $this->request->getConfig('proxy_user'),
306
- $this->request->getConfig('proxy_password')
307
- );
308
- // handle request timeouts ASAP
309
- $this->socket->setDeadline($deadline, $this->request->getConfig('timeout'));
310
- $this->socket->connect($reqHost, $reqPort);
311
- if (!$secure) {
312
- $conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}";
313
- } else {
314
- $this->socket->enableCrypto();
315
- $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
316
- }
317
-
318
- } elseif ($secure && $httpProxy && !$tunnel) {
319
- $this->establishTunnel();
320
- $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
321
-
322
- } else {
323
- $this->socket = new HTTP_Request2_SocketWrapper(
324
- $remote, $this->request->getConfig('connect_timeout'), $options
325
- );
326
- }
327
- $this->request->setLastEvent('connect', empty($conninfo)? $remote: $conninfo);
328
- self::$sockets[$socketKey] =& $this->socket;
329
- }
330
- $this->socket->setDeadline($deadline, $this->request->getConfig('timeout'));
331
- return $keepAlive;
332
- }
333
-
334
- /**
335
- * Establishes a tunnel to a secure remote server via HTTP CONNECT request
336
- *
337
- * This method will fail if 'ssl_verify_peer' is enabled. Probably because PHP
338
- * sees that we are connected to a proxy server (duh!) rather than the server
339
- * that presents its certificate.
340
- *
341
- * @link http://tools.ietf.org/html/rfc2817#section-5.2
342
- * @throws HTTP_Request2_Exception
343
- */
344
- protected function establishTunnel()
345
- {
346
- $donor = new self;
347
- $connect = new HTTP_Request2(
348
- $this->request->getUrl(), HTTP_Request2::METHOD_CONNECT,
349
- array_merge($this->request->getConfig(), array('adapter' => $donor))
350
- );
351
- $response = $connect->send();
352
- // Need any successful (2XX) response
353
- if (200 > $response->getStatus() || 300 <= $response->getStatus()) {
354
- throw new HTTP_Request2_ConnectionException(
355
- 'Failed to connect via HTTPS proxy. Proxy response: ' .
356
- $response->getStatus() . ' ' . $response->getReasonPhrase()
357
- );
358
- }
359
- $this->socket = $donor->socket;
360
- $this->socket->enableCrypto();
361
- }
362
-
363
- /**
364
- * Checks whether current connection may be reused or should be closed
365
- *
366
- * @param boolean $requestKeepAlive whether connection could
367
- * be persistent in the first place
368
- * @param HTTP_Request2_Response $response response object to check
369
- *
370
- * @return boolean
371
- */
372
- protected function canKeepAlive($requestKeepAlive, HTTP_Request2_Response $response)
373
- {
374
- // Do not close socket on successful CONNECT request
375
- if (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod()
376
- && 200 <= $response->getStatus() && 300 > $response->getStatus()
377
- ) {
378
- return true;
379
- }
380
-
381
- $lengthKnown = 'chunked' == strtolower($response->getHeader('transfer-encoding'))
382
- || null !== $response->getHeader('content-length')
383
- // no body possible for such responses, see also request #17031
384
- || HTTP_Request2::METHOD_HEAD == $this->request->getMethod()
385
- || in_array($response->getStatus(), array(204, 304));
386
- $persistent = 'keep-alive' == strtolower($response->getHeader('connection')) ||
387
- (null === $response->getHeader('connection') &&
388
- '1.1' == $response->getVersion());
389
- return $requestKeepAlive && $lengthKnown && $persistent;
390
- }
391
-
392
- /**
393
- * Disconnects from the remote server
394
- */
395
- protected function disconnect()
396
- {
397
- if (!empty($this->socket)) {
398
- $this->socket = null;
399
- $this->request->setLastEvent('disconnect');
400
- }
401
- }
402
-
403
- /**
404
- * Handles HTTP redirection
405
- *
406
- * This method will throw an Exception if redirect to a non-HTTP(S) location
407
- * is attempted, also if number of redirects performed already is equal to
408
- * 'max_redirects' configuration parameter.
409
- *
410
- * @param HTTP_Request2 $request Original request
411
- * @param HTTP_Request2_Response $response Response containing redirect
412
- *
413
- * @return HTTP_Request2_Response Response from a new location
414
- * @throws HTTP_Request2_Exception
415
- */
416
- protected function handleRedirect(
417
- HTTP_Request2 $request, HTTP_Request2_Response $response
418
- ) {
419
- if (is_null($this->redirectCountdown)) {
420
- $this->redirectCountdown = $request->getConfig('max_redirects');
421
- }
422
- if (0 == $this->redirectCountdown) {
423
- $this->redirectCountdown = null;
424
- // Copying cURL behaviour
425
- throw new HTTP_Request2_MessageException(
426
- 'Maximum (' . $request->getConfig('max_redirects') . ') redirects followed',
427
- HTTP_Request2_Exception::TOO_MANY_REDIRECTS
428
- );
429
- }
430
- $redirectUrl = new Net_URL2(
431
- $response->getHeader('location'),
432
- array(Net_URL2::OPTION_USE_BRACKETS => $request->getConfig('use_brackets'))
433
- );
434
- // refuse non-HTTP redirect
435
- if ($redirectUrl->isAbsolute()
436
- && !in_array($redirectUrl->getScheme(), array('http', 'https'))
437
- ) {
438
- $this->redirectCountdown = null;
439
- throw new HTTP_Request2_MessageException(
440
- 'Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString(),
441
- HTTP_Request2_Exception::NON_HTTP_REDIRECT
442
- );
443
- }
444
- // Theoretically URL should be absolute (see http://tools.ietf.org/html/rfc2616#section-14.30),
445
- // but in practice it is often not
446
- if (!$redirectUrl->isAbsolute()) {
447
- $redirectUrl = $request->getUrl()->resolve($redirectUrl);
448
- }
449
- $redirect = clone $request;
450
- $redirect->setUrl($redirectUrl);
451
- if (303 == $response->getStatus()
452
- || (!$request->getConfig('strict_redirects')
453
- && in_array($response->getStatus(), array(301, 302)))
454
- ) {
455
- $redirect->setMethod(HTTP_Request2::METHOD_GET);
456
- $redirect->setBody('');
457
- }
458
-
459
- if (0 < $this->redirectCountdown) {
460
- $this->redirectCountdown--;
461
- }
462
- return $this->sendRequest($redirect);
463
- }
464
-
465
- /**
466
- * Checks whether another request should be performed with server digest auth
467
- *
468
- * Several conditions should be satisfied for it to return true:
469
- * - response status should be 401
470
- * - auth credentials should be set in the request object
471
- * - response should contain WWW-Authenticate header with digest challenge
472
- * - there is either no challenge stored for this URL or new challenge
473
- * contains stale=true parameter (in other case we probably just failed
474
- * due to invalid username / password)
475
- *
476
- * The method stores challenge values in $challenges static property
477
- *
478
- * @param HTTP_Request2_Response $response response to check
479
- *
480
- * @return boolean whether another request should be performed
481
- * @throws HTTP_Request2_Exception in case of unsupported challenge parameters
482
- */
483
- protected function shouldUseServerDigestAuth(HTTP_Request2_Response $response)
484
- {
485
- // no sense repeating a request if we don't have credentials
486
- if (401 != $response->getStatus() || !$this->request->getAuth()) {
487
- return false;
488
- }
489
- if (!$challenge = $this->parseDigestChallenge($response->getHeader('www-authenticate'))) {
490
- return false;
491
- }
492
-
493
- $url = $this->request->getUrl();
494
- $scheme = $url->getScheme();
495
- $host = $scheme . '://' . $url->getHost();
496
- if ($port = $url->getPort()) {
497
- if ((0 == strcasecmp($scheme, 'http') && 80 != $port)
498
- || (0 == strcasecmp($scheme, 'https') && 443 != $port)
499
- ) {
500
- $host .= ':' . $port;
501
- }
502
- }
503
-
504
- if (!empty($challenge['domain'])) {
505
- $prefixes = array();
506
- foreach (preg_split('/\\s+/', $challenge['domain']) as $prefix) {
507
- // don't bother with different servers
508
- if ('/' == substr($prefix, 0, 1)) {
509
- $prefixes[] = $host . $prefix;
510
- }
511
- }
512
- }
513
- if (empty($prefixes)) {
514
- $prefixes = array($host . '/');
515
- }
516
-
517
- $ret = true;
518
- foreach ($prefixes as $prefix) {
519
- if (!empty(self::$challenges[$prefix])
520
- && (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
521
- ) {
522
- // probably credentials are invalid
523
- $ret = false;
524
- }
525
- self::$challenges[$prefix] =& $challenge;
526
- }
527
- return $ret;
528
- }
529
-
530
- /**
531
- * Checks whether another request should be performed with proxy digest auth
532
- *
533
- * Several conditions should be satisfied for it to return true:
534
- * - response status should be 407
535
- * - proxy auth credentials should be set in the request object
536
- * - response should contain Proxy-Authenticate header with digest challenge
537
- * - there is either no challenge stored for this proxy or new challenge
538
- * contains stale=true parameter (in other case we probably just failed
539
- * due to invalid username / password)
540
- *
541
- * The method stores challenge values in $challenges static property
542
- *
543
- * @param HTTP_Request2_Response $response response to check
544
- *
545
- * @return boolean whether another request should be performed
546
- * @throws HTTP_Request2_Exception in case of unsupported challenge parameters
547
- */
548
- protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response)
549
- {
550
- if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) {
551
- return false;
552
- }
553
- if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) {
554
- return false;
555
- }
556
-
557
- $key = 'proxy://' . $this->request->getConfig('proxy_host') .
558
- ':' . $this->request->getConfig('proxy_port');
559
-
560
- if (!empty(self::$challenges[$key])
561
- && (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
562
- ) {
563
- $ret = false;
564
- } else {
565
- $ret = true;
566
- }
567
- self::$challenges[$key] = $challenge;
568
- return $ret;
569
- }
570
-
571
- /**
572
- * Extracts digest method challenge from (WWW|Proxy)-Authenticate header value
573
- *
574
- * There is a problem with implementation of RFC 2617: several of the parameters
575
- * are defined as quoted-string there and thus may contain backslash escaped
576
- * double quotes (RFC 2616, section 2.2). However, RFC 2617 defines unq(X) as
577
- * just value of quoted-string X without surrounding quotes, it doesn't speak
578
- * about removing backslash escaping.
579
- *
580
- * Now realm parameter is user-defined and human-readable, strange things
581
- * happen when it contains quotes:
582
- * - Apache allows quotes in realm, but apparently uses realm value without
583
- * backslashes for digest computation
584
- * - Squid allows (manually escaped) quotes there, but it is impossible to
585
- * authorize with either escaped or unescaped quotes used in digest,
586
- * probably it can't parse the response (?)
587
- * - Both IE and Firefox display realm value with backslashes in
588
- * the password popup and apparently use the same value for digest
589
- *
590
- * HTTP_Request2 follows IE and Firefox (and hopefully RFC 2617) in
591
- * quoted-string handling, unfortunately that means failure to authorize
592
- * sometimes
593
- *
594
- * @param string $headerValue value of WWW-Authenticate or Proxy-Authenticate header
595
- *
596
- * @return mixed associative array with challenge parameters, false if
597
- * no challenge is present in header value
598
- * @throws HTTP_Request2_NotImplementedException in case of unsupported challenge parameters
599
- */
600
- protected function parseDigestChallenge($headerValue)
601
- {
602
- $authParam = '(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
603
- self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')';
604
- $challenge = "!(?<=^|\\s|,)Digest ({$authParam}\\s*(,\\s*|$))+!";
605
- if (!preg_match($challenge, $headerValue, $matches)) {
606
- return false;
607
- }
608
-
609
- preg_match_all('!' . $authParam . '!', $matches[0], $params);
610
- $paramsAry = array();
611
- $knownParams = array('realm', 'domain', 'nonce', 'opaque', 'stale',
612
- 'algorithm', 'qop');
613
- for ($i = 0; $i < count($params[0]); $i++) {
614
- // section 3.2.1: Any unrecognized directive MUST be ignored.
615
- if (in_array($params[1][$i], $knownParams)) {
616
- if ('"' == substr($params[2][$i], 0, 1)) {
617
- $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
618
- } else {
619
- $paramsAry[$params[1][$i]] = $params[2][$i];
620
- }
621
- }
622
- }
623
- // we only support qop=auth
624
- if (!empty($paramsAry['qop'])
625
- && !in_array('auth', array_map('trim', explode(',', $paramsAry['qop'])))
626
- ) {
627
- throw new HTTP_Request2_NotImplementedException(
628
- "Only 'auth' qop is currently supported in digest authentication, " .
629
- "server requested '{$paramsAry['qop']}'"
630
- );
631
- }
632
- // we only support algorithm=MD5
633
- if (!empty($paramsAry['algorithm']) && 'MD5' != $paramsAry['algorithm']) {
634
- throw new HTTP_Request2_NotImplementedException(
635
- "Only 'MD5' algorithm is currently supported in digest authentication, " .
636
- "server requested '{$paramsAry['algorithm']}'"
637
- );
638
- }
639
-
640
- return $paramsAry;
641
- }
642
-
643
- /**
644
- * Parses [Proxy-]Authentication-Info header value and updates challenge
645
- *
646
- * @param array &$challenge challenge to update
647
- * @param string $headerValue value of [Proxy-]Authentication-Info header
648
- *
649
- * @todo validate server rspauth response
650
- */
651
- protected function updateChallenge(&$challenge, $headerValue)
652
- {
653
- $authParam = '!(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
654
- self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')!';
655
- $paramsAry = array();
656
-
657
- preg_match_all($authParam, $headerValue, $params);
658
- for ($i = 0; $i < count($params[0]); $i++) {
659
- if ('"' == substr($params[2][$i], 0, 1)) {
660
- $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
661
- } else {
662
- $paramsAry[$params[1][$i]] = $params[2][$i];
663
- }
664
- }
665
- // for now, just update the nonce value
666
- if (!empty($paramsAry['nextnonce'])) {
667
- $challenge['nonce'] = $paramsAry['nextnonce'];
668
- $challenge['nc'] = 1;
669
- }
670
- }
671
-
672
- /**
673
- * Creates a value for [Proxy-]Authorization header when using digest authentication
674
- *
675
- * @param string $user user name
676
- * @param string $password password
677
- * @param string $url request URL
678
- * @param array &$challenge digest challenge parameters
679
- *
680
- * @return string value of [Proxy-]Authorization request header
681
- * @link http://tools.ietf.org/html/rfc2617#section-3.2.2
682
- */
683
- protected function createDigestResponse($user, $password, $url, &$challenge)
684
- {
685
- if (false !== ($q = strpos($url, '?'))
686
- && $this->request->getConfig('digest_compat_ie')
687
- ) {
688
- $url = substr($url, 0, $q);
689
- }
690
-
691
- $a1 = md5($user . ':' . $challenge['realm'] . ':' . $password);
692
- $a2 = md5($this->request->getMethod() . ':' . $url);
693
-
694
- if (empty($challenge['qop'])) {
695
- $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $a2);
696
- } else {
697
- $challenge['cnonce'] = 'Req2.' . rand();
698
- if (empty($challenge['nc'])) {
699
- $challenge['nc'] = 1;
700
- }
701
- $nc = sprintf('%08x', $challenge['nc']++);
702
- $digest = md5(
703
- $a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' .
704
- $challenge['cnonce'] . ':auth:' . $a2
705
- );
706
- }
707
- return 'Digest username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $user) . '", ' .
708
- 'realm="' . $challenge['realm'] . '", ' .
709
- 'nonce="' . $challenge['nonce'] . '", ' .
710
- 'uri="' . $url . '", ' .
711
- 'response="' . $digest . '"' .
712
- (!empty($challenge['opaque'])?
713
- ', opaque="' . $challenge['opaque'] . '"':
714
- '') .
715
- (!empty($challenge['qop'])?
716
- ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"':
717
- '');
718
- }
719
-
720
- /**
721
- * Adds 'Authorization' header (if needed) to request headers array
722
- *
723
- * @param array &$headers request headers
724
- * @param string $requestHost request host (needed for digest authentication)
725
- * @param string $requestUrl request URL (needed for digest authentication)
726
- *
727
- * @throws HTTP_Request2_NotImplementedException
728
- */
729
- protected function addAuthorizationHeader(&$headers, $requestHost, $requestUrl)
730
- {
731
- if (!($auth = $this->request->getAuth())) {
732
- return;
733
- }
734
- switch ($auth['scheme']) {
735
- case HTTP_Request2::AUTH_BASIC:
736
- $headers['authorization'] = 'Basic ' . base64_encode(
737
- $auth['user'] . ':' . $auth['password']
738
- );
739
- break;
740
-
741
- case HTTP_Request2::AUTH_DIGEST:
742
- unset($this->serverChallenge);
743
- $fullUrl = ('/' == $requestUrl[0])?
744
- $this->request->getUrl()->getScheme() . '://' .
745
- $requestHost . $requestUrl:
746
- $requestUrl;
747
- foreach (array_keys(self::$challenges) as $key) {
748
- if ($key == substr($fullUrl, 0, strlen($key))) {
749
- $headers['authorization'] = $this->createDigestResponse(
750
- $auth['user'], $auth['password'],
751
- $requestUrl, self::$challenges[$key]
752
- );
753
- $this->serverChallenge =& self::$challenges[$key];
754
- break;
755
- }
756
- }
757
- break;
758
-
759
- default:
760
- throw new HTTP_Request2_NotImplementedException(
761
- "Unknown HTTP authentication scheme '{$auth['scheme']}'"
762
- );
763
- }
764
- }
765
-
766
- /**
767
- * Adds 'Proxy-Authorization' header (if needed) to request headers array
768
- *
769
- * @param array &$headers request headers
770
- * @param string $requestUrl request URL (needed for digest authentication)
771
- *
772
- * @throws HTTP_Request2_NotImplementedException
773
- */
774
- protected function addProxyAuthorizationHeader(&$headers, $requestUrl)
775
- {
776
- if (!$this->request->getConfig('proxy_host')
777
- || !($user = $this->request->getConfig('proxy_user'))
778
- || (0 == strcasecmp('https', $this->request->getUrl()->getScheme())
779
- && HTTP_Request2::METHOD_CONNECT != $this->request->getMethod())
780
- ) {
781
- return;
782
- }
783
-
784
- $password = $this->request->getConfig('proxy_password');
785
- switch ($this->request->getConfig('proxy_auth_scheme')) {
786
- case HTTP_Request2::AUTH_BASIC:
787
- $headers['proxy-authorization'] = 'Basic ' . base64_encode(
788
- $user . ':' . $password
789
- );
790
- break;
791
-
792
- case HTTP_Request2::AUTH_DIGEST:
793
- unset($this->proxyChallenge);
794
- $proxyUrl = 'proxy://' . $this->request->getConfig('proxy_host') .
795
- ':' . $this->request->getConfig('proxy_port');
796
- if (!empty(self::$challenges[$proxyUrl])) {
797
- $headers['proxy-authorization'] = $this->createDigestResponse(
798
- $user, $password,
799
- $requestUrl, self::$challenges[$proxyUrl]
800
- );
801
- $this->proxyChallenge =& self::$challenges[$proxyUrl];
802
- }
803
- break;
804
-
805
- default:
806
- throw new HTTP_Request2_NotImplementedException(
807
- "Unknown HTTP authentication scheme '" .
808
- $this->request->getConfig('proxy_auth_scheme') . "'"
809
- );
810
- }
811
- }
812
-
813
-
814
- /**
815
- * Creates the string with the Request-Line and request headers
816
- *
817
- * @return string
818
- * @throws HTTP_Request2_Exception
819
- */
820
- protected function prepareHeaders()
821
- {
822
- $headers = $this->request->getHeaders();
823
- $url = $this->request->getUrl();
824
- $connect = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
825
- $host = $url->getHost();
826
-
827
- $defaultPort = 0 == strcasecmp($url->getScheme(), 'https')? 443: 80;
828
- if (($port = $url->getPort()) && $port != $defaultPort || $connect) {
829
- $host .= ':' . (empty($port)? $defaultPort: $port);
830
- }
831
- // Do not overwrite explicitly set 'Host' header, see bug #16146
832
- if (!isset($headers['host'])) {
833
- $headers['host'] = $host;
834
- }
835
-
836
- if ($connect) {
837
- $requestUrl = $host;
838
-
839
- } else {
840
- if (!$this->request->getConfig('proxy_host')
841
- || 'http' != $this->request->getConfig('proxy_type')
842
- || 0 == strcasecmp($url->getScheme(), 'https')
843
- ) {
844
- $requestUrl = '';
845
- } else {
846
- $requestUrl = $url->getScheme() . '://' . $host;
847
- }
848
- $path = $url->getPath();
849
- $query = $url->getQuery();
850
- $requestUrl .= (empty($path)? '/': $path) . (empty($query)? '': '?' . $query);
851
- }
852
-
853
- if ('1.1' == $this->request->getConfig('protocol_version')
854
- && extension_loaded('zlib') && !isset($headers['accept-encoding'])
855
- ) {
856
- $headers['accept-encoding'] = 'gzip, deflate';
857
- }
858
- if (($jar = $this->request->getCookieJar())
859
- && ($cookies = $jar->getMatching($this->request->getUrl(), true))
860
- ) {
861
- $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies;
862
- }
863
-
864
- $this->addAuthorizationHeader($headers, $host, $requestUrl);
865
- $this->addProxyAuthorizationHeader($headers, $requestUrl);
866
- $this->calculateRequestLength($headers);
867
- if ('1.1' == $this->request->getConfig('protocol_version')) {
868
- $this->updateExpectHeader($headers);
869
- } else {
870
- $this->expect100Continue = false;
871
- }
872
-
873
- $headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' .
874
- $this->request->getConfig('protocol_version') . "\r\n";
875
- foreach ($headers as $name => $value) {
876
- $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
877
- $headersStr .= $canonicalName . ': ' . $value . "\r\n";
878
- }
879
- return $headersStr . "\r\n";
880
- }
881
-
882
- /**
883
- * Adds or removes 'Expect: 100-continue' header from request headers
884
- *
885
- * Also sets the $expect100Continue property. Parsing of existing header
886
- * is somewhat needed due to its complex structure and due to the
887
- * requirement in section 8.2.3 of RFC 2616:
888
- * > A client MUST NOT send an Expect request-header field (section
889
- * > 14.20) with the "100-continue" expectation if it does not intend
890
- * > to send a request body.
891
- *
892
- * @param array &$headers Array of headers prepared for the request
893
- *
894
- * @throws HTTP_Request2_LogicException
895
- * @link http://pear.php.net/bugs/bug.php?id=19233
896
- * @link http://tools.ietf.org/html/rfc2616#section-8.2.3
897
- */
898
- protected function updateExpectHeader(&$headers)
899
- {
900
- $this->expect100Continue = false;
901
- $expectations = array();
902
- if (isset($headers['expect'])) {
903
- if ('' === $headers['expect']) {
904
- // empty 'Expect' header is technically invalid, so just get rid of it
905
- unset($headers['expect']);
906
- return;
907
- }
908
- // build regexp to parse the value of existing Expect header
909
- $expectParam = ';\s*' . self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
910
- . self::REGEXP_TOKEN . '|'
911
- . self::REGEXP_QUOTED_STRING . '))?\s*';
912
- $expectExtension = self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
913
- . self::REGEXP_TOKEN . '|'
914
- . self::REGEXP_QUOTED_STRING . ')\s*(?:'
915
- . $expectParam . ')*)?';
916
- $expectItem = '!(100-continue|' . $expectExtension . ')!A';
917
-
918
- $pos = 0;
919
- $length = strlen($headers['expect']);
920
-
921
- while ($pos < $length) {
922
- $pos += strspn($headers['expect'], " \t", $pos);
923
- if (',' === substr($headers['expect'], $pos, 1)) {
924
- $pos++;
925
- continue;
926
-
927
- } elseif (!preg_match($expectItem, $headers['expect'], $m, 0, $pos)) {
928
- throw new HTTP_Request2_LogicException(
929
- "Cannot parse value '{$headers['expect']}' of Expect header",
930
- HTTP_Request2_Exception::INVALID_ARGUMENT
931
- );
932
-
933
- } else {
934
- $pos += strlen($m[0]);
935
- if (strcasecmp('100-continue', $m[0])) {
936
- $expectations[] = $m[0];
937
- }
938
- }
939
- }
940
- }
941
-
942
- if (1024 < $this->contentLength) {
943
- $expectations[] = '100-continue';
944
- $this->expect100Continue = true;
945
- }
946
-
947
- if (empty($expectations)) {
948
- unset($headers['expect']);
949
- } else {
950
- $headers['expect'] = implode(',', $expectations);
951
- }
952
- }
953
-
954
- /**
955
- * Sends the request body
956
- *
957
- * @throws HTTP_Request2_MessageException
958
- */
959
- protected function writeBody()
960
- {
961
- if (in_array($this->request->getMethod(), self::$bodyDisallowed)
962
- || 0 == $this->contentLength
963
- ) {
964
- return;
965
- }
966
-
967
- $position = 0;
968
- $bufferSize = $this->request->getConfig('buffer_size');
969
- $headers = $this->request->getHeaders();
970
- $chunked = isset($headers['transfer-encoding']);
971
- while ($position < $this->contentLength) {
972
- if (is_string($this->requestBody)) {
973
- $str = substr($this->requestBody, $position, $bufferSize);
974
- } elseif (is_resource($this->requestBody)) {
975
- $str = fread($this->requestBody, $bufferSize);
976
- } else {
977
- $str = $this->requestBody->read($bufferSize);
978
- }
979
- if (!$chunked) {
980
- $this->socket->write($str);
981
- } else {
982
- $this->socket->write(dechex(strlen($str)) . "\r\n{$str}\r\n");
983
- }
984
- // Provide the length of written string to the observer, request #7630
985
- $this->request->setLastEvent('sentBodyPart', strlen($str));
986
- $position += strlen($str);
987
- }
988
-
989
- // write zero-length chunk
990
- if ($chunked) {
991
- $this->socket->write("0\r\n\r\n");
992
- }
993
- $this->request->setLastEvent('sentBody', $this->contentLength);
994
- }
995
-
996
- /**
997
- * Reads the remote server's response
998
- *
999
- * @return HTTP_Request2_Response
1000
- * @throws HTTP_Request2_Exception
1001
- */
1002
- protected function readResponse()
1003
- {
1004
- $bufferSize = $this->request->getConfig('buffer_size');
1005
- // http://tools.ietf.org/html/rfc2616#section-8.2.3
1006
- // ...the client SHOULD NOT wait for an indefinite period before sending the request body
1007
- $timeout = $this->expect100Continue ? 1 : null;
1008
-
1009
- do {
1010
- try {
1011
- $response = new HTTP_Request2_Response(
1012
- $this->socket->readLine($bufferSize, $timeout), true, $this->request->getUrl()
1013
- );
1014
- do {
1015
- $headerLine = $this->socket->readLine($bufferSize);
1016
- $response->parseHeaderLine($headerLine);
1017
- } while ('' != $headerLine);
1018
-
1019
- } catch (HTTP_Request2_MessageException $e) {
1020
- if (HTTP_Request2_Exception::TIMEOUT === $e->getCode()
1021
- && $this->expect100Continue
1022
- ) {
1023
- return null;
1024
- }
1025
- throw $e;
1026
- }
1027
- if ($this->expect100Continue && 100 == $response->getStatus()) {
1028
- return $response;
1029
- }
1030
- } while (in_array($response->getStatus(), array(100, 101)));
1031
-
1032
- $this->request->setLastEvent('receivedHeaders', $response);
1033
-
1034
- // No body possible in such responses
1035
- if (HTTP_Request2::METHOD_HEAD == $this->request->getMethod()
1036
- || (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod()
1037
- && 200 <= $response->getStatus() && 300 > $response->getStatus())
1038
- || in_array($response->getStatus(), array(204, 304))
1039
- ) {
1040
- return $response;
1041
- }
1042
-
1043
- $chunked = 'chunked' == $response->getHeader('transfer-encoding');
1044
- $length = $response->getHeader('content-length');
1045
- $hasBody = false;
1046
- if ($chunked || null === $length || 0 < intval($length)) {
1047
- // RFC 2616, section 4.4:
1048
- // 3. ... If a message is received with both a
1049
- // Transfer-Encoding header field and a Content-Length header field,
1050
- // the latter MUST be ignored.
1051
- $toRead = ($chunked || null === $length)? null: $length;
1052
- $this->chunkLength = 0;
1053
-
1054
- while (!$this->socket->eof() && (is_null($toRead) || 0 < $toRead)) {
1055
- if ($chunked) {
1056
- $data = $this->readChunked($bufferSize);
1057
- } elseif (is_null($toRead)) {
1058
- $data = $this->socket->read($bufferSize);
1059
- } else {
1060
- $data = $this->socket->read(min($toRead, $bufferSize));
1061
- $toRead -= strlen($data);
1062
- }
1063
- if ('' == $data && (!$this->chunkLength || $this->socket->eof())) {
1064
- break;
1065
- }
1066
-
1067
- $hasBody = true;
1068
- if ($this->request->getConfig('store_body')) {
1069
- $response->appendBody($data);
1070
- }
1071
- if (!in_array($response->getHeader('content-encoding'), array('identity', null))) {
1072
- $this->request->setLastEvent('receivedEncodedBodyPart', $data);
1073
- } else {
1074
- $this->request->setLastEvent('receivedBodyPart', $data);
1075
- }
1076
- }
1077
- }
1078
-
1079
- if ($hasBody) {
1080
- $this->request->setLastEvent('receivedBody', $response);
1081
- }
1082
- return $response;
1083
- }
1084
-
1085
- /**
1086
- * Reads a part of response body encoded with chunked Transfer-Encoding
1087
- *
1088
- * @param int $bufferSize buffer size to use for reading
1089
- *
1090
- * @return string
1091
- * @throws HTTP_Request2_MessageException
1092
- */
1093
- protected function readChunked($bufferSize)
1094
- {
1095
- // at start of the next chunk?
1096
- if (0 == $this->chunkLength) {
1097
- $line = $this->socket->readLine($bufferSize);
1098
- if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
1099
- throw new HTTP_Request2_MessageException(
1100
- "Cannot decode chunked response, invalid chunk length '{$line}'",
1101
- HTTP_Request2_Exception::DECODE_ERROR
1102
- );
1103
- } else {
1104
- $this->chunkLength = hexdec($matches[1]);
1105
- // Chunk with zero length indicates the end
1106
- if (0 == $this->chunkLength) {
1107
- $this->socket->readLine($bufferSize);
1108
- return '';
1109
- }
1110
- }
1111
- }
1112
- $data = $this->socket->read(min($this->chunkLength, $bufferSize));
1113
- $this->chunkLength -= strlen($data);
1114
- if (0 == $this->chunkLength) {
1115
- $this->socket->readLine($bufferSize); // Trailing CRLF
1116
- }
1117
- return $data;
1118
- }
1119
- }
1120
-
1121
  ?>
1
+ <?php
2
+ /**
3
+ * Socket-based adapter for HTTP_Request2
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /** Base class for HTTP_Request2 adapters */
22
+ require_once 'HTTP/Request2/Adapter.php';
23
+
24
+ /** Socket wrapper class */
25
+ require_once 'HTTP/Request2/SocketWrapper.php';
26
+
27
+ /**
28
+ * Socket-based adapter for HTTP_Request2
29
+ *
30
+ * This adapter uses only PHP sockets and will work on almost any PHP
31
+ * environment. Code is based on original HTTP_Request PEAR package.
32
+ *
33
+ * @category HTTP
34
+ * @package HTTP_Request2
35
+ * @author Alexey Borzov <avb@php.net>
36
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
37
+ * @version Release: 2.2.1
38
+ * @link http://pear.php.net/package/HTTP_Request2
39
+ */
40
+ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
41
+ {
42
+ /**
43
+ * Regular expression for 'token' rule from RFC 2616
44
+ */
45
+ const REGEXP_TOKEN = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+';
46
+
47
+ /**
48
+ * Regular expression for 'quoted-string' rule from RFC 2616
49
+ */
50
+ const REGEXP_QUOTED_STRING = '"(?>[^"\\\\]+|\\\\.)*"';
51
+
52
+ /**
53
+ * Connected sockets, needed for Keep-Alive support
54
+ * @var array
55
+ * @see connect()
56
+ */
57
+ protected static $sockets = array();
58
+
59
+ /**
60
+ * Data for digest authentication scheme
61
+ *
62
+ * The keys for the array are URL prefixes.
63
+ *
64
+ * The values are associative arrays with data (realm, nonce, nonce-count,
65
+ * opaque...) needed for digest authentication. Stored here to prevent making
66
+ * duplicate requests to digest-protected resources after we have already
67
+ * received the challenge.
68
+ *
69
+ * @var array
70
+ */
71
+ protected static $challenges = array();
72
+
73
+ /**
74
+ * Connected socket
75
+ * @var HTTP_Request2_SocketWrapper
76
+ * @see connect()
77
+ */
78
+ protected $socket;
79
+
80
+ /**
81
+ * Challenge used for server digest authentication
82
+ * @var array
83
+ */
84
+ protected $serverChallenge;
85
+
86
+ /**
87
+ * Challenge used for proxy digest authentication
88
+ * @var array
89
+ */
90
+ protected $proxyChallenge;
91
+
92
+ /**
93
+ * Remaining length of the current chunk, when reading chunked response
94
+ * @var integer
95
+ * @see readChunked()
96
+ */
97
+ protected $chunkLength = 0;
98
+
99
+ /**
100
+ * Remaining amount of redirections to follow
101
+ *
102
+ * Starts at 'max_redirects' configuration parameter and is reduced on each
103
+ * subsequent redirect. An Exception will be thrown once it reaches zero.
104
+ *
105
+ * @var integer
106
+ */
107
+ protected $redirectCountdown = null;
108
+
109
+ /**
110
+ * Whether to wait for "100 Continue" response before sending request body
111
+ * @var bool
112
+ */
113
+ protected $expect100Continue = false;
114
+
115
+ /**
116
+ * Sends request to the remote server and returns its response
117
+ *
118
+ * @param HTTP_Request2 $request HTTP request message
119
+ *
120
+ * @return HTTP_Request2_Response
121
+ * @throws HTTP_Request2_Exception
122
+ */
123
+ public function sendRequest(HTTP_Request2 $request)
124
+ {
125
+ $this->request = $request;
126
+
127
+ try {
128
+ $keepAlive = $this->connect();
129
+ $headers = $this->prepareHeaders();
130
+ $this->socket->write($headers);
131
+ // provide request headers to the observer, see request #7633
132
+ $this->request->setLastEvent('sentHeaders', $headers);
133
+
134
+ if (!$this->expect100Continue) {
135
+ $this->writeBody();
136
+ $response = $this->readResponse();
137
+
138
+ } else {
139
+ $response = $this->readResponse();
140
+ if (!$response || 100 == $response->getStatus()) {
141
+ $this->expect100Continue = false;
142
+ // either got "100 Continue" or timed out -> send body
143
+ $this->writeBody();
144
+ $response = $this->readResponse();
145
+ }
146
+ }
147
+
148
+
149
+ if ($jar = $request->getCookieJar()) {
150
+ $jar->addCookiesFromResponse($response, $request->getUrl());
151
+ }
152
+
153
+ if (!$this->canKeepAlive($keepAlive, $response)) {
154
+ $this->disconnect();
155
+ }
156
+
157
+ if ($this->shouldUseProxyDigestAuth($response)) {
158
+ return $this->sendRequest($request);
159
+ }
160
+ if ($this->shouldUseServerDigestAuth($response)) {
161
+ return $this->sendRequest($request);
162
+ }
163
+ if ($authInfo = $response->getHeader('authentication-info')) {
164
+ $this->updateChallenge($this->serverChallenge, $authInfo);
165
+ }
166
+ if ($proxyInfo = $response->getHeader('proxy-authentication-info')) {
167
+ $this->updateChallenge($this->proxyChallenge, $proxyInfo);
168
+ }
169
+
170
+ } catch (Exception $e) {
171
+ $this->disconnect();
172
+ }
173
+
174
+ unset($this->request, $this->requestBody);
175
+
176
+ if (!empty($e)) {
177
+ $this->redirectCountdown = null;
178
+ throw $e;
179
+ }
180
+
181
+ if (!$request->getConfig('follow_redirects') || !$response->isRedirect()) {
182
+ $this->redirectCountdown = null;
183
+ return $response;
184
+ } else {
185
+ return $this->handleRedirect($request, $response);
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Connects to the remote server
191
+ *
192
+ * @return bool whether the connection can be persistent
193
+ * @throws HTTP_Request2_Exception
194
+ */
195
+ protected function connect()
196
+ {
197
+ $secure = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https');
198
+ $tunnel = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
199
+ $headers = $this->request->getHeaders();
200
+ $reqHost = $this->request->getUrl()->getHost();
201
+ if (!($reqPort = $this->request->getUrl()->getPort())) {
202
+ $reqPort = $secure? 443: 80;
203
+ }
204
+
205
+ $httpProxy = $socksProxy = false;
206
+ if (!($host = $this->request->getConfig('proxy_host'))) {
207
+ $host = $reqHost;
208
+ $port = $reqPort;
209
+ } else {
210
+ if (!($port = $this->request->getConfig('proxy_port'))) {
211
+ throw new HTTP_Request2_LogicException(
212
+ 'Proxy port not provided',
213
+ HTTP_Request2_Exception::MISSING_VALUE
214
+ );
215
+ }
216
+ if ('http' == ($type = $this->request->getConfig('proxy_type'))) {
217
+ $httpProxy = true;
218
+ } elseif ('socks5' == $type) {
219
+ $socksProxy = true;
220
+ } else {
221
+ throw new HTTP_Request2_NotImplementedException(
222
+ "Proxy type '{$type}' is not supported"
223
+ );
224
+ }
225
+ }
226
+
227
+ if ($tunnel && !$httpProxy) {
228
+ throw new HTTP_Request2_LogicException(
229
+ "Trying to perform CONNECT request without proxy",
230
+ HTTP_Request2_Exception::MISSING_VALUE
231
+ );
232
+ }
233
+ if ($secure && !in_array('ssl', stream_get_transports())) {
234
+ throw new HTTP_Request2_LogicException(
235
+ 'Need OpenSSL support for https:// requests',
236
+ HTTP_Request2_Exception::MISCONFIGURATION
237
+ );
238
+ }
239
+
240
+ // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
241
+ // connection token to a proxy server...
242
+ if ($httpProxy && !$secure && !empty($headers['connection'])
243
+ && 'Keep-Alive' == $headers['connection']
244
+ ) {
245
+ $this->request->setHeader('connection');
246
+ }
247
+
248
+ $keepAlive = ('1.1' == $this->request->getConfig('protocol_version') &&
249
+ empty($headers['connection'])) ||
250
+ (!empty($headers['connection']) &&
251
+ 'Keep-Alive' == $headers['connection']);
252
+
253
+ $options = array();
254
+ if ($ip = $this->request->getConfig('local_ip')) {
255
+ $options['socket'] = array(
256
+ 'bindto' => (false === strpos($ip, ':') ? $ip : '[' . $ip . ']') . ':0'
257
+ );
258
+ }
259
+ if ($secure || $tunnel) {
260
+ $options['ssl'] = array();
261
+ foreach ($this->request->getConfig() as $name => $value) {
262
+ if ('ssl_' == substr($name, 0, 4) && null !== $value) {
263
+ if ('ssl_verify_host' == $name) {
264
+ if ($value) {
265
+ $options['ssl']['CN_match'] = $reqHost;
266
+ }
267
+ } else {
268
+ $options['ssl'][substr($name, 4)] = $value;
269
+ }
270
+ }
271
+ }
272
+ ksort($options['ssl']);
273
+ }
274
+
275
+ // Use global request timeout if given, see feature requests #5735, #8964
276
+ if ($timeout = $this->request->getConfig('timeout')) {
277
+ $deadline = time() + $timeout;
278
+ } else {
279
+ $deadline = null;
280
+ }
281
+
282
+ // Changing SSL context options after connection is established does *not*
283
+ // work, we need a new connection if options change
284
+ $remote = ((!$secure || $httpProxy || $socksProxy)? 'tcp://': 'ssl://')
285
+ . $host . ':' . $port;
286
+ $socketKey = $remote . (
287
+ ($secure && $httpProxy || $socksProxy)
288
+ ? "->{$reqHost}:{$reqPort}" : ''
289
+ ) . (empty($options)? '': ':' . serialize($options));
290
+ unset($this->socket);
291
+
292
+ // We use persistent connections and have a connected socket?
293
+ // Ensure that the socket is still connected, see bug #16149
294
+ if ($keepAlive && !empty(self::$sockets[$socketKey])
295
+ && !self::$sockets[$socketKey]->eof()
296
+ ) {
297
+ $this->socket =& self::$sockets[$socketKey];
298
+
299
+ } else {
300
+ if ($socksProxy) {
301
+ require_once 'HTTP/Request2/SOCKS5.php';
302
+
303
+ $this->socket = new HTTP_Request2_SOCKS5(
304
+ $remote, $this->request->getConfig('connect_timeout'),
305
+ $options, $this->request->getConfig('proxy_user'),
306
+ $this->request->getConfig('proxy_password')
307
+ );
308
+ // handle request timeouts ASAP
309
+ $this->socket->setDeadline($deadline, $this->request->getConfig('timeout'));
310
+ $this->socket->connect($reqHost, $reqPort);
311
+ if (!$secure) {
312
+ $conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}";
313
+ } else {
314
+ $this->socket->enableCrypto();
315
+ $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
316
+ }
317
+
318
+ } elseif ($secure && $httpProxy && !$tunnel) {
319
+ $this->establishTunnel();
320
+ $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
321
+
322
+ } else {
323
+ $this->socket = new HTTP_Request2_SocketWrapper(
324
+ $remote, $this->request->getConfig('connect_timeout'), $options
325
+ );
326
+ }
327
+ $this->request->setLastEvent('connect', empty($conninfo)? $remote: $conninfo);
328
+ self::$sockets[$socketKey] =& $this->socket;
329
+ }
330
+ $this->socket->setDeadline($deadline, $this->request->getConfig('timeout'));
331
+ return $keepAlive;
332
+ }
333
+
334
+ /**
335
+ * Establishes a tunnel to a secure remote server via HTTP CONNECT request
336
+ *
337
+ * This method will fail if 'ssl_verify_peer' is enabled. Probably because PHP
338
+ * sees that we are connected to a proxy server (duh!) rather than the server
339
+ * that presents its certificate.
340
+ *
341
+ * @link http://tools.ietf.org/html/rfc2817#section-5.2
342
+ * @throws HTTP_Request2_Exception
343
+ */
344
+ protected function establishTunnel()
345
+ {
346
+ $donor = new self;
347
+ $connect = new HTTP_Request2(
348
+ $this->request->getUrl(), HTTP_Request2::METHOD_CONNECT,
349
+ array_merge($this->request->getConfig(), array('adapter' => $donor))
350
+ );
351
+ $response = $connect->send();
352
+ // Need any successful (2XX) response
353
+ if (200 > $response->getStatus() || 300 <= $response->getStatus()) {
354
+ throw new HTTP_Request2_ConnectionException(
355
+ 'Failed to connect via HTTPS proxy. Proxy response: ' .
356
+ $response->getStatus() . ' ' . $response->getReasonPhrase()
357
+ );
358
+ }
359
+ $this->socket = $donor->socket;
360
+ $this->socket->enableCrypto();
361
+ }
362
+
363
+ /**
364
+ * Checks whether current connection may be reused or should be closed
365
+ *
366
+ * @param boolean $requestKeepAlive whether connection could
367
+ * be persistent in the first place
368
+ * @param HTTP_Request2_Response $response response object to check
369
+ *
370
+ * @return boolean
371
+ */
372
+ protected function canKeepAlive($requestKeepAlive, HTTP_Request2_Response $response)
373
+ {
374
+ // Do not close socket on successful CONNECT request
375
+ if (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod()
376
+ && 200 <= $response->getStatus() && 300 > $response->getStatus()
377
+ ) {
378
+ return true;
379
+ }
380
+
381
+ $lengthKnown = 'chunked' == strtolower($response->getHeader('transfer-encoding'))
382
+ || null !== $response->getHeader('content-length')
383
+ // no body possible for such responses, see also request #17031
384
+ || HTTP_Request2::METHOD_HEAD == $this->request->getMethod()
385
+ || in_array($response->getStatus(), array(204, 304));
386
+ $persistent = 'keep-alive' == strtolower($response->getHeader('connection')) ||
387
+ (null === $response->getHeader('connection') &&
388
+ '1.1' == $response->getVersion());
389
+ return $requestKeepAlive && $lengthKnown && $persistent;
390
+ }
391
+
392
+ /**
393
+ * Disconnects from the remote server
394
+ */
395
+ protected function disconnect()
396
+ {
397
+ if (!empty($this->socket)) {
398
+ $this->socket = null;
399
+ $this->request->setLastEvent('disconnect');
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Handles HTTP redirection
405
+ *
406
+ * This method will throw an Exception if redirect to a non-HTTP(S) location
407
+ * is attempted, also if number of redirects performed already is equal to
408
+ * 'max_redirects' configuration parameter.
409
+ *
410
+ * @param HTTP_Request2 $request Original request
411
+ * @param HTTP_Request2_Response $response Response containing redirect
412
+ *
413
+ * @return HTTP_Request2_Response Response from a new location
414
+ * @throws HTTP_Request2_Exception
415
+ */
416
+ protected function handleRedirect(
417
+ HTTP_Request2 $request, HTTP_Request2_Response $response
418
+ ) {
419
+ if (is_null($this->redirectCountdown)) {
420
+ $this->redirectCountdown = $request->getConfig('max_redirects');
421
+ }
422
+ if (0 == $this->redirectCountdown) {
423
+ $this->redirectCountdown = null;
424
+ // Copying cURL behaviour
425
+ throw new HTTP_Request2_MessageException(
426
+ 'Maximum (' . $request->getConfig('max_redirects') . ') redirects followed',
427
+ HTTP_Request2_Exception::TOO_MANY_REDIRECTS
428
+ );
429
+ }
430
+ $redirectUrl = new Net_URL2(
431
+ $response->getHeader('location'),
432
+ array(Net_URL2::OPTION_USE_BRACKETS => $request->getConfig('use_brackets'))
433
+ );
434
+ // refuse non-HTTP redirect
435
+ if ($redirectUrl->isAbsolute()
436
+ && !in_array($redirectUrl->getScheme(), array('http', 'https'))
437
+ ) {
438
+ $this->redirectCountdown = null;
439
+ throw new HTTP_Request2_MessageException(
440
+ 'Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString(),
441
+ HTTP_Request2_Exception::NON_HTTP_REDIRECT
442
+ );
443
+ }
444
+ // Theoretically URL should be absolute (see http://tools.ietf.org/html/rfc2616#section-14.30),
445
+ // but in practice it is often not
446
+ if (!$redirectUrl->isAbsolute()) {
447
+ $redirectUrl = $request->getUrl()->resolve($redirectUrl);
448
+ }
449
+ $redirect = clone $request;
450
+ $redirect->setUrl($redirectUrl);
451
+ if (303 == $response->getStatus()
452
+ || (!$request->getConfig('strict_redirects')
453
+ && in_array($response->getStatus(), array(301, 302)))
454
+ ) {
455
+ $redirect->setMethod(HTTP_Request2::METHOD_GET);
456
+ $redirect->setBody('');
457
+ }
458
+
459
+ if (0 < $this->redirectCountdown) {
460
+ $this->redirectCountdown--;
461
+ }
462
+ return $this->sendRequest($redirect);
463
+ }
464
+
465
+ /**
466
+ * Checks whether another request should be performed with server digest auth
467
+ *
468
+ * Several conditions should be satisfied for it to return true:
469
+ * - response status should be 401
470
+ * - auth credentials should be set in the request object
471
+ * - response should contain WWW-Authenticate header with digest challenge
472
+ * - there is either no challenge stored for this URL or new challenge
473
+ * contains stale=true parameter (in other case we probably just failed
474
+ * due to invalid username / password)
475
+ *
476
+ * The method stores challenge values in $challenges static property
477
+ *
478
+ * @param HTTP_Request2_Response $response response to check
479
+ *
480
+ * @return boolean whether another request should be performed
481
+ * @throws HTTP_Request2_Exception in case of unsupported challenge parameters
482
+ */
483
+ protected function shouldUseServerDigestAuth(HTTP_Request2_Response $response)
484
+ {
485
+ // no sense repeating a request if we don't have credentials
486
+ if (401 != $response->getStatus() || !$this->request->getAuth()) {
487
+ return false;
488
+ }
489
+ if (!$challenge = $this->parseDigestChallenge($response->getHeader('www-authenticate'))) {
490
+ return false;
491
+ }
492
+
493
+ $url = $this->request->getUrl();
494
+ $scheme = $url->getScheme();
495
+ $host = $scheme . '://' . $url->getHost();
496
+ if ($port = $url->getPort()) {
497
+ if ((0 == strcasecmp($scheme, 'http') && 80 != $port)
498
+ || (0 == strcasecmp($scheme, 'https') && 443 != $port)
499
+ ) {
500
+ $host .= ':' . $port;
501
+ }
502
+ }
503
+
504
+ if (!empty($challenge['domain'])) {
505
+ $prefixes = array();
506
+ foreach (preg_split('/\\s+/', $challenge['domain']) as $prefix) {
507
+ // don't bother with different servers
508
+ if ('/' == substr($prefix, 0, 1)) {
509
+ $prefixes[] = $host . $prefix;
510
+ }
511
+ }
512
+ }
513
+ if (empty($prefixes)) {
514
+ $prefixes = array($host . '/');
515
+ }
516
+
517
+ $ret = true;
518
+ foreach ($prefixes as $prefix) {
519
+ if (!empty(self::$challenges[$prefix])
520
+ && (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
521
+ ) {
522
+ // probably credentials are invalid
523
+ $ret = false;
524
+ }
525
+ self::$challenges[$prefix] =& $challenge;
526
+ }
527
+ return $ret;
528
+ }
529
+
530
+ /**
531
+ * Checks whether another request should be performed with proxy digest auth
532
+ *
533
+ * Several conditions should be satisfied for it to return true:
534
+ * - response status should be 407
535
+ * - proxy auth credentials should be set in the request object
536
+ * - response should contain Proxy-Authenticate header with digest challenge
537
+ * - there is either no challenge stored for this proxy or new challenge
538
+ * contains stale=true parameter (in other case we probably just failed
539
+ * due to invalid username / password)
540
+ *
541
+ * The method stores challenge values in $challenges static property
542
+ *
543
+ * @param HTTP_Request2_Response $response response to check
544
+ *
545
+ * @return boolean whether another request should be performed
546
+ * @throws HTTP_Request2_Exception in case of unsupported challenge parameters
547
+ */
548
+ protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response)
549
+ {
550
+ if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) {
551
+ return false;
552
+ }
553
+ if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) {
554
+ return false;
555
+ }
556
+
557
+ $key = 'proxy://' . $this->request->getConfig('proxy_host') .
558
+ ':' . $this->request->getConfig('proxy_port');
559
+
560
+ if (!empty(self::$challenges[$key])
561
+ && (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
562
+ ) {
563
+ $ret = false;
564
+ } else {
565
+ $ret = true;
566
+ }
567
+ self::$challenges[$key] = $challenge;
568
+ return $ret;
569
+ }
570
+
571
+ /**
572
+ * Extracts digest method challenge from (WWW|Proxy)-Authenticate header value
573
+ *
574
+ * There is a problem with implementation of RFC 2617: several of the parameters
575
+ * are defined as quoted-string there and thus may contain backslash escaped
576
+ * double quotes (RFC 2616, section 2.2). However, RFC 2617 defines unq(X) as
577
+ * just value of quoted-string X without surrounding quotes, it doesn't speak
578
+ * about removing backslash escaping.
579
+ *
580
+ * Now realm parameter is user-defined and human-readable, strange things
581
+ * happen when it contains quotes:
582
+ * - Apache allows quotes in realm, but apparently uses realm value without
583
+ * backslashes for digest computation
584
+ * - Squid allows (manually escaped) quotes there, but it is impossible to
585
+ * authorize with either escaped or unescaped quotes used in digest,
586
+ * probably it can't parse the response (?)
587
+ * - Both IE and Firefox display realm value with backslashes in
588
+ * the password popup and apparently use the same value for digest
589
+ *
590
+ * HTTP_Request2 follows IE and Firefox (and hopefully RFC 2617) in
591
+ * quoted-string handling, unfortunately that means failure to authorize
592
+ * sometimes
593
+ *
594
+ * @param string $headerValue value of WWW-Authenticate or Proxy-Authenticate header
595
+ *
596
+ * @return mixed associative array with challenge parameters, false if
597
+ * no challenge is present in header value
598
+ * @throws HTTP_Request2_NotImplementedException in case of unsupported challenge parameters
599
+ */
600
+ protected function parseDigestChallenge($headerValue)
601
+ {
602
+ $authParam = '(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
603
+ self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')';
604
+ $challenge = "!(?<=^|\\s|,)Digest ({$authParam}\\s*(,\\s*|$))+!";
605
+ if (!preg_match($challenge, $headerValue, $matches)) {
606
+ return false;
607
+ }
608
+
609
+ preg_match_all('!' . $authParam . '!', $matches[0], $params);
610
+ $paramsAry = array();
611
+ $knownParams = array('realm', 'domain', 'nonce', 'opaque', 'stale',
612
+ 'algorithm', 'qop');
613
+ for ($i = 0; $i < count($params[0]); $i++) {
614
+ // section 3.2.1: Any unrecognized directive MUST be ignored.
615
+ if (in_array($params[1][$i], $knownParams)) {
616
+ if ('"' == substr($params[2][$i], 0, 1)) {
617
+ $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
618
+ } else {
619
+ $paramsAry[$params[1][$i]] = $params[2][$i];
620
+ }
621
+ }
622
+ }
623
+ // we only support qop=auth
624
+ if (!empty($paramsAry['qop'])
625
+ && !in_array('auth', array_map('trim', explode(',', $paramsAry['qop'])))
626
+ ) {
627
+ throw new HTTP_Request2_NotImplementedException(
628
+ "Only 'auth' qop is currently supported in digest authentication, " .
629
+ "server requested '{$paramsAry['qop']}'"
630
+ );
631
+ }
632
+ // we only support algorithm=MD5
633
+ if (!empty($paramsAry['algorithm']) && 'MD5' != $paramsAry['algorithm']) {
634
+ throw new HTTP_Request2_NotImplementedException(
635
+ "Only 'MD5' algorithm is currently supported in digest authentication, " .
636
+ "server requested '{$paramsAry['algorithm']}'"
637
+ );
638
+ }
639
+
640
+ return $paramsAry;
641
+ }
642
+
643
+ /**
644
+ * Parses [Proxy-]Authentication-Info header value and updates challenge
645
+ *
646
+ * @param array &$challenge challenge to update
647
+ * @param string $headerValue value of [Proxy-]Authentication-Info header
648
+ *
649
+ * @todo validate server rspauth response
650
+ */
651
+ protected function updateChallenge(&$challenge, $headerValue)
652
+ {
653
+ $authParam = '!(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
654
+ self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')!';
655
+ $paramsAry = array();
656
+
657
+ preg_match_all($authParam, $headerValue, $params);
658
+ for ($i = 0; $i < count($params[0]); $i++) {
659
+ if ('"' == substr($params[2][$i], 0, 1)) {
660
+ $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
661
+ } else {
662
+ $paramsAry[$params[1][$i]] = $params[2][$i];
663
+ }
664
+ }
665
+ // for now, just update the nonce value
666
+ if (!empty($paramsAry['nextnonce'])) {
667
+ $challenge['nonce'] = $paramsAry['nextnonce'];
668
+ $challenge['nc'] = 1;
669
+ }
670
+ }
671
+
672
+ /**
673
+ * Creates a value for [Proxy-]Authorization header when using digest authentication
674
+ *
675
+ * @param string $user user name
676
+ * @param string $password password
677
+ * @param string $url request URL
678
+ * @param array &$challenge digest challenge parameters
679
+ *
680
+ * @return string value of [Proxy-]Authorization request header
681
+ * @link http://tools.ietf.org/html/rfc2617#section-3.2.2
682
+ */
683
+ protected function createDigestResponse($user, $password, $url, &$challenge)
684
+ {
685
+ if (false !== ($q = strpos($url, '?'))
686
+ && $this->request->getConfig('digest_compat_ie')
687
+ ) {
688
+ $url = substr($url, 0, $q);
689
+ }
690
+
691
+ $a1 = md5($user . ':' . $challenge['realm'] . ':' . $password);
692
+ $a2 = md5($this->request->getMethod() . ':' . $url);
693
+
694
+ if (empty($challenge['qop'])) {
695
+ $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $a2);
696
+ } else {
697
+ $challenge['cnonce'] = 'Req2.' . rand();
698
+ if (empty($challenge['nc'])) {
699
+ $challenge['nc'] = 1;
700
+ }
701
+ $nc = sprintf('%08x', $challenge['nc']++);
702
+ $digest = md5(
703
+ $a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' .
704
+ $challenge['cnonce'] . ':auth:' . $a2
705
+ );
706
+ }
707
+ return 'Digest username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $user) . '", ' .
708
+ 'realm="' . $challenge['realm'] . '", ' .
709
+ 'nonce="' . $challenge['nonce'] . '", ' .
710
+ 'uri="' . $url . '", ' .
711
+ 'response="' . $digest . '"' .
712
+ (!empty($challenge['opaque'])?
713
+ ', opaque="' . $challenge['opaque'] . '"':
714
+ '') .
715
+ (!empty($challenge['qop'])?
716
+ ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"':
717
+ '');
718
+ }
719
+
720
+ /**
721
+ * Adds 'Authorization' header (if needed) to request headers array
722
+ *
723
+ * @param array &$headers request headers
724
+ * @param string $requestHost request host (needed for digest authentication)
725
+ * @param string $requestUrl request URL (needed for digest authentication)
726
+ *
727
+ * @throws HTTP_Request2_NotImplementedException
728
+ */
729
+ protected function addAuthorizationHeader(&$headers, $requestHost, $requestUrl)
730
+ {
731
+ if (!($auth = $this->request->getAuth())) {
732
+ return;
733
+ }
734
+ switch ($auth['scheme']) {
735
+ case HTTP_Request2::AUTH_BASIC:
736
+ $headers['authorization'] = 'Basic ' . base64_encode(
737
+ $auth['user'] . ':' . $auth['password']
738
+ );
739
+ break;
740
+
741
+ case HTTP_Request2::AUTH_DIGEST:
742
+ unset($this->serverChallenge);
743
+ $fullUrl = ('/' == $requestUrl[0])?
744
+ $this->request->getUrl()->getScheme() . '://' .
745
+ $requestHost . $requestUrl:
746
+ $requestUrl;
747
+ foreach (array_keys(self::$challenges) as $key) {
748
+ if ($key == substr($fullUrl, 0, strlen($key))) {
749
+ $headers['authorization'] = $this->createDigestResponse(
750
+ $auth['user'], $auth['password'],
751
+ $requestUrl, self::$challenges[$key]
752
+ );
753
+ $this->serverChallenge =& self::$challenges[$key];
754
+ break;
755
+ }
756
+ }
757
+ break;
758
+
759
+ default:
760
+ throw new HTTP_Request2_NotImplementedException(
761
+ "Unknown HTTP authentication scheme '{$auth['scheme']}'"
762
+ );
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Adds 'Proxy-Authorization' header (if needed) to request headers array
768
+ *
769
+ * @param array &$headers request headers
770
+ * @param string $requestUrl request URL (needed for digest authentication)
771
+ *
772
+ * @throws HTTP_Request2_NotImplementedException
773
+ */
774
+ protected function addProxyAuthorizationHeader(&$headers, $requestUrl)
775
+ {
776
+ if (!$this->request->getConfig('proxy_host')
777
+ || !($user = $this->request->getConfig('proxy_user'))
778
+ || (0 == strcasecmp('https', $this->request->getUrl()->getScheme())
779
+ && HTTP_Request2::METHOD_CONNECT != $this->request->getMethod())
780
+ ) {
781
+ return;
782
+ }
783
+
784
+ $password = $this->request->getConfig('proxy_password');
785
+ switch ($this->request->getConfig('proxy_auth_scheme')) {
786
+ case HTTP_Request2::AUTH_BASIC:
787
+ $headers['proxy-authorization'] = 'Basic ' . base64_encode(
788
+ $user . ':' . $password
789
+ );
790
+ break;
791
+
792
+ case HTTP_Request2::AUTH_DIGEST:
793
+ unset($this->proxyChallenge);
794
+ $proxyUrl = 'proxy://' . $this->request->getConfig('proxy_host') .
795
+ ':' . $this->request->getConfig('proxy_port');
796
+ if (!empty(self::$challenges[$proxyUrl])) {
797
+ $headers['proxy-authorization'] = $this->createDigestResponse(
798
+ $user, $password,
799
+ $requestUrl, self::$challenges[$proxyUrl]
800
+ );
801
+ $this->proxyChallenge =& self::$challenges[$proxyUrl];
802
+ }
803
+ break;
804
+
805
+ default:
806
+ throw new HTTP_Request2_NotImplementedException(
807
+ "Unknown HTTP authentication scheme '" .
808
+ $this->request->getConfig('proxy_auth_scheme') . "'"
809
+ );
810
+ }
811
+ }
812
+
813
+
814
+ /**
815
+ * Creates the string with the Request-Line and request headers
816
+ *
817
+ * @return string
818
+ * @throws HTTP_Request2_Exception
819
+ */
820
+ protected function prepareHeaders()
821
+ {
822
+ $headers = $this->request->getHeaders();
823
+ $url = $this->request->getUrl();
824
+ $connect = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
825
+ $host = $url->getHost();
826
+
827
+ $defaultPort = 0 == strcasecmp($url->getScheme(), 'https')? 443: 80;
828
+ if (($port = $url->getPort()) && $port != $defaultPort || $connect) {
829
+ $host .= ':' . (empty($port)? $defaultPort: $port);
830
+ }
831
+ // Do not overwrite explicitly set 'Host' header, see bug #16146
832
+ if (!isset($headers['host'])) {
833
+ $headers['host'] = $host;
834
+ }
835
+
836
+ if ($connect) {
837
+ $requestUrl = $host;
838
+
839
+ } else {
840
+ if (!$this->request->getConfig('proxy_host')
841
+ || 'http' != $this->request->getConfig('proxy_type')
842
+ || 0 == strcasecmp($url->getScheme(), 'https')
843
+ ) {
844
+ $requestUrl = '';
845
+ } else {
846
+ $requestUrl = $url->getScheme() . '://' . $host;
847
+ }
848
+ $path = $url->getPath();
849
+ $query = $url->getQuery();
850
+ $requestUrl .= (empty($path)? '/': $path) . (empty($query)? '': '?' . $query);
851
+ }
852
+
853
+ if ('1.1' == $this->request->getConfig('protocol_version')
854
+ && extension_loaded('zlib') && !isset($headers['accept-encoding'])
855
+ ) {
856
+ $headers['accept-encoding'] = 'gzip, deflate';
857
+ }
858
+ if (($jar = $this->request->getCookieJar())
859
+ && ($cookies = $jar->getMatching($this->request->getUrl(), true))
860
+ ) {
861
+ $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies;
862
+ }
863
+
864
+ $this->addAuthorizationHeader($headers, $host, $requestUrl);
865
+ $this->addProxyAuthorizationHeader($headers, $requestUrl);
866
+ $this->calculateRequestLength($headers);
867
+ if ('1.1' == $this->request->getConfig('protocol_version')) {
868
+ $this->updateExpectHeader($headers);
869
+ } else {
870
+ $this->expect100Continue = false;
871
+ }
872
+
873
+ $headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' .
874
+ $this->request->getConfig('protocol_version') . "\r\n";
875
+ foreach ($headers as $name => $value) {
876
+ $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
877
+ $headersStr .= $canonicalName . ': ' . $value . "\r\n";
878
+ }
879
+ return $headersStr . "\r\n";
880
+ }
881
+
882
+ /**
883
+ * Adds or removes 'Expect: 100-continue' header from request headers
884
+ *
885
+ * Also sets the $expect100Continue property. Parsing of existing header
886
+ * is somewhat needed due to its complex structure and due to the
887
+ * requirement in section 8.2.3 of RFC 2616:
888
+ * > A client MUST NOT send an Expect request-header field (section
889
+ * > 14.20) with the "100-continue" expectation if it does not intend
890
+ * > to send a request body.
891
+ *
892
+ * @param array &$headers Array of headers prepared for the request
893
+ *
894
+ * @throws HTTP_Request2_LogicException
895
+ * @link http://pear.php.net/bugs/bug.php?id=19233
896
+ * @link http://tools.ietf.org/html/rfc2616#section-8.2.3
897
+ */
898
+ protected function updateExpectHeader(&$headers)
899
+ {
900
+ $this->expect100Continue = false;
901
+ $expectations = array();
902
+ if (isset($headers['expect'])) {
903
+ if ('' === $headers['expect']) {
904
+ // empty 'Expect' header is technically invalid, so just get rid of it
905
+ unset($headers['expect']);
906
+ return;
907
+ }
908
+ // build regexp to parse the value of existing Expect header
909
+ $expectParam = ';\s*' . self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
910
+ . self::REGEXP_TOKEN . '|'
911
+ . self::REGEXP_QUOTED_STRING . '))?\s*';
912
+ $expectExtension = self::REGEXP_TOKEN . '(?:\s*=\s*(?:'
913
+ . self::REGEXP_TOKEN . '|'
914
+ . self::REGEXP_QUOTED_STRING . ')\s*(?:'
915
+ . $expectParam . ')*)?';
916
+ $expectItem = '!(100-continue|' . $expectExtension . ')!A';
917
+
918
+ $pos = 0;
919
+ $length = strlen($headers['expect']);
920
+
921
+ while ($pos < $length) {
922
+ $pos += strspn($headers['expect'], " \t", $pos);
923
+ if (',' === substr($headers['expect'], $pos, 1)) {
924
+ $pos++;
925
+ continue;
926
+
927
+ } elseif (!preg_match($expectItem, $headers['expect'], $m, 0, $pos)) {
928
+ throw new HTTP_Request2_LogicException(
929
+ "Cannot parse value '{$headers['expect']}' of Expect header",
930
+ HTTP_Request2_Exception::INVALID_ARGUMENT
931
+ );
932
+
933
+ } else {
934
+ $pos += strlen($m[0]);
935
+ if (strcasecmp('100-continue', $m[0])) {
936
+ $expectations[] = $m[0];
937
+ }
938
+ }
939
+ }
940
+ }
941
+
942
+ if (1024 < $this->contentLength) {
943
+ $expectations[] = '100-continue';
944
+ $this->expect100Continue = true;
945
+ }
946
+
947
+ if (empty($expectations)) {
948
+ unset($headers['expect']);
949
+ } else {
950
+ $headers['expect'] = implode(',', $expectations);
951
+ }
952
+ }
953
+
954
+ /**
955
+ * Sends the request body
956
+ *
957
+ * @throws HTTP_Request2_MessageException
958
+ */
959
+ protected function writeBody()
960
+ {
961
+ if (in_array($this->request->getMethod(), self::$bodyDisallowed)
962
+ || 0 == $this->contentLength
963
+ ) {
964
+ return;
965
+ }
966
+
967
+ $position = 0;
968
+ $bufferSize = $this->request->getConfig('buffer_size');
969
+ $headers = $this->request->getHeaders();
970
+ $chunked = isset($headers['transfer-encoding']);
971
+ while ($position < $this->contentLength) {
972
+ if (is_string($this->requestBody)) {
973
+ $str = substr($this->requestBody, $position, $bufferSize);
974
+ } elseif (is_resource($this->requestBody)) {
975
+ $str = fread($this->requestBody, $bufferSize);
976
+ } else {
977
+ $str = $this->requestBody->read($bufferSize);
978
+ }
979
+ if (!$chunked) {
980
+ $this->socket->write($str);
981
+ } else {
982
+ $this->socket->write(dechex(strlen($str)) . "\r\n{$str}\r\n");
983
+ }
984
+ // Provide the length of written string to the observer, request #7630
985
+ $this->request->setLastEvent('sentBodyPart', strlen($str));
986
+ $position += strlen($str);
987
+ }
988
+
989
+ // write zero-length chunk
990
+ if ($chunked) {
991
+ $this->socket->write("0\r\n\r\n");
992
+ }
993
+ $this->request->setLastEvent('sentBody', $this->contentLength);
994
+ }
995
+
996
+ /**
997
+ * Reads the remote server's response
998
+ *
999
+ * @return HTTP_Request2_Response
1000
+ * @throws HTTP_Request2_Exception
1001
+ */
1002
+ protected function readResponse()
1003
+ {
1004
+ $bufferSize = $this->request->getConfig('buffer_size');
1005
+ // http://tools.ietf.org/html/rfc2616#section-8.2.3
1006
+ // ...the client SHOULD NOT wait for an indefinite period before sending the request body
1007
+ $timeout = $this->expect100Continue ? 1 : null;
1008
+
1009
+ do {
1010
+ try {
1011
+ $response = new HTTP_Request2_Response(
1012
+ $this->socket->readLine($bufferSize, $timeout), true, $this->request->getUrl()
1013
+ );
1014
+ do {
1015
+ $headerLine = $this->socket->readLine($bufferSize);
1016
+ $response->parseHeaderLine($headerLine);
1017
+ } while ('' != $headerLine);
1018
+
1019
+ } catch (HTTP_Request2_MessageException $e) {
1020
+ if (HTTP_Request2_Exception::TIMEOUT === $e->getCode()
1021
+ && $this->expect100Continue
1022
+ ) {
1023
+ return null;
1024
+ }
1025
+ throw $e;
1026
+ }
1027
+ if ($this->expect100Continue && 100 == $response->getStatus()) {
1028
+ return $response;
1029
+ }
1030
+ } while (in_array($response->getStatus(), array(100, 101)));
1031
+
1032
+ $this->request->setLastEvent('receivedHeaders', $response);
1033
+
1034
+ // No body possible in such responses
1035
+ if (HTTP_Request2::METHOD_HEAD == $this->request->getMethod()
1036
+ || (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod()
1037
+ && 200 <= $response->getStatus() && 300 > $response->getStatus())
1038
+ || in_array($response->getStatus(), array(204, 304))
1039
+ ) {
1040
+ return $response;
1041
+ }
1042
+
1043
+ $chunked = 'chunked' == $response->getHeader('transfer-encoding');
1044
+ $length = $response->getHeader('content-length');
1045
+ $hasBody = false;
1046
+ if ($chunked || null === $length || 0 < intval($length)) {
1047
+ // RFC 2616, section 4.4:
1048
+ // 3. ... If a message is received with both a
1049
+ // Transfer-Encoding header field and a Content-Length header field,
1050
+ // the latter MUST be ignored.
1051
+ $toRead = ($chunked || null === $length)? null: $length;
1052
+ $this->chunkLength = 0;
1053
+
1054
+ while (!$this->socket->eof() && (is_null($toRead) || 0 < $toRead)) {
1055
+ if ($chunked) {
1056
+ $data = $this->readChunked($bufferSize);
1057
+ } elseif (is_null($toRead)) {
1058
+ $data = $this->socket->read($bufferSize);
1059
+ } else {
1060
+ $data = $this->socket->read(min($toRead, $bufferSize));
1061
+ $toRead -= strlen($data);
1062
+ }
1063
+ if ('' == $data && (!$this->chunkLength || $this->socket->eof())) {
1064
+ break;
1065
+ }
1066
+
1067
+ $hasBody = true;
1068
+ if ($this->request->getConfig('store_body')) {
1069
+ $response->appendBody($data);
1070
+ }
1071
+ if (!in_array($response->getHeader('content-encoding'), array('identity', null))) {
1072
+ $this->request->setLastEvent('receivedEncodedBodyPart', $data);
1073
+ } else {
1074
+ $this->request->setLastEvent('receivedBodyPart', $data);
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ if ($hasBody) {
1080
+ $this->request->setLastEvent('receivedBody', $response);
1081
+ }
1082
+ return $response;
1083
+ }
1084
+
1085
+ /**
1086
+ * Reads a part of response body encoded with chunked Transfer-Encoding
1087
+ *
1088
+ * @param int $bufferSize buffer size to use for reading
1089
+ *
1090
+ * @return string
1091
+ * @throws HTTP_Request2_MessageException
1092
+ */
1093
+ protected function readChunked($bufferSize)
1094
+ {
1095
+ // at start of the next chunk?
1096
+ if (0 == $this->chunkLength) {
1097
+ $line = $this->socket->readLine($bufferSize);
1098
+ if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
1099
+ throw new HTTP_Request2_MessageException(
1100
+ "Cannot decode chunked response, invalid chunk length '{$line}'",
1101
+ HTTP_Request2_Exception::DECODE_ERROR
1102
+ );
1103
+ } else {
1104
+ $this->chunkLength = hexdec($matches[1]);
1105
+ // Chunk with zero length indicates the end
1106
+ if (0 == $this->chunkLength) {
1107
+ $this->socket->readLine($bufferSize);
1108
+ return '';
1109
+ }
1110
+ }
1111
+ }
1112
+ $data = $this->socket->read(min($this->chunkLength, $bufferSize));
1113
+ $this->chunkLength -= strlen($data);
1114
+ if (0 == $this->chunkLength) {
1115
+ $this->socket->readLine($bufferSize); // Trailing CRLF
1116
+ }
1117
+ return $data;
1118
+ }
1119
+ }
1120
+
1121
  ?>
vendor/PEAR/HTTP/Request2/CookieJar.php CHANGED
@@ -1,494 +1,494 @@
1
- <?php
2
- /**
3
- * Stores cookies and passes them between HTTP requests
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /** Class representing a HTTP request message */
22
- require_once 'HTTP/Request2.php';
23
-
24
- /**
25
- * Stores cookies and passes them between HTTP requests
26
- *
27
- * @category HTTP
28
- * @package HTTP_Request2
29
- * @author Alexey Borzov <avb@php.net>
30
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
31
- * @version Release: @package_version@
32
- * @link http://pear.php.net/package/HTTP_Request2
33
- */
34
- class HTTP_Request2_CookieJar implements Serializable
35
- {
36
- /**
37
- * Array of stored cookies
38
- *
39
- * The array is indexed by domain, path and cookie name
40
- * .example.com
41
- * /
42
- * some_cookie => cookie data
43
- * /subdir
44
- * other_cookie => cookie data
45
- * .example.org
46
- * ...
47
- *
48
- * @var array
49
- */
50
- protected $cookies = array();
51
-
52
- /**
53
- * Whether session cookies should be serialized when serializing the jar
54
- * @var bool
55
- */
56
- protected $serializeSession = false;
57
-
58
- /**
59
- * Whether Public Suffix List should be used for domain matching
60
- * @var bool
61
- */
62
- protected $useList = true;
63
-
64
- /**
65
- * Array with Public Suffix List data
66
- * @var array
67
- * @link http://publicsuffix.org/
68
- */
69
- protected static $psl = array();
70
-
71
- /**
72
- * Class constructor, sets various options
73
- *
74
- * @param bool $serializeSessionCookies Controls serializing session cookies,
75
- * see {@link serializeSessionCookies()}
76
- * @param bool $usePublicSuffixList Controls using Public Suffix List,
77
- * see {@link usePublicSuffixList()}
78
- */
79
- public function __construct(
80
- $serializeSessionCookies = false, $usePublicSuffixList = true
81
- ) {
82
- $this->serializeSessionCookies($serializeSessionCookies);
83
- $this->usePublicSuffixList($usePublicSuffixList);
84
- }
85
-
86
- /**
87
- * Returns current time formatted in ISO-8601 at UTC timezone
88
- *
89
- * @return string
90
- */
91
- protected function now()
92
- {
93
- $dt = new DateTime();
94
- $dt->setTimezone(new DateTimeZone('UTC'));
95
- return $dt->format(DateTime::ISO8601);
96
- }
97
-
98
- /**
99
- * Checks cookie array for correctness, possibly updating its 'domain', 'path' and 'expires' fields
100
- *
101
- * The checks are as follows:
102
- * - cookie array should contain 'name' and 'value' fields;
103
- * - name and value should not contain disallowed symbols;
104
- * - 'expires' should be either empty parseable by DateTime;
105
- * - 'domain' and 'path' should be either not empty or an URL where
106
- * cookie was set should be provided.
107
- * - if $setter is provided, then document at that URL should be allowed
108
- * to set a cookie for that 'domain'. If $setter is not provided,
109
- * then no domain checks will be made.
110
- *
111
- * 'expires' field will be converted to ISO8601 format from COOKIE format,
112
- * 'domain' and 'path' will be set from setter URL if empty.
113
- *
114
- * @param array $cookie cookie data, as returned by
115
- * {@link HTTP_Request2_Response::getCookies()}
116
- * @param Net_URL2 $setter URL of the document that sent Set-Cookie header
117
- *
118
- * @return array Updated cookie array
119
- * @throws HTTP_Request2_LogicException
120
- * @throws HTTP_Request2_MessageException
121
- */
122
- protected function checkAndUpdateFields(array $cookie, Net_URL2 $setter = null)
123
- {
124
- if ($missing = array_diff(array('name', 'value'), array_keys($cookie))) {
125
- throw new HTTP_Request2_LogicException(
126
- "Cookie array should contain 'name' and 'value' fields",
127
- HTTP_Request2_Exception::MISSING_VALUE
128
- );
129
- }
130
- if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['name'])) {
131
- throw new HTTP_Request2_LogicException(
132
- "Invalid cookie name: '{$cookie['name']}'",
133
- HTTP_Request2_Exception::INVALID_ARGUMENT
134
- );
135
- }
136
- if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['value'])) {
137
- throw new HTTP_Request2_LogicException(
138
- "Invalid cookie value: '{$cookie['value']}'",
139
- HTTP_Request2_Exception::INVALID_ARGUMENT
140
- );
141
- }
142
- $cookie += array('domain' => '', 'path' => '', 'expires' => null, 'secure' => false);
143
-
144
- // Need ISO-8601 date @ UTC timezone
145
- if (!empty($cookie['expires'])
146
- && !preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+0000$/', $cookie['expires'])
147
- ) {
148
- try {
149
- $dt = new DateTime($cookie['expires']);
150
- $dt->setTimezone(new DateTimeZone('UTC'));
151
- $cookie['expires'] = $dt->format(DateTime::ISO8601);
152
- } catch (Exception $e) {
153
- throw new HTTP_Request2_LogicException($e->getMessage());
154
- }
155
- }
156
-
157
- if (empty($cookie['domain']) || empty($cookie['path'])) {
158
- if (!$setter) {
159
- throw new HTTP_Request2_LogicException(
160
- 'Cookie misses domain and/or path component, cookie setter URL needed',
161
- HTTP_Request2_Exception::MISSING_VALUE
162
- );
163
- }
164
- if (empty($cookie['domain'])) {
165
- if ($host = $setter->getHost()) {
166
- $cookie['domain'] = $host;
167
- } else {
168
- throw new HTTP_Request2_LogicException(
169
- 'Setter URL does not contain host part, can\'t set cookie domain',
170
- HTTP_Request2_Exception::MISSING_VALUE
171
- );
172
- }
173
- }
174
- if (empty($cookie['path'])) {
175
- $path = $setter->getPath();
176
- $cookie['path'] = empty($path)? '/': substr($path, 0, strrpos($path, '/') + 1);
177
- }
178
- }
179
-
180
- if ($setter && !$this->domainMatch($setter->getHost(), $cookie['domain'])) {
181
- throw new HTTP_Request2_MessageException(
182
- "Domain " . $setter->getHost() . " cannot set cookies for "
183
- . $cookie['domain']
184
- );
185
- }
186
-
187
- return $cookie;
188
- }
189
-
190
- /**
191
- * Stores a cookie in the jar
192
- *
193
- * @param array $cookie cookie data, as returned by
194
- * {@link HTTP_Request2_Response::getCookies()}
195
- * @param Net_URL2 $setter URL of the document that sent Set-Cookie header
196
- *
197
- * @throws HTTP_Request2_Exception
198
- */
199
- public function store(array $cookie, Net_URL2 $setter = null)
200
- {
201
- $cookie = $this->checkAndUpdateFields($cookie, $setter);
202
-
203
- if (strlen($cookie['value'])
204
- && (is_null($cookie['expires']) || $cookie['expires'] > $this->now())
205
- ) {
206
- if (!isset($this->cookies[$cookie['domain']])) {
207
- $this->cookies[$cookie['domain']] = array();
208
- }
209
- if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
210
- $this->cookies[$cookie['domain']][$cookie['path']] = array();
211
- }
212
- $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
213
-
214
- } elseif (isset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']])) {
215
- unset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']]);
216
- }
217
- }
218
-
219
- /**
220
- * Adds cookies set in HTTP response to the jar
221
- *
222
- * @param HTTP_Request2_Response $response HTTP response message
223
- * @param Net_URL2 $setter original request URL, needed for
224
- * setting default domain/path
225
- */
226
- public function addCookiesFromResponse(HTTP_Request2_Response $response, Net_URL2 $setter)
227
- {
228
- foreach ($response->getCookies() as $cookie) {
229
- $this->store($cookie, $setter);
230
- }
231
- }
232
-
233
- /**
234
- * Returns all cookies matching a given request URL
235
- *
236
- * The following checks are made:
237
- * - cookie domain should match request host
238
- * - cookie path should be a prefix for request path
239
- * - 'secure' cookies will only be sent for HTTPS requests
240
- *
241
- * @param Net_URL2 $url Request url
242
- * @param bool $asString Whether to return cookies as string for "Cookie: " header
243
- *
244
- * @return array|string Matching cookies
245
- */
246
- public function getMatching(Net_URL2 $url, $asString = false)
247
- {
248
- $host = $url->getHost();
249
- $path = $url->getPath();
250
- $secure = 0 == strcasecmp($url->getScheme(), 'https');
251
-
252
- $matched = $ret = array();
253
- foreach (array_keys($this->cookies) as $domain) {
254
- if ($this->domainMatch($host, $domain)) {
255
- foreach (array_keys($this->cookies[$domain]) as $cPath) {
256
- if (0 === strpos($path, $cPath)) {
257
- foreach ($this->cookies[$domain][$cPath] as $name => $cookie) {
258
- if (!$cookie['secure'] || $secure) {
259
- $matched[$name][strlen($cookie['path'])] = $cookie;
260
- }
261
- }
262
- }
263
- }
264
- }
265
- }
266
- foreach ($matched as $cookies) {
267
- krsort($cookies);
268
- $ret = array_merge($ret, $cookies);
269
- }
270
- if (!$asString) {
271
- return $ret;
272
- } else {
273
- $str = '';
274
- foreach ($ret as $c) {
275
- $str .= (empty($str)? '': '; ') . $c['name'] . '=' . $c['value'];
276
- }
277
- return $str;
278
- }
279
- }
280
-
281
- /**
282
- * Returns all cookies stored in a jar
283
- *
284
- * @return array
285
- */
286
- public function getAll()
287
- {
288
- $cookies = array();
289
- foreach (array_keys($this->cookies) as $domain) {
290
- foreach (array_keys($this->cookies[$domain]) as $path) {
291
- foreach ($this->cookies[$domain][$path] as $name => $cookie) {
292
- $cookies[] = $cookie;
293
- }
294
- }
295
- }
296
- return $cookies;
297
- }
298
-
299
- /**
300
- * Sets whether session cookies should be serialized when serializing the jar
301
- *
302
- * @param boolean $serialize serialize?
303
- */
304
- public function serializeSessionCookies($serialize)
305
- {
306
- $this->serializeSession = (bool)$serialize;
307
- }
308
-
309
- /**
310
- * Sets whether Public Suffix List should be used for restricting cookie-setting
311
- *
312
- * Without PSL {@link domainMatch()} will only prevent setting cookies for
313
- * top-level domains like '.com' or '.org'. However, it will not prevent
314
- * setting a cookie for '.co.uk' even though only third-level registrations
315
- * are possible in .uk domain.
316
- *
317
- * With the List it is possible to find the highest level at which a domain
318
- * may be registered for a particular top-level domain and consequently
319
- * prevent cookies set for '.co.uk' or '.msk.ru'. The same list is used by
320
- * Firefox, Chrome and Opera browsers to restrict cookie setting.
321
- *
322
- * Note that PSL is licensed differently to HTTP_Request2 package (refer to
323
- * the license information in public-suffix-list.php), so you can disable
324
- * its use if this is an issue for you.
325
- *
326
- * @param boolean $useList use the list?
327
- *
328
- * @link http://publicsuffix.org/learn/
329
- */
330
- public function usePublicSuffixList($useList)
331
- {
332
- $this->useList = (bool)$useList;
333
- }
334
-
335
- /**
336
- * Returns string representation of object
337
- *
338
- * @return string
339
- *
340
- * @see Serializable::serialize()
341
- */
342
- public function serialize()
343
- {
344
- $cookies = $this->getAll();
345
- if (!$this->serializeSession) {
346
- for ($i = count($cookies) - 1; $i >= 0; $i--) {
347
- if (empty($cookies[$i]['expires'])) {
348
- unset($cookies[$i]);
349
- }
350
- }
351
- }
352
- return serialize(array(
353
- 'cookies' => $cookies,
354
- 'serializeSession' => $this->serializeSession,
355
- 'useList' => $this->useList
356
- ));
357
- }
358
-
359
- /**
360
- * Constructs the object from serialized string
361
- *
362
- * @param string $serialized string representation
363
- *
364
- * @see Serializable::unserialize()
365
- */
366
- public function unserialize($serialized)
367
- {
368
- $data = unserialize($serialized);
369
- $now = $this->now();
370
- $this->serializeSessionCookies($data['serializeSession']);
371
- $this->usePublicSuffixList($data['useList']);
372
- foreach ($data['cookies'] as $cookie) {
373
- if (!empty($cookie['expires']) && $cookie['expires'] <= $now) {
374
- continue;
375
- }
376
- if (!isset($this->cookies[$cookie['domain']])) {
377
- $this->cookies[$cookie['domain']] = array();
378
- }
379
- if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
380
- $this->cookies[$cookie['domain']][$cookie['path']] = array();
381
- }
382
- $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
383
- }
384
- }
385
-
386
- /**
387
- * Checks whether a cookie domain matches a request host.
388
- *
389
- * The method is used by {@link store()} to check for whether a document
390
- * at given URL can set a cookie with a given domain attribute and by
391
- * {@link getMatching()} to find cookies matching the request URL.
392
- *
393
- * @param string $requestHost request host
394
- * @param string $cookieDomain cookie domain
395
- *
396
- * @return bool match success
397
- */
398
- public function domainMatch($requestHost, $cookieDomain)
399
- {
400
- if ($requestHost == $cookieDomain) {
401
- return true;
402
- }
403
- // IP address, we require exact match
404
- if (preg_match('/^(?:\d{1,3}\.){3}\d{1,3}$/', $requestHost)) {
405
- return false;
406
- }
407
- if ('.' != $cookieDomain[0]) {
408
- $cookieDomain = '.' . $cookieDomain;
409
- }
410
- // prevents setting cookies for '.com' and similar domains
411
- if (!$this->useList && substr_count($cookieDomain, '.') < 2
412
- || $this->useList && !self::getRegisteredDomain($cookieDomain)
413
- ) {
414
- return false;
415
- }
416
- return substr('.' . $requestHost, -strlen($cookieDomain)) == $cookieDomain;
417
- }
418
-
419
- /**
420
- * Removes subdomains to get the registered domain (the first after top-level)
421
- *
422
- * The method will check Public Suffix List to find out where top-level
423
- * domain ends and registered domain starts. It will remove domain parts
424
- * to the left of registered one.
425
- *
426
- * @param string $domain domain name
427
- *
428
- * @return string|bool registered domain, will return false if $domain is
429
- * either invalid or a TLD itself
430
- */
431
- public static function getRegisteredDomain($domain)
432
- {
433
- $domainParts = explode('.', ltrim($domain, '.'));
434
-
435
- // load the list if needed
436
- if (empty(self::$psl)) {
437
- $path = '@data_dir@' . DIRECTORY_SEPARATOR . 'HTTP_Request2';
438
- if (0 === strpos($path, '@' . 'data_dir@')) {
439
- $path = realpath(
440
- dirname(__FILE__) . DIRECTORY_SEPARATOR . '..'
441
- . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data'
442
- );
443
- }
444
- self::$psl = include_once $path . DIRECTORY_SEPARATOR . 'public-suffix-list.php';
445
- }
446
-
447
- if (!($result = self::checkDomainsList($domainParts, self::$psl))) {
448
- // known TLD, invalid domain name
449
- return false;
450
- }
451
-
452
- // unknown TLD
453
- if (!strpos($result, '.')) {
454
- // fallback to checking that domain "has at least two dots"
455
- if (2 > ($count = count($domainParts))) {
456
- return false;
457
- }
458
- return $domainParts[$count - 2] . '.' . $domainParts[$count - 1];
459
- }
460
- return $result;
461
- }
462
-
463
- /**
464
- * Recursive helper method for {@link getRegisteredDomain()}
465
- *
466
- * @param array $domainParts remaining domain parts
467
- * @param mixed $listNode node in {@link HTTP_Request2_CookieJar::$psl} to check
468
- *
469
- * @return string|null concatenated domain parts, null in case of error
470
- */
471
- protected static function checkDomainsList(array $domainParts, $listNode)
472
- {
473
- $sub = array_pop($domainParts);
474
- $result = null;
475
-
476
- if (!is_array($listNode) || is_null($sub)
477
- || array_key_exists('!' . $sub, $listNode)
478
- ) {
479
- return $sub;
480
-
481
- } elseif (array_key_exists($sub, $listNode)) {
482
- $result = self::checkDomainsList($domainParts, $listNode[$sub]);
483
-
484
- } elseif (array_key_exists('*', $listNode)) {
485
- $result = self::checkDomainsList($domainParts, $listNode['*']);
486
-
487
- } else {
488
- return $sub;
489
- }
490
-
491
- return (strlen($result) > 0) ? ($result . '.' . $sub) : null;
492
- }
493
- }
494
  ?>
1
+ <?php
2
+ /**
3
+ * Stores cookies and passes them between HTTP requests
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /** Class representing a HTTP request message */
22
+ require_once 'HTTP/Request2.php';
23
+
24
+ /**
25
+ * Stores cookies and passes them between HTTP requests
26
+ *
27
+ * @category HTTP
28
+ * @package HTTP_Request2
29
+ * @author Alexey Borzov <avb@php.net>
30
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
31
+ * @version Release: @package_version@
32
+ * @link http://pear.php.net/package/HTTP_Request2
33
+ */
34
+ class HTTP_Request2_CookieJar implements Serializable
35
+ {
36
+ /**
37
+ * Array of stored cookies
38
+ *
39
+ * The array is indexed by domain, path and cookie name
40
+ * .example.com
41
+ * /
42
+ * some_cookie => cookie data
43
+ * /subdir
44
+ * other_cookie => cookie data
45
+ * .example.org
46
+ * ...
47
+ *
48
+ * @var array
49
+ */
50
+ protected $cookies = array();
51
+
52
+ /**
53
+ * Whether session cookies should be serialized when serializing the jar
54
+ * @var bool
55
+ */
56
+ protected $serializeSession = false;
57
+
58
+ /**
59
+ * Whether Public Suffix List should be used for domain matching
60
+ * @var bool
61
+ */
62
+ protected $useList = true;
63
+
64
+ /**
65
+ * Array with Public Suffix List data
66
+ * @var array
67
+ * @link http://publicsuffix.org/
68
+ */
69
+ protected static $psl = array();
70
+
71
+ /**
72
+ * Class constructor, sets various options
73
+ *
74
+ * @param bool $serializeSessionCookies Controls serializing session cookies,
75
+ * see {@link serializeSessionCookies()}
76
+ * @param bool $usePublicSuffixList Controls using Public Suffix List,
77
+ * see {@link usePublicSuffixList()}
78
+ */
79
+ public function __construct(
80
+ $serializeSessionCookies = false, $usePublicSuffixList = true
81
+ ) {
82
+ $this->serializeSessionCookies($serializeSessionCookies);
83
+ $this->usePublicSuffixList($usePublicSuffixList);
84
+ }
85
+
86
+ /**
87
+ * Returns current time formatted in ISO-8601 at UTC timezone
88
+ *
89
+ * @return string
90
+ */
91
+ protected function now()
92
+ {
93
+ $dt = new DateTime();
94
+ $dt->setTimezone(new DateTimeZone('UTC'));
95
+ return $dt->format(DateTime::ISO8601);
96
+ }
97
+
98
+ /**
99
+ * Checks cookie array for correctness, possibly updating its 'domain', 'path' and 'expires' fields
100
+ *
101
+ * The checks are as follows:
102
+ * - cookie array should contain 'name' and 'value' fields;
103
+ * - name and value should not contain disallowed symbols;
104
+ * - 'expires' should be either empty parseable by DateTime;
105
+ * - 'domain' and 'path' should be either not empty or an URL where
106
+ * cookie was set should be provided.
107
+ * - if $setter is provided, then document at that URL should be allowed
108
+ * to set a cookie for that 'domain'. If $setter is not provided,
109
+ * then no domain checks will be made.
110
+ *
111
+ * 'expires' field will be converted to ISO8601 format from COOKIE format,
112
+ * 'domain' and 'path' will be set from setter URL if empty.
113
+ *
114
+ * @param array $cookie cookie data, as returned by
115
+ * {@link HTTP_Request2_Response::getCookies()}
116
+ * @param Net_URL2 $setter URL of the document that sent Set-Cookie header
117
+ *
118
+ * @return array Updated cookie array
119
+ * @throws HTTP_Request2_LogicException
120
+ * @throws HTTP_Request2_MessageException
121
+ */
122
+ protected function checkAndUpdateFields(array $cookie, Net_URL2 $setter = null)
123
+ {
124
+ if ($missing = array_diff(array('name', 'value'), array_keys($cookie))) {
125
+ throw new HTTP_Request2_LogicException(
126
+ "Cookie array should contain 'name' and 'value' fields",
127
+ HTTP_Request2_Exception::MISSING_VALUE
128
+ );
129
+ }
130
+ if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['name'])) {
131
+ throw new HTTP_Request2_LogicException(
132
+ "Invalid cookie name: '{$cookie['name']}'",
133
+ HTTP_Request2_Exception::INVALID_ARGUMENT
134
+ );
135
+ }
136
+ if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['value'])) {
137
+ throw new HTTP_Request2_LogicException(
138
+ "Invalid cookie value: '{$cookie['value']}'",
139
+ HTTP_Request2_Exception::INVALID_ARGUMENT
140
+ );
141
+ }
142
+ $cookie += array('domain' => '', 'path' => '', 'expires' => null, 'secure' => false);
143
+
144
+ // Need ISO-8601 date @ UTC timezone
145
+ if (!empty($cookie['expires'])
146
+ && !preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+0000$/', $cookie['expires'])
147
+ ) {
148
+ try {
149
+ $dt = new DateTime($cookie['expires']);
150
+ $dt->setTimezone(new DateTimeZone('UTC'));
151
+ $cookie['expires'] = $dt->format(DateTime::ISO8601);
152
+ } catch (Exception $e) {
153
+ throw new HTTP_Request2_LogicException($e->getMessage());
154
+ }
155
+ }
156
+
157
+ if (empty($cookie['domain']) || empty($cookie['path'])) {
158
+ if (!$setter) {
159
+ throw new HTTP_Request2_LogicException(
160
+ 'Cookie misses domain and/or path component, cookie setter URL needed',
161
+ HTTP_Request2_Exception::MISSING_VALUE
162
+ );
163
+ }
164
+ if (empty($cookie['domain'])) {
165
+ if ($host = $setter->getHost()) {
166
+ $cookie['domain'] = $host;
167
+ } else {
168
+ throw new HTTP_Request2_LogicException(
169
+ 'Setter URL does not contain host part, can\'t set cookie domain',
170
+ HTTP_Request2_Exception::MISSING_VALUE
171
+ );
172
+ }
173
+ }
174
+ if (empty($cookie['path'])) {
175
+ $path = $setter->getPath();
176
+ $cookie['path'] = empty($path)? '/': substr($path, 0, strrpos($path, '/') + 1);
177
+ }
178
+ }
179
+
180
+ if ($setter && !$this->domainMatch($setter->getHost(), $cookie['domain'])) {
181
+ throw new HTTP_Request2_MessageException(
182
+ "Domain " . $setter->getHost() . " cannot set cookies for "
183
+ . $cookie['domain']
184
+ );
185
+ }
186
+
187
+ return $cookie;
188
+ }
189
+
190
+ /**
191
+ * Stores a cookie in the jar
192
+ *
193
+ * @param array $cookie cookie data, as returned by
194
+ * {@link HTTP_Request2_Response::getCookies()}
195
+ * @param Net_URL2 $setter URL of the document that sent Set-Cookie header
196
+ *
197
+ * @throws HTTP_Request2_Exception
198
+ */
199
+ public function store(array $cookie, Net_URL2 $setter = null)
200
+ {
201
+ $cookie = $this->checkAndUpdateFields($cookie, $setter);
202
+
203
+ if (strlen($cookie['value'])
204
+ && (is_null($cookie['expires']) || $cookie['expires'] > $this->now())
205
+ ) {
206
+ if (!isset($this->cookies[$cookie['domain']])) {
207
+ $this->cookies[$cookie['domain']] = array();
208
+ }
209
+ if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
210
+ $this->cookies[$cookie['domain']][$cookie['path']] = array();
211
+ }
212
+ $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
213
+
214
+ } elseif (isset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']])) {
215
+ unset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']]);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Adds cookies set in HTTP response to the jar
221
+ *
222
+ * @param HTTP_Request2_Response $response HTTP response message
223
+ * @param Net_URL2 $setter original request URL, needed for
224
+ * setting default domain/path
225
+ */
226
+ public function addCookiesFromResponse(HTTP_Request2_Response $response, Net_URL2 $setter)
227
+ {
228
+ foreach ($response->getCookies() as $cookie) {
229
+ $this->store($cookie, $setter);
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Returns all cookies matching a given request URL
235
+ *
236
+ * The following checks are made:
237
+ * - cookie domain should match request host
238
+ * - cookie path should be a prefix for request path
239
+ * - 'secure' cookies will only be sent for HTTPS requests
240
+ *
241
+ * @param Net_URL2 $url Request url
242
+ * @param bool $asString Whether to return cookies as string for "Cookie: " header
243
+ *
244
+ * @return array|string Matching cookies
245
+ */
246
+ public function getMatching(Net_URL2 $url, $asString = false)
247
+ {
248
+ $host = $url->getHost();
249
+ $path = $url->getPath();
250
+ $secure = 0 == strcasecmp($url->getScheme(), 'https');
251
+
252
+ $matched = $ret = array();
253
+ foreach (array_keys($this->cookies) as $domain) {
254
+ if ($this->domainMatch($host, $domain)) {
255
+ foreach (array_keys($this->cookies[$domain]) as $cPath) {
256
+ if (0 === strpos($path, $cPath)) {
257
+ foreach ($this->cookies[$domain][$cPath] as $name => $cookie) {
258
+ if (!$cookie['secure'] || $secure) {
259
+ $matched[$name][strlen($cookie['path'])] = $cookie;
260
+ }
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ foreach ($matched as $cookies) {
267
+ krsort($cookies);
268
+ $ret = array_merge($ret, $cookies);
269
+ }
270
+ if (!$asString) {
271
+ return $ret;
272
+ } else {
273
+ $str = '';
274
+ foreach ($ret as $c) {
275
+ $str .= (empty($str)? '': '; ') . $c['name'] . '=' . $c['value'];
276
+ }
277
+ return $str;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Returns all cookies stored in a jar
283
+ *
284
+ * @return array
285
+ */
286
+ public function getAll()
287
+ {
288
+ $cookies = array();
289
+ foreach (array_keys($this->cookies) as $domain) {
290
+ foreach (array_keys($this->cookies[$domain]) as $path) {
291
+ foreach ($this->cookies[$domain][$path] as $name => $cookie) {
292
+ $cookies[] = $cookie;
293
+ }
294
+ }
295
+ }
296
+ return $cookies;
297
+ }
298
+
299
+ /**
300
+ * Sets whether session cookies should be serialized when serializing the jar
301
+ *
302
+ * @param boolean $serialize serialize?
303
+ */
304
+ public function serializeSessionCookies($serialize)
305
+ {
306
+ $this->serializeSession = (bool)$serialize;
307
+ }
308
+
309
+ /**
310
+ * Sets whether Public Suffix List should be used for restricting cookie-setting
311
+ *
312
+ * Without PSL {@link domainMatch()} will only prevent setting cookies for
313
+ * top-level domains like '.com' or '.org'. However, it will not prevent
314
+ * setting a cookie for '.co.uk' even though only third-level registrations
315
+ * are possible in .uk domain.
316
+ *
317
+ * With the List it is possible to find the highest level at which a domain
318
+ * may be registered for a particular top-level domain and consequently
319
+ * prevent cookies set for '.co.uk' or '.msk.ru'. The same list is used by
320
+ * Firefox, Chrome and Opera browsers to restrict cookie setting.
321
+ *
322
+ * Note that PSL is licensed differently to HTTP_Request2 package (refer to
323
+ * the license information in public-suffix-list.php), so you can disable
324
+ * its use if this is an issue for you.
325
+ *
326
+ * @param boolean $useList use the list?
327
+ *
328
+ * @link http://publicsuffix.org/learn/
329
+ */
330
+ public function usePublicSuffixList($useList)
331
+ {
332
+ $this->useList = (bool)$useList;
333
+ }
334
+
335
+ /**
336
+ * Returns string representation of object
337
+ *
338
+ * @return string
339
+ *
340
+ * @see Serializable::serialize()
341
+ */
342
+ public function serialize()
343
+ {
344
+ $cookies = $this->getAll();
345
+ if (!$this->serializeSession) {
346
+ for ($i = count($cookies) - 1; $i >= 0; $i--) {
347
+ if (empty($cookies[$i]['expires'])) {
348
+ unset($cookies[$i]);
349
+ }
350
+ }
351
+ }
352
+ return serialize(array(
353
+ 'cookies' => $cookies,
354
+ 'serializeSession' => $this->serializeSession,
355
+ 'useList' => $this->useList
356
+ ));
357
+ }
358
+
359
+ /**
360
+ * Constructs the object from serialized string
361
+ *
362
+ * @param string $serialized string representation
363
+ *
364
+ * @see Serializable::unserialize()
365
+ */
366
+ public function unserialize($serialized)
367
+ {
368
+ $data = unserialize($serialized);
369
+ $now = $this->now();
370
+ $this->serializeSessionCookies($data['serializeSession']);
371
+ $this->usePublicSuffixList($data['useList']);
372
+ foreach ($data['cookies'] as $cookie) {
373
+ if (!empty($cookie['expires']) && $cookie['expires'] <= $now) {
374
+ continue;
375
+ }
376
+ if (!isset($this->cookies[$cookie['domain']])) {
377
+ $this->cookies[$cookie['domain']] = array();
378
+ }
379
+ if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) {
380
+ $this->cookies[$cookie['domain']][$cookie['path']] = array();
381
+ }
382
+ $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Checks whether a cookie domain matches a request host.
388
+ *
389
+ * The method is used by {@link store()} to check for whether a document
390
+ * at given URL can set a cookie with a given domain attribute and by
391
+ * {@link getMatching()} to find cookies matching the request URL.
392
+ *
393
+ * @param string $requestHost request host
394
+ * @param string $cookieDomain cookie domain
395
+ *
396
+ * @return bool match success
397
+ */
398
+ public function domainMatch($requestHost, $cookieDomain)
399
+ {
400
+ if ($requestHost == $cookieDomain) {
401
+ return true;
402
+ }
403
+ // IP address, we require exact match
404
+ if (preg_match('/^(?:\d{1,3}\.){3}\d{1,3}$/', $requestHost)) {
405
+ return false;
406
+ }
407
+ if ('.' != $cookieDomain[0]) {
408
+ $cookieDomain = '.' . $cookieDomain;
409
+ }
410
+ // prevents setting cookies for '.com' and similar domains
411
+ if (!$this->useList && substr_count($cookieDomain, '.') < 2
412
+ || $this->useList && !self::getRegisteredDomain($cookieDomain)
413
+ ) {
414
+ return false;
415
+ }
416
+ return substr('.' . $requestHost, -strlen($cookieDomain)) == $cookieDomain;
417
+ }
418
+
419
+ /**
420
+ * Removes subdomains to get the registered domain (the first after top-level)
421
+ *
422
+ * The method will check Public Suffix List to find out where top-level
423
+ * domain ends and registered domain starts. It will remove domain parts
424
+ * to the left of registered one.
425
+ *
426
+ * @param string $domain domain name
427
+ *
428
+ * @return string|bool registered domain, will return false if $domain is
429
+ * either invalid or a TLD itself
430
+ */
431
+ public static function getRegisteredDomain($domain)
432
+ {
433
+ $domainParts = explode('.', ltrim($domain, '.'));
434
+
435
+ // load the list if needed
436
+ if (empty(self::$psl)) {
437
+ $path = '@data_dir@' . DIRECTORY_SEPARATOR . 'HTTP_Request2';
438
+ if (0 === strpos($path, '@' . 'data_dir@')) {
439
+ $path = realpath(
440
+ dirname(__FILE__) . DIRECTORY_SEPARATOR . '..'
441
+ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data'
442
+ );
443
+ }
444
+ self::$psl = include_once $path . DIRECTORY_SEPARATOR . 'public-suffix-list.php';
445
+ }
446
+
447
+ if (!($result = self::checkDomainsList($domainParts, self::$psl))) {
448
+ // known TLD, invalid domain name
449
+ return false;
450
+ }
451
+
452
+ // unknown TLD
453
+ if (!strpos($result, '.')) {
454
+ // fallback to checking that domain "has at least two dots"
455
+ if (2 > ($count = count($domainParts))) {
456
+ return false;
457
+ }
458
+ return $domainParts[$count - 2] . '.' . $domainParts[$count - 1];
459
+ }
460
+ return $result;
461
+ }
462
+
463
+ /**
464
+ * Recursive helper method for {@link getRegisteredDomain()}
465
+ *
466
+ * @param array $domainParts remaining domain parts
467
+ * @param mixed $listNode node in {@link HTTP_Request2_CookieJar::$psl} to check
468
+ *
469
+ * @return string|null concatenated domain parts, null in case of error
470
+ */
471
+ protected static function checkDomainsList(array $domainParts, $listNode)
472
+ {
473
+ $sub = array_pop($domainParts);
474
+ $result = null;
475
+
476
+ if (!is_array($listNode) || is_null($sub)
477
+ || array_key_exists('!' . $sub, $listNode)
478
+ ) {
479
+ return $sub;
480
+
481
+ } elseif (array_key_exists($sub, $listNode)) {
482
+ $result = self::checkDomainsList($domainParts, $listNode[$sub]);
483
+
484
+ } elseif (array_key_exists('*', $listNode)) {
485
+ $result = self::checkDomainsList($domainParts, $listNode['*']);
486
+
487
+ } else {
488
+ return $sub;
489
+ }
490
+
491
+ return (strlen($result) > 0) ? ($result . '.' . $sub) : null;
492
+ }
493
+ }
494
  ?>
vendor/PEAR/HTTP/Request2/Exception.php CHANGED
@@ -1,160 +1,160 @@
1
- <?php
2
- /**
3
- * Exception classes for HTTP_Request2 package
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * Base class for exceptions in PEAR
23
- */
24
- require_once 'PEAR/Exception.php';
25
-
26
- /**
27
- * Base exception class for HTTP_Request2 package
28
- *
29
- * @category HTTP
30
- * @package HTTP_Request2
31
- * @author Alexey Borzov <avb@php.net>
32
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
33
- * @version Release: 2.2.1
34
- * @link http://pear.php.net/package/HTTP_Request2
35
- * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=132
36
- */
37
- class HTTP_Request2_Exception extends PEAR_Exception
38
- {
39
- /** An invalid argument was passed to a method */
40
- const INVALID_ARGUMENT = 1;
41
- /** Some required value was not available */
42
- const MISSING_VALUE = 2;
43
- /** Request cannot be processed due to errors in PHP configuration */
44
- const MISCONFIGURATION = 3;
45
- /** Error reading the local file */
46
- const READ_ERROR = 4;
47
-
48
- /** Server returned a response that does not conform to HTTP protocol */
49
- const MALFORMED_RESPONSE = 10;
50
- /** Failure decoding Content-Encoding or Transfer-Encoding of response */
51
- const DECODE_ERROR = 20;
52
- /** Operation timed out */
53
- const TIMEOUT = 30;
54
- /** Number of redirects exceeded 'max_redirects' configuration parameter */
55
- const TOO_MANY_REDIRECTS = 40;
56
- /** Redirect to a protocol other than http(s):// */
57
- const NON_HTTP_REDIRECT = 50;
58
-
59
- /**
60
- * Native error code
61
- * @var int
62
- */
63
- private $_nativeCode;
64
-
65
- /**
66
- * Constructor, can set package error code and native error code
67
- *
68
- * @param string $message exception message
69
- * @param int $code package error code, one of class constants
70
- * @param int $nativeCode error code from underlying PHP extension
71
- */
72
- public function __construct($message = null, $code = null, $nativeCode = null)
73
- {
74
- parent::__construct($message, $code);
75
- $this->_nativeCode = $nativeCode;
76
- }
77
-
78
- /**
79
- * Returns error code produced by underlying PHP extension
80
- *
81
- * For Socket Adapter this may contain error number returned by
82
- * stream_socket_client(), for Curl Adapter this will contain error number
83
- * returned by curl_errno()
84
- *
85
- * @return integer
86
- */
87
- public function getNativeCode()
88
- {
89
- return $this->_nativeCode;
90
- }
91
- }
92
-
93
- /**
94
- * Exception thrown in case of missing features
95
- *
96
- * @category HTTP
97
- * @package HTTP_Request2
98
- * @author Alexey Borzov <avb@php.net>
99
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
100
- * @version Release: 2.2.1
101
- * @link http://pear.php.net/package/HTTP_Request2
102
- */
103
- class HTTP_Request2_NotImplementedException extends HTTP_Request2_Exception
104
- {
105
- }
106
-
107
- /**
108
- * Exception that represents error in the program logic
109
- *
110
- * This exception usually implies a programmer's error, like passing invalid
111
- * data to methods or trying to use PHP extensions that weren't installed or
112
- * enabled. Usually exceptions of this kind will be thrown before request even
113
- * starts.
114
- *
115
- * The exception will usually contain a package error code.
116
- *
117
- * @category HTTP
118
- * @package HTTP_Request2
119
- * @author Alexey Borzov <avb@php.net>
120
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
121
- * @version Release: 2.2.1
122
- * @link http://pear.php.net/package/HTTP_Request2
123
- */
124
- class HTTP_Request2_LogicException extends HTTP_Request2_Exception
125
- {
126
- }
127
-
128
- /**
129
- * Exception thrown when connection to a web or proxy server fails
130
- *
131
- * The exception will not contain a package error code, but will contain
132
- * native error code, as returned by stream_socket_client() or curl_errno().
133
- *
134
- * @category HTTP
135
- * @package HTTP_Request2
136
- * @author Alexey Borzov <avb@php.net>
137
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
138
- * @version Release: 2.2.1
139
- * @link http://pear.php.net/package/HTTP_Request2
140
- */
141
- class HTTP_Request2_ConnectionException extends HTTP_Request2_Exception
142
- {
143
- }
144
-
145
- /**
146
- * Exception thrown when sending or receiving HTTP message fails
147
- *
148
- * The exception may contain both package error code and native error code.
149
- *
150
- * @category HTTP
151
- * @package HTTP_Request2
152
- * @author Alexey Borzov <avb@php.net>
153
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
154
- * @version Release: 2.2.1
155
- * @link http://pear.php.net/package/HTTP_Request2
156
- */
157
- class HTTP_Request2_MessageException extends HTTP_Request2_Exception
158
- {
159
- }
160
  ?>
1
+ <?php
2
+ /**
3
+ * Exception classes for HTTP_Request2 package
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * Base class for exceptions in PEAR
23
+ */
24
+ require_once 'PEAR/Exception.php';
25
+
26
+ /**
27
+ * Base exception class for HTTP_Request2 package
28
+ *
29
+ * @category HTTP
30
+ * @package HTTP_Request2
31
+ * @author Alexey Borzov <avb@php.net>
32
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
33
+ * @version Release: 2.2.1
34
+ * @link http://pear.php.net/package/HTTP_Request2
35
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=132
36
+ */
37
+ class HTTP_Request2_Exception extends PEAR_Exception
38
+ {
39
+ /** An invalid argument was passed to a method */
40
+ const INVALID_ARGUMENT = 1;
41
+ /** Some required value was not available */
42
+ const MISSING_VALUE = 2;
43
+ /** Request cannot be processed due to errors in PHP configuration */
44
+ const MISCONFIGURATION = 3;
45
+ /** Error reading the local file */
46
+ const READ_ERROR = 4;
47
+
48
+ /** Server returned a response that does not conform to HTTP protocol */
49
+ const MALFORMED_RESPONSE = 10;
50
+ /** Failure decoding Content-Encoding or Transfer-Encoding of response */
51
+ const DECODE_ERROR = 20;
52
+ /** Operation timed out */
53
+ const TIMEOUT = 30;
54
+ /** Number of redirects exceeded 'max_redirects' configuration parameter */
55
+ const TOO_MANY_REDIRECTS = 40;
56
+ /** Redirect to a protocol other than http(s):// */
57
+ const NON_HTTP_REDIRECT = 50;
58
+
59
+ /**
60
+ * Native error code
61
+ * @var int
62
+ */
63
+ private $_nativeCode;
64
+
65
+ /**
66
+ * Constructor, can set package error code and native error code
67
+ *
68
+ * @param string $message exception message
69
+ * @param int $code package error code, one of class constants
70
+ * @param int $nativeCode error code from underlying PHP extension
71
+ */
72
+ public function __construct($message = null, $code = null, $nativeCode = null)
73
+ {
74
+ parent::__construct($message, $code);
75
+ $this->_nativeCode = $nativeCode;
76
+ }
77
+
78
+ /**
79
+ * Returns error code produced by underlying PHP extension
80
+ *
81
+ * For Socket Adapter this may contain error number returned by
82
+ * stream_socket_client(), for Curl Adapter this will contain error number
83
+ * returned by curl_errno()
84
+ *
85
+ * @return integer
86
+ */
87
+ public function getNativeCode()
88
+ {
89
+ return $this->_nativeCode;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Exception thrown in case of missing features
95
+ *
96
+ * @category HTTP
97
+ * @package HTTP_Request2
98
+ * @author Alexey Borzov <avb@php.net>
99
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
100
+ * @version Release: 2.2.1
101
+ * @link http://pear.php.net/package/HTTP_Request2
102
+ */
103
+ class HTTP_Request2_NotImplementedException extends HTTP_Request2_Exception
104
+ {
105
+ }
106
+
107
+ /**
108
+ * Exception that represents error in the program logic
109
+ *
110
+ * This exception usually implies a programmer's error, like passing invalid
111
+ * data to methods or trying to use PHP extensions that weren't installed or
112
+ * enabled. Usually exceptions of this kind will be thrown before request even
113
+ * starts.
114
+ *
115
+ * The exception will usually contain a package error code.
116
+ *
117
+ * @category HTTP
118
+ * @package HTTP_Request2
119
+ * @author Alexey Borzov <avb@php.net>
120
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
121
+ * @version Release: 2.2.1
122
+ * @link http://pear.php.net/package/HTTP_Request2
123
+ */
124
+ class HTTP_Request2_LogicException extends HTTP_Request2_Exception
125
+ {
126
+ }
127
+
128
+ /**
129
+ * Exception thrown when connection to a web or proxy server fails
130
+ *
131
+ * The exception will not contain a package error code, but will contain
132
+ * native error code, as returned by stream_socket_client() or curl_errno().
133
+ *
134
+ * @category HTTP
135
+ * @package HTTP_Request2
136
+ * @author Alexey Borzov <avb@php.net>
137
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
138
+ * @version Release: 2.2.1
139
+ * @link http://pear.php.net/package/HTTP_Request2
140
+ */
141
+ class HTTP_Request2_ConnectionException extends HTTP_Request2_Exception
142
+ {
143
+ }
144
+
145
+ /**
146
+ * Exception thrown when sending or receiving HTTP message fails
147
+ *
148
+ * The exception may contain both package error code and native error code.
149
+ *
150
+ * @category HTTP
151
+ * @package HTTP_Request2
152
+ * @author Alexey Borzov <avb@php.net>
153
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
154
+ * @version Release: 2.2.1
155
+ * @link http://pear.php.net/package/HTTP_Request2
156
+ */
157
+ class HTTP_Request2_MessageException extends HTTP_Request2_Exception
158
+ {
159
+ }
160
  ?>
vendor/PEAR/HTTP/Request2/MultipartBody.php CHANGED
@@ -1,268 +1,268 @@
1
- <?php
2
- /**
3
- * Helper class for building multipart/form-data request body
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /** Exception class for HTTP_Request2 package */
22
- require_once 'HTTP/Request2/Exception.php';
23
-
24
- /**
25
- * Class for building multipart/form-data request body
26
- *
27
- * The class helps to reduce memory consumption by streaming large file uploads
28
- * from disk, it also allows monitoring of upload progress (see request #7630)
29
- *
30
- * @category HTTP
31
- * @package HTTP_Request2
32
- * @author Alexey Borzov <avb@php.net>
33
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
34
- * @version Release: 2.2.1
35
- * @link http://pear.php.net/package/HTTP_Request2
36
- * @link http://tools.ietf.org/html/rfc1867
37
- */
38
- class HTTP_Request2_MultipartBody
39
- {
40
- /**
41
- * MIME boundary
42
- * @var string
43
- */
44
- private $_boundary;
45
-
46
- /**
47
- * Form parameters added via {@link HTTP_Request2::addPostParameter()}
48
- * @var array
49
- */
50
- private $_params = array();
51
-
52
- /**
53
- * File uploads added via {@link HTTP_Request2::addUpload()}
54
- * @var array
55
- */
56
- private $_uploads = array();
57
-
58
- /**
59
- * Header for parts with parameters
60
- * @var string
61
- */
62
- private $_headerParam = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n";
63
-
64
- /**
65
- * Header for parts with uploads
66
- * @var string
67
- */
68
- private $_headerUpload = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n";
69
-
70
- /**
71
- * Current position in parameter and upload arrays
72
- *
73
- * First number is index of "current" part, second number is position within
74
- * "current" part
75
- *
76
- * @var array
77
- */
78
- private $_pos = array(0, 0);
79
-
80
-
81
- /**
82
- * Constructor. Sets the arrays with POST data.
83
- *
84
- * @param array $params values of form fields set via
85
- * {@link HTTP_Request2::addPostParameter()}
86
- * @param array $uploads file uploads set via
87
- * {@link HTTP_Request2::addUpload()}
88
- * @param bool $useBrackets whether to append brackets to array variable names
89
- */
90
- public function __construct(array $params, array $uploads, $useBrackets = true)
91
- {
92
- $this->_params = self::_flattenArray('', $params, $useBrackets);
93
- foreach ($uploads as $fieldName => $f) {
94
- if (!is_array($f['fp'])) {
95
- $this->_uploads[] = $f + array('name' => $fieldName);
96
- } else {
97
- for ($i = 0; $i < count($f['fp']); $i++) {
98
- $upload = array(
99
- 'name' => ($useBrackets? $fieldName . '[' . $i . ']': $fieldName)
100
- );
101
- foreach (array('fp', 'filename', 'size', 'type') as $key) {
102
- $upload[$key] = $f[$key][$i];
103
- }
104
- $this->_uploads[] = $upload;
105
- }
106
- }
107
- }
108
- }
109
-
110
- /**
111
- * Returns the length of the body to use in Content-Length header
112
- *
113
- * @return integer
114
- */
115
- public function getLength()
116
- {
117
- $boundaryLength = strlen($this->getBoundary());
118
- $headerParamLength = strlen($this->_headerParam) - 4 + $boundaryLength;
119
- $headerUploadLength = strlen($this->_headerUpload) - 8 + $boundaryLength;
120
- $length = $boundaryLength + 6;
121
- foreach ($this->_params as $p) {
122
- $length += $headerParamLength + strlen($p[0]) + strlen($p[1]) + 2;
123
- }
124
- foreach ($this->_uploads as $u) {
125
- $length += $headerUploadLength + strlen($u['name']) + strlen($u['type']) +
126
- strlen($u['filename']) + $u['size'] + 2;
127
- }
128
- return $length;
129
- }
130
-
131
- /**
132
- * Returns the boundary to use in Content-Type header
133
- *
134
- * @return string
135
- */
136
- public function getBoundary()
137
- {
138
- if (empty($this->_boundary)) {
139
- $this->_boundary = '--' . md5('PEAR-HTTP_Request2-' . microtime());
140
- }
141
- return $this->_boundary;
142
- }
143
-
144
- /**
145
- * Returns next chunk of request body
146
- *
147
- * @param integer $length Number of bytes to read
148
- *
149
- * @return string Up to $length bytes of data, empty string if at end
150
- * @throws HTTP_Request2_LogicException
151
- */
152
- public function read($length)
153
- {
154
- $ret = '';
155
- $boundary = $this->getBoundary();
156
- $paramCount = count($this->_params);
157
- $uploadCount = count($this->_uploads);
158
- while ($length > 0 && $this->_pos[0] <= $paramCount + $uploadCount) {
159
- $oldLength = $length;
160
- if ($this->_pos[0] < $paramCount) {
161
- $param = sprintf(
162
- $this->_headerParam, $boundary, $this->_params[$this->_pos[0]][0]
163
- ) . $this->_params[$this->_pos[0]][1] . "\r\n";
164
- $ret .= substr($param, $this->_pos[1], $length);
165
- $length -= min(strlen($param) - $this->_pos[1], $length);
166
-
167
- } elseif ($this->_pos[0] < $paramCount + $uploadCount) {
168
- $pos = $this->_pos[0] - $paramCount;
169
- $header = sprintf(
170
- $this->_headerUpload, $boundary, $this->_uploads[$pos]['name'],
171
- $this->_uploads[$pos]['filename'], $this->_uploads[$pos]['type']
172
- );
173
- if ($this->_pos[1] < strlen($header)) {
174
- $ret .= substr($header, $this->_pos[1], $length);
175
- $length -= min(strlen($header) - $this->_pos[1], $length);
176
- }
177
- $filePos = max(0, $this->_pos[1] - strlen($header));
178
- if ($filePos < $this->_uploads[$pos]['size']) {
179
- while ($length > 0 && !feof($this->_uploads[$pos]['fp'])) {
180
- if (false === ($chunk = fread($this->_uploads[$pos]['fp'], $length))) {
181
- throw new HTTP_Request2_LogicException(
182
- 'Failed reading file upload', HTTP_Request2_Exception::READ_ERROR
183
- );
184
- }
185
- $ret .= $chunk;
186
- $length -= strlen($chunk);
187
- }
188
- }
189
- if ($length > 0) {
190
- $start = $this->_pos[1] + ($oldLength - $length) -
191
- strlen($header) - $this->_uploads[$pos]['size'];
192
- $ret .= substr("\r\n", $start, $length);
193
- $length -= min(2 - $start, $length);
194
- }
195
-
196
- } else {
197
- $closing = '--' . $boundary . "--\r\n";
198
- $ret .= substr($closing, $this->_pos[1], $length);
199
- $length -= min(strlen($closing) - $this->_pos[1], $length);
200
- }
201
- if ($length > 0) {
202
- $this->_pos = array($this->_pos[0] + 1, 0);
203
- } else {
204
- $this->_pos[1] += $oldLength;
205
- }
206
- }
207
- return $ret;
208
- }
209
-
210
- /**
211
- * Sets the current position to the start of the body
212
- *
213
- * This allows reusing the same body in another request
214
- */
215
- public function rewind()
216
- {
217
- $this->_pos = array(0, 0);
218
- foreach ($this->_uploads as $u) {
219
- rewind($u['fp']);
220
- }
221
- }
222
-
223
- /**
224
- * Returns the body as string
225
- *
226
- * Note that it reads all file uploads into memory so it is a good idea not
227
- * to use this method with large file uploads and rely on read() instead.
228
- *
229
- * @return string
230
- */
231
- public function __toString()
232
- {
233
- $this->rewind();
234
- return $this->read($this->getLength());
235
- }
236
-
237
-
238
- /**
239
- * Helper function to change the (probably multidimensional) associative array
240
- * into the simple one.
241
- *
242
- * @param string $name name for item
243
- * @param mixed $values item's values
244
- * @param bool $useBrackets whether to append [] to array variables' names
245
- *
246
- * @return array array with the following items: array('item name', 'item value');
247
- */
248
- private static function _flattenArray($name, $values, $useBrackets)
249
- {
250
- if (!is_array($values)) {
251
- return array(array($name, $values));
252
- } else {
253
- $ret = array();
254
- foreach ($values as $k => $v) {
255
- if (empty($name)) {
256
- $newName = $k;
257
- } elseif ($useBrackets) {
258
- $newName = $name . '[' . $k . ']';
259
- } else {
260
- $newName = $name;
261
- }
262
- $ret = array_merge($ret, self::_flattenArray($newName, $v, $useBrackets));
263
- }
264
- return $ret;
265
- }
266
- }
267
- }
268
- ?>
1
+ <?php
2
+ /**
3
+ * Helper class for building multipart/form-data request body
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /** Exception class for HTTP_Request2 package */
22
+ require_once 'HTTP/Request2/Exception.php';
23
+
24
+ /**
25
+ * Class for building multipart/form-data request body
26
+ *
27
+ * The class helps to reduce memory consumption by streaming large file uploads
28
+ * from disk, it also allows monitoring of upload progress (see request #7630)
29
+ *
30
+ * @category HTTP
31
+ * @package HTTP_Request2
32
+ * @author Alexey Borzov <avb@php.net>
33
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
34
+ * @version Release: 2.2.1
35
+ * @link http://pear.php.net/package/HTTP_Request2
36
+ * @link http://tools.ietf.org/html/rfc1867
37
+ */
38
+ class HTTP_Request2_MultipartBody
39
+ {
40
+ /**
41
+ * MIME boundary
42
+ * @var string
43
+ */
44
+ private $_boundary;
45
+
46
+ /**
47
+ * Form parameters added via {@link HTTP_Request2::addPostParameter()}
48
+ * @var array
49
+ */
50
+ private $_params = array();
51
+
52
+ /**
53
+ * File uploads added via {@link HTTP_Request2::addUpload()}
54
+ * @var array
55
+ */
56
+ private $_uploads = array();
57
+
58
+ /**
59
+ * Header for parts with parameters
60
+ * @var string
61
+ */
62
+ private $_headerParam = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n";
63
+
64
+ /**
65
+ * Header for parts with uploads
66
+ * @var string
67
+ */
68
+ private $_headerUpload = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n";
69
+
70
+ /**
71
+ * Current position in parameter and upload arrays
72
+ *
73
+ * First number is index of "current" part, second number is position within
74
+ * "current" part
75
+ *
76
+ * @var array
77
+ */
78
+ private $_pos = array(0, 0);
79
+
80
+
81
+ /**
82
+ * Constructor. Sets the arrays with POST data.
83
+ *
84
+ * @param array $params values of form fields set via
85
+ * {@link HTTP_Request2::addPostParameter()}
86
+ * @param array $uploads file uploads set via
87
+ * {@link HTTP_Request2::addUpload()}
88
+ * @param bool $useBrackets whether to append brackets to array variable names
89
+ */
90
+ public function __construct(array $params, array $uploads, $useBrackets = true)
91
+ {
92
+ $this->_params = self::_flattenArray('', $params, $useBrackets);
93
+ foreach ($uploads as $fieldName => $f) {
94
+ if (!is_array($f['fp'])) {
95
+ $this->_uploads[] = $f + array('name' => $fieldName);
96
+ } else {
97
+ for ($i = 0; $i < count($f['fp']); $i++) {
98
+ $upload = array(
99
+ 'name' => ($useBrackets? $fieldName . '[' . $i . ']': $fieldName)
100
+ );
101
+ foreach (array('fp', 'filename', 'size', 'type') as $key) {
102
+ $upload[$key] = $f[$key][$i];
103
+ }
104
+ $this->_uploads[] = $upload;
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Returns the length of the body to use in Content-Length header
112
+ *
113
+ * @return integer
114
+ */
115
+ public function getLength()
116
+ {
117
+ $boundaryLength = strlen($this->getBoundary());
118
+ $headerParamLength = strlen($this->_headerParam) - 4 + $boundaryLength;
119
+ $headerUploadLength = strlen($this->_headerUpload) - 8 + $boundaryLength;
120
+ $length = $boundaryLength + 6;
121
+ foreach ($this->_params as $p) {
122
+ $length += $headerParamLength + strlen($p[0]) + strlen($p[1]) + 2;
123
+ }
124
+ foreach ($this->_uploads as $u) {
125
+ $length += $headerUploadLength + strlen($u['name']) + strlen($u['type']) +
126
+ strlen($u['filename']) + $u['size'] + 2;
127
+ }
128
+ return $length;
129
+ }
130
+
131
+ /**
132
+ * Returns the boundary to use in Content-Type header
133
+ *
134
+ * @return string
135
+ */
136
+ public function getBoundary()
137
+ {
138
+ if (empty($this->_boundary)) {
139
+ $this->_boundary = '--' . md5('PEAR-HTTP_Request2-' . microtime());
140
+ }
141
+ return $this->_boundary;
142
+ }
143
+
144
+ /**
145
+ * Returns next chunk of request body
146
+ *
147
+ * @param integer $length Number of bytes to read
148
+ *
149
+ * @return string Up to $length bytes of data, empty string if at end
150
+ * @throws HTTP_Request2_LogicException
151
+ */
152
+ public function read($length)
153
+ {
154
+ $ret = '';
155
+ $boundary = $this->getBoundary();
156
+ $paramCount = count($this->_params);
157
+ $uploadCount = count($this->_uploads);
158
+ while ($length > 0 && $this->_pos[0] <= $paramCount + $uploadCount) {
159
+ $oldLength = $length;
160
+ if ($this->_pos[0] < $paramCount) {
161
+ $param = sprintf(
162
+ $this->_headerParam, $boundary, $this->_params[$this->_pos[0]][0]
163
+ ) . $this->_params[$this->_pos[0]][1] . "\r\n";
164
+ $ret .= substr($param, $this->_pos[1], $length);
165
+ $length -= min(strlen($param) - $this->_pos[1], $length);
166
+
167
+ } elseif ($this->_pos[0] < $paramCount + $uploadCount) {
168
+ $pos = $this->_pos[0] - $paramCount;
169
+ $header = sprintf(
170
+ $this->_headerUpload, $boundary, $this->_uploads[$pos]['name'],
171
+ $this->_uploads[$pos]['filename'], $this->_uploads[$pos]['type']
172
+ );
173
+ if ($this->_pos[1] < strlen($header)) {
174
+ $ret .= substr($header, $this->_pos[1], $length);
175
+ $length -= min(strlen($header) - $this->_pos[1], $length);
176
+ }
177
+ $filePos = max(0, $this->_pos[1] - strlen($header));
178
+ if ($filePos < $this->_uploads[$pos]['size']) {
179
+ while ($length > 0 && !feof($this->_uploads[$pos]['fp'])) {
180
+ if (false === ($chunk = fread($this->_uploads[$pos]['fp'], $length))) {
181
+ throw new HTTP_Request2_LogicException(
182
+ 'Failed reading file upload', HTTP_Request2_Exception::READ_ERROR
183
+ );
184
+ }
185
+ $ret .= $chunk;
186
+ $length -= strlen($chunk);
187
+ }
188
+ }
189
+ if ($length > 0) {
190
+ $start = $this->_pos[1] + ($oldLength - $length) -
191
+ strlen($header) - $this->_uploads[$pos]['size'];
192
+ $ret .= substr("\r\n", $start, $length);
193
+ $length -= min(2 - $start, $length);
194
+ }
195
+
196
+ } else {
197
+ $closing = '--' . $boundary . "--\r\n";
198
+ $ret .= substr($closing, $this->_pos[1], $length);
199
+ $length -= min(strlen($closing) - $this->_pos[1], $length);
200
+ }
201
+ if ($length > 0) {
202
+ $this->_pos = array($this->_pos[0] + 1, 0);
203
+ } else {
204
+ $this->_pos[1] += $oldLength;
205
+ }
206
+ }
207
+ return $ret;
208
+ }
209
+
210
+ /**
211
+ * Sets the current position to the start of the body
212
+ *
213
+ * This allows reusing the same body in another request
214
+ */
215
+ public function rewind()
216
+ {
217
+ $this->_pos = array(0, 0);
218
+ foreach ($this->_uploads as $u) {
219
+ rewind($u['fp']);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Returns the body as string
225
+ *
226
+ * Note that it reads all file uploads into memory so it is a good idea not
227
+ * to use this method with large file uploads and rely on read() instead.
228
+ *
229
+ * @return string
230
+ */
231
+ public function __toString()
232
+ {
233
+ $this->rewind();
234
+ return $this->read($this->getLength());
235
+ }
236
+
237
+
238
+ /**
239
+ * Helper function to change the (probably multidimensional) associative array
240
+ * into the simple one.
241
+ *
242
+ * @param string $name name for item
243
+ * @param mixed $values item's values
244
+ * @param bool $useBrackets whether to append [] to array variables' names
245
+ *
246
+ * @return array array with the following items: array('item name', 'item value');
247
+ */
248
+ private static function _flattenArray($name, $values, $useBrackets)
249
+ {
250
+ if (!is_array($values)) {
251
+ return array(array($name, $values));
252
+ } else {
253
+ $ret = array();
254
+ foreach ($values as $k => $v) {
255
+ if (empty($name)) {
256
+ $newName = $k;
257
+ } elseif ($useBrackets) {
258
+ $newName = $name . '[' . $k . ']';
259
+ } else {
260
+ $newName = $name;
261
+ }
262
+ $ret = array_merge($ret, self::_flattenArray($newName, $v, $useBrackets));
263
+ }
264
+ return $ret;
265
+ }
266
+ }
267
+ }
268
+ ?>
vendor/PEAR/HTTP/Request2/Observer/Log.php CHANGED
@@ -1,192 +1,192 @@
1
- <?php
2
- /**
3
- * An observer useful for debugging / testing.
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author David Jean Louis <izi@php.net>
16
- * @author Alexey Borzov <avb@php.net>
17
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
18
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
19
- * @link http://pear.php.net/package/HTTP_Request2
20
- */
21
-
22
- /**
23
- * Exception class for HTTP_Request2 package
24
- */
25
- require_once 'HTTP/Request2/Exception.php';
26
-
27
- /**
28
- * A debug observer useful for debugging / testing.
29
- *
30
- * This observer logs to a log target data corresponding to the various request
31
- * and response events, it logs by default to php://output but can be configured
32
- * to log to a file or via the PEAR Log package.
33
- *
34
- * A simple example:
35
- * <code>
36
- * require_once 'HTTP/Request2.php';
37
- * require_once 'HTTP/Request2/Observer/Log.php';
38
- *
39
- * $request = new HTTP_Request2('http://www.example.com');
40
- * $observer = new HTTP_Request2_Observer_Log();
41
- * $request->attach($observer);
42
- * $request->send();
43
- * </code>
44
- *
45
- * A more complex example with PEAR Log:
46
- * <code>
47
- * require_once 'HTTP/Request2.php';
48
- * require_once 'HTTP/Request2/Observer/Log.php';
49
- * require_once 'Log.php';
50
- *
51
- * $request = new HTTP_Request2('http://www.example.com');
52
- * // we want to log with PEAR log
53
- * $observer = new HTTP_Request2_Observer_Log(Log::factory('console'));
54
- *
55
- * // we only want to log received headers
56
- * $observer->events = array('receivedHeaders');
57
- *
58
- * $request->attach($observer);
59
- * $request->send();
60
- * </code>
61
- *
62
- * @category HTTP
63
- * @package HTTP_Request2
64
- * @author David Jean Louis <izi@php.net>
65
- * @author Alexey Borzov <avb@php.net>
66
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
67
- * @version Release: 2.2.1
68
- * @link http://pear.php.net/package/HTTP_Request2
69
- */
70
- class HTTP_Request2_Observer_Log implements SplObserver
71
- {
72
- // properties {{{
73
-
74
- /**
75
- * The log target, it can be a a resource or a PEAR Log instance.
76
- *
77
- * @var resource|Log $target
78
- */
79
- protected $target = null;
80
-
81
- /**
82
- * The events to log.
83
- *
84
- * @var array $events
85
- */
86
- public $events = array(
87
- 'connect',
88
- 'sentHeaders',
89
- 'sentBody',
90
- 'receivedHeaders',
91
- 'receivedBody',
92
- 'disconnect',
93
- );
94
-
95
- // }}}
96
- // __construct() {{{
97
-
98
- /**
99
- * Constructor.
100
- *
101
- * @param mixed $target Can be a file path (default: php://output), a resource,
102
- * or an instance of the PEAR Log class.
103
- * @param array $events Array of events to listen to (default: all events)
104
- *
105
- * @return void
106
- */
107
- public function __construct($target = 'php://output', array $events = array())
108
- {
109
- if (!empty($events)) {
110
- $this->events = $events;
111
- }
112
- if (is_resource($target) || $target instanceof Log) {
113
- $this->target = $target;
114
- } elseif (false === ($this->target = @fopen($target, 'ab'))) {
115
- throw new HTTP_Request2_Exception("Unable to open '{$target}'");
116
- }
117
- }
118
-
119
- // }}}
120
- // update() {{{
121
-
122
- /**
123
- * Called when the request notifies us of an event.
124
- *
125
- * @param HTTP_Request2 $subject The HTTP_Request2 instance
126
- *
127
- * @return void
128
- */
129
- public function update(SplSubject $subject)
130
- {
131
- $event = $subject->getLastEvent();
132
- if (!in_array($event['name'], $this->events)) {
133
- return;
134
- }
135
-
136
- switch ($event['name']) {
137
- case 'connect':
138
- $this->log('* Connected to ' . $event['data']);
139
- break;
140
- case 'sentHeaders':
141
- $headers = explode("\r\n", $event['data']);
142
- array_pop($headers);
143
- foreach ($headers as $header) {
144
- $this->log('> ' . $header);
145
- }
146
- break;
147
- case 'sentBody':
148
- $this->log('> ' . $event['data'] . ' byte(s) sent');
149
- break;
150
- case 'receivedHeaders':
151
- $this->log(sprintf(
152
- '< HTTP/%s %s %s', $event['data']->getVersion(),
153
- $event['data']->getStatus(), $event['data']->getReasonPhrase()
154
- ));
155
- $headers = $event['data']->getHeader();
156
- foreach ($headers as $key => $val) {
157
- $this->log('< ' . $key . ': ' . $val);
158
- }
159
- $this->log('< ');
160
- break;
161
- case 'receivedBody':
162
- $this->log($event['data']->getBody());
163
- break;
164
- case 'disconnect':
165
- $this->log('* Disconnected');
166
- break;
167
- }
168
- }
169
-
170
- // }}}
171
- // log() {{{
172
-
173
- /**
174
- * Logs the given message to the configured target.
175
- *
176
- * @param string $message Message to display
177
- *
178
- * @return void
179
- */
180
- protected function log($message)
181
- {
182
- if ($this->target instanceof Log) {
183
- $this->target->debug($message);
184
- } elseif (is_resource($this->target)) {
185
- fwrite($this->target, $message . "\r\n");
186
- }
187
- }
188
-
189
- // }}}
190
- }
191
-
192
  ?>
1
+ <?php
2
+ /**
3
+ * An observer useful for debugging / testing.
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author David Jean Louis <izi@php.net>
16
+ * @author Alexey Borzov <avb@php.net>
17
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
18
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
19
+ * @link http://pear.php.net/package/HTTP_Request2
20
+ */
21
+
22
+ /**
23
+ * Exception class for HTTP_Request2 package
24
+ */
25
+ require_once 'HTTP/Request2/Exception.php';
26
+
27
+ /**
28
+ * A debug observer useful for debugging / testing.
29
+ *
30
+ * This observer logs to a log target data corresponding to the various request
31
+ * and response events, it logs by default to php://output but can be configured
32
+ * to log to a file or via the PEAR Log package.
33
+ *
34
+ * A simple example:
35
+ * <code>
36
+ * require_once 'HTTP/Request2.php';
37
+ * require_once 'HTTP/Request2/Observer/Log.php';
38
+ *
39
+ * $request = new HTTP_Request2('http://www.example.com');
40
+ * $observer = new HTTP_Request2_Observer_Log();
41
+ * $request->attach($observer);
42
+ * $request->send();
43
+ * </code>
44
+ *
45
+ * A more complex example with PEAR Log:
46
+ * <code>
47
+ * require_once 'HTTP/Request2.php';
48
+ * require_once 'HTTP/Request2/Observer/Log.php';
49
+ * require_once 'Log.php';
50
+ *
51
+ * $request = new HTTP_Request2('http://www.example.com');
52
+ * // we want to log with PEAR log
53
+ * $observer = new HTTP_Request2_Observer_Log(Log::factory('console'));
54
+ *
55
+ * // we only want to log received headers
56
+ * $observer->events = array('receivedHeaders');
57
+ *
58
+ * $request->attach($observer);
59
+ * $request->send();
60
+ * </code>
61
+ *
62
+ * @category HTTP
63
+ * @package HTTP_Request2
64
+ * @author David Jean Louis <izi@php.net>
65
+ * @author Alexey Borzov <avb@php.net>
66
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
67
+ * @version Release: 2.2.1
68
+ * @link http://pear.php.net/package/HTTP_Request2
69
+ */
70
+ class HTTP_Request2_Observer_Log implements SplObserver
71
+ {
72
+ // properties {{{
73
+
74
+ /**
75
+ * The log target, it can be a a resource or a PEAR Log instance.
76
+ *
77
+ * @var resource|Log $target
78
+ */
79
+ protected $target = null;
80
+
81
+ /**
82
+ * The events to log.
83
+ *
84
+ * @var array $events
85
+ */
86
+ public $events = array(
87
+ 'connect',
88
+ 'sentHeaders',
89
+ 'sentBody',
90
+ 'receivedHeaders',
91
+ 'receivedBody',
92
+ 'disconnect',
93
+ );
94
+
95
+ // }}}
96
+ // __construct() {{{
97
+
98
+ /**
99
+ * Constructor.
100
+ *
101
+ * @param mixed $target Can be a file path (default: php://output), a resource,
102
+ * or an instance of the PEAR Log class.
103
+ * @param array $events Array of events to listen to (default: all events)
104
+ *
105
+ * @return void
106
+ */
107
+ public function __construct($target = 'php://output', array $events = array())
108
+ {
109
+ if (!empty($events)) {
110
+ $this->events = $events;
111
+ }
112
+ if (is_resource($target) || $target instanceof Log) {
113
+ $this->target = $target;
114
+ } elseif (false === ($this->target = @fopen($target, 'ab'))) {
115
+ throw new HTTP_Request2_Exception("Unable to open '{$target}'");
116
+ }
117
+ }
118
+
119
+ // }}}
120
+ // update() {{{
121
+
122
+ /**
123
+ * Called when the request notifies us of an event.
124
+ *
125
+ * @param HTTP_Request2 $subject The HTTP_Request2 instance
126
+ *
127
+ * @return void
128
+ */
129
+ public function update(SplSubject $subject)
130
+ {
131
+ $event = $subject->getLastEvent();
132
+ if (!in_array($event['name'], $this->events)) {
133
+ return;
134
+ }
135
+
136
+ switch ($event['name']) {
137
+ case 'connect':
138
+ $this->log('* Connected to ' . $event['data']);
139
+ break;
140
+ case 'sentHeaders':
141
+ $headers = explode("\r\n", $event['data']);
142
+ array_pop($headers);
143
+ foreach ($headers as $header) {
144
+ $this->log('> ' . $header);
145
+ }
146
+ break;
147
+ case 'sentBody':
148
+ $this->log('> ' . $event['data'] . ' byte(s) sent');
149
+ break;
150
+ case 'receivedHeaders':
151
+ $this->log(sprintf(
152
+ '< HTTP/%s %s %s', $event['data']->getVersion(),
153
+ $event['data']->getStatus(), $event['data']->getReasonPhrase()
154
+ ));
155
+ $headers = $event['data']->getHeader();
156
+ foreach ($headers as $key => $val) {
157
+ $this->log('< ' . $key . ': ' . $val);
158
+ }
159
+ $this->log('< ');
160
+ break;
161
+ case 'receivedBody':
162
+ $this->log($event['data']->getBody());
163
+ break;
164
+ case 'disconnect':
165
+ $this->log('* Disconnected');
166
+ break;
167
+ }
168
+ }
169
+
170
+ // }}}
171
+ // log() {{{
172
+
173
+ /**
174
+ * Logs the given message to the configured target.
175
+ *
176
+ * @param string $message Message to display
177
+ *
178
+ * @return void
179
+ */
180
+ protected function log($message)
181
+ {
182
+ if ($this->target instanceof Log) {
183
+ $this->target->debug($message);
184
+ } elseif (is_resource($this->target)) {
185
+ fwrite($this->target, $message . "\r\n");
186
+ }
187
+ }
188
+
189
+ // }}}
190
+ }
191
+
192
  ?>
vendor/PEAR/HTTP/Request2/Response.php CHANGED
@@ -1,631 +1,631 @@
1
- <?php
2
- /**
3
- * Class representing a HTTP response
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /**
22
- * Exception class for HTTP_Request2 package
23
- */
24
- require_once 'HTTP/Request2/Exception.php';
25
-
26
- /**
27
- * Class representing a HTTP response
28
- *
29
- * The class is designed to be used in "streaming" scenario, building the
30
- * response as it is being received:
31
- * <code>
32
- * $statusLine = read_status_line();
33
- * $response = new HTTP_Request2_Response($statusLine);
34
- * do {
35
- * $headerLine = read_header_line();
36
- * $response->parseHeaderLine($headerLine);
37
- * } while ($headerLine != '');
38
- *
39
- * while ($chunk = read_body()) {
40
- * $response->appendBody($chunk);
41
- * }
42
- *
43
- * var_dump($response->getHeader(), $response->getCookies(), $response->getBody());
44
- * </code>
45
- *
46
- * @category HTTP
47
- * @package HTTP_Request2
48
- * @author Alexey Borzov <avb@php.net>
49
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
50
- * @version Release: 2.2.1
51
- * @link http://pear.php.net/package/HTTP_Request2
52
- * @link http://tools.ietf.org/html/rfc2616#section-6
53
- */
54
- class HTTP_Request2_Response
55
- {
56
- /**
57
- * HTTP protocol version (e.g. 1.0, 1.1)
58
- * @var string
59
- */
60
- protected $version;
61
-
62
- /**
63
- * Status code
64
- * @var integer
65
- * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
66
- */
67
- protected $code;
68
-
69
- /**
70
- * Reason phrase
71
- * @var string
72
- * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
73
- */
74
- protected $reasonPhrase;
75
-
76
- /**
77
- * Effective URL (may be different from original request URL in case of redirects)
78
- * @var string
79
- */
80
- protected $effectiveUrl;
81
-
82
- /**
83
- * Associative array of response headers
84
- * @var array
85
- */
86
- protected $headers = array();
87
-
88
- /**
89
- * Cookies set in the response
90
- * @var array
91
- */
92
- protected $cookies = array();
93
-
94
- /**
95
- * Name of last header processed by parseHederLine()
96
- *
97
- * Used to handle the headers that span multiple lines
98
- *
99
- * @var string
100
- */
101
- protected $lastHeader = null;
102
-
103
- /**
104
- * Response body
105
- * @var string
106
- */
107
- protected $body = '';
108
-
109
- /**
110
- * Whether the body is still encoded by Content-Encoding
111
- *
112
- * cURL provides the decoded body to the callback; if we are reading from
113
- * socket the body is still gzipped / deflated
114
- *
115
- * @var bool
116
- */
117
- protected $bodyEncoded;
118
-
119
- /**
120
- * Associative array of HTTP status code / reason phrase.
121
- *
122
- * @var array
123
- * @link http://tools.ietf.org/html/rfc2616#section-10
124
- */
125
- protected static $phrases = array(
126
-
127
- // 1xx: Informational - Request received, continuing process
128
- 100 => 'Continue',
129
- 101 => 'Switching Protocols',
130
-
131
- // 2xx: Success - The action was successfully received, understood and
132
- // accepted
133
- 200 => 'OK',
134
- 201 => 'Created',
135
- 202 => 'Accepted',
136
- 203 => 'Non-Authoritative Information',
137
- 204 => 'No Content',
138
- 205 => 'Reset Content',
139
- 206 => 'Partial Content',
140
-
141
- // 3xx: Redirection - Further action must be taken in order to complete
142
- // the request
143
- 300 => 'Multiple Choices',
144
- 301 => 'Moved Permanently',
145
- 302 => 'Found', // 1.1
146
- 303 => 'See Other',
147
- 304 => 'Not Modified',
148
- 305 => 'Use Proxy',
149
- 307 => 'Temporary Redirect',
150
-
151
- // 4xx: Client Error - The request contains bad syntax or cannot be
152
- // fulfilled
153
- 400 => 'Bad Request',
154
- 401 => 'Unauthorized',
155
- 402 => 'Payment Required',
156
- 403 => 'Forbidden',
157
- 404 => 'Not Found',
158
- 405 => 'Method Not Allowed',
159
- 406 => 'Not Acceptable',
160
- 407 => 'Proxy Authentication Required',
161
- 408 => 'Request Timeout',
162
- 409 => 'Conflict',
163
- 410 => 'Gone',
164
- 411 => 'Length Required',
165
- 412 => 'Precondition Failed',
166
- 413 => 'Request Entity Too Large',
167
- 414 => 'Request-URI Too Long',
168
- 415 => 'Unsupported Media Type',
169
- 416 => 'Requested Range Not Satisfiable',
170
- 417 => 'Expectation Failed',
171
-
172
- // 5xx: Server Error - The server failed to fulfill an apparently
173
- // valid request
174
- 500 => 'Internal Server Error',
175
- 501 => 'Not Implemented',
176
- 502 => 'Bad Gateway',
177
- 503 => 'Service Unavailable',
178
- 504 => 'Gateway Timeout',
179
- 505 => 'HTTP Version Not Supported',
180
- 509 => 'Bandwidth Limit Exceeded',
181
-
182
- );
183
-
184
- /**
185
- * Returns the default reason phrase for the given code or all reason phrases
186
- *
187
- * @param int $code Response code
188
- *
189
- * @return string|array|null Default reason phrase for $code if $code is given
190
- * (null if no phrase is available), array of all
191
- * reason phrases if $code is null
192
- * @link http://pear.php.net/bugs/18716
193
- */
194
- public static function getDefaultReasonPhrase($code = null)
195
- {
196
- if (null === $code) {
197
- return self::$phrases;
198
- } else {
199
- return isset(self::$phrases[$code]) ? self::$phrases[$code] : null;
200
- }
201
- }
202
-
203
- /**
204
- * Constructor, parses the response status line
205
- *
206
- * @param string $statusLine Response status line (e.g. "HTTP/1.1 200 OK")
207
- * @param bool $bodyEncoded Whether body is still encoded by Content-Encoding
208
- * @param string $effectiveUrl Effective URL of the response
209
- *
210
- * @throws HTTP_Request2_MessageException if status line is invalid according to spec
211
- */
212
- public function __construct($statusLine, $bodyEncoded = true, $effectiveUrl = null)
213
- {
214
- if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) {
215
- throw new HTTP_Request2_MessageException(
216
- "Malformed response: {$statusLine}",
217
- HTTP_Request2_Exception::MALFORMED_RESPONSE
218
- );
219
- }
220
- $this->version = $m[1];
221
- $this->code = intval($m[2]);
222
- $this->reasonPhrase = !empty($m[3]) ? trim($m[3]) : self::getDefaultReasonPhrase($this->code);
223
- $this->bodyEncoded = (bool)$bodyEncoded;
224
- $this->effectiveUrl = (string)$effectiveUrl;
225
- }
226
-
227
- /**
228
- * Parses the line from HTTP response filling $headers array
229
- *
230
- * The method should be called after reading the line from socket or receiving
231
- * it into cURL callback. Passing an empty string here indicates the end of
232
- * response headers and triggers additional processing, so be sure to pass an
233
- * empty string in the end.
234
- *
235
- * @param string $headerLine Line from HTTP response
236
- */
237
- public function parseHeaderLine($headerLine)
238
- {
239
- $headerLine = trim($headerLine, "\r\n");
240
-
241
- if ('' == $headerLine) {
242
- // empty string signals the end of headers, process the received ones
243
- if (!empty($this->headers['set-cookie'])) {
244
- $cookies = is_array($this->headers['set-cookie'])?
245
- $this->headers['set-cookie']:
246
- array($this->headers['set-cookie']);
247
- foreach ($cookies as $cookieString) {
248
- $this->parseCookie($cookieString);
249
- }
250
- unset($this->headers['set-cookie']);
251
- }
252
- foreach (array_keys($this->headers) as $k) {
253
- if (is_array($this->headers[$k])) {
254
- $this->headers[$k] = implode(', ', $this->headers[$k]);
255
- }
256
- }
257
-
258
- } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) {
259
- // string of the form header-name: header value
260
- $name = strtolower($m[1]);
261
- $value = trim($m[2]);
262
- if (empty($this->headers[$name])) {
263
- $this->headers[$name] = $value;
264
- } else {
265
- if (!is_array($this->headers[$name])) {
266
- $this->headers[$name] = array($this->headers[$name]);
267
- }
268
- $this->headers[$name][] = $value;
269
- }
270
- $this->lastHeader = $name;
271
-
272
- } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) {
273
- // continuation of a previous header
274
- if (!is_array($this->headers[$this->lastHeader])) {
275
- $this->headers[$this->lastHeader] .= ' ' . trim($m[1]);
276
- } else {
277
- $key = count($this->headers[$this->lastHeader]) - 1;
278
- $this->headers[$this->lastHeader][$key] .= ' ' . trim($m[1]);
279
- }
280
- }
281
- }
282
-
283
- /**
284
- * Parses a Set-Cookie header to fill $cookies array
285
- *
286
- * @param string $cookieString value of Set-Cookie header
287
- *
288
- * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
289
- */
290
- protected function parseCookie($cookieString)
291
- {
292
- $cookie = array(
293
- 'expires' => null,
294
- 'domain' => null,
295
- 'path' => null,
296
- 'secure' => false
297
- );
298
-
299
- if (!strpos($cookieString, ';')) {
300
- // Only a name=value pair
301
- $pos = strpos($cookieString, '=');
302
- $cookie['name'] = trim(substr($cookieString, 0, $pos));
303
- $cookie['value'] = trim(substr($cookieString, $pos + 1));
304
-
305
- } else {
306
- // Some optional parameters are supplied
307
- $elements = explode(';', $cookieString);
308
- $pos = strpos($elements[0], '=');
309
- $cookie['name'] = trim(substr($elements[0], 0, $pos));
310
- $cookie['value'] = trim(substr($elements[0], $pos + 1));
311
-
312
- for ($i = 1; $i < count($elements); $i++) {
313
- if (false === strpos($elements[$i], '=')) {
314
- $elName = trim($elements[$i]);
315
- $elValue = null;
316
- } else {
317
- list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
318
- }
319
- $elName = strtolower($elName);
320
- if ('secure' == $elName) {
321
- $cookie['secure'] = true;
322
- } elseif ('expires' == $elName) {
323
- $cookie['expires'] = str_replace('"', '', $elValue);
324
- } elseif ('path' == $elName || 'domain' == $elName) {
325
- $cookie[$elName] = urldecode($elValue);
326
- } else {
327
- $cookie[$elName] = $elValue;
328
- }
329
- }
330
- }
331
- $this->cookies[] = $cookie;
332
- }
333
-
334
- /**
335
- * Appends a string to the response body
336
- *
337
- * @param string $bodyChunk part of response body
338
- */
339
- public function appendBody($bodyChunk)
340
- {
341
- $this->body .= $bodyChunk;
342
- }
343
-
344
- /**
345
- * Returns the effective URL of the response
346
- *
347
- * This may be different from the request URL if redirects were followed.
348
- *
349
- * @return string
350
- * @link http://pear.php.net/bugs/bug.php?id=18412
351
- */
352
- public function getEffectiveUrl()
353
- {
354
- return $this->effectiveUrl;
355
- }
356
-
357
- /**
358
- * Returns the status code
359
- *
360
- * @return integer
361
- */
362
- public function getStatus()
363
- {
364
- return $this->code;
365
- }
366
-
367
- /**
368
- * Returns the reason phrase
369
- *
370
- * @return string
371
- */
372
- public function getReasonPhrase()
373
- {
374
- return $this->reasonPhrase;
375
- }
376
-
377
- /**
378
- * Whether response is a redirect that can be automatically handled by HTTP_Request2
379
- *
380
- * @return bool
381
- */
382
- public function isRedirect()
383
- {
384
- return in_array($this->code, array(300, 301, 302, 303, 307))
385
- && isset($this->headers['location']);
386
- }
387
-
388
- /**
389
- * Returns either the named header or all response headers
390
- *
391
- * @param string $headerName Name of header to return
392
- *
393
- * @return string|array Value of $headerName header (null if header is
394
- * not present), array of all response headers if
395
- * $headerName is null
396
- */
397
- public function getHeader($headerName = null)
398
- {
399
- if (null === $headerName) {
400
- return $this->headers;
401
- } else {
402
- $headerName = strtolower($headerName);
403
- return isset($this->headers[$headerName])? $this->headers[$headerName]: null;
404
- }
405
- }
406
-
407
- /**
408
- * Returns cookies set in response
409
- *
410
- * @return array
411
- */
412
- public function getCookies()
413
- {
414
- return $this->cookies;
415
- }
416
-
417
- /**
418
- * Returns the body of the response
419
- *
420
- * @return string
421
- * @throws HTTP_Request2_Exception if body cannot be decoded
422
- */
423
- public function getBody()
424
- {
425
- if (0 == strlen($this->body) || !$this->bodyEncoded
426
- || !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))
427
- ) {
428
- return $this->body;
429
-
430
- } else {
431
- if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
432
- $oldEncoding = mb_internal_encoding();
433
- mb_internal_encoding('8bit');
434
- }
435
-
436
- try {
437
- switch (strtolower($this->getHeader('content-encoding'))) {
438
- case 'gzip':
439
- $decoded = self::decodeGzip($this->body);
440
- break;
441
- case 'deflate':
442
- $decoded = self::decodeDeflate($this->body);
443
- }
444
- } catch (Exception $e) {
445
- }
446
-
447
- if (!empty($oldEncoding)) {
448
- mb_internal_encoding($oldEncoding);
449
- }
450
- if (!empty($e)) {
451
- throw $e;
452
- }
453
- return $decoded;
454
- }
455
- }
456
-
457
- /**
458
- * Get the HTTP version of the response
459
- *
460
- * @return string
461
- */
462
- public function getVersion()
463
- {
464
- return $this->version;
465
- }
466
-
467
- /**
468
- * Decodes the message-body encoded by gzip
469
- *
470
- * The real decoding work is done by gzinflate() built-in function, this
471
- * method only parses the header and checks data for compliance with
472
- * RFC 1952
473
- *
474
- * @param string $data gzip-encoded data
475
- *
476
- * @return string decoded data
477
- * @throws HTTP_Request2_LogicException
478
- * @throws HTTP_Request2_MessageException
479
- * @link http://tools.ietf.org/html/rfc1952
480
- */
481
- public static function decodeGzip($data)
482
- {
483
- $length = strlen($data);
484
- // If it doesn't look like gzip-encoded data, don't bother
485
- if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
486
- return $data;
487
- }
488
- if (!function_exists('gzinflate')) {
489
- throw new HTTP_Request2_LogicException(
490
- 'Unable to decode body: gzip extension not available',
491
- HTTP_Request2_Exception::MISCONFIGURATION
492
- );
493
- }
494
- $method = ord(substr($data, 2, 1));
495
- if (8 != $method) {
496
- throw new HTTP_Request2_MessageException(
497
- 'Error parsing gzip header: unknown compression method',
498
- HTTP_Request2_Exception::DECODE_ERROR
499
- );
500
- }
501
- $flags = ord(substr($data, 3, 1));
502
- if ($flags & 224) {
503
- throw new HTTP_Request2_MessageException(
504
- 'Error parsing gzip header: reserved bits are set',
505
- HTTP_Request2_Exception::DECODE_ERROR
506
- );
507
- }
508
-
509
- // header is 10 bytes minimum. may be longer, though.
510
- $headerLength = 10;
511
- // extra fields, need to skip 'em
512
- if ($flags & 4) {
513
- if ($length - $headerLength - 2 < 8) {
514
- throw new HTTP_Request2_MessageException(
515
- 'Error parsing gzip header: data too short',
516
- HTTP_Request2_Exception::DECODE_ERROR
517
- );
518
- }
519
- $extraLength = unpack('v', substr($data, 10, 2));
520
- if ($length - $headerLength - 2 - $extraLength[1] < 8) {
521
- throw new HTTP_Request2_MessageException(
522
- 'Error parsing gzip header: data too short',
523
- HTTP_Request2_Exception::DECODE_ERROR
524
- );
525
- }
526
- $headerLength += $extraLength[1] + 2;
527
- }
528
- // file name, need to skip that
529
- if ($flags & 8) {
530
- if ($length - $headerLength - 1 < 8) {
531
- throw new HTTP_Request2_MessageException(
532
- 'Error parsing gzip header: data too short',
533
- HTTP_Request2_Exception::DECODE_ERROR
534
- );
535
- }
536
- $filenameLength = strpos(substr($data, $headerLength), chr(0));
537
- if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
538
- throw new HTTP_Request2_MessageException(
539
- 'Error parsing gzip header: data too short',
540
- HTTP_Request2_Exception::DECODE_ERROR
541
- );
542
- }
543
- $headerLength += $filenameLength + 1;
544
- }
545
- // comment, need to skip that also
546
- if ($flags & 16) {
547
- if ($length - $headerLength - 1 < 8) {
548
- throw new HTTP_Request2_MessageException(
549
- 'Error parsing gzip header: data too short',
550
- HTTP_Request2_Exception::DECODE_ERROR
551
- );
552
- }
553
- $commentLength = strpos(substr($data, $headerLength), chr(0));
554
- if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
555
- throw new HTTP_Request2_MessageException(
556
- 'Error parsing gzip header: data too short',
557
- HTTP_Request2_Exception::DECODE_ERROR
558
- );
559
- }
560
- $headerLength += $commentLength + 1;
561
- }
562
- // have a CRC for header. let's check
563
- if ($flags & 2) {
564
- if ($length - $headerLength - 2 < 8) {
565
- throw new HTTP_Request2_MessageException(
566
- 'Error parsing gzip header: data too short',
567
- HTTP_Request2_Exception::DECODE_ERROR
568
- );
569
- }
570
- $crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
571
- $crcStored = unpack('v', substr($data, $headerLength, 2));
572
- if ($crcReal != $crcStored[1]) {
573
- throw new HTTP_Request2_MessageException(
574
- 'Header CRC check failed',
575
- HTTP_Request2_Exception::DECODE_ERROR
576
- );
577
- }
578
- $headerLength += 2;
579
- }
580
- // unpacked data CRC and size at the end of encoded data
581
- $tmp = unpack('V2', substr($data, -8));
582
- $dataCrc = $tmp[1];
583
- $dataSize = $tmp[2];
584
-
585
- // finally, call the gzinflate() function
586
- // don't pass $dataSize to gzinflate, see bugs #13135, #14370
587
- $unpacked = gzinflate(substr($data, $headerLength, -8));
588
- if (false === $unpacked) {
589
- throw new HTTP_Request2_MessageException(
590
- 'gzinflate() call failed',
591
- HTTP_Request2_Exception::DECODE_ERROR
592
- );
593
- } elseif ($dataSize != strlen($unpacked)) {
594
- throw new HTTP_Request2_MessageException(
595
- 'Data size check failed',
596
- HTTP_Request2_Exception::DECODE_ERROR
597
- );
598
- } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
599
- throw new HTTP_Request2_Exception(
600
- 'Data CRC check failed',
601
- HTTP_Request2_Exception::DECODE_ERROR
602
- );
603
- }
604
- return $unpacked;
605
- }
606
-
607
- /**
608
- * Decodes the message-body encoded by deflate
609
- *
610
- * @param string $data deflate-encoded data
611
- *
612
- * @return string decoded data
613
- * @throws HTTP_Request2_LogicException
614
- */
615
- public static function decodeDeflate($data)
616
- {
617
- if (!function_exists('gzuncompress')) {
618
- throw new HTTP_Request2_LogicException(
619
- 'Unable to decode body: gzip extension not available',
620
- HTTP_Request2_Exception::MISCONFIGURATION
621
- );
622
- }
623
- // RFC 2616 defines 'deflate' encoding as zlib format from RFC 1950,
624
- // while many applications send raw deflate stream from RFC 1951.
625
- // We should check for presence of zlib header and use gzuncompress() or
626
- // gzinflate() as needed. See bug #15305
627
- $header = unpack('n', substr($data, 0, 2));
628
- return (0 == $header[1] % 31)? gzuncompress($data): gzinflate($data);
629
- }
630
- }
631
  ?>
1
+ <?php
2
+ /**
3
+ * Class representing a HTTP response
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /**
22
+ * Exception class for HTTP_Request2 package
23
+ */
24
+ require_once 'HTTP/Request2/Exception.php';
25
+
26
+ /**
27
+ * Class representing a HTTP response
28
+ *
29
+ * The class is designed to be used in "streaming" scenario, building the
30
+ * response as it is being received:
31
+ * <code>
32
+ * $statusLine = read_status_line();
33
+ * $response = new HTTP_Request2_Response($statusLine);
34
+ * do {
35
+ * $headerLine = read_header_line();
36
+ * $response->parseHeaderLine($headerLine);
37
+ * } while ($headerLine != '');
38
+ *
39
+ * while ($chunk = read_body()) {
40
+ * $response->appendBody($chunk);
41
+ * }
42
+ *
43
+ * var_dump($response->getHeader(), $response->getCookies(), $response->getBody());
44
+ * </code>
45
+ *
46
+ * @category HTTP
47
+ * @package HTTP_Request2
48
+ * @author Alexey Borzov <avb@php.net>
49
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
50
+ * @version Release: 2.2.1
51
+ * @link http://pear.php.net/package/HTTP_Request2
52
+ * @link http://tools.ietf.org/html/rfc2616#section-6
53
+ */
54
+ class HTTP_Request2_Response
55
+ {
56
+ /**
57
+ * HTTP protocol version (e.g. 1.0, 1.1)
58
+ * @var string
59
+ */
60
+ protected $version;
61
+
62
+ /**
63
+ * Status code
64
+ * @var integer
65
+ * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
66
+ */
67
+ protected $code;
68
+
69
+ /**
70
+ * Reason phrase
71
+ * @var string
72
+ * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
73
+ */
74
+ protected $reasonPhrase;
75
+
76
+ /**
77
+ * Effective URL (may be different from original request URL in case of redirects)
78
+ * @var string
79
+ */
80
+ protected $effectiveUrl;
81
+
82
+ /**
83
+ * Associative array of response headers
84
+ * @var array
85
+ */
86
+ protected $headers = array();
87
+
88
+ /**
89
+ * Cookies set in the response
90
+ * @var array
91
+ */
92
+ protected $cookies = array();
93
+
94
+ /**
95
+ * Name of last header processed by parseHederLine()
96
+ *
97
+ * Used to handle the headers that span multiple lines
98
+ *
99
+ * @var string
100
+ */
101
+ protected $lastHeader = null;
102
+
103
+ /**
104
+ * Response body
105
+ * @var string
106
+ */
107
+ protected $body = '';
108
+
109
+ /**
110
+ * Whether the body is still encoded by Content-Encoding
111
+ *
112
+ * cURL provides the decoded body to the callback; if we are reading from
113
+ * socket the body is still gzipped / deflated
114
+ *
115
+ * @var bool
116
+ */
117
+ protected $bodyEncoded;
118
+
119
+ /**
120
+ * Associative array of HTTP status code / reason phrase.
121
+ *
122
+ * @var array
123
+ * @link http://tools.ietf.org/html/rfc2616#section-10
124
+ */
125
+ protected static $phrases = array(
126
+
127
+ // 1xx: Informational - Request received, continuing process
128
+ 100 => 'Continue',
129
+ 101 => 'Switching Protocols',
130
+
131
+ // 2xx: Success - The action was successfully received, understood and
132
+ // accepted
133
+ 200 => 'OK',
134
+ 201 => 'Created',
135
+ 202 => 'Accepted',
136
+ 203 => 'Non-Authoritative Information',
137
+ 204 => 'No Content',
138
+ 205 => 'Reset Content',
139
+ 206 => 'Partial Content',
140
+
141
+ // 3xx: Redirection - Further action must be taken in order to complete
142
+ // the request
143
+ 300 => 'Multiple Choices',
144
+ 301 => 'Moved Permanently',
145
+ 302 => 'Found', // 1.1
146
+ 303 => 'See Other',
147
+ 304 => 'Not Modified',
148
+ 305 => 'Use Proxy',
149
+ 307 => 'Temporary Redirect',
150
+
151
+ // 4xx: Client Error - The request contains bad syntax or cannot be
152
+ // fulfilled
153
+ 400 => 'Bad Request',
154
+ 401 => 'Unauthorized',
155
+ 402 => 'Payment Required',
156
+ 403 => 'Forbidden',
157
+ 404 => 'Not Found',
158
+ 405 => 'Method Not Allowed',
159
+ 406 => 'Not Acceptable',
160
+ 407 => 'Proxy Authentication Required',
161
+ 408 => 'Request Timeout',
162
+ 409 => 'Conflict',
163
+ 410 => 'Gone',
164
+ 411 => 'Length Required',
165
+ 412 => 'Precondition Failed',
166
+ 413 => 'Request Entity Too Large',
167
+ 414 => 'Request-URI Too Long',
168
+ 415 => 'Unsupported Media Type',
169
+ 416 => 'Requested Range Not Satisfiable',
170
+ 417 => 'Expectation Failed',
171
+
172
+ // 5xx: Server Error - The server failed to fulfill an apparently
173
+ // valid request
174
+ 500 => 'Internal Server Error',
175
+ 501 => 'Not Implemented',
176
+ 502 => 'Bad Gateway',
177
+ 503 => 'Service Unavailable',
178
+ 504 => 'Gateway Timeout',
179
+ 505 => 'HTTP Version Not Supported',
180
+ 509 => 'Bandwidth Limit Exceeded',
181
+
182
+ );
183
+
184
+ /**
185
+ * Returns the default reason phrase for the given code or all reason phrases
186
+ *
187
+ * @param int $code Response code
188
+ *
189
+ * @return string|array|null Default reason phrase for $code if $code is given
190
+ * (null if no phrase is available), array of all
191
+ * reason phrases if $code is null
192
+ * @link http://pear.php.net/bugs/18716
193
+ */
194
+ public static function getDefaultReasonPhrase($code = null)
195
+ {
196
+ if (null === $code) {
197
+ return self::$phrases;
198
+ } else {
199
+ return isset(self::$phrases[$code]) ? self::$phrases[$code] : null;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Constructor, parses the response status line
205
+ *
206
+ * @param string $statusLine Response status line (e.g. "HTTP/1.1 200 OK")
207
+ * @param bool $bodyEncoded Whether body is still encoded by Content-Encoding
208
+ * @param string $effectiveUrl Effective URL of the response
209
+ *
210
+ * @throws HTTP_Request2_MessageException if status line is invalid according to spec
211
+ */
212
+ public function __construct($statusLine, $bodyEncoded = true, $effectiveUrl = null)
213
+ {
214
+ if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) {
215
+ throw new HTTP_Request2_MessageException(
216
+ "Malformed response: {$statusLine}",
217
+ HTTP_Request2_Exception::MALFORMED_RESPONSE
218
+ );
219
+ }
220
+ $this->version = $m[1];
221
+ $this->code = intval($m[2]);
222
+ $this->reasonPhrase = !empty($m[3]) ? trim($m[3]) : self::getDefaultReasonPhrase($this->code);
223
+ $this->bodyEncoded = (bool)$bodyEncoded;
224
+ $this->effectiveUrl = (string)$effectiveUrl;
225
+ }
226
+
227
+ /**
228
+ * Parses the line from HTTP response filling $headers array
229
+ *
230
+ * The method should be called after reading the line from socket or receiving
231
+ * it into cURL callback. Passing an empty string here indicates the end of
232
+ * response headers and triggers additional processing, so be sure to pass an
233
+ * empty string in the end.
234
+ *
235
+ * @param string $headerLine Line from HTTP response
236
+ */
237
+ public function parseHeaderLine($headerLine)
238
+ {
239
+ $headerLine = trim($headerLine, "\r\n");
240
+
241
+ if ('' == $headerLine) {
242
+ // empty string signals the end of headers, process the received ones
243
+ if (!empty($this->headers['set-cookie'])) {
244
+ $cookies = is_array($this->headers['set-cookie'])?
245
+ $this->headers['set-cookie']:
246
+ array($this->headers['set-cookie']);
247
+ foreach ($cookies as $cookieString) {
248
+ $this->parseCookie($cookieString);
249
+ }
250
+ unset($this->headers['set-cookie']);
251
+ }
252
+ foreach (array_keys($this->headers) as $k) {
253
+ if (is_array($this->headers[$k])) {
254
+ $this->headers[$k] = implode(', ', $this->headers[$k]);
255
+ }
256
+ }
257
+
258
+ } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) {
259
+ // string of the form header-name: header value
260
+ $name = strtolower($m[1]);
261
+ $value = trim($m[2]);
262
+ if (empty($this->headers[$name])) {
263
+ $this->headers[$name] = $value;
264
+ } else {
265
+ if (!is_array($this->headers[$name])) {
266
+ $this->headers[$name] = array($this->headers[$name]);
267
+ }
268
+ $this->headers[$name][] = $value;
269
+ }
270
+ $this->lastHeader = $name;
271
+
272
+ } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) {
273
+ // continuation of a previous header
274
+ if (!is_array($this->headers[$this->lastHeader])) {
275
+ $this->headers[$this->lastHeader] .= ' ' . trim($m[1]);
276
+ } else {
277
+ $key = count($this->headers[$this->lastHeader]) - 1;
278
+ $this->headers[$this->lastHeader][$key] .= ' ' . trim($m[1]);
279
+ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Parses a Set-Cookie header to fill $cookies array
285
+ *
286
+ * @param string $cookieString value of Set-Cookie header
287
+ *
288
+ * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
289
+ */
290
+ protected function parseCookie($cookieString)
291
+ {
292
+ $cookie = array(
293
+ 'expires' => null,
294
+ 'domain' => null,
295
+ 'path' => null,
296
+ 'secure' => false
297
+ );
298
+
299
+ if (!strpos($cookieString, ';')) {
300
+ // Only a name=value pair
301
+ $pos = strpos($cookieString, '=');
302
+ $cookie['name'] = trim(substr($cookieString, 0, $pos));
303
+ $cookie['value'] = trim(substr($cookieString, $pos + 1));
304
+
305
+ } else {
306
+ // Some optional parameters are supplied
307
+ $elements = explode(';', $cookieString);
308
+ $pos = strpos($elements[0], '=');
309
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
310
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
311
+
312
+ for ($i = 1; $i < count($elements); $i++) {
313
+ if (false === strpos($elements[$i], '=')) {
314
+ $elName = trim($elements[$i]);
315
+ $elValue = null;
316
+ } else {
317
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
318
+ }
319
+ $elName = strtolower($elName);
320
+ if ('secure' == $elName) {
321
+ $cookie['secure'] = true;
322
+ } elseif ('expires' == $elName) {
323
+ $cookie['expires'] = str_replace('"', '', $elValue);
324
+ } elseif ('path' == $elName || 'domain' == $elName) {
325
+ $cookie[$elName] = urldecode($elValue);
326
+ } else {
327
+ $cookie[$elName] = $elValue;
328
+ }
329
+ }
330
+ }
331
+ $this->cookies[] = $cookie;
332
+ }
333
+
334
+ /**
335
+ * Appends a string to the response body
336
+ *
337
+ * @param string $bodyChunk part of response body
338
+ */
339
+ public function appendBody($bodyChunk)
340
+ {
341
+ $this->body .= $bodyChunk;
342
+ }
343
+
344
+ /**
345
+ * Returns the effective URL of the response
346
+ *
347
+ * This may be different from the request URL if redirects were followed.
348
+ *
349
+ * @return string
350
+ * @link http://pear.php.net/bugs/bug.php?id=18412
351
+ */
352
+ public function getEffectiveUrl()
353
+ {
354
+ return $this->effectiveUrl;
355
+ }
356
+
357
+ /**
358
+ * Returns the status code
359
+ *
360
+ * @return integer
361
+ */
362
+ public function getStatus()
363
+ {
364
+ return $this->code;
365
+ }
366
+
367
+ /**
368
+ * Returns the reason phrase
369
+ *
370
+ * @return string
371
+ */
372
+ public function getReasonPhrase()
373
+ {
374
+ return $this->reasonPhrase;
375
+ }
376
+
377
+ /**
378
+ * Whether response is a redirect that can be automatically handled by HTTP_Request2
379
+ *
380
+ * @return bool
381
+ */
382
+ public function isRedirect()
383
+ {
384
+ return in_array($this->code, array(300, 301, 302, 303, 307))
385
+ && isset($this->headers['location']);
386
+ }
387
+
388
+ /**
389
+ * Returns either the named header or all response headers
390
+ *
391
+ * @param string $headerName Name of header to return
392
+ *
393
+ * @return string|array Value of $headerName header (null if header is
394
+ * not present), array of all response headers if
395
+ * $headerName is null
396
+ */
397
+ public function getHeader($headerName = null)
398
+ {
399
+ if (null === $headerName) {
400
+ return $this->headers;
401
+ } else {
402
+ $headerName = strtolower($headerName);
403
+ return isset($this->headers[$headerName])? $this->headers[$headerName]: null;
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Returns cookies set in response
409
+ *
410
+ * @return array
411
+ */
412
+ public function getCookies()
413
+ {
414
+ return $this->cookies;
415
+ }
416
+
417
+ /**
418
+ * Returns the body of the response
419
+ *
420
+ * @return string
421
+ * @throws HTTP_Request2_Exception if body cannot be decoded
422
+ */
423
+ public function getBody()
424
+ {
425
+ if (0 == strlen($this->body) || !$this->bodyEncoded
426
+ || !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))
427
+ ) {
428
+ return $this->body;
429
+
430
+ } else {
431
+ if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
432
+ $oldEncoding = mb_internal_encoding();
433
+ mb_internal_encoding('8bit');
434
+ }
435
+
436
+ try {
437
+ switch (strtolower($this->getHeader('content-encoding'))) {
438
+ case 'gzip':
439
+ $decoded = self::decodeGzip($this->body);
440
+ break;
441
+ case 'deflate':
442
+ $decoded = self::decodeDeflate($this->body);
443
+ }
444
+ } catch (Exception $e) {
445
+ }
446
+
447
+ if (!empty($oldEncoding)) {
448
+ mb_internal_encoding($oldEncoding);
449
+ }
450
+ if (!empty($e)) {
451
+ throw $e;
452
+ }
453
+ return $decoded;
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Get the HTTP version of the response
459
+ *
460
+ * @return string
461
+ */
462
+ public function getVersion()
463
+ {
464
+ return $this->version;
465
+ }
466
+
467
+ /**
468
+ * Decodes the message-body encoded by gzip
469
+ *
470
+ * The real decoding work is done by gzinflate() built-in function, this
471
+ * method only parses the header and checks data for compliance with
472
+ * RFC 1952
473
+ *
474
+ * @param string $data gzip-encoded data
475
+ *
476
+ * @return string decoded data
477
+ * @throws HTTP_Request2_LogicException
478
+ * @throws HTTP_Request2_MessageException
479
+ * @link http://tools.ietf.org/html/rfc1952
480
+ */
481
+ public static function decodeGzip($data)
482
+ {
483
+ $length = strlen($data);
484
+ // If it doesn't look like gzip-encoded data, don't bother
485
+ if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
486
+ return $data;
487
+ }
488
+ if (!function_exists('gzinflate')) {
489
+ throw new HTTP_Request2_LogicException(
490
+ 'Unable to decode body: gzip extension not available',
491
+ HTTP_Request2_Exception::MISCONFIGURATION
492
+ );
493
+ }
494
+ $method = ord(substr($data, 2, 1));
495
+ if (8 != $method) {
496
+ throw new HTTP_Request2_MessageException(
497
+ 'Error parsing gzip header: unknown compression method',
498
+ HTTP_Request2_Exception::DECODE_ERROR
499
+ );
500
+ }
501
+ $flags = ord(substr($data, 3, 1));
502
+ if ($flags & 224) {
503
+ throw new HTTP_Request2_MessageException(
504
+ 'Error parsing gzip header: reserved bits are set',
505
+ HTTP_Request2_Exception::DECODE_ERROR
506
+ );
507
+ }
508
+
509
+ // header is 10 bytes minimum. may be longer, though.
510
+ $headerLength = 10;
511
+ // extra fields, need to skip 'em
512
+ if ($flags & 4) {
513
+ if ($length - $headerLength - 2 < 8) {
514
+ throw new HTTP_Request2_MessageException(
515
+ 'Error parsing gzip header: data too short',
516
+ HTTP_Request2_Exception::DECODE_ERROR
517
+ );
518
+ }
519
+ $extraLength = unpack('v', substr($data, 10, 2));
520
+ if ($length - $headerLength - 2 - $extraLength[1] < 8) {
521
+ throw new HTTP_Request2_MessageException(
522
+ 'Error parsing gzip header: data too short',
523
+ HTTP_Request2_Exception::DECODE_ERROR
524
+ );
525
+ }
526
+ $headerLength += $extraLength[1] + 2;
527
+ }
528
+ // file name, need to skip that
529
+ if ($flags & 8) {
530
+ if ($length - $headerLength - 1 < 8) {
531
+ throw new HTTP_Request2_MessageException(
532
+ 'Error parsing gzip header: data too short',
533
+ HTTP_Request2_Exception::DECODE_ERROR
534
+ );
535
+ }
536
+ $filenameLength = strpos(substr($data, $headerLength), chr(0));
537
+ if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
538
+ throw new HTTP_Request2_MessageException(
539
+ 'Error parsing gzip header: data too short',
540
+ HTTP_Request2_Exception::DECODE_ERROR
541
+ );
542
+ }
543
+ $headerLength += $filenameLength + 1;
544
+ }
545
+ // comment, need to skip that also
546
+ if ($flags & 16) {
547
+ if ($length - $headerLength - 1 < 8) {
548
+ throw new HTTP_Request2_MessageException(
549
+ 'Error parsing gzip header: data too short',
550
+ HTTP_Request2_Exception::DECODE_ERROR
551
+ );
552
+ }
553
+ $commentLength = strpos(substr($data, $headerLength), chr(0));
554
+ if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
555
+ throw new HTTP_Request2_MessageException(
556
+ 'Error parsing gzip header: data too short',
557
+ HTTP_Request2_Exception::DECODE_ERROR
558
+ );
559
+ }
560
+ $headerLength += $commentLength + 1;
561
+ }
562
+ // have a CRC for header. let's check
563
+ if ($flags & 2) {
564
+ if ($length - $headerLength - 2 < 8) {
565
+ throw new HTTP_Request2_MessageException(
566
+ 'Error parsing gzip header: data too short',
567
+ HTTP_Request2_Exception::DECODE_ERROR
568
+ );
569
+ }
570
+ $crcReal = 0xffff & crc32(substr($data, 0, $headerLength));
571
+ $crcStored = unpack('v', substr($data, $headerLength, 2));
572
+ if ($crcReal != $crcStored[1]) {
573
+ throw new HTTP_Request2_MessageException(
574
+ 'Header CRC check failed',
575
+ HTTP_Request2_Exception::DECODE_ERROR
576
+ );
577
+ }
578
+ $headerLength += 2;
579
+ }
580
+ // unpacked data CRC and size at the end of encoded data
581
+ $tmp = unpack('V2', substr($data, -8));
582
+ $dataCrc = $tmp[1];
583
+ $dataSize = $tmp[2];
584
+
585
+ // finally, call the gzinflate() function
586
+ // don't pass $dataSize to gzinflate, see bugs #13135, #14370
587
+ $unpacked = gzinflate(substr($data, $headerLength, -8));
588
+ if (false === $unpacked) {
589
+ throw new HTTP_Request2_MessageException(
590
+ 'gzinflate() call failed',
591
+ HTTP_Request2_Exception::DECODE_ERROR
592
+ );
593
+ } elseif ($dataSize != strlen($unpacked)) {
594
+ throw new HTTP_Request2_MessageException(
595
+ 'Data size check failed',
596
+ HTTP_Request2_Exception::DECODE_ERROR
597
+ );
598
+ } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
599
+ throw new HTTP_Request2_Exception(
600
+ 'Data CRC check failed',
601
+ HTTP_Request2_Exception::DECODE_ERROR
602
+ );
603
+ }
604
+ return $unpacked;
605
+ }
606
+
607
+ /**
608
+ * Decodes the message-body encoded by deflate
609
+ *
610
+ * @param string $data deflate-encoded data
611
+ *
612
+ * @return string decoded data
613
+ * @throws HTTP_Request2_LogicException
614
+ */
615
+ public static function decodeDeflate($data)
616
+ {
617
+ if (!function_exists('gzuncompress')) {
618
+ throw new HTTP_Request2_LogicException(
619
+ 'Unable to decode body: gzip extension not available',
620
+ HTTP_Request2_Exception::MISCONFIGURATION
621
+ );
622
+ }
623
+ // RFC 2616 defines 'deflate' encoding as zlib format from RFC 1950,
624
+ // while many applications send raw deflate stream from RFC 1951.
625
+ // We should check for presence of zlib header and use gzuncompress() or
626
+ // gzinflate() as needed. See bug #15305
627
+ $header = unpack('n', substr($data, 0, 2));
628
+ return (0 == $header[1] % 31)? gzuncompress($data): gzinflate($data);
629
+ }
630
+ }
631
  ?>
vendor/PEAR/HTTP/Request2/SOCKS5.php CHANGED
@@ -1,135 +1,135 @@
1
- <?php
2
- /**
3
- * SOCKS5 proxy connection class
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /** Socket wrapper class used by Socket Adapter */
22
- require_once 'HTTP/Request2/SocketWrapper.php';
23
-
24
- /**
25
- * SOCKS5 proxy connection class (used by Socket Adapter)
26
- *
27
- * @category HTTP
28
- * @package HTTP_Request2
29
- * @author Alexey Borzov <avb@php.net>
30
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
31
- * @version Release: 2.2.1
32
- * @link http://pear.php.net/package/HTTP_Request2
33
- * @link http://pear.php.net/bugs/bug.php?id=19332
34
- * @link http://tools.ietf.org/html/rfc1928
35
- */
36
- class HTTP_Request2_SOCKS5 extends HTTP_Request2_SocketWrapper
37
- {
38
- /**
39
- * Constructor, tries to connect and authenticate to a SOCKS5 proxy
40
- *
41
- * @param string $address Proxy address, e.g. 'tcp://localhost:1080'
42
- * @param int $timeout Connection timeout (seconds)
43
- * @param array $contextOptions Stream context options
44
- * @param string $username Proxy user name
45
- * @param string $password Proxy password
46
- *
47
- * @throws HTTP_Request2_LogicException
48
- * @throws HTTP_Request2_ConnectionException
49
- * @throws HTTP_Request2_MessageException
50
- */
51
- public function __construct(
52
- $address, $timeout = 10, array $contextOptions = array(),
53
- $username = null, $password = null
54
- ) {
55
- parent::__construct($address, $timeout, $contextOptions);
56
-
57
- if (strlen($username)) {
58
- $request = pack('C4', 5, 2, 0, 2);
59
- } else {
60
- $request = pack('C3', 5, 1, 0);
61
- }
62
- $this->write($request);
63
- $response = unpack('Cversion/Cmethod', $this->read(3));
64
- if (5 != $response['version']) {
65
- throw new HTTP_Request2_MessageException(
66
- 'Invalid version received from SOCKS5 proxy: ' . $response['version'],
67
- HTTP_Request2_Exception::MALFORMED_RESPONSE
68
- );
69
- }
70
- switch ($response['method']) {
71
- case 2:
72
- $this->performAuthentication($username, $password);
73
- case 0:
74
- break;
75
- default:
76
- throw new HTTP_Request2_ConnectionException(
77
- "Connection rejected by proxy due to unsupported auth method"
78
- );
79
- }
80
- }
81
-
82
- /**
83
- * Performs username/password authentication for SOCKS5
84
- *
85
- * @param string $username Proxy user name
86
- * @param string $password Proxy password
87
- *
88
- * @throws HTTP_Request2_ConnectionException
89
- * @throws HTTP_Request2_MessageException
90
- * @link http://tools.ietf.org/html/rfc1929
91
- */
92
- protected function performAuthentication($username, $password)
93
- {
94
- $request = pack('C2', 1, strlen($username)) . $username
95
- . pack('C', strlen($password)) . $password;
96
-
97
- $this->write($request);
98
- $response = unpack('Cvn/Cstatus', $this->read(3));
99
- if (1 != $response['vn'] || 0 != $response['status']) {
100
- throw new HTTP_Request2_ConnectionException(
101
- 'Connection rejected by proxy due to invalid username and/or password'
102
- );
103
- }
104
- }
105
-
106
- /**
107
- * Connects to a remote host via proxy
108
- *
109
- * @param string $remoteHost Remote host
110
- * @param int $remotePort Remote port
111
- *
112
- * @throws HTTP_Request2_ConnectionException
113
- * @throws HTTP_Request2_MessageException
114
- */
115
- public function connect($remoteHost, $remotePort)
116
- {
117
- $request = pack('C5', 0x05, 0x01, 0x00, 0x03, strlen($remoteHost))
118
- . $remoteHost . pack('n', $remotePort);
119
-
120
- $this->write($request);
121
- $response = unpack('Cversion/Creply/Creserved', $this->read(1024));
122
- if (5 != $response['version'] || 0 != $response['reserved']) {
123
- throw new HTTP_Request2_MessageException(
124
- 'Invalid response received from SOCKS5 proxy',
125
- HTTP_Request2_Exception::MALFORMED_RESPONSE
126
- );
127
- } elseif (0 != $response['reply']) {
128
- throw new HTTP_Request2_ConnectionException(
129
- "Unable to connect to {$remoteHost}:{$remotePort} through SOCKS5 proxy",
130
- 0, $response['reply']
131
- );
132
- }
133
- }
134
- }
135
  ?>
1
+ <?php
2
+ /**
3
+ * SOCKS5 proxy connection class
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /** Socket wrapper class used by Socket Adapter */
22
+ require_once 'HTTP/Request2/SocketWrapper.php';
23
+
24
+ /**
25
+ * SOCKS5 proxy connection class (used by Socket Adapter)
26
+ *
27
+ * @category HTTP
28
+ * @package HTTP_Request2
29
+ * @author Alexey Borzov <avb@php.net>
30
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
31
+ * @version Release: 2.2.1
32
+ * @link http://pear.php.net/package/HTTP_Request2
33
+ * @link http://pear.php.net/bugs/bug.php?id=19332
34
+ * @link http://tools.ietf.org/html/rfc1928
35
+ */
36
+ class HTTP_Request2_SOCKS5 extends HTTP_Request2_SocketWrapper
37
+ {
38
+ /**
39
+ * Constructor, tries to connect and authenticate to a SOCKS5 proxy
40
+ *
41
+ * @param string $address Proxy address, e.g. 'tcp://localhost:1080'
42
+ * @param int $timeout Connection timeout (seconds)
43
+ * @param array $contextOptions Stream context options
44
+ * @param string $username Proxy user name
45
+ * @param string $password Proxy password
46
+ *
47
+ * @throws HTTP_Request2_LogicException
48
+ * @throws HTTP_Request2_ConnectionException
49
+ * @throws HTTP_Request2_MessageException
50
+ */
51
+ public function __construct(
52
+ $address, $timeout = 10, array $contextOptions = array(),
53
+ $username = null, $password = null
54
+ ) {
55
+ parent::__construct($address, $timeout, $contextOptions);
56
+
57
+ if (strlen($username)) {
58
+ $request = pack('C4', 5, 2, 0, 2);
59
+ } else {
60
+ $request = pack('C3', 5, 1, 0);
61
+ }
62
+ $this->write($request);
63
+ $response = unpack('Cversion/Cmethod', $this->read(3));
64
+ if (5 != $response['version']) {
65
+ throw new HTTP_Request2_MessageException(
66
+ 'Invalid version received from SOCKS5 proxy: ' . $response['version'],
67
+ HTTP_Request2_Exception::MALFORMED_RESPONSE
68
+ );
69
+ }
70
+ switch ($response['method']) {
71
+ case 2:
72
+ $this->performAuthentication($username, $password);
73
+ case 0:
74
+ break;
75
+ default:
76
+ throw new HTTP_Request2_ConnectionException(
77
+ "Connection rejected by proxy due to unsupported auth method"
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Performs username/password authentication for SOCKS5
84
+ *
85
+ * @param string $username Proxy user name
86
+ * @param string $password Proxy password
87
+ *
88
+ * @throws HTTP_Request2_ConnectionException
89
+ * @throws HTTP_Request2_MessageException
90
+ * @link http://tools.ietf.org/html/rfc1929
91
+ */
92
+ protected function performAuthentication($username, $password)
93
+ {
94
+ $request = pack('C2', 1, strlen($username)) . $username
95
+ . pack('C', strlen($password)) . $password;
96
+
97
+ $this->write($request);
98
+ $response = unpack('Cvn/Cstatus', $this->read(3));
99
+ if (1 != $response['vn'] || 0 != $response['status']) {
100
+ throw new HTTP_Request2_ConnectionException(
101
+ 'Connection rejected by proxy due to invalid username and/or password'
102
+ );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Connects to a remote host via proxy
108
+ *
109
+ * @param string $remoteHost Remote host
110
+ * @param int $remotePort Remote port
111
+ *
112
+ * @throws HTTP_Request2_ConnectionException
113
+ * @throws HTTP_Request2_MessageException
114
+ */
115
+ public function connect($remoteHost, $remotePort)
116
+ {
117
+ $request = pack('C5', 0x05, 0x01, 0x00, 0x03, strlen($remoteHost))
118
+ . $remoteHost . pack('n', $remotePort);
119
+
120
+ $this->write($request);
121
+ $response = unpack('Cversion/Creply/Creserved', $this->read(1024));
122
+ if (5 != $response['version'] || 0 != $response['reserved']) {
123
+ throw new HTTP_Request2_MessageException(
124
+ 'Invalid response received from SOCKS5 proxy',
125
+ HTTP_Request2_Exception::MALFORMED_RESPONSE
126
+ );
127
+ } elseif (0 != $response['reply']) {
128
+ throw new HTTP_Request2_ConnectionException(
129
+ "Unable to connect to {$remoteHost}:{$remotePort} through SOCKS5 proxy",
130
+ 0, $response['reply']
131
+ );
132
+ }
133
+ }
134
+ }
135
  ?>
vendor/PEAR/HTTP/Request2/SocketWrapper.php CHANGED
@@ -1,297 +1,297 @@
1
- <?php
2
- /**
3
- * Socket wrapper class used by Socket Adapter
4
- *
5
- * PHP version 5
6
- *
7
- * LICENSE
8
- *
9
- * This source file is subject to BSD 3-Clause License that is bundled
10
- * with this package in the file LICENSE and available at the URL
11
- * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
- *
13
- * @category HTTP
14
- * @package HTTP_Request2
15
- * @author Alexey Borzov <avb@php.net>
16
- * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
- * @link http://pear.php.net/package/HTTP_Request2
19
- */
20
-
21
- /** Exception classes for HTTP_Request2 package */
22
- require_once 'HTTP/Request2/Exception.php';
23
-
24
- /**
25
- * Socket wrapper class used by Socket Adapter
26
- *
27
- * Needed to properly handle connection errors, global timeout support and
28
- * similar things. Loosely based on Net_Socket used by older HTTP_Request.
29
- *
30
- * @category HTTP
31
- * @package HTTP_Request2
32
- * @author Alexey Borzov <avb@php.net>
33
- * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
34
- * @version Release: 2.2.1
35
- * @link http://pear.php.net/package/HTTP_Request2
36
- * @link http://pear.php.net/bugs/bug.php?id=19332
37
- * @link http://tools.ietf.org/html/rfc1928
38
- */
39
- class HTTP_Request2_SocketWrapper
40
- {
41
- /**
42
- * PHP warning messages raised during stream_socket_client() call
43
- * @var array
44
- */
45
- protected $connectionWarnings = array();
46
-
47
- /**
48
- * Connected socket
49
- * @var resource
50
- */
51
- protected $socket;
52
-
53
- /**
54
- * Sum of start time and global timeout, exception will be thrown if request continues past this time
55
- * @var integer
56
- */
57
- protected $deadline;
58
-
59
- /**
60
- * Global timeout value, mostly for exception messages
61
- * @var integer
62
- */
63
- protected $timeout;
64
-
65
- /**
66
- * Class constructor, tries to establish connection
67
- *
68
- * @param string $address Address for stream_socket_client() call,
69
- * e.g. 'tcp://localhost:80'
70
- * @param int $timeout Connection timeout (seconds)
71
- * @param array $contextOptions Context options
72
- *
73
- * @throws HTTP_Request2_LogicException
74
- * @throws HTTP_Request2_ConnectionException
75
- */
76
- public function __construct($address, $timeout, array $contextOptions = array())
77
- {
78
- if (!empty($contextOptions)
79
- && !isset($contextOptions['socket']) && !isset($contextOptions['ssl'])
80
- ) {
81
- // Backwards compatibility with 2.1.0 and 2.1.1 releases
82
- $contextOptions = array('ssl' => $contextOptions);
83
- }
84
- $context = stream_context_create();
85
- foreach ($contextOptions as $wrapper => $options) {
86
- foreach ($options as $name => $value) {
87
- if (!stream_context_set_option($context, $wrapper, $name, $value)) {
88
- throw new HTTP_Request2_LogicException(
89
- "Error setting '{$wrapper}' wrapper context option '{$name}'"
90
- );
91
- }
92
- }
93
- }
94
- set_error_handler(array($this, 'connectionWarningsHandler'));
95
- $this->socket = stream_socket_client(
96
- $address, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context
97
- );
98
- restore_error_handler();
99
- // if we fail to bind to a specified local address (see request #19515),
100
- // connection still succeeds, albeit with a warning. Throw an Exception
101
- // with the warning text in this case as that connection is unlikely
102
- // to be what user wants and as Curl throws an error in similar case.
103
- if ($this->connectionWarnings) {
104
- if ($this->socket) {
105
- fclose($this->socket);
106
- }
107
- $error = $errstr ? $errstr : implode("\n", $this->connectionWarnings);
108
- throw new HTTP_Request2_ConnectionException(
109
- "Unable to connect to {$address}. Error: {$error}", 0, $errno
110
- );
111
- }
112
- }
113
-
114
- /**
115
- * Destructor, disconnects socket
116
- */
117
- public function __destruct()
118
- {
119
- fclose($this->socket);
120
- }
121
-
122
- /**
123
- * Wrapper around fread(), handles global request timeout
124
- *
125
- * @param int $length Reads up to this number of bytes
126
- *
127
- * @return string Data read from socket
128
- * @throws HTTP_Request2_MessageException In case of timeout
129
- */
130
- public function read($length)
131
- {
132
- if ($this->deadline) {
133
- stream_set_timeout($this->socket, max($this->deadline - time(), 1));
134
- }
135
- $data = fread($this->socket, $length);
136
- $this->checkTimeout();
137
- return $data;
138
- }
139
-
140
- /**
141
- * Reads until either the end of the socket or a newline, whichever comes first
142
- *
143
- * Strips the trailing newline from the returned data, handles global
144
- * request timeout. Method idea borrowed from Net_Socket PEAR package.
145
- *
146
- * @param int $bufferSize buffer size to use for reading
147
- * @param int $localTimeout timeout value to use just for this call
148
- * (used when waiting for "100 Continue" response)
149
- *
150
- * @return string Available data up to the newline (not including newline)
151
- * @throws HTTP_Request2_MessageException In case of timeout
152
- */
153
- public function readLine($bufferSize, $localTimeout = null)
154
- {
155
- $line = '';
156
- while (!feof($this->socket)) {
157
- if (null !== $localTimeout) {
158
- stream_set_timeout($this->socket, $localTimeout);
159
- } elseif ($this->deadline) {
160
- stream_set_timeout($this->socket, max($this->deadline - time(), 1));
161
- }
162
-
163
- $line .= @fgets($this->socket, $bufferSize);
164
-
165
- if (null === $localTimeout) {
166
- $this->checkTimeout();
167
-
168
- } else {
169
- $info = stream_get_meta_data($this->socket);
170
- // reset socket timeout if we don't have request timeout specified,
171
- // prevents further calls failing with a bogus Exception
172
- if (!$this->deadline) {
173
- $default = (int)@ini_get('default_socket_timeout');
174
- stream_set_timeout($this->socket, $default > 0 ? $default : PHP_INT_MAX);
175
- }
176
- if ($info['timed_out']) {
177
- throw new HTTP_Request2_MessageException(
178
- "readLine() call timed out", HTTP_Request2_Exception::TIMEOUT
179
- );
180
- }
181
- }
182
- if (substr($line, -1) == "\n") {
183
- return rtrim($line, "\r\n");
184
- }
185
- }
186
- return $line;
187
- }
188
-
189
- /**
190
- * Wrapper around fwrite(), handles global request timeout
191
- *
192
- * @param string $data String to be written
193
- *
194
- * @return int
195
- * @throws HTTP_Request2_MessageException
196
- */
197
- public function write($data)
198
- {
199
- if ($this->deadline) {
200
- stream_set_timeout($this->socket, max($this->deadline - time(), 1));
201
- }
202
- $written = fwrite($this->socket, $data);
203
- $this->checkTimeout();
204
- // http://www.php.net/manual/en/function.fwrite.php#96951
205
- if ($written < strlen($data)) {
206
- throw new HTTP_Request2_MessageException('Error writing request');
207
- }
208
- return $written;
209
- }
210
-
211
- /**
212
- * Tests for end-of-file on a socket
213
- *
214
- * @return bool
215
- */
216
- public function eof()
217
- {
218
- return feof($this->socket);
219
- }
220
-
221
- /**
222
- * Sets request deadline
223
- *
224
- * @param int $deadline Exception will be thrown if request continues
225
- * past this time
226
- * @param int $timeout Original request timeout value, to use in
227
- * Exception message
228
- */
229
- public function setDeadline($deadline, $timeout)
230
- {
231
- $this->deadline = $deadline;
232
- $this->timeout = $timeout;
233
- }
234
-
235
- /**
236
- * Turns on encryption on a socket
237
- *
238
- * @throws HTTP_Request2_ConnectionException
239
- */
240
- public function enableCrypto()
241
- {
242
- $modes = array(
243
- STREAM_CRYPTO_METHOD_TLS_CLIENT,
244
- STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
245
- STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
246
- STREAM_CRYPTO_METHOD_SSLv2_CLIENT
247
- );
248
-
249
- foreach ($modes as $mode) {
250
- if (stream_socket_enable_crypto($this->socket, true, $mode)) {
251
- return;
252
- }
253
- }
254
- throw new HTTP_Request2_ConnectionException(
255
- 'Failed to enable secure connection when connecting through proxy'
256
- );
257
- }
258
-
259
- /**
260
- * Throws an Exception if stream timed out
261
- *
262
- * @throws HTTP_Request2_MessageException
263
- */
264
- protected function checkTimeout()
265
- {
266
- $info = stream_get_meta_data($this->socket);
267
- if ($info['timed_out'] || $this->deadline && time() > $this->deadline) {
268
- $reason = $this->deadline
269
- ? "after {$this->timeout} second(s)"
270
- : 'due to default_socket_timeout php.ini setting';
271
- throw new HTTP_Request2_MessageException(
272
- "Request timed out {$reason}", HTTP_Request2_Exception::TIMEOUT
273
- );
274
- }
275
- }
276
-
277
- /**
278
- * Error handler to use during stream_socket_client() call
279
- *
280
- * One stream_socket_client() call may produce *multiple* PHP warnings
281
- * (especially OpenSSL-related), we keep them in an array to later use for
282
- * the message of HTTP_Request2_ConnectionException
283
- *
284
- * @param int $errno error level
285
- * @param string $errstr error message
286
- *
287
- * @return bool
288
- */
289
- protected function connectionWarningsHandler($errno, $errstr)
290
- {
291
- if ($errno & E_WARNING) {
292
- array_unshift($this->connectionWarnings, $errstr);
293
- }
294
- return true;
295
- }
296
- }
297
- ?>
1
+ <?php
2
+ /**
3
+ * Socket wrapper class used by Socket Adapter
4
+ *
5
+ * PHP version 5
6
+ *
7
+ * LICENSE
8
+ *
9
+ * This source file is subject to BSD 3-Clause License that is bundled
10
+ * with this package in the file LICENSE and available at the URL
11
+ * https://raw.github.com/pear/HTTP_Request2/trunk/docs/LICENSE
12
+ *
13
+ * @category HTTP
14
+ * @package HTTP_Request2
15
+ * @author Alexey Borzov <avb@php.net>
16
+ * @copyright 2008-2014 Alexey Borzov <avb@php.net>
17
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
18
+ * @link http://pear.php.net/package/HTTP_Request2
19
+ */
20
+
21
+ /** Exception classes for HTTP_Request2 package */
22
+ require_once 'HTTP/Request2/Exception.php';
23
+
24
+ /**
25
+ * Socket wrapper class used by Socket Adapter
26
+ *
27
+ * Needed to properly handle connection errors, global timeout support and
28
+ * similar things. Loosely based on Net_Socket used by older HTTP_Request.
29
+ *
30
+ * @category HTTP
31
+ * @package HTTP_Request2
32
+ * @author Alexey Borzov <avb@php.net>
33
+ * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
34
+ * @version Release: 2.2.1
35
+ * @link http://pear.php.net/package/HTTP_Request2
36
+ * @link http://pear.php.net/bugs/bug.php?id=19332
37
+ * @link http://tools.ietf.org/html/rfc1928
38
+ */
39
+ class HTTP_Request2_SocketWrapper
40
+ {
41
+ /**
42
+ * PHP warning messages raised during stream_socket_client() call
43
+ * @var array
44
+ */
45
+ protected $connectionWarnings = array();
46
+
47
+ /**
48
+ * Connected socket
49
+ * @var resource
50
+ */
51
+ protected $socket;
52
+
53
+ /**
54
+ * Sum of start time and global timeout, exception will be thrown if request continues past this time
55
+ * @var integer
56
+ */
57
+ protected $deadline;
58
+
59
+ /**
60
+ * Global timeout value, mostly for exception messages
61
+ * @var integer
62
+ */
63
+ protected $timeout;
64
+
65
+ /**
66
+ * Class constructor, tries to establish connection
67
+ *
68
+ * @param string $address Address for stream_socket_client() call,
69
+ * e.g. 'tcp://localhost:80'
70
+ * @param int $timeout Connection timeout (seconds)
71
+ * @param array $contextOptions Context options
72
+ *
73
+ * @throws HTTP_Request2_LogicException
74
+ * @throws HTTP_Request2_ConnectionException
75
+ */
76
+ public function __construct($address, $timeout, array $contextOptions = array())
77
+ {
78
+ if (!empty($contextOptions)
79
+ && !isset($contextOptions['socket']) && !isset($contextOptions['ssl'])
80
+ ) {
81
+ // Backwards compatibility with 2.1.0 and 2.1.1 releases
82
+ $contextOptions = array('ssl' => $contextOptions);
83
+ }
84
+ $context = stream_context_create();
85
+ foreach ($contextOptions as $wrapper => $options) {
86
+ foreach ($options as $name => $value) {
87
+ if (!stream_context_set_option($context, $wrapper, $name, $value)) {
88
+ throw new HTTP_Request2_LogicException(
89
+ "Error setting '{$wrapper}' wrapper context option '{$name}'"
90
+ );
91
+ }
92
+ }
93
+ }
94
+ set_error_handler(array($this, 'connectionWarningsHandler'));
95
+ $this->socket = stream_socket_client(
96
+ $address, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context
97
+ );
98
+ restore_error_handler();
99
+ // if we fail to bind to a specified local address (see request #19515),
100
+ // connection still succeeds, albeit with a warning. Throw an Exception
101
+ // with the warning text in this case as that connection is unlikely
102
+ // to be what user wants and as Curl throws an error in similar case.
103
+ if ($this->connectionWarnings) {
104
+ if ($this->socket) {
105
+ fclose($this->socket);
106
+ }
107
+ $error = $errstr ? $errstr : implode("\n", $this->connectionWarnings);
108
+ throw new HTTP_Request2_ConnectionException(
109
+ "Unable to connect to {$address}. Error: {$error}", 0, $errno
110
+ );
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Destructor, disconnects socket
116
+ */
117
+ public function __destruct()
118
+ {
119
+ fclose($this->socket);
120
+ }
121
+
122
+ /**
123
+ * Wrapper around fread(), handles global request timeout
124
+ *
125
+ * @param int $length Reads up to this number of bytes
126
+ *
127
+ * @return string Data read from socket
128
+ * @throws HTTP_Request2_MessageException In case of timeout
129
+ */
130
+ public function read($length)
131
+ {
132
+ if ($this->deadline) {
133
+ stream_set_timeout($this->socket, max($this->deadline - time(), 1));
134
+ }
135
+ $data = fread($this->socket, $length);
136
+ $this->checkTimeout();
137
+ return $data;
138
+ }
139
+
140
+ /**
141
+ * Reads until either the end of the socket or a newline, whichever comes first
142
+ *
143
+ * Strips the trailing newline from the returned data, handles global
144
+ * request timeout. Method idea borrowed from Net_Socket PEAR package.
145
+ *
146
+ * @param int $bufferSize buffer size to use for reading
147
+ * @param int $localTimeout timeout value to use just for this call
148
+ * (used when waiting for "100 Continue" response)
149
+ *
150
+ * @return string Available data up to the newline (not including newline)
151
+ * @throws HTTP_Request2_MessageException In case of timeout
152
+ */
153
+ public function readLine($bufferSize, $localTimeout = null)
154
+ {
155
+ $line = '';
156
+ while (!feof($this->socket)) {
157
+ if (null !== $localTimeout) {
158
+ stream_set_timeout($this->socket, $localTimeout);
159
+ } elseif ($this->deadline) {
160
+ stream_set_timeout($this->socket, max($this->deadline - time(), 1));
161
+ }
162
+
163
+ $line .= @fgets($this->socket, $bufferSize);
164
+
165
+ if (null === $localTimeout) {
166
+ $this->checkTimeout();
167
+
168
+ } else {
169
+ $info = stream_get_meta_data($this->socket);
170
+ // reset socket timeout if we don't have request timeout specified,
171
+ // prevents further calls failing with a bogus Exception
172
+ if (!$this->deadline) {
173
+ $default = (int)@ini_get('default_socket_timeout');
174
+ stream_set_timeout($this->socket, $default > 0 ? $default : PHP_INT_MAX);
175
+ }
176
+ if ($info['timed_out']) {
177
+ throw new HTTP_Request2_MessageException(
178
+ "readLine() call timed out", HTTP_Request2_Exception::TIMEOUT
179
+ );
180
+ }
181
+ }
182
+ if (substr($line, -1) == "\n") {
183
+ return rtrim($line, "\r\n");
184
+ }
185
+ }
186
+ return $line;
187
+ }
188
+
189
+ /**
190
+ * Wrapper around fwrite(), handles global request timeout
191
+ *
192
+ * @param string $data String to be written
193
+ *
194
+ * @return int
195
+ * @throws HTTP_Request2_MessageException
196
+ */
197
+ public function write($data)
198
+ {
199
+ if ($this->deadline) {
200
+ stream_set_timeout($this->socket, max($this->deadline - time(), 1));
201
+ }
202
+ $written = fwrite($this->socket, $data);
203
+ $this->checkTimeout();
204
+ // http://www.php.net/manual/en/function.fwrite.php#96951
205
+ if ($written < strlen($data)) {
206
+ throw new HTTP_Request2_MessageException('Error writing request');
207
+ }
208
+ return $written;
209
+ }
210
+
211
+ /**
212
+ * Tests for end-of-file on a socket
213
+ *
214
+ * @return bool
215
+ */
216
+ public function eof()
217
+ {
218
+ return feof($this->socket);
219
+ }
220
+
221
+ /**
222
+ * Sets request deadline
223
+ *
224
+ * @param int $deadline Exception will be thrown if request continues
225
+ * past this time
226
+ * @param int $timeout Original request timeout value, to use in
227
+ * Exception message
228
+ */
229
+ public function setDeadline($deadline, $timeout)
230
+ {
231
+ $this->deadline = $deadline;
232
+ $this->timeout = $timeout;
233
+ }
234
+
235
+ /**
236
+ * Turns on encryption on a socket
237
+ *
238
+ * @throws HTTP_Request2_ConnectionException
239
+ */
240
+ public function enableCrypto()
241
+ {
242
+ $modes = array(
243
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
244
+ STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
245
+ STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
246
+ STREAM_CRYPTO_METHOD_SSLv2_CLIENT
247
+ );
248
+
249
+ foreach ($modes as $mode) {
250
+ if (stream_socket_enable_crypto($this->socket, true, $mode)) {
251
+ return;
252
+ }
253
+ }
254
+ throw new HTTP_Request2_ConnectionException(
255
+ 'Failed to enable secure connection when connecting through proxy'
256
+ );
257
+ }
258
+
259
+ /**
260
+ * Throws an Exception if stream timed out
261
+ *
262
+ * @throws HTTP_Request2_MessageException
263
+ */
264
+ protected function checkTimeout()
265
+ {
266
+ $info = stream_get_meta_data($this->socket);
267
+ if ($info['timed_out'] || $this->deadline && time() > $this->deadline) {
268
+ $reason = $this->deadline
269
+ ? "after {$this->timeout} second(s)"
270
+ : 'due to default_socket_timeout php.ini setting';
271
+ throw new HTTP_Request2_MessageException(
272
+ "Request timed out {$reason}", HTTP_Request2_Exception::TIMEOUT
273
+ );
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Error handler to use during stream_socket_client() call
279
+ *
280
+ * One stream_socket_client() call may produce *multiple* PHP warnings
281
+ * (especially OpenSSL-related), we keep them in an array to later use for
282
+ * the message of HTTP_Request2_ConnectionException
283
+ *
284
+ * @param int $errno error level
285
+ * @param string $errstr error message
286
+ *
287
+ * @return bool
288
+ */
289
+ protected function connectionWarningsHandler($errno, $errstr)
290
+ {
291
+ if ($errno & E_WARNING) {
292
+ array_unshift($this->connectionWarnings, $errstr);
293
+ }
294
+ return true;
295
+ }
296
+ }
297
+ ?>