2016-11-28 21:52:15 -08:00
< ? php
if ( ! defined ( 'UPDRAFTPLUS_DIR' )) die ( 'No direct access allowed.' );
2018-01-26 15:50:15 +01:00
// Converted to array options: yes
// Converted to job_options: yes
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
// Migrate options to new-style storage - May 2014
2016-11-28 21:52:15 -08:00
if ( ! is_array ( UpdraftPlus_Options :: get_updraft_option ( 'updraft_ftp' )) && '' != UpdraftPlus_Options :: get_updraft_option ( 'updraft_server_address' , '' )) {
$opts = array (
'user' => UpdraftPlus_Options :: get_updraft_option ( 'updraft_ftp_login' ),
'pass' => UpdraftPlus_Options :: get_updraft_option ( 'updraft_ftp_pass' ),
'host' => UpdraftPlus_Options :: get_updraft_option ( 'updraft_server_address' ),
'path' => UpdraftPlus_Options :: get_updraft_option ( 'updraft_ftp_remote_path' ),
'passive' => true
);
UpdraftPlus_Options :: update_updraft_option ( 'updraft_ftp' , $opts );
UpdraftPlus_Options :: delete_updraft_option ( 'updraft_server_address' );
UpdraftPlus_Options :: delete_updraft_option ( 'updraft_ftp_pass' );
UpdraftPlus_Options :: delete_updraft_option ( 'updraft_ftp_remote_path' );
UpdraftPlus_Options :: delete_updraft_option ( 'updraft_ftp_login' );
}
2018-01-26 15:50:15 +01:00
if ( ! class_exists ( 'UpdraftPlus_BackupModule' )) require_once ( UPDRAFTPLUS_DIR . '/methods/backup-module.php' );
class UpdraftPlus_BackupModule_ftp extends UpdraftPlus_BackupModule {
/**
* Get FTP object with parameters set
*
* @ param string $server Specify Server
* @ param string $user Specify Username
* @ param string $pass Specify Password
* @ param boolean $disable_ssl Indicate whether to disable SSL
* @ param boolean $disable_verify Indicate whether to disable verifiction
* @ param boolean $use_server_certs Indicate whether to use server certificates
* @ param boolean $passive Indicate whether to use passive FTP mode
* @ return array
*/
2016-11-28 21:52:15 -08:00
private function getFTP ( $server , $user , $pass , $disable_ssl = false , $disable_verify = true , $use_server_certs = false , $passive = true ) {
2018-01-26 15:50:15 +01:00
if ( '' == trim ( $server ) || '' == trim ( $user ) || '' == trim ( $pass )) return new WP_Error ( 'no_settings' , sprintf ( __ ( 'No %s settings were found' , 'updraftplus' ), 'FTP' ));
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( ! class_exists ( 'UpdraftPlus_ftp_wrapper' )) include_once ( UPDRAFTPLUS_DIR . '/includes/ftp.class.php' );
2016-11-28 21:52:15 -08:00
$port = 21 ;
if ( preg_match ( '/^(.*):(\d+)$/' , $server , $matches )) {
$server = $matches [ 1 ];
$port = $matches [ 2 ];
}
$ftp = new UpdraftPlus_ftp_wrapper ( $server , $user , $pass , $port );
if ( $disable_ssl ) $ftp -> ssl = false ;
$ftp -> use_server_certs = $use_server_certs ;
$ftp -> disable_verify = $disable_verify ;
$ftp -> passive = ( $passive ) ? true : false ;
return $ftp ;
}
2018-01-26 15:50:15 +01:00
/**
* WordPress options filter , sanitising the FTP options saved from the options page
*
* @ param Array $settings - the options , prior to sanitisation
*
* @ return Array - the sanitised options for saving
*/
public function options_filter ( $settings ) {
if ( is_array ( $settings ) && ! empty ( $settings [ 'version' ]) && ! empty ( $settings [ 'settings' ])) {
foreach ( $settings [ 'settings' ] as $instance_id => $instance_settings ) {
if ( ! empty ( $instance_settings [ 'host' ]) && preg_match ( '#ftp(es|s)?://(.*)#i' , $instance_settings [ 'host' ], $matches )) {
$settings [ 'settings' ][ $instance_id ][ 'host' ] = rtrim ( $matches [ 2 ], " / \t \n \r \0 x0B " );
}
if ( isset ( $instance_settings [ 'pass' ])) {
$settings [ 'settings' ][ $instance_id ][ 'pass' ] = trim ( $instance_settings [ 'pass' ], " \n \r \0 \x0B " );
}
}
}
return $settings ;
}
public function get_supported_features () {
// The 'multi_options' options format is handled via only accessing options via $this->get_options()
return array ( 'multi_options' , 'config_templates' , 'multi_storage' );
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
public function get_default_options () {
return array (
'host' => '' ,
'user' => '' ,
'pass' => '' ,
'path' => '' ,
'passive' => 1
);
}
2016-11-28 21:52:15 -08:00
public function backup ( $backup_array ) {
2018-01-26 15:50:15 +01:00
global $updraftplus ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$opts = $this -> get_options ();
2016-11-28 21:52:15 -08:00
$ftp = $this -> getFTP (
$opts [ 'host' ],
$opts [ 'user' ],
$opts [ 'pass' ],
$updraftplus -> get_job_option ( 'updraft_ssl_nossl' ),
$updraftplus -> get_job_option ( 'updraft_ssl_disableverify' ),
$updraftplus -> get_job_option ( 'updraft_ssl_useservercerts' ),
$opts [ 'passive' ]
);
if ( is_wp_error ( $ftp ) || ! $ftp -> connect ()) {
if ( is_wp_error ( $ftp )) {
$updraftplus -> log_wp_error ( $ftp );
} else {
$updraftplus -> log ( " FTP Failure: we did not successfully log in with those credentials. " );
}
2018-01-26 15:50:15 +01:00
$updraftplus -> log ( sprintf ( __ ( " %s login failure " , 'updraftplus' ), 'FTP' ), 'error' );
2016-11-28 21:52:15 -08:00
return false ;
}
2018-01-26 15:50:15 +01:00
// $ftp->make_dir(); we may need to recursively create dirs? TODO
2016-11-28 21:52:15 -08:00
$updraft_dir = $updraftplus -> backups_dir_location () . '/' ;
$ftp_remote_path = trailingslashit ( $opts [ 'path' ]);
2018-01-26 15:50:15 +01:00
foreach ( $backup_array as $file ) {
2016-11-28 21:52:15 -08:00
$fullpath = $updraft_dir . $file ;
$updraftplus -> log ( " FTP upload attempt: $file -> ftp:// " . $opts [ 'user' ] . " @ " . $opts [ 'host' ] . " / ${ ftp_remote_path}${file } " );
$timer_start = microtime ( true );
2018-01-26 15:50:15 +01:00
$size_k = round ( filesize ( $fullpath ) / 1024 , 1 );
// Note :Setting $resume to true unnecessarily is not meant to be a problem. Only ever (Feb 2014) seen one weird FTP server where calling SIZE on a non-existent file did create a problem. So, this code just helps that case. (the check for non-empty upload_status[p] is being cautious.
2016-11-28 21:52:15 -08:00
$upload_status = $updraftplus -> jobdata_get ( 'uploading_substatus' );
2018-01-26 15:50:15 +01:00
if ( 0 == $updraftplus -> current_resumption || ( is_array ( $upload_status ) && ! empty ( $upload_status [ 'p' ]) && 0 == $upload_status [ 'p' ])) {
2016-11-28 21:52:15 -08:00
$resume = false ;
} else {
$resume = true ;
}
if ( $ftp -> put ( $fullpath , $ftp_remote_path . $file , FTP_BINARY , $resume , $updraftplus )) {
2018-01-26 15:50:15 +01:00
$updraftplus -> log ( " FTP upload attempt successful ( " . $size_k . " KB in " . ( round ( microtime ( true ) - $timer_start , 2 )) . 's)' );
2016-11-28 21:52:15 -08:00
$updraftplus -> uploaded_file ( $file );
} else {
2018-01-26 15:50:15 +01:00
$updraftplus -> log ( " ERROR: FTP upload failed " );
$updraftplus -> log ( sprintf ( __ ( " %s upload failed " , 'updraftplus' ), 'FTP' ), 'error' );
2016-11-28 21:52:15 -08:00
}
}
return array ( 'ftp_object' => $ftp , 'ftp_remote_path' => $ftp_remote_path );
}
public function listfiles ( $match = 'backup_' ) {
global $updraftplus ;
2018-01-26 15:50:15 +01:00
$opts = $this -> get_options ();
2016-11-28 21:52:15 -08:00
$ftp = $this -> getFTP (
$opts [ 'host' ],
$opts [ 'user' ],
$opts [ 'pass' ],
$updraftplus -> get_job_option ( 'updraft_ssl_nossl' ),
$updraftplus -> get_job_option ( 'updraft_ssl_disableverify' ),
$updraftplus -> get_job_option ( 'updraft_ssl_useservercerts' ),
$opts [ 'passive' ]
);
if ( is_wp_error ( $ftp )) return $ftp ;
2018-01-26 15:50:15 +01:00
if ( ! $ftp -> connect ()) return new WP_Error ( 'ftp_login_failed' , sprintf ( __ ( " %s login failure " , 'updraftplus' ), 'FTP' ));
2016-11-28 21:52:15 -08:00
$ftp_remote_path = $opts [ 'path' ];
if ( $ftp_remote_path ) $ftp_remote_path = trailingslashit ( $ftp_remote_path );
$dirlist = $ftp -> dir_list ( $ftp_remote_path );
if ( ! is_array ( $dirlist )) return array ();
$results = array ();
foreach ( $dirlist as $k => $path ) {
if ( $ftp_remote_path ) {
// Feb 2015 - found a case where the directory path was not prefixed on
if ( 0 !== strpos ( $path , $ftp_remote_path ) && ( false !== strpos ( '/' , $ftp_remote_path ) && false !== strpos ( '\\' , $ftp_remote_path ))) continue ;
if ( 0 === strpos ( $path , $ftp_remote_path )) $path = substr ( $path , strlen ( $ftp_remote_path ));
// if (0 !== strpos($path, $ftp_remote_path)) continue;
// $path = substr($path, strlen($ftp_remote_path));
if ( 0 === strpos ( $path , $match )) $results [][ 'name' ] = $path ;
} else {
if ( '/' == substr ( $path , 0 , 1 )) $path = substr ( $path , 1 );
if ( false !== strpos ( $path , '/' )) continue ;
if ( 0 === strpos ( $path , $match )) $results [][ 'name' ] = $path ;
}
unset ( $dirlist [ $k ]);
}
2018-01-26 15:50:15 +01:00
// ftp_nlist() doesn't return file sizes. rawlist() does, but is tricky to parse. So, we get the sizes manually.
2016-11-28 21:52:15 -08:00
foreach ( $results as $ind => $name ) {
$size = $ftp -> size ( $ftp_remote_path . $name [ 'name' ]);
if ( 0 === $size ) {
unset ( $results [ $ind ]);
} elseif ( $size > 0 ) {
$results [ $ind ][ 'size' ] = $size ;
}
}
return $results ;
}
public function delete ( $files , $ftparr = array (), $sizeinfo = array ()) {
global $updraftplus ;
2018-01-26 15:50:15 +01:00
if ( is_string ( $files )) $files = array ( $files );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$opts = $this -> get_options ();
2016-11-28 21:52:15 -08:00
if ( is_array ( $ftparr ) && isset ( $ftparr [ 'ftp_object' ])) {
$ftp = $ftparr [ 'ftp_object' ];
} else {
$ftp = $this -> getFTP (
$opts [ 'host' ],
$opts [ 'user' ],
$opts [ 'pass' ],
$updraftplus -> get_job_option ( 'updraft_ssl_nossl' ),
$updraftplus -> get_job_option ( 'updraft_ssl_disableverify' ),
$updraftplus -> get_job_option ( 'updraft_ssl_useservercerts' ),
$opts [ 'passive' ]
);
if ( is_wp_error ( $ftp ) || ! $ftp -> connect ()) {
if ( is_wp_error ( $ftp )) $updraftplus -> log_wp_error ( $ftp );
$updraftplus -> log ( " FTP Failure: we did not successfully log in with those credentials (host= " . $opts [ 'host' ] . " ). " );
return false ;
}
}
$ftp_remote_path = isset ( $ftparr [ 'ftp_remote_path' ]) ? $ftparr [ 'ftp_remote_path' ] : trailingslashit ( $opts [ 'path' ]);
$ret = true ;
foreach ( $files as $file ) {
if ( @ $ftp -> delete ( $ftp_remote_path . $file )) {
$updraftplus -> log ( " FTP delete: succeeded ( ${ ftp_remote_path}${file } ) " );
} else {
$updraftplus -> log ( " FTP delete: failed ( ${ ftp_remote_path}${file } ) " );
$ret = false ;
}
}
return $ret ;
}
public function download ( $file ) {
global $updraftplus ;
2018-01-26 15:50:15 +01:00
$opts = $this -> get_options ();
2016-11-28 21:52:15 -08:00
$ftp = $this -> getFTP (
$opts [ 'host' ],
$opts [ 'user' ],
$opts [ 'pass' ],
$updraftplus -> get_job_option ( 'updraft_ssl_nossl' ),
$updraftplus -> get_job_option ( 'updraft_ssl_disableverify' ),
$updraftplus -> get_job_option ( 'updraft_ssl_useservercerts' ),
$opts [ 'passive' ]
);
if ( is_wp_error ( $ftp )) return $ftp ;
if ( ! $ftp -> connect ()) {
$updraftplus -> log ( " FTP Failure: we did not successfully log in with those credentials. " );
2018-01-26 15:50:15 +01:00
$updraftplus -> log ( sprintf ( __ ( " %s login failure " , 'updraftplus' ), 'FTP' ), 'error' );
2016-11-28 21:52:15 -08:00
return false ;
}
2018-01-26 15:50:15 +01:00
// $ftp->make_dir(); we may need to recursively create dirs? TODO
2016-11-28 21:52:15 -08:00
$ftp_remote_path = trailingslashit ( $opts [ 'path' ]);
$fullpath = $updraftplus -> backups_dir_location () . '/' . $file ;
$resume = false ;
if ( file_exists ( $fullpath )) {
$resume = true ;
$updraftplus -> log ( " File already exists locally; will resume: size: " . filesize ( $fullpath ));
}
return $ftp -> get ( $fullpath , $ftp_remote_path . $file , FTP_BINARY , $resume , $updraftplus );
}
private function ftp_possible () {
$funcs_disabled = array ();
foreach ( array ( 'ftp_connect' , 'ftp_login' , 'ftp_nb_fput' ) as $func ) {
if ( ! function_exists ( $func )) $funcs_disabled [ 'ftp' ][] = $func ;
}
$funcs_disabled = apply_filters ( 'updraftplus_ftp_possible' , $funcs_disabled );
return ( 0 == count ( $funcs_disabled )) ? true : $funcs_disabled ;
}
2018-01-26 15:50:15 +01:00
/**
* Get the pre configuration template
*
* @ return String - the template
*/
public function get_pre_configuration_template () {
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
global $updraftplus_admin ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$classes = $this -> get_css_classes ( false );
2016-11-28 21:52:15 -08:00
?>
2018-01-26 15:50:15 +01:00
< tr class = " <?php echo $classes . ' ' . 'ftp_pre_config_container';?> " >
< td colspan = " 2 " >
< h3 >< ? php echo 'FTP' ; ?> </h3>
< ? php
$possible = $this -> ftp_possible ();
if ( is_array ( $possible )) {
// Check requirements.
global $updraftplus_admin ;
$trans = array (
'ftp' => __ ( 'regular non-encrypted FTP' , 'updraftplus' ),
'ftpsslimplicit' => __ ( 'encrypted FTP (implicit encryption)' , 'updraftplus' ),
'ftpsslexplicit' => __ ( 'encrypted FTP (explicit encryption)' , 'updraftplus' )
);
foreach ( $possible as $type => $missing ) {
$updraftplus_admin -> show_double_warning ( '<strong>' . __ ( 'Warning' , 'updraftplus' ) . ':</strong> ' . sprintf ( __ ( " Your web server's PHP installation has these functions disabled: %s. " , 'updraftplus' ), implode ( ', ' , $missing )) . ' ' . sprintf ( __ ( 'Your hosting company must enable these functions before %s can work.' , 'updraftplus' ), $trans [ $type ]), 'ftp' );
}
}
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
?>
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
< em >< ? php echo '<p>' . apply_filters ( 'updraft_sftp_ftps_notice' , '<strong>' . htmlspecialchars ( __ ( 'Only non-encrypted FTP is supported by regular UpdraftPlus.' )) . '</strong> <a href="' . apply_filters ( " updraftplus_com_link " , " https://updraftplus.com/shop/sftp/ " ) . '">' . __ ( 'If you want encryption (e.g. you are storing sensitive business data), then an add-on is available.' , 'updraftplus' )) . '</a></p>' ; ?> </em>
</ td >
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
< ? php
}
/**
* Get the configuration template
*
* @ return String - the template , ready for substitutions to be carried out
*/
public function get_configuration_template () {
ob_start ();
$classes = $this -> get_css_classes ();
?>
< tr class = " <?php echo $classes ;?> " >
< th >< ? php _e ( 'FTP server' , 'updraftplus' ); ?> :</th>
< td >< input type = " text " size = " 40 " data - updraft_settings_test = " server " < ? php $this -> output_settings_field_name_and_id ( 'host' ); ?> value="{{host}}" /></td>
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
< tr class = " <?php echo $classes ;?> " >
< th >< ? php _e ( 'FTP login' , 'updraftplus' ); ?> :</th>
< td >< input type = " text " size = " 40 " data - updraft_settings_test = " login " < ? php $this -> output_settings_field_name_and_id ( 'user' ); ?> value="{{user}}" /></td>
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
< tr class = " <?php echo $classes ;?> " >
< th >< ? php _e ( 'FTP password' , 'updraftplus' ); ?> :</th>
< td >< input type = " <?php echo apply_filters('updraftplus_admin_secret_field_type', 'password'); ?> " size = " 40 " data - updraft_settings_test = " pass " < ? php $this -> output_settings_field_name_and_id ( 'pass' ); ?> value="{{pass}}" /></td>
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
< tr class = " <?php echo $classes ;?> " >
< th >< ? php _e ( 'Remote path' , 'updraftplus' ); ?> :</th>
< td >< input type = " text " size = " 64 " data - updraft_settings_test = " path " < ? php $this -> output_settings_field_name_and_id ( 'path' ); ?> value="{{path}}" /> <em><?php _e('Needs to already exist', 'updraftplus');?></em></td>
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
< tr class = " <?php echo $classes ;?> " >
< th >< ? php _e ( 'Passive mode' , 'updraftplus' ); ?> :</th>
2016-11-28 21:52:15 -08:00
< td >
2018-01-26 15:50:15 +01:00
< input type = " checkbox " data - updraft_settings_test = " passive " < ? php $this -> output_settings_field_name_and_id ( 'passive' ); ?> value="1" {{#ifeq '1' passive}}checked="checked"{{/ifeq}}> <br><em><?php echo __('Almost all FTP servers will want passive mode; but if you need active mode, then uncheck this.', 'updraftplus');?></em></td>
2016-11-28 21:52:15 -08:00
</ tr >
2018-01-26 15:50:15 +01:00
2016-11-28 21:52:15 -08:00
< ? php
2018-01-26 15:50:15 +01:00
echo $this -> get_test_button_html ( 'FTP' );
return ob_get_clean ();
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
/**
* Perform a test of user - supplied credentials , and echo the result
*
* @ param Array $posted_settings - settings to test
*/
2016-11-28 21:52:15 -08:00
public function credentials_test ( $posted_settings ) {
$server = $posted_settings [ 'server' ];
2018-01-26 15:50:15 +01:00
$login = $posted_settings [ 'login' ];
$pass = $posted_settings [ 'pass' ];
2016-11-28 21:52:15 -08:00
$path = $posted_settings [ 'path' ];
$nossl = $posted_settings [ 'nossl' ];
$passive = empty ( $posted_settings [ 'passive' ]) ? false : true ;
$disable_verify = $posted_settings [ 'disableverify' ];
$use_server_certs = $posted_settings [ 'useservercerts' ];
if ( empty ( $server )) {
2018-01-26 15:50:15 +01:00
_e ( 'Failure: No server details were given.' , 'updraftplus' );
2016-11-28 21:52:15 -08:00
return ;
}
if ( empty ( $login )) {
2018-01-26 15:50:15 +01:00
printf ( __ ( 'Failure: No %s was given.' , 'updraftplus' ), __ ( 'login' , 'updraftplus' ));
2016-11-28 21:52:15 -08:00
return ;
}
if ( empty ( $pass )) {
2018-01-26 15:50:15 +01:00
printf ( __ ( 'Failure: No %s was given.' , 'updraftplus' ), __ ( 'password' , 'updraftplus' ));
2016-11-28 21:52:15 -08:00
return ;
}
if ( preg_match ( '#ftp(es|s)?://(.*)#i' , $server , $matches )) $server = untrailingslashit ( $matches [ 2 ]);
2018-01-26 15:50:15 +01:00
// $ftp = $this->getFTP($server, $login, $pass, $nossl, $disable_verify, $use_server_certs);
2016-11-28 21:52:15 -08:00
$ftp = $this -> getFTP ( $server , $login , $pass , $nossl , $disable_verify , $use_server_certs , $passive );
if ( ! $ftp -> connect ()) {
_e ( 'Failure: we did not successfully log in with those credentials.' , 'updraftplus' );
return ;
}
2018-01-26 15:50:15 +01:00
// $ftp->make_dir(); we may need to recursively create dirs? TODO
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$file = md5 ( rand ( 0 , 99999999 )) . '.tmp' ;
2016-11-28 21:52:15 -08:00
$fullpath = trailingslashit ( $path ) . $file ;
if ( $ftp -> put ( ABSPATH . WPINC . '/version.php' , $fullpath , FTP_BINARY , false , true )) {
echo __ ( " Success: we successfully logged in, and confirmed our ability to create a file in the given directory (login type: " , 'updraftplus' ) . " " . $ftp -> login_type . ')' ;
@ $ftp -> delete ( $fullpath );
} else {
_e ( 'Failure: we successfully logged in, but were not able to create a file in the given directory.' , 'updraftplus' );
if ( ! empty ( $ftp -> ssl )) {
echo ' ' . __ ( 'This is sometimes caused by a firewall - try turning off SSL in the expert settings, and testing again.' , 'updraftplus' );
}
}
}
}