ManageWP Worker - Version 3.9.8

Version Description

  • Conversion goals integration
  • Update notifications
  • Enhanced security for your account
  • Better backups
  • Better update interface
  • Full changelog
Download this release

Release Info

Developer freediver
Plugin Icon 128x128 ManageWP Worker
Version 3.9.8
Comparing to
See all releases

Code changes from version 3.9.7 to 3.9.8

Files changed (10) hide show
  1. backup.class.php +114 -39
  2. core.class.php +86 -80
  3. helper.class.php +16 -10
  4. init.php +51 -14
  5. installer.class.php +19 -10
  6. lib/s3.php +1224 -212
  7. post.class.php +2 -2
  8. readme.txt +21 -11
  9. stats.class.php +95 -61
  10. version +1 -1
backup.class.php CHANGED
@@ -11,10 +11,12 @@
11
  **************************************************************/
12
 
13
  class MMB_Backup extends MMB_Core
14
- {
 
15
  function __construct()
16
  {
17
  parent::__construct();
 
18
  }
19
 
20
  function get_backup_settings()
@@ -42,19 +44,23 @@ class MMB_Backup extends MMB_Core
42
  'removed' => true
43
  );
44
  } else {
45
- $args['account_info'] = $account_info;
 
 
 
46
  $before[$task_name]['task_args'] = $args;
47
-
48
- if (!empty($args['schedule']))
49
  $before[$task_name]['task_args']['next'] = $this->schedule_next($args['type'], $args['schedule']);
50
 
51
  $return = $before[$task_name];
52
  }
53
 
54
  if ($error) {
55
- $before[$task_name]['task_results'][count($before[$task_name]['task_results'])] = array(
56
- 'error' => $error
57
- );
 
 
58
  }
59
  update_option('mwp_backup_tasks', $before);
60
 
@@ -79,17 +85,21 @@ class MMB_Backup extends MMB_Core
79
  if (is_array($settings) && !empty($settings)) {
80
  foreach ($settings as $task_name => $setting) {
81
  if ($setting['task_args']['next'] && $setting['task_args']['next'] < time()) {
82
- //Update task with next schedule and possible error
83
  $this->set_backup_task(array(
84
  'task_name' => $task_name,
85
- 'args' => $settings[$task_name]['task_args'],
86
- 'error' => $error
87
- )); //Update
88
 
89
  $result = $this->backup($setting['task_args'], $task_name);
90
-
91
  if (is_array($result) && array_key_exists('error', $result)) {
92
- $error = $result['error'];
 
 
 
 
 
93
  } else {
94
  $error = '';
95
  }
@@ -118,11 +128,8 @@ class MMB_Backup extends MMB_Core
118
  if (!$args || empty($args))
119
  return false;
120
 
121
-
122
-
123
  extract($args); //extract settings
124
-
125
-
126
  //try increase memory limit
127
  @ini_set('memory_limit', '1000M');
128
  @set_time_limit(600); //ten minutes
@@ -148,6 +155,7 @@ class MMB_Backup extends MMB_Core
148
  }
149
 
150
  @file_put_contents($new_file_path . '/index.php', ''); //safe
 
151
  //Delete possible breaked previous backups - don't need it anymore (works only for previous wrokers)
152
  foreach (glob($new_file_path . "/*.zip") as $filename) {
153
  $short = basename($filename);
@@ -190,7 +198,7 @@ class MMB_Backup extends MMB_Core
190
  if ($this->mmb_exec('which zip')) {
191
  chdir(WP_CONTENT_DIR);
192
  $command = "zip -r $backup_file 'mwp_db'";
193
- $this->_log($command);
194
  ob_start();
195
  $result = $this->mmb_exec($command);
196
  ob_get_clean();
@@ -280,17 +288,26 @@ class MMB_Backup extends MMB_Core
280
  if ($task_name != 'Backup Now') {
281
  if (isset($account_info['mwp_ftp']) && !empty($account_info['mwp_ftp'])) {
282
  $account_info['mwp_ftp']['backup_file'] = $backup_file;
283
- $this->ftp_backup($account_info['mwp_ftp']);
 
 
 
284
  }
285
 
286
  if (isset($account_info['mwp_amazon_s3']) && !empty($account_info['mwp_amazon_s3'])) {
287
  $account_info['mwp_amazon_s3']['backup_file'] = $backup_file;
288
- $this->amazons3_backup($account_info['mwp_amazon_s3']);
 
 
 
289
  }
290
 
291
  if (isset($account_info['mwp_dropbox']) && !empty($account_info['mwp_dropbox'])) {
292
  $account_info['mwp_dropbox']['backup_file'] = $backup_file;
293
- $this->dropbox_backup($account_info['mwp_dropbox']);
 
 
 
294
  }
295
 
296
  if (isset($email_backup) && is_email($email_backup)) {
@@ -569,7 +586,7 @@ class MMB_Backup extends MMB_Core
569
  $db_host = DB_HOST;
570
  $db_user = DB_USER;
571
  $db_password = DB_PASSWORD;
572
- $home = get_option('home');
573
  $site_url = get_option('site_url');
574
  }
575
 
@@ -634,6 +651,7 @@ class MMB_Backup extends MMB_Core
634
  //Replace options
635
  $query = "SELECT option_value FROM " . $new_table_prefix . "options WHERE option_name = 'home'";
636
  $old = $wpdb->get_var($wpdb->prepare($query));
 
637
  $query = "UPDATE " . $new_table_prefix . "options SET option_value = '$home' WHERE option_name = 'home'";
638
  $wpdb->query($wpdb->prepare($query));
639
  $query = "UPDATE " . $new_table_prefix . "options SET option_value = '$home' WHERE option_name = 'siteurl'";
@@ -919,36 +937,44 @@ class MMB_Backup extends MMB_Core
919
  function ftp_backup($args)
920
  {
921
  extract($args);
922
- //Args: $ftp_username, $ftp_password, $ftp_hostname, $backup_file, $ftp_remote_folder
923
-
 
 
 
924
  if ($ftp_ssl && function_exists('ftp_ssl_connect')) {
925
  $conn_id = ftp_ssl_connect($ftp_hostname);
926
  } else if (function_exists('ftp_connect')) {
927
  $conn_id = ftp_connect($ftp_hostname);
928
  if ($conn_id === false) {
929
  return array(
930
- 'error' => 'Failed to connect to ' . $ftp_hostname
 
931
  );
932
  }
933
  } else {
934
  return array(
935
- 'error' => 'Your server doesn\'t support FTP'
 
936
  );
937
  }
938
 
939
  $login = @ftp_login($conn_id, $ftp_username, $ftp_password);
940
  if ($login === false) {
941
  return array(
942
- 'error' => 'FTP login failed for ' . $ftp_username . ', ' . $ftp_password
 
943
  );
944
- }
945
 
946
  @ftp_mkdir($conn_id, $ftp_remote_folder);
947
 
 
948
  $upload = @ftp_put($conn_id, $ftp_remote_folder . '/' . basename($backup_file), $backup_file, FTP_BINARY);
949
  if ($upload === false) {
950
  return array(
951
- 'error' => 'Failed to upload file. Please check your specified path.'
 
952
  );
953
  }
954
 
@@ -975,6 +1001,9 @@ class MMB_Backup extends MMB_Core
975
  } else {
976
  }
977
 
 
 
 
978
  $delete = ftp_delete($conn_id, $ftp_remote_folder . '/' . $backup_file);
979
  if ($delete === false) {
980
  } else {
@@ -1003,6 +1032,9 @@ class MMB_Backup extends MMB_Core
1003
  } else {
1004
  }
1005
 
 
 
 
1006
  $temp = ABSPATH . 'mwp_temp_backup.zip';
1007
  $get = ftp_get($conn_id, $temp, $ftp_remote_folder . '/' . $backup_file, FTP_BINARY);
1008
  if ($get === false) {
@@ -1015,18 +1047,22 @@ class MMB_Backup extends MMB_Core
1015
  }
1016
 
1017
  function dropbox_backup($args)
1018
- {
1019
  require_once('lib/dropbox.php');
1020
  extract($args);
1021
 
1022
- //$email, $password, $backup_file, $destination
1023
- try {
 
 
 
1024
  $uploader = new DropboxUploader($dropbox_username, $dropbox_password);
1025
  $uploader->upload($backup_file, $dropbox_destination);
1026
  }
1027
  catch (Exception $e) {
1028
  return array(
1029
- 'error' => $e->getMessage()
 
1030
  );
1031
  }
1032
 
@@ -1039,7 +1075,10 @@ class MMB_Backup extends MMB_Core
1039
  require_once('lib/s3.php');
1040
  extract($args);
1041
 
1042
- $start_time = time();
 
 
 
1043
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1044
 
1045
  $s3->putBucket($as3_bucket, S3::ACL_PUBLIC_READ);
@@ -1048,7 +1087,8 @@ class MMB_Backup extends MMB_Core
1048
  return true;
1049
  } else {
1050
  return array(
1051
- 'error' => 'Failed to upload to Amazon S3. Please check your details.'
 
1052
  );
1053
  }
1054
 
@@ -1058,7 +1098,9 @@ class MMB_Backup extends MMB_Core
1058
  {
1059
  require_once('lib/s3.php');
1060
  extract($args);
1061
-
 
 
1062
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1063
  $s3->deleteObject($as3_bucket, $as3_directory . '/' . $backup_file);
1064
  }
@@ -1068,14 +1110,16 @@ class MMB_Backup extends MMB_Core
1068
  require_once('lib/s3.php');
1069
  extract($args);
1070
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1071
- $s3->deleteObject($as3_bucket, $as3_directory . '/' . $backup_file);
 
 
1072
  $s3->getObject($as3_bucket, $as3_directory . '/' . $backup_file, $temp);
1073
  $temp = ABSPATH . 'mwp_temp_backup.zip';
1074
  return $temp;
1075
  }
1076
 
1077
  function schedule_next($type, $schedule)
1078
- {
1079
  $schedule = explode("|", $schedule);
1080
  if (empty($schedule))
1081
  return false;
@@ -1152,6 +1196,7 @@ class MMB_Backup extends MMB_Core
1152
  $time += $delay_time;
1153
  }
1154
 
 
1155
  return $time;
1156
 
1157
  }
@@ -1163,7 +1208,31 @@ class MMB_Backup extends MMB_Core
1163
  $tasks = get_option('mwp_backup_tasks');
1164
  if (is_array($tasks) && !empty($tasks)) {
1165
  foreach ($tasks as $task_name => $info) {
 
 
 
 
 
 
 
 
 
 
 
 
1166
  $stats[$task_name] = $info['task_results'];
 
 
 
 
 
 
 
 
 
 
 
 
1167
  }
1168
  }
1169
  return $stats;
@@ -1267,6 +1336,11 @@ class MMB_Backup extends MMB_Core
1267
  $backup_folder = WP_CONTENT_DIR . '/' . md5('mmb-worker') . '/mwp_backups/';
1268
  $files = glob($backup_folder . "*.*");
1269
  $deleted = array();
 
 
 
 
 
1270
  if (is_array($files) && count($files)) {
1271
  $results = array();
1272
  if (count($tasks)) {
@@ -1283,7 +1357,7 @@ class MMB_Backup extends MMB_Core
1283
 
1284
  $num_deleted = 0;
1285
  foreach ($files as $file) {
1286
- if (!in_array($file, $results) && basename($file) != 'index.php') {
1287
  @unlink($file);
1288
  $deleted[] = basename($file);
1289
  $num_deleted++;
@@ -1306,6 +1380,7 @@ class MMB_Backup extends MMB_Core
1306
 
1307
  function remote_backup_now($args)
1308
  {
 
1309
  if (!empty($args))
1310
  extract($args);
1311
  $tasks = $this->get_backup_settings();
11
  **************************************************************/
12
 
13
  class MMB_Backup extends MMB_Core
14
+ {
15
+ var $site_name;
16
  function __construct()
17
  {
18
  parent::__construct();
19
+ $this->site_name = str_replace("/","-",rtrim($this->remove_http(get_bloginfo('home')),"/"));
20
  }
21
 
22
  function get_backup_settings()
44
  'removed' => true
45
  );
46
  } else {
47
+ if(is_array($params['account_info'])){ //only if sends from master first time(secure data)
48
+ $args['account_info'] = $account_info;
49
+ }
50
+
51
  $before[$task_name]['task_args'] = $args;
52
+ if (strlen($args['schedule']))
 
53
  $before[$task_name]['task_args']['next'] = $this->schedule_next($args['type'], $args['schedule']);
54
 
55
  $return = $before[$task_name];
56
  }
57
 
58
  if ($error) {
59
+ if(is_array($error)){
60
+ $before[$task_name]['task_results'][count($before[$task_name]['task_results'])-1]['error'] = $error['error'];
61
+ } else {
62
+ $before[$task_name]['task_results'][count($before[$task_name]['task_results'])]['error'] = $error;
63
+ }
64
  }
65
  update_option('mwp_backup_tasks', $before);
66
 
85
  if (is_array($settings) && !empty($settings)) {
86
  foreach ($settings as $task_name => $setting) {
87
  if ($setting['task_args']['next'] && $setting['task_args']['next'] < time()) {
88
+ //Update task with next schedule
89
  $this->set_backup_task(array(
90
  'task_name' => $task_name,
91
+ 'args' => $settings[$task_name]['task_args']
92
+ ));
 
93
 
94
  $result = $this->backup($setting['task_args'], $task_name);
95
+ $error = '';
96
  if (is_array($result) && array_key_exists('error', $result)) {
97
+ $error = $result;
98
+ $this->set_backup_task(array(
99
+ 'task_name' => $task_name,
100
+ 'args' => $settings[$task_name]['task_args'],
101
+ 'error' => $error
102
+ ));
103
  } else {
104
  $error = '';
105
  }
128
  if (!$args || empty($args))
129
  return false;
130
 
 
 
131
  extract($args); //extract settings
132
+
 
133
  //try increase memory limit
134
  @ini_set('memory_limit', '1000M');
135
  @set_time_limit(600); //ten minutes
155
  }
156
 
157
  @file_put_contents($new_file_path . '/index.php', ''); //safe
158
+
159
  //Delete possible breaked previous backups - don't need it anymore (works only for previous wrokers)
160
  foreach (glob($new_file_path . "/*.zip") as $filename) {
161
  $short = basename($filename);
198
  if ($this->mmb_exec('which zip')) {
199
  chdir(WP_CONTENT_DIR);
200
  $command = "zip -r $backup_file 'mwp_db'";
201
+
202
  ob_start();
203
  $result = $this->mmb_exec($command);
204
  ob_get_clean();
288
  if ($task_name != 'Backup Now') {
289
  if (isset($account_info['mwp_ftp']) && !empty($account_info['mwp_ftp'])) {
290
  $account_info['mwp_ftp']['backup_file'] = $backup_file;
291
+ $ftp_result = $this->ftp_backup($account_info['mwp_ftp']);
292
+ if(is_array($ftp_result) && isset($ftp_result['error'])){
293
+ return $ftp_result;
294
+ }
295
  }
296
 
297
  if (isset($account_info['mwp_amazon_s3']) && !empty($account_info['mwp_amazon_s3'])) {
298
  $account_info['mwp_amazon_s3']['backup_file'] = $backup_file;
299
+ $amazons3_result = $this->amazons3_backup($account_info['mwp_amazon_s3']);
300
+ if(is_array($amazons3_result) && isset($amazons3_result['error'])){
301
+ return $amazons3_result;
302
+ }
303
  }
304
 
305
  if (isset($account_info['mwp_dropbox']) && !empty($account_info['mwp_dropbox'])) {
306
  $account_info['mwp_dropbox']['backup_file'] = $backup_file;
307
+ $dropbox_result = $this->dropbox_backup($account_info['mwp_dropbox']);
308
+ if(is_array($dropbox_result) && isset($dropbox_result['error'])){
309
+ return $dropbox_result;
310
+ }
311
  }
312
 
313
  if (isset($email_backup) && is_email($email_backup)) {
586
  $db_host = DB_HOST;
587
  $db_user = DB_USER;
588
  $db_password = DB_PASSWORD;
589
+ $home = rtrim(get_option('home'),"/");
590
  $site_url = get_option('site_url');
591
  }
592
 
651
  //Replace options
652
  $query = "SELECT option_value FROM " . $new_table_prefix . "options WHERE option_name = 'home'";
653
  $old = $wpdb->get_var($wpdb->prepare($query));
654
+ $old = rtrim($old,"/");
655
  $query = "UPDATE " . $new_table_prefix . "options SET option_value = '$home' WHERE option_name = 'home'";
656
  $wpdb->query($wpdb->prepare($query));
657
  $query = "UPDATE " . $new_table_prefix . "options SET option_value = '$home' WHERE option_name = 'siteurl'";
937
  function ftp_backup($args)
938
  {
939
  extract($args);
940
+ //Args: $ftp_username, $ftp_password, $ftp_hostname, $backup_file, $ftp_remote_folder, $ftp_site_folder
941
+ if($ftp_site_folder)
942
+ $ftp_remote_folder .= '/'.$this->site_name;
943
+
944
+
945
  if ($ftp_ssl && function_exists('ftp_ssl_connect')) {
946
  $conn_id = ftp_ssl_connect($ftp_hostname);
947
  } else if (function_exists('ftp_connect')) {
948
  $conn_id = ftp_connect($ftp_hostname);
949
  if ($conn_id === false) {
950
  return array(
951
+ 'error' => 'Failed to connect to ' . $ftp_hostname,
952
+ 'partial' => 1
953
  );
954
  }
955
  } else {
956
  return array(
957
+ 'error' => 'Your server doesn\'t support FTP',
958
+ 'partial' => 1
959
  );
960
  }
961
 
962
  $login = @ftp_login($conn_id, $ftp_username, $ftp_password);
963
  if ($login === false) {
964
  return array(
965
+ 'error' => 'FTP login failed for ' . $ftp_username . ', ' . $ftp_password,
966
+ 'partial' => 1
967
  );
968
+ }
969
 
970
  @ftp_mkdir($conn_id, $ftp_remote_folder);
971
 
972
+
973
  $upload = @ftp_put($conn_id, $ftp_remote_folder . '/' . basename($backup_file), $backup_file, FTP_BINARY);
974
  if ($upload === false) {
975
  return array(
976
+ 'error' => 'Failed to upload file to FTP. Please check your specified path.',
977
+ 'partial' => 1
978
  );
979
  }
980
 
1001
  } else {
1002
  }
1003
 
1004
+ if($ftp_site_folder)
1005
+ $ftp_remote_folder .= '/'.$this->site_name;
1006
+
1007
  $delete = ftp_delete($conn_id, $ftp_remote_folder . '/' . $backup_file);
1008
  if ($delete === false) {
1009
  } else {
1032
  } else {
1033
  }
1034
 
1035
+ if($ftp_site_folder)
1036
+ $ftp_remote_folder .= '/'.$this->site_name;
1037
+
1038
  $temp = ABSPATH . 'mwp_temp_backup.zip';
1039
  $get = ftp_get($conn_id, $temp, $ftp_remote_folder . '/' . $backup_file, FTP_BINARY);
1040
  if ($get === false) {
1047
  }
1048
 
1049
  function dropbox_backup($args)
1050
+ {
1051
  require_once('lib/dropbox.php');
1052
  extract($args);
1053
 
1054
+ //$email, $password, $backup_file, $destination, $dropbox_site_folder
1055
+ if($dropbox_site_folder == true)
1056
+ $dropbox_destination .= '/'.$this->site_name;
1057
+
1058
+ try {
1059
  $uploader = new DropboxUploader($dropbox_username, $dropbox_password);
1060
  $uploader->upload($backup_file, $dropbox_destination);
1061
  }
1062
  catch (Exception $e) {
1063
  return array(
1064
+ 'error' => $e->getMessage(),
1065
+ 'partial' => 1
1066
  );
1067
  }
1068
 
1075
  require_once('lib/s3.php');
1076
  extract($args);
1077
 
1078
+ if($as3_site_folder == true)
1079
+ $as3_directory .= '/'.$this->site_name;
1080
+
1081
+
1082
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1083
 
1084
  $s3->putBucket($as3_bucket, S3::ACL_PUBLIC_READ);
1087
  return true;
1088
  } else {
1089
  return array(
1090
+ 'error' => 'Failed to upload to Amazon S3. Please check your details and set upload/delete permissions on your bucket.',
1091
+ 'partial' => 1
1092
  );
1093
  }
1094
 
1098
  {
1099
  require_once('lib/s3.php');
1100
  extract($args);
1101
+ if($as3_site_folder == true)
1102
+ $as3_directory .= '/'.$this->site_name;
1103
+
1104
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1105
  $s3->deleteObject($as3_bucket, $as3_directory . '/' . $backup_file);
1106
  }
1110
  require_once('lib/s3.php');
1111
  extract($args);
1112
  $s3 = new S3($as3_access_key, str_replace(' ', '+', $as3_secure_key));
1113
+ if($as3_site_folder == true)
1114
+ $as3_directory .= '/'.$this->site_name;
1115
+
1116
  $s3->getObject($as3_bucket, $as3_directory . '/' . $backup_file, $temp);
1117
  $temp = ABSPATH . 'mwp_temp_backup.zip';
1118
  return $temp;
1119
  }
1120
 
1121
  function schedule_next($type, $schedule)
1122
+ {
1123
  $schedule = explode("|", $schedule);
1124
  if (empty($schedule))
1125
  return false;
1196
  $time += $delay_time;
1197
  }
1198
 
1199
+
1200
  return $time;
1201
 
1202
  }
1208
  $tasks = get_option('mwp_backup_tasks');
1209
  if (is_array($tasks) && !empty($tasks)) {
1210
  foreach ($tasks as $task_name => $info) {
1211
+
1212
+ if(is_array($info['task_results']) && !empty($info['task_results']))
1213
+ {
1214
+ foreach($info['task_results'] as $key => $result){
1215
+ if(isset($result['server'])){
1216
+ if(!file_exists($result['server']['file_path'])){
1217
+ unset($info['task_results'][$key]);
1218
+ }
1219
+ }
1220
+ }
1221
+ }
1222
+
1223
  $stats[$task_name] = $info['task_results'];
1224
+
1225
+ }
1226
+ }
1227
+ return $stats;
1228
+ }
1229
+
1230
+ function get_next_schedules(){
1231
+ $stats = array();
1232
+ $tasks = get_option('mwp_backup_tasks');
1233
+ if (is_array($tasks) && !empty($tasks)) {
1234
+ foreach ($tasks as $task_name => $info) {
1235
+ $stats[$task_name] = $info['task_args']['next'];
1236
  }
1237
  }
1238
  return $stats;
1336
  $backup_folder = WP_CONTENT_DIR . '/' . md5('mmb-worker') . '/mwp_backups/';
1337
  $files = glob($backup_folder . "*.*");
1338
  $deleted = array();
1339
+ $clone_backup = get_option('mwp_manual_backup');
1340
+ if(isset($clone_backup['file_path'])){
1341
+ $clone_backup = $clone_backup['file_path'];
1342
+ }
1343
+
1344
  if (is_array($files) && count($files)) {
1345
  $results = array();
1346
  if (count($tasks)) {
1357
 
1358
  $num_deleted = 0;
1359
  foreach ($files as $file) {
1360
+ if (!in_array($file, $results) && basename($file) != 'index.php' && basename($file) != basename($clone_backup)) {
1361
  @unlink($file);
1362
  $deleted[] = basename($file);
1363
  $num_deleted++;
1380
 
1381
  function remote_backup_now($args)
1382
  {
1383
+
1384
  if (!empty($args))
1385
  extract($args);
1386
  $tasks = $this->get_backup_settings();
core.class.php CHANGED
@@ -47,15 +47,15 @@ class MMB_Core extends MMB_Helper
47
  }
48
 
49
 
50
-
51
- if (function_exists('is_multisite')){
52
- if (is_multisite()) {
53
- $this->mmb_multisite = $blog_id;
54
- }
55
- } else if (!empty($wpmu_version)) {
56
- $this->mmb_multisite = $blog_id;
57
- }
58
-
59
  add_action('rightnow_end', array(
60
  $this,
61
  'add_right_now_info'
@@ -71,8 +71,8 @@ class MMB_Core extends MMB_Helper
71
  add_action('init', array(
72
  $this,
73
  'automatic_login'
74
- ));
75
-
76
  if (!get_option('_worker_public_key'))
77
  add_action('admin_notices', array(
78
  $this,
@@ -81,7 +81,7 @@ class MMB_Core extends MMB_Helper
81
 
82
 
83
  }
84
-
85
  /**
86
  * Add notice to admin dashboard for security reasons
87
  *
@@ -263,27 +263,28 @@ class MMB_Core extends MMB_Helper
263
  function install()
264
  {
265
  global $wp_object_cache, $wpdb;
266
- if(!empty($wp_object_cache))
267
- @$wp_object_cache->flush();
268
-
269
- // delete plugin options, just in case
270
- if($this->mmb_multisite != false){
271
- $blog_ids = $wpdb->get_results($wpdb->prepare('SELECT blog_id FROM `wp_blogs`'));
272
- if(!empty($blog_ids)){
273
- foreach($blog_ids as $blog_id){
274
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_worker_nossl_key";'));
275
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_worker_public_key";'));
276
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_action_message_id";'));
277
- }
278
- }
279
- } else {
280
- delete_option('_worker_nossl_key');
281
- delete_option('_worker_public_key');
282
- delete_option('_action_message_id');
283
- }
284
-
285
- //Reset backup tasks
286
- delete_option('mwp_backup_tasks');
 
287
  }
288
 
289
  /**
@@ -304,29 +305,34 @@ class MMB_Core extends MMB_Helper
304
  *
305
  */
306
  function uninstall()
307
- {
308
- global $wp_object_cache, $wpdb;
309
- if(!empty($wp_object_cache))
310
- @$wp_object_cache->flush();
311
-
312
- if($this->mmb_multisite != false){
313
- $blog_ids = $wpdb->get_results($wpdb->prepare('SELECT blog_id FROM `wp_blogs`'));
314
- if(!empty($blog_ids)){
315
- foreach($blog_ids as $blog){
316
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_worker_nossl_key";'));
317
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_worker_public_key";'));
318
- $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.$blog->blog_id.'_options WHERE `option_name` = "_action_message_id";'));
319
- }
320
- }
321
- } else {
322
- delete_option('_worker_nossl_key');
323
- delete_option('_worker_public_key');
324
- delete_option('_action_message_id');
325
- }
326
-
327
- //Delete backup tasks
328
- delete_option('mwp_backup_tasks');
329
- wp_clear_scheduled_hook('mwp_backup_tasks');
 
 
 
 
 
330
  }
331
 
332
 
@@ -353,10 +359,11 @@ class MMB_Core extends MMB_Helper
353
  {
354
  extract($params);
355
  if ($download_url) {
356
- include_once ABSPATH . 'wp-admin/includes/file.php';
357
- include_once ABSPATH . 'wp-admin/includes/misc.php';
358
- include_once ABSPATH . 'wp-admin/includes/template.php';
359
- include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
 
360
 
361
  if (!$this->is_server_writable()) {
362
  return array(
@@ -379,11 +386,11 @@ class MMB_Core extends MMB_Helper
379
  ob_end_clean();
380
  if (is_wp_error($result) || !$result) {
381
  return array(
382
- 'error' => 'ManageWP Worker plugin could not be upgraded.'
383
  );
384
  } else {
385
  return array(
386
- 'success' => 'ManageWP Worker plugin successfully upgraded.'
387
  );
388
  }
389
  }
@@ -398,37 +405,36 @@ class MMB_Core extends MMB_Helper
398
  */
399
  function automatic_login()
400
  {
401
- global $current_user;
402
-
403
- $where = isset($_GET['mwp_goto']) ? $_GET['mwp_goto'] : '';
404
- $username = isset($_GET['username']) ? $_GET['username'] : '';
405
- $auto_login = isset($_GET['auto_login']) ? $_GET['auto_login'] : 0;
406
-
407
- if ((!is_user_logged_in() || ($this->mmb_multisite && $username != $current_user->user_login)) && $auto_login) {
408
-
409
- $signature = base64_decode($_GET['signature']);
410
- $message_id = trim($_GET['message_id']);
411
 
412
  $auth = $this->authenticate_message($where . $message_id, $signature, $message_id);
413
  if ($auth === true) {
414
- if(isset($current_user->user_login))
415
- do_action('wp_logout');
416
  $user = get_user_by('login', $username);
417
  $user_id = $user->ID;
418
  wp_set_current_user($user_id, $username);
419
  wp_set_auth_cookie($user_id);
420
  do_action('wp_login', $username);
421
  } else {
422
- unset($_SESSION['mwp_frame_options_header']);
423
  wp_die($auth['error']);
424
  }
425
  }
426
 
427
- if ($_GET['auto_login']) {
428
- update_option('mwp_iframe_options_header', microtime(true));
429
- if(!headers_sent())
430
- header('P3P: CP="CAO PSA OUR"'); // IE redirect iframe header
431
- wp_redirect(get_option('siteurl') . "/wp-admin/" . $where);
432
  exit();
433
  }
434
  }
47
  }
48
 
49
 
50
+
51
+ if (function_exists('is_multisite')) {
52
+ if (is_multisite()) {
53
+ $this->mmb_multisite = $blog_id;
54
+ }
55
+ } else if (!empty($wpmu_version)) {
56
+ $this->mmb_multisite = $blog_id;
57
+ }
58
+
59
  add_action('rightnow_end', array(
60
  $this,
61
  'add_right_now_info'
71
  add_action('init', array(
72
  $this,
73
  'automatic_login'
74
+ ));
75
+
76
  if (!get_option('_worker_public_key'))
77
  add_action('admin_notices', array(
78
  $this,
81
 
82
 
83
  }
84
+
85
  /**
86
  * Add notice to admin dashboard for security reasons
87
  *
263
  function install()
264
  {
265
  global $wp_object_cache, $wpdb;
266
+ if (!empty($wp_object_cache))
267
+ @$wp_object_cache->flush();
268
+
269
+ //delete plugin options, just in case
270
+ if ($this->mmb_multisite != false) {
271
+ $blog_ids = $wpdb->get_results($wpdb->prepare('SELECT blog_id FROM `wp_blogs`'));
272
+ if (!empty($blog_ids)) {
273
+ foreach ($blog_ids as $blog_id) {
274
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_worker_nossl_key";'));
275
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_worker_public_key";'));
276
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_action_message_id";'));
277
+ }
278
+ }
279
+ } else {
280
+ delete_option('_worker_nossl_key');
281
+ delete_option('_worker_public_key');
282
+ delete_option('_action_message_id');
283
+ }
284
+
285
+ delete_option('mwp_backup_tasks');
286
+ delete_option('mwp_notifications');
287
+
288
  }
289
 
290
  /**
305
  *
306
  */
307
  function uninstall()
308
+ {
309
+ global $wp_object_cache, $wpdb;
310
+ if (!empty($wp_object_cache))
311
+ @$wp_object_cache->flush();
312
+
313
+ if ($this->mmb_multisite != false) {
314
+ $blog_ids = $wpdb->get_results($wpdb->prepare('SELECT blog_id FROM `wp_blogs`'));
315
+ if (!empty($blog_ids)) {
316
+ foreach ($blog_ids as $blog) {
317
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_worker_nossl_key";'));
318
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_worker_public_key";'));
319
+ $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . $blog->blog_id . '_options WHERE `option_name` = "_action_message_id";'));
320
+ }
321
+ }
322
+ } else {
323
+ delete_option('_worker_nossl_key');
324
+ delete_option('_worker_public_key');
325
+ delete_option('_action_message_id');
326
+ }
327
+
328
+ //Delete backup tasks
329
+ delete_option('mwp_backup_tasks');
330
+ wp_clear_scheduled_hook('mwp_backup_tasks');
331
+
332
+ //Delete notifications
333
+ delete_option('mwp_notifications');
334
+ wp_clear_scheduled_hook('mwp_notifications');
335
+
336
  }
337
 
338
 
359
  {
360
  extract($params);
361
  if ($download_url) {
362
+ @include_once ABSPATH . 'wp-admin/includes/file.php';
363
+ @include_once ABSPATH . 'wp-admin/includes/misc.php';
364
+ @include_once ABSPATH . 'wp-admin/includes/template.php';
365
+ @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
366
+ @include_once ABSPATH . 'wp-admin/includes/screen.php';
367
 
368
  if (!$this->is_server_writable()) {
369
  return array(
386
  ob_end_clean();
387
  if (is_wp_error($result) || !$result) {
388
  return array(
389
+ 'error' => 'ManageWP Worker plugin could not be updated.'
390
  );
391
  } else {
392
  return array(
393
+ 'success' => 'ManageWP Worker plugin successfully updated.'
394
  );
395
  }
396
  }
405
  */
406
  function automatic_login()
407
  {
408
+ global $current_user;
409
+
410
+ $where = isset($_GET['mwp_goto']) ? $_GET['mwp_goto'] : '';
411
+ $username = isset($_GET['username']) ? $_GET['username'] : '';
412
+ $auto_login = isset($_GET['auto_login']) ? $_GET['auto_login'] : 0;
413
+
414
+ if ((!is_user_logged_in() || ($this->mmb_multisite && $username != $current_user->user_login)) && $auto_login) {
415
+ $signature = base64_decode($_GET['signature']);
416
+ $message_id = trim($_GET['message_id']);
 
417
 
418
  $auth = $this->authenticate_message($where . $message_id, $signature, $message_id);
419
  if ($auth === true) {
420
+ if (isset($current_user->user_login))
421
+ do_action('wp_logout');
422
  $user = get_user_by('login', $username);
423
  $user_id = $user->ID;
424
  wp_set_current_user($user_id, $username);
425
  wp_set_auth_cookie($user_id);
426
  do_action('wp_login', $username);
427
  } else {
428
+ unset($_SESSION['mwp_frame_options_header']);
429
  wp_die($auth['error']);
430
  }
431
  }
432
 
433
+ if ($auto_login) {
434
+ update_option('mwp_iframe_options_header', microtime(true));
435
+ if (!headers_sent())
436
+ header('P3P: CP="CAO PSA OUR"'); // IE redirect iframe header
437
+ wp_redirect(get_option('siteurl') . "/wp-admin/" . $where);
438
  exit();
439
  }
440
  }
helper.class.php CHANGED
@@ -281,13 +281,13 @@ class MMB_Helper
281
 
282
  if ((int) $current_message > (int) $message_id)
283
  return array(
284
- 'error' => 'Invalid message recieved. You can try to reinstall worker plugin and re-add the site to your account.'
285
  );
286
 
287
  $pl_key = $this->get_master_public_key();
288
  if (!$pl_key) {
289
  return array(
290
- 'error' => 'Authentication failed (public key). You can try to reinstall worker plugin and re-add the site to your account.'
291
  );
292
  }
293
 
@@ -298,7 +298,7 @@ class MMB_Helper
298
  return true;
299
  } else if ($verify == 0) {
300
  return array(
301
- 'error' => 'Invalid message signature. You can try to reinstall worker plugin and re-add the site to your account.'
302
  );
303
  } else {
304
  return array(
@@ -311,13 +311,13 @@ class MMB_Helper
311
  return true;
312
  }
313
  return array(
314
- 'error' => 'Invalid message signature. You can try to reinstall the worker plugin and then re-add the site to your dashboard.'
315
  );
316
  }
317
  // no rand key - deleted in get_stat maybe
318
  else
319
  return array(
320
- 'error' => 'Invalid message signature, try reinstalling worker plugin and re-adding the site to your dashboard.'
321
  );
322
  }
323
 
@@ -333,11 +333,11 @@ class MMB_Helper
333
  if( function_exists('openssl_public_decrypt') && !$this->get_random_signature()){
334
  if(is_array($data) && !empty($data)){
335
  foreach($data as $input){
336
- openssl_public_decrypt($input, &$decrypted, $pl_key);
337
  $secure .= $decrypted;
338
  }
339
  } else {
340
- openssl_public_decrypt($input, &$decrypted, $pl_key);
341
  $secure = $decrypted;
342
  }
343
  return $secure;
@@ -393,9 +393,15 @@ class MMB_Helper
393
  return $error_object != '' ? $error_object : '';
394
  } else {
395
  $errors = array();
396
- foreach ($error_object->error_data as $error_key => $error_string) {
397
- $errors[] = str_replace('_', ' ', ucfirst($error_key)) . ': ' . $error_string;
398
- }
 
 
 
 
 
 
399
  return implode('<br />', $errors);
400
  }
401
  }
281
 
282
  if ((int) $current_message > (int) $message_id)
283
  return array(
284
+ 'error' => 'Invalid message recieved. Deactivate and activate the ManageWP Worker plugin on this site, then remove the website from your ManageWP account and add it again.'
285
  );
286
 
287
  $pl_key = $this->get_master_public_key();
288
  if (!$pl_key) {
289
  return array(
290
+ 'error' => 'Authentication failed. Deactivate and activate the ManageWP Worker plugin on this site, then remove the website from your ManageWP account and add it again.'
291
  );
292
  }
293
 
298
  return true;
299
  } else if ($verify == 0) {
300
  return array(
301
+ 'error' => 'Invalid message signature. Deactivate and activate the ManageWP Worker plugin on this site, then remove the website from your ManageWP account and add it again.'
302
  );
303
  } else {
304
  return array(
311
  return true;
312
  }
313
  return array(
314
+ 'error' => 'Invalid message signature. Deactivate and activate the ManageWP Worker plugin on this site, then remove the website from your ManageWP account and add it again.'
315
  );
316
  }
317
  // no rand key - deleted in get_stat maybe
318
  else
319
  return array(
320
+ 'error' => 'Invalid message signature. Deactivate and activate the ManageWP Worker plugin on this site, then remove the website from your ManageWP account and add it again.'
321
  );
322
  }
323
 
333
  if( function_exists('openssl_public_decrypt') && !$this->get_random_signature()){
334
  if(is_array($data) && !empty($data)){
335
  foreach($data as $input){
336
+ openssl_public_decrypt($input, $decrypted, $pl_key);
337
  $secure .= $decrypted;
338
  }
339
  } else {
340
+ openssl_public_decrypt($input, $decrypted, $pl_key);
341
  $secure = $decrypted;
342
  }
343
  return $secure;
393
  return $error_object != '' ? $error_object : '';
394
  } else {
395
  $errors = array();
396
+ if(!empty($error_object->error_data)) {
397
+ foreach ($error_object->error_data as $error_key => $error_string) {
398
+ $errors[] = str_replace('_', ' ', ucfirst($error_key)) . ': ' . $error_string;
399
+ }
400
+ } elseif (!empty($error_object->errors)){
401
+ foreach ($error_object->errors as $error_key => $err) {
402
+ $errors[] = 'Error: '.str_replace('_', ' ', strtolower($error_key));
403
+ }
404
+ }
405
  return implode('<br />', $errors);
406
  }
407
  }
init.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: ManageWP - Worker
4
  Plugin URI: http://managewp.com/
5
  Description: Manage all your blogs from one dashboard. Visit <a href="http://managewp.com">ManageWP.com</a> to sign up.
6
  Author: Prelovac Media
7
- Version: 3.9.7
8
  Author URI: http://www.prelovac.com
9
  */
10
 
@@ -20,7 +20,7 @@ Author URI: http://www.prelovac.com
20
  **************************************************************/
21
 
22
 
23
- define('MMB_WORKER_VERSION', '3.9.7');
24
 
25
  global $wpdb, $mmb_plugin_dir, $mmb_plugin_url;
26
 
@@ -38,7 +38,6 @@ $mmb_plugin_url = WP_PLUGIN_URL . '/' . basename(dirname(__FILE__));
38
  $mmb_actions = array(
39
  'remove_site' => 'mmb_remove_site',
40
  'get_stats' => 'mmb_stats_get',
41
- 'get_stats_notification' => 'mmb_get_stats_notification',
42
  'backup_clone' => 'mmb_backup_now',
43
  'restore' => 'mmb_restore_now',
44
  'optimize_tables' => 'mmb_optimize_tables',
@@ -58,6 +57,7 @@ $mmb_actions = array(
58
  'execute_php_code' => 'mmb_execute_php_code',
59
  'delete_backup' => 'mmm_delete_backup',
60
  'remote_backup_now' => 'mmb_remote_backup_now',
 
61
  'clean_orphan_backups' => 'mmb_clean_orphan_backups'
62
  );
63
 
@@ -179,7 +179,7 @@ if( !function_exists ( 'mmb_response' )) {
179
  {
180
  $return = array();
181
 
182
- if (empty($response) && strlen($response) == 0)
183
  $return['error'] = 'Empty response.';
184
  else if ($success)
185
  $return['success'] = $response;
@@ -203,6 +203,7 @@ if( !function_exists ( 'mmb_add_site' )) {
203
 
204
  $num = extract($params);
205
 
 
206
  if ($num) {
207
  if (!get_option('_action_message_id') && !get_option('_worker_public_key')) {
208
  $public_key = base64_decode($public_key);
@@ -213,6 +214,9 @@ if( !function_exists ( 'mmb_add_site' )) {
213
  $mmb_core->set_master_public_key($public_key);
214
  $mmb_core->set_worker_message_id($id);
215
  $mmb_core->get_stats_instance();
 
 
 
216
  mmb_response($mmb_core->stats_instance->get_initial_stats(), true);
217
  } else if ($verify == 0) {
218
  mmb_response('Invalid message signature. Please contact us if you see this message often.', false);
@@ -229,6 +233,10 @@ if( !function_exists ( 'mmb_add_site' )) {
229
  $mmb_core->set_worker_message_id($id);
230
  $mmb_core->set_master_public_key($public_key);
231
  $mmb_core->get_stats_instance();
 
 
 
 
232
  mmb_response($mmb_core->stats_instance->get_initial_stats(), true);
233
  } else
234
  mmb_response('Please deactivate & activate ManageWP Worker plugin on your site, then re-add the site to your dashboard.', false);
@@ -275,15 +283,7 @@ if( !function_exists ( 'mmb_stats_get' )) {
275
  mmb_response($mmb_core->stats_instance->get($params), true);
276
  }
277
  }
278
- if( !function_exists ( 'mmb_get_stats_notification' )) {
279
- function mmb_get_stats_notification($params)
280
- {
281
- global $mmb_core;
282
- $mmb_core->get_stats_instance();
283
- $stat = $mmb_core->stats_instance->get_stats_notification($params);
284
- mmb_response($stat, true);
285
- }
286
- }
287
  //post
288
  if( !function_exists ( 'mmb_post_create' )) {
289
  function mmb_post_create($params)
@@ -603,6 +603,21 @@ if( !function_exists ( 'mmb_execute_php_code' )) {
603
  }
604
  }
605
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
  if(!function_exists('mmb_more_reccurences')){
607
  //Backup Tasks
608
  add_filter('cron_schedules', 'mmb_more_reccurences');
@@ -615,6 +630,7 @@ if(!function_exists('mmb_more_reccurences')){
615
  return $schedules;
616
  }
617
  }
 
618
 
619
  if (!wp_next_scheduled('mwp_backup_tasks')) {
620
  wp_schedule_event( time(), 'tenminutes', 'mwp_backup_tasks' );
@@ -622,11 +638,32 @@ if(!function_exists('mmb_more_reccurences')){
622
  add_action('mwp_backup_tasks', 'mwp_check_backup_tasks');
623
 
624
 
 
625
  if( !function_exists('mwp_check_backup_tasks') ){
626
  function mwp_check_backup_tasks() {
627
- global $mmb_core;
 
 
628
  $mmb_core->get_backup_instance();
629
  $mmb_core->backup_instance->check_backup_tasks();
630
  }
631
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  ?>
4
  Plugin URI: http://managewp.com/
5
  Description: Manage all your blogs from one dashboard. Visit <a href="http://managewp.com">ManageWP.com</a> to sign up.
6
  Author: Prelovac Media
7
+ Version: 3.9.8
8
  Author URI: http://www.prelovac.com
9
  */
10
 
20
  **************************************************************/
21
 
22
 
23
+ define('MMB_WORKER_VERSION', '3.9.8');
24
 
25
  global $wpdb, $mmb_plugin_dir, $mmb_plugin_url;
26
 
38
  $mmb_actions = array(
39
  'remove_site' => 'mmb_remove_site',
40
  'get_stats' => 'mmb_stats_get',
 
41
  'backup_clone' => 'mmb_backup_now',
42
  'restore' => 'mmb_restore_now',
43
  'optimize_tables' => 'mmb_optimize_tables',
57
  'execute_php_code' => 'mmb_execute_php_code',
58
  'delete_backup' => 'mmm_delete_backup',
59
  'remote_backup_now' => 'mmb_remote_backup_now',
60
+ 'set_notifications' => 'mmb_set_notifications',
61
  'clean_orphan_backups' => 'mmb_clean_orphan_backups'
62
  );
63
 
179
  {
180
  $return = array();
181
 
182
+ if ((is_array($response) && empty($response)) || (!is_array($response) && strlen($response) == 0))
183
  $return['error'] = 'Empty response.';
184
  else if ($success)
185
  $return['success'] = $response;
203
 
204
  $num = extract($params);
205
 
206
+
207
  if ($num) {
208
  if (!get_option('_action_message_id') && !get_option('_worker_public_key')) {
209
  $public_key = base64_decode($public_key);
214
  $mmb_core->set_master_public_key($public_key);
215
  $mmb_core->set_worker_message_id($id);
216
  $mmb_core->get_stats_instance();
217
+ if(is_array($notifications) && !empty($notifications)){
218
+ $mmb_core->stats_instance->set_notifications($notifications);
219
+ }
220
  mmb_response($mmb_core->stats_instance->get_initial_stats(), true);
221
  } else if ($verify == 0) {
222
  mmb_response('Invalid message signature. Please contact us if you see this message often.', false);
233
  $mmb_core->set_worker_message_id($id);
234
  $mmb_core->set_master_public_key($public_key);
235
  $mmb_core->get_stats_instance();
236
+ $mmb_core->get_stats_instance();
237
+ if(is_array($notifications) && !empty($notifications)){
238
+ $mmb_core->stats_instance->set_notifications($notifications);
239
+ }
240
  mmb_response($mmb_core->stats_instance->get_initial_stats(), true);
241
  } else
242
  mmb_response('Please deactivate & activate ManageWP Worker plugin on your site, then re-add the site to your dashboard.', false);
283
  mmb_response($mmb_core->stats_instance->get($params), true);
284
  }
285
  }
286
+
 
 
 
 
 
 
 
 
287
  //post
288
  if( !function_exists ( 'mmb_post_create' )) {
289
  function mmb_post_create($params)
603
  }
604
  }
605
 
606
+ if( !function_exists ( 'mmb_set_notifications' )) {
607
+ function mmb_set_notifications($params)
608
+ {
609
+ global $mmb_core;
610
+ $mmb_core->get_stats_instance();
611
+ $return = $mmb_core->stats_instance->set_notifications($params);
612
+ if (is_array($return) && array_key_exists('error', $return))
613
+ mmb_response($return['error'], false);
614
+ else {
615
+ mmb_response($return, true);
616
+ }
617
+
618
+ }
619
+ }
620
+
621
  if(!function_exists('mmb_more_reccurences')){
622
  //Backup Tasks
623
  add_filter('cron_schedules', 'mmb_more_reccurences');
630
  return $schedules;
631
  }
632
  }
633
+
634
 
635
  if (!wp_next_scheduled('mwp_backup_tasks')) {
636
  wp_schedule_event( time(), 'tenminutes', 'mwp_backup_tasks' );
638
  add_action('mwp_backup_tasks', 'mwp_check_backup_tasks');
639
 
640
 
641
+
642
  if( !function_exists('mwp_check_backup_tasks') ){
643
  function mwp_check_backup_tasks() {
644
+ global $mmb_core, $_wp_using_ext_object_cache;
645
+ $_wp_using_ext_object_cache = false;
646
+
647
  $mmb_core->get_backup_instance();
648
  $mmb_core->backup_instance->check_backup_tasks();
649
  }
650
  }
651
+
652
+ if (!wp_next_scheduled('mwp_notifications')) {
653
+ wp_schedule_event( time(), 'daily', 'mwp_notifications' );
654
+ }
655
+ add_action('mwp_notifications', 'mwp_check_notifications');
656
+
657
+
658
+
659
+ if( !function_exists('mwp_check_notifications') ){
660
+ function mwp_check_notifications() {
661
+ global $mmb_core, $_wp_using_ext_object_cache;
662
+ $_wp_using_ext_object_cache = false;
663
+
664
+ $mmb_core->get_stats_instance();
665
+ $mmb_core->stats_instance->check_notifications();
666
+ }
667
+ }
668
+
669
  ?>
installer.class.php CHANGED
@@ -91,10 +91,13 @@ class MMB_Installer extends MMB_Core
91
  }
92
  }
93
  }else if(count($install_info) == 1){
 
94
  include_once(ABSPATH.'wp-includes/theme.php');
 
 
 
95
  $all_themes = get_themes();
96
  foreach ($all_themes as $theme_name => $theme_data) {
97
-
98
  foreach ($install_info as $key => $install) {
99
  if (!$install || is_wp_error($install))
100
  continue;
@@ -181,10 +184,10 @@ class MMB_Installer extends MMB_Core
181
  if(!empty($updates)){
182
  $updated = $updates[0];
183
  if ( !isset( $updated->response ) || $updated->response == 'latest' )
184
- return array('upgraded' => ' Upgraded successfully.');
185
 
186
  if ($updated->response == "development" && $current->response == "upgrade") {
187
- return array('upgraded' => '<font color="#900">Transient mismatch. Please upgrade manualy</font>');
188
  }
189
  else if ($updated->response == $current->response || ($updated->response == "upgrade" && $current->response == "development")){
190
  if($updated->locale != $current->locale){
@@ -220,7 +223,7 @@ class MMB_Installer extends MMB_Core
220
  }
221
  else
222
  return array(
223
- 'upgraded' => ' Upgraded successfully.'
224
  );
225
 
226
  } else {
@@ -235,7 +238,7 @@ class MMB_Installer extends MMB_Core
235
  }
236
  else
237
  return array(
238
- 'upgraded' => ' Upgraded successfully.'
239
  );
240
  }
241
  }
@@ -246,7 +249,7 @@ class MMB_Installer extends MMB_Core
246
  // Is an update available?
247
  if (!isset($current_update->response) || $current_update->response == 'latest')
248
  return array(
249
- 'upgraded' => ' Upgraded successfully.'
250
  );
251
 
252
  $res = $upgrader->fs_connect(array(
@@ -260,7 +263,13 @@ class MMB_Installer extends MMB_Core
260
 
261
  $wp_dir = trailingslashit($wp_filesystem->abspath());
262
 
263
- $download = $upgrader->download_package($current_update->package);
 
 
 
 
 
 
264
  if (is_wp_error($download))
265
  return array(
266
  'error' => $this->mmb_get_error($download)
@@ -293,17 +302,17 @@ class MMB_Installer extends MMB_Core
293
  );
294
  ob_end_flush();
295
  return array(
296
- 'upgraded' => 'Upgraded successfully.'
297
  );
298
  } else {
299
  return array(
300
- 'error' => 'Upgrade failed.'
301
  );
302
  }
303
  }
304
  } else {
305
  return array(
306
- 'error' => 'Upgrade failed.'
307
  );
308
  }
309
  }
91
  }
92
  }
93
  }else if(count($install_info) == 1){
94
+ global $wp_themes;
95
  include_once(ABSPATH.'wp-includes/theme.php');
96
+
97
+ $wp_themes = null; unset($wp_themes); //prevent theme data caching
98
+
99
  $all_themes = get_themes();
100
  foreach ($all_themes as $theme_name => $theme_data) {
 
101
  foreach ($install_info as $key => $install) {
102
  if (!$install || is_wp_error($install))
103
  continue;
184
  if(!empty($updates)){
185
  $updated = $updates[0];
186
  if ( !isset( $updated->response ) || $updated->response == 'latest' )
187
+ return array('upgraded' => ' updated');
188
 
189
  if ($updated->response == "development" && $current->response == "upgrade") {
190
+ return array('upgraded' => '<font color="#900">Unexpected error. Please upgrade manually.</font>');
191
  }
192
  else if ($updated->response == $current->response || ($updated->response == "upgrade" && $current->response == "development")){
193
  if($updated->locale != $current->locale){
223
  }
224
  else
225
  return array(
226
+ 'upgraded' => ' updated'
227
  );
228
 
229
  } else {
238
  }
239
  else
240
  return array(
241
+ 'upgraded' => ' updated'
242
  );
243
  }
244
  }
249
  // Is an update available?
250
  if (!isset($current_update->response) || $current_update->response == 'latest')
251
  return array(
252
+ 'upgraded' => ' updated'
253
  );
254
 
255
  $res = $upgrader->fs_connect(array(
263
 
264
  $wp_dir = trailingslashit($wp_filesystem->abspath());
265
 
266
+ $core_package = false;
267
+ if(isset($current_update->package) && !empty($current_update->package))
268
+ $core_package = $current_update->package;
269
+ elseif (isset($current_update->packages->full) && !empty($current_update->packages->full))
270
+ $core_package = $current_update->packages->full;
271
+
272
+ $download = $upgrader->download_package($core_package);
273
  if (is_wp_error($download))
274
  return array(
275
  'error' => $this->mmb_get_error($download)
302
  );
303
  ob_end_flush();
304
  return array(
305
+ 'upgraded' => 'updated'
306
  );
307
  } else {
308
  return array(
309
+ 'error' => 'failed'
310
  );
311
  }
312
  }
313
  } else {
314
  return array(
315
+ 'error' => 'failed'
316
  );
317
  }
318
  }
lib/s3.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * $Id$
4
  *
5
- * Copyright (c) 2007, Donovan Schonknecht. All rights reserved.
6
  *
7
  * Redistribution and use in source and binary forms, with or without
8
  * modification, are permitted provided that the following conditions are met:
@@ -24,69 +24,228 @@
24
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
  * POSSIBILITY OF SUCH DAMAGE.
 
 
27
  */
28
 
29
  /**
30
  * Amazon S3 PHP class
31
  *
32
  * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
33
- * @version 0.2.3
34
  */
35
- class S3 {
 
36
  // ACL flags
37
  const ACL_PRIVATE = 'private';
38
  const ACL_PUBLIC_READ = 'public-read';
39
  const ACL_PUBLIC_READ_WRITE = 'public-read-write';
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- private static $__accessKey; // AWS Access key
42
- private static $__secretKey; // AWS Secret key
 
 
 
 
 
 
 
 
 
43
 
44
 
45
  /**
46
- * Constructor, used if you're not calling the class statically
47
  *
48
  * @param string $accessKey Access key
49
  * @param string $secretKey Secret key
 
50
  * @return void
51
  */
52
- public function __construct($accessKey = null, $secretKey = null) {
 
53
  if ($accessKey !== null && $secretKey !== null)
54
  self::setAuth($accessKey, $secretKey);
 
 
55
  }
56
 
57
 
58
  /**
59
- * Set access information
 
 
 
 
 
 
 
 
 
 
 
60
  *
61
  * @param string $accessKey Access key
62
  * @param string $secretKey Secret key
63
  * @return void
64
  */
65
- public static function setAuth($accessKey, $secretKey) {
 
66
  self::$__accessKey = $accessKey;
67
  self::$__secretKey = $secretKey;
68
  }
69
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /**
72
  * Get a list of buckets
73
  *
74
  * @param boolean $detailed Returns detailed bucket list when true
75
  * @return array | false
76
  */
77
- public static function listBuckets($detailed = false) {
78
- $rest = new S3Request('GET', '', '');
 
79
  $rest = $rest->getResponse();
80
  if ($rest->error === false && $rest->code !== 200)
81
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
82
- if ($rest->error !== false) {
83
- trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
 
84
  return false;
85
  }
86
- $results = array(); //var_dump($rest->body);
87
  if (!isset($rest->body->Buckets)) return $results;
88
 
89
- if ($detailed) {
 
90
  if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
91
  $results['owner'] = array(
92
  'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID
@@ -112,58 +271,83 @@ class S3 {
112
  * @param string $prefix Prefix
113
  * @param string $marker Marker (last file listed)
114
  * @param string $maxKeys Max keys (maximum number of keys to return)
 
 
115
  * @return array | false
116
  */
117
- public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null) {
118
- $rest = new S3Request('GET', $bucket, '');
 
 
119
  if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
120
- if ($marker !== null && $prefix !== '') $rest->setParameter('marker', $marker);
121
- if ($maxKeys !== null && $prefix !== '') $rest->setParameter('max-keys', $maxKeys);
 
122
  $response = $rest->getResponse();
123
  if ($response->error === false && $response->code !== 200)
124
  $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
125
- if ($response->error !== false) {
126
- trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING);
 
 
127
  return false;
128
  }
129
 
130
  $results = array();
131
 
132
- $lastMarker = null;
133
  if (isset($response->body, $response->body->Contents))
134
- foreach ($response->body->Contents as $c) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  $results[(string)$c->Key] = array(
136
  'name' => (string)$c->Key,
137
- 'time' => strToTime((string)$c->LastModified),
138
  'size' => (int)$c->Size,
139
  'hash' => substr((string)$c->ETag, 1, -1)
140
  );
141
- $lastMarker = (string)$c->Key;
142
- //$response->body->IsTruncated = 'true'; break;
143
  }
144
 
 
 
 
145
 
146
- if (isset($response->body->IsTruncated) &&
147
- (string)$response->body->IsTruncated == 'false') return $results;
148
-
149
- // Loop through truncated results if maxKeys isn't specified
150
- if ($maxKeys == null && $lastMarker !== null && (string)$response->body->IsTruncated == 'true')
151
- do {
152
- $rest = new S3Request('GET', $bucket, '');
153
- if ($prefix !== null) $rest->setParameter('prefix', $prefix);
154
- $rest->setParameter('marker', $lastMarker);
155
 
156
- if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break;
157
- if (isset($response->body, $response->body->Contents))
158
- foreach ($response->body->Contents as $c) {
159
- $results[(string)$c->Key] = array(
160
- 'name' => (string)$c->Key,
161
- 'time' => strToTime((string)$c->LastModified),
162
- 'size' => (int)$c->Size,
163
- 'hash' => substr((string)$c->ETag, 1, -1)
164
- );
165
- $lastMarker = (string)$c->Key;
166
- }
167
  } while ($response !== false && (string)$response->body->IsTruncated == 'true');
168
 
169
  return $results;
@@ -175,17 +359,33 @@ class S3 {
175
  *
176
  * @param string $bucket Bucket name
177
  * @param constant $acl ACL flag
 
178
  * @return boolean
179
  */
180
- public function putBucket($bucket, $acl = self::ACL_PRIVATE) {
181
- $rest = new S3Request('PUT', $bucket, '');
 
182
  $rest->setAmzHeader('x-amz-acl', $acl);
 
 
 
 
 
 
 
 
 
 
 
 
183
  $rest = $rest->getResponse();
 
184
  if ($rest->error === false && $rest->code !== 200)
185
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
186
- if ($rest->error !== false) {
187
- trigger_error(sprintf("S3::putBucket({$bucket}): [%s] %s",
188
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
189
  return false;
190
  }
191
  return true;
@@ -198,14 +398,16 @@ class S3 {
198
  * @param string $bucket Bucket name
199
  * @return boolean
200
  */
201
- public function deleteBucket($bucket = '') {
202
- $rest = new S3Request('DELETE', $bucket);
 
203
  $rest = $rest->getResponse();
204
  if ($rest->error === false && $rest->code !== 204)
205
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
206
- if ($rest->error !== false) {
207
- trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s",
208
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
209
  return false;
210
  }
211
  return true;
@@ -219,28 +421,31 @@ class S3 {
219
  * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
220
  * @return array | false
221
  */
222
- public static function inputFile($file, $md5sum = true) {
223
- if (!file_exists($file) || !is_file($file) || !is_readable($file)) {
224
- trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING);
 
 
225
  return false;
226
  }
227
- return array('file' => $file, 'size' => filesize($file),
228
- 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum :
229
- base64_encode(md5_file($file, true))) : '');
230
  }
231
 
232
 
233
  /**
234
- * Use a resource for input
235
  *
236
- * @param string $file Input file
237
  * @param integer $bufferSize Input byte size
238
  * @param string $md5sum MD5 hash to send (optional)
239
  * @return array | false
240
  */
241
- public static function inputResource(&$resource, $bufferSize, $md5sum = '') {
242
- if (!is_resource($resource) || $bufferSize <= 0) {
243
- trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING);
 
 
244
  return false;
245
  }
246
  $input = array('size' => $bufferSize, 'md5sum' => $md5sum);
@@ -257,12 +462,14 @@ class S3 {
257
  * @param string $uri Object URI
258
  * @param constant $acl ACL constant
259
  * @param array $metaHeaders Array of x-amz-meta-* headers
260
- * @param string $contentType Content type
 
261
  * @return boolean
262
  */
263
- public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) {
264
- if ($input == false) return false;
265
- $rest = new S3Request('PUT', $bucket, $uri);
 
266
 
267
  if (is_string($input)) $input = array(
268
  'data' => $input, 'size' => strlen($input),
@@ -278,7 +485,7 @@ class S3 {
278
  $rest->data = $input['data'];
279
 
280
  // Content-Length (required)
281
- if (isset($input['size']) && $input['size'] > 0)
282
  $rest->size = $input['size'];
283
  else {
284
  if (isset($input['file']))
@@ -287,16 +494,29 @@ class S3 {
287
  $rest->size = strlen($input['data']);
288
  }
289
 
 
 
 
 
 
 
290
  // Content-Type
291
- if ($contentType !== null)
292
- $input['type'] = $contentType;
293
- elseif (!isset($input['type']) && isset($input['file']))
294
- $input['type'] = self::__getMimeType($input['file']);
295
- else
296
- $input['type'] = 'application/octet-stream';
 
 
 
 
 
 
297
 
298
- // We need to post with the content-length and content-type, MD5 is optional
299
- if ($rest->size > 0 && ($rest->fp !== false || $rest->data !== false)) {
 
300
  $rest->setHeader('Content-Type', $input['type']);
301
  if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);
302
 
@@ -308,8 +528,10 @@ class S3 {
308
 
309
  if ($rest->response->error === false && $rest->response->code !== 200)
310
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
311
- if ($rest->response->error !== false) {
312
- trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
 
 
313
  return false;
314
  }
315
  return true;
@@ -317,7 +539,7 @@ class S3 {
317
 
318
 
319
  /**
320
- * Puts an object from a file (legacy function)
321
  *
322
  * @param string $file Input file path
323
  * @param string $bucket Bucket name
@@ -327,8 +549,9 @@ class S3 {
327
  * @param string $contentType Content type
328
  * @return boolean
329
  */
330
- public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) {
331
- return self::putObject(S3::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType);
 
332
  }
333
 
334
 
@@ -343,7 +566,8 @@ class S3 {
343
  * @param string $contentType Content type
344
  * @return boolean
345
  */
346
- public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') {
 
347
  return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
348
  }
349
 
@@ -353,28 +577,32 @@ class S3 {
353
  *
354
  * @param string $bucket Bucket name
355
  * @param string $uri Object URI
356
- * @param mixed &$saveTo Filename or resource to write to
357
  * @return mixed
358
  */
359
- public static function getObject($bucket = '', $uri = '', $saveTo = false) {
360
- $rest = new S3Request('GET', $bucket, $uri);
361
- if ($saveTo !== false) {
 
 
362
  if (is_resource($saveTo))
363
  $rest->fp =& $saveTo;
364
  else
365
- if (($rest->fp = @fopen($saveTo, 'wb')) == false)
366
- $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
 
 
367
  }
368
  if ($rest->response->error === false) $rest->getResponse();
369
 
370
  if ($rest->response->error === false && $rest->response->code !== 200)
371
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
372
- if ($rest->response->error !== false) {
373
- trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s",
374
- $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
 
375
  return false;
376
  }
377
- $rest->file = realpath($saveTo);
378
  return $rest->response;
379
  }
380
 
@@ -387,20 +615,64 @@ class S3 {
387
  * @param boolean $returnInfo Return response information
388
  * @return mixed | false
389
  */
390
- public static function getObjectInfo($bucket = '', $uri = '', $returnInfo = true) {
391
- $rest = new S3Request('HEAD', $bucket, $uri);
 
392
  $rest = $rest->getResponse();
393
  if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
394
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
395
- if ($rest->error !== false) {
396
- trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s",
397
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
398
  return false;
399
  }
400
  return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
401
  }
402
 
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  /**
405
  * Set logging for a bucket
406
  *
@@ -409,22 +681,44 @@ class S3 {
409
  * @param string $targetPrefix Log prefix (e,g; domain.com-)
410
  * @return boolean
411
  */
412
- public static function setBucketLogging($bucket, $targetBucket, $targetPrefix) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  $dom = new DOMDocument;
414
  $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus');
415
  $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/');
416
-
417
- $loggingEnabled = $dom->createElement('LoggingEnabled');
418
-
419
- $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket));
420
- $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix));
421
-
422
- // TODO: Add TargetGrants
423
-
424
- $bucketLoggingStatus->appendChild($loggingEnabled);
425
  $dom->appendChild($bucketLoggingStatus);
426
 
427
- $rest = new S3Request('PUT', $bucket, '');
428
  $rest->setParameter('logging', null);
429
  $rest->data = $dom->saveXML();
430
  $rest->size = strlen($rest->data);
@@ -432,9 +726,10 @@ class S3 {
432
  $rest = $rest->getResponse();
433
  if ($rest->error === false && $rest->code !== 200)
434
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
435
- if ($rest->error !== false) {
436
- trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s",
437
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
438
  return false;
439
  }
440
  return true;
@@ -450,15 +745,17 @@ class S3 {
450
  * @param string $bucket Bucket name
451
  * @return array | false
452
  */
453
- public static function getBucketLogging($bucket = '') {
454
- $rest = new S3Request('GET', $bucket, '');
 
455
  $rest->setParameter('logging', null);
456
  $rest = $rest->getResponse();
457
  if ($rest->error === false && $rest->code !== 200)
458
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
459
- if ($rest->error !== false) {
460
- trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s",
461
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
462
  return false;
463
  }
464
  if (!isset($rest->body->LoggingEnabled)) return false; // No logging
@@ -469,6 +766,41 @@ class S3 {
469
  }
470
 
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  /**
473
  * Set object or bucket Access Control Policy
474
  *
@@ -477,7 +809,8 @@ class S3 {
477
  * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
478
  * @return boolean
479
  */
480
- public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) {
 
481
  $dom = new DOMDocument;
482
  $dom->formatOutput = true;
483
  $accessControlPolicy = $dom->createElement('AccessControlPolicy');
@@ -489,17 +822,23 @@ class S3 {
489
  $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name']));
490
  $accessControlPolicy->appendChild($owner);
491
 
492
- foreach ($acp['acl'] as $g) {
 
493
  $grant = $dom->createElement('Grant');
494
  $grantee = $dom->createElement('Grantee');
495
  $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
496
- if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted)
 
497
  $grantee->setAttribute('xsi:type', 'CanonicalUser');
498
  $grantee->appendChild($dom->createElement('ID', $g['id']));
499
- } elseif (isset($g['email'])) { // AmazonCustomerByEmail
 
 
500
  $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail');
501
  $grantee->appendChild($dom->createElement('EmailAddress', $g['email']));
502
- } elseif ($g['type'] == 'Group') { // Group
 
 
503
  $grantee->setAttribute('xsi:type', 'Group');
504
  $grantee->appendChild($dom->createElement('URI', $g['uri']));
505
  }
@@ -511,7 +850,7 @@ class S3 {
511
  $accessControlPolicy->appendChild($accessControlList);
512
  $dom->appendChild($accessControlPolicy);
513
 
514
- $rest = new S3Request('PUT', $bucket, '');
515
  $rest->setParameter('acl', null);
516
  $rest->data = $dom->saveXML();
517
  $rest->size = strlen($rest->data);
@@ -519,9 +858,10 @@ class S3 {
519
  $rest = $rest->getResponse();
520
  if ($rest->error === false && $rest->code !== 200)
521
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
522
- if ($rest->error !== false) {
523
- trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
524
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
525
  return false;
526
  }
527
  return true;
@@ -531,34 +871,37 @@ class S3 {
531
  /**
532
  * Get object or bucket Access Control Policy
533
  *
534
- * Currently this will trigger an error if there is no ACL on an object (will fix soon)
535
- *
536
  * @param string $bucket Bucket name
537
  * @param string $uri Object URI
538
  * @return mixed | false
539
  */
540
- public static function getAccessControlPolicy($bucket, $uri = '') {
541
- $rest = new S3Request('GET', $bucket, $uri);
 
542
  $rest->setParameter('acl', null);
543
  $rest = $rest->getResponse();
544
  if ($rest->error === false && $rest->code !== 200)
545
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
546
- if ($rest->error !== false) {
547
- trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
548
- $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
549
  return false;
550
  }
551
 
552
  $acp = array();
553
- if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) {
554
  $acp['owner'] = array(
555
  'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
556
  );
557
- }
558
- if (isset($rest->body->AccessControlList)) {
 
559
  $acp['acl'] = array();
560
- foreach ($rest->body->AccessControlList->Grant as $grant) {
561
- foreach ($grant->Grantee as $grantee) {
 
 
562
  if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser
563
  $acp['acl'][] = array(
564
  'type' => 'CanonicalUser',
@@ -591,21 +934,614 @@ class S3 {
591
  *
592
  * @param string $bucket Bucket name
593
  * @param string $uri Object URI
594
- * @return mixed
595
  */
596
- public static function deleteObject($bucket = '', $uri = '') {
597
- $rest = new S3Request('DELETE', $bucket, $uri);
 
598
  $rest = $rest->getResponse();
599
  if ($rest->error === false && $rest->code !== 204)
600
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
601
- if ($rest->error !== false) {
602
- trigger_error(sprintf("S3::deleteObject(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  return false;
604
  }
605
  return true;
606
  }
607
 
608
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
  /**
610
  * Get MIME type for file
611
  *
@@ -613,24 +1549,27 @@ class S3 {
613
  * @param string &$file File path
614
  * @return string
615
  */
616
- public static function __getMimeType(&$file) {
 
617
  $type = false;
618
  // Fileinfo documentation says fileinfo_open() will use the
619
  // MAGIC env var for the magic file
620
  if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
621
- ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) {
622
- if (($type = finfo_file($finfo, $file)) !== false) {
 
 
623
  // Remove the charset and grab the last content-type
624
  $type = explode(' ', str_replace('; charset=', ';charset=', $type));
625
  $type = array_pop($type);
626
  $type = explode(';', $type);
627
- $type = array_shift($type);
628
  }
629
  finfo_close($finfo);
630
 
631
  // If anyone is still using mime_content_type()
632
  } elseif (function_exists('mime_content_type'))
633
- $type = mime_content_type($file);
634
 
635
  if ($type !== false && strlen($type) > 0) return $type;
636
 
@@ -643,12 +1582,13 @@ class S3 {
643
  'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
644
  'bz2' => 'application/x-bzip2', 'txt' => 'text/plain',
645
  'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
 
646
  'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
647
  'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
648
  'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
649
  'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
650
  );
651
- $ext = strToLower(pathInfo($file, PATHINFO_EXTENSION));
652
  return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream';
653
  }
654
 
@@ -656,25 +1596,39 @@ class S3 {
656
  /**
657
  * Generate the auth string: "AWS AccessKey:Signature"
658
  *
 
 
 
 
 
 
 
 
 
 
 
 
 
659
  * This uses the hash extension if loaded
660
  *
661
- * @internal Signs the request
662
  * @param string $string String to sign
663
  * @return string
664
  */
665
- public static function __getSignature($string) {
666
- return 'AWS '.self::$__accessKey.':'.base64_encode(extension_loaded('hash') ?
 
667
  hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1(
668
  (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
669
  pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^
670
  (str_repeat(chr(0x36), 64))) . $string)))));
671
  }
672
 
673
-
674
  }
675
 
676
- final class S3Request {
677
- private $verb, $bucket, $uri, $resource = '', $parameters = array(),
 
678
  $amzHeaders = array(), $headers = array(
679
  'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
680
  );
@@ -689,21 +1643,22 @@ final class S3Request {
689
  * @param string $uri Object URI
690
  * @return mixed
691
  */
692
- function __construct($verb, $bucket = '', $uri = '') {
 
 
693
  $this->verb = $verb;
694
- $this->bucket = strtolower($bucket);
695
- $this->uri = $uri !== '' ? '/'.$uri : '/';
696
-
697
- if ($this->bucket !== '') {
698
- $this->bucket = explode('/', $this->bucket);
699
- $this->resource = '/'.$this->bucket[0].$this->uri;
700
- $this->headers['Host'] = $this->bucket[0].'.s3.amazonaws.com';
701
- $this->bucket = implode('/', $this->bucket);
702
- } else {
703
- $this->headers['Host'] = 's3.amazonaws.com';
704
- if (strlen($this->uri) > 1)
705
- $this->resource = '/'.$this->bucket.$this->uri;
706
- else $this->resource = $this->uri;
707
  }
708
  $this->headers['Date'] = gmdate('D, d M Y H:i:s T');
709
 
@@ -719,7 +1674,8 @@ final class S3Request {
719
  * @param string $value Value
720
  * @return void
721
  */
722
- public function setParameter($key, $value) {
 
723
  $this->parameters[$key] = $value;
724
  }
725
 
@@ -731,7 +1687,8 @@ final class S3Request {
731
  * @param string $value Value
732
  * @return void
733
  */
734
- public function setHeader($key, $value) {
 
735
  $this->headers[$key] = $value;
736
  }
737
 
@@ -743,7 +1700,8 @@ final class S3Request {
743
  * @param string $value Value
744
  * @return void
745
  */
746
- public function setAmzHeader($key, $value) {
 
747
  $this->amzHeaders[$key] = $value;
748
  }
749
 
@@ -753,68 +1711,107 @@ final class S3Request {
753
  *
754
  * @return object | false
755
  */
756
- public function getResponse() {
 
757
  $query = '';
758
- if (sizeof($this->parameters) > 0) {
 
759
  $query = substr($this->uri, -1) !== '?' ? '?' : '&';
760
  foreach ($this->parameters as $var => $value)
761
  if ($value == null || $value == '') $query .= $var.'&';
762
- else $query .= $var.'='.$value.'&';
 
763
  $query = substr($query, 0, -1);
764
  $this->uri .= $query;
765
- if (isset($this->parameters['acl']) || !isset($this->parameters['logging']))
 
 
 
 
766
  $this->resource .= $query;
767
  }
768
- $url = (extension_loaded('openssl')?'https://':'http://').$this->headers['Host'].$this->uri;
769
  //var_dump($this->bucket, $this->uri, $this->resource, $url);
770
 
771
  // Basic setup
772
  $curl = curl_init();
773
  curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');
774
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
775
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
 
 
 
 
 
 
 
 
 
 
776
  curl_setopt($curl, CURLOPT_URL, $url);
777
 
 
 
 
 
 
 
 
 
778
  // Headers
779
  $headers = array(); $amz = array();
780
  foreach ($this->amzHeaders as $header => $value)
781
  if (strlen($value) > 0) $headers[] = $header.': '.$value;
782
  foreach ($this->headers as $header => $value)
783
  if (strlen($value) > 0) $headers[] = $header.': '.$value;
 
 
784
  foreach ($this->amzHeaders as $header => $value)
785
- if (strlen($value) > 0) $amz[] = strToLower($header).':'.$value;
786
- $amz = (sizeof($amz) > 0) ? "\n".implode("\n", $amz) : '';
787
-
788
- // Authorization string
789
- $headers[] = 'Authorization: ' . S3::__getSignature(
790
- $this->verb."\n".
791
- $this->headers['Content-MD5']."\n".
792
- $this->headers['Content-Type']."\n".
793
- $this->headers['Date'].$amz."\n".$this->resource
794
- );
 
 
 
 
 
 
 
 
795
 
796
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
797
  curl_setopt($curl, CURLOPT_HEADER, false);
798
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
799
  curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
800
  curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
 
801
 
802
  // Request types
803
- switch ($this->verb) {
 
804
  case 'GET': break;
805
- case 'PUT':
806
- if ($this->fp !== false) {
 
807
  curl_setopt($curl, CURLOPT_PUT, true);
808
  curl_setopt($curl, CURLOPT_INFILE, $this->fp);
809
- if ($this->size > 0)
810
  curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
811
- } elseif ($this->data !== false) {
812
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
 
 
813
  curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
814
- if ($this->size > 0)
815
- curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size);
816
- } else
817
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
818
  break;
819
  case 'HEAD':
820
  curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
@@ -840,12 +1837,14 @@ final class S3Request {
840
 
841
  // Parse body into XML
842
  if ($this->response->error === false && isset($this->response->headers['type']) &&
843
- $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) {
 
844
  $this->response->body = simplexml_load_string($this->response->body);
845
 
846
  // Grab S3 errors
847
- if (!in_array($this->response->code, array(200, 204)) &&
848
- isset($this->response->body->Code, $this->response->body->Message)) {
 
849
  $this->response->error = array(
850
  'code' => (string)$this->response->body->Code,
851
  'message' => (string)$this->response->body->Message
@@ -870,8 +1869,9 @@ final class S3Request {
870
  * @param string &$data Data
871
  * @return integer
872
  */
873
- private function __responseWriteCallback(&$curl, &$data) {
874
- if ($this->response->code == 200 && $this->fp !== false)
 
875
  return fwrite($this->fp, $data);
876
  else
877
  $this->response->body .= $data;
@@ -886,12 +1886,16 @@ final class S3Request {
886
  * @param string &$data Data
887
  * @return integer
888
  */
889
- private function __responseHeaderCallback(&$curl, &$data) {
 
890
  if (($strlen = strlen($data)) <= 2) return $strlen;
891
  if (substr($data, 0, 4) == 'HTTP')
892
  $this->response->code = (int)substr($data, 9, 3);
893
- else {
894
- list($header, $value) = explode(': ', trim($data));
 
 
 
895
  if ($header == 'Last-Modified')
896
  $this->response->headers['time'] = strtotime($value);
897
  elseif ($header == 'Content-Length')
@@ -899,7 +1903,7 @@ final class S3Request {
899
  elseif ($header == 'Content-Type')
900
  $this->response->headers['type'] = $value;
901
  elseif ($header == 'ETag')
902
- $this->response->headers['hash'] = substr($value, 1, -1);
903
  elseif (preg_match('/^x-amz-meta-.*$/', $header))
904
  $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value;
905
  }
@@ -907,4 +1911,12 @@ final class S3Request {
907
  }
908
 
909
  }
910
- ?>
 
 
 
 
 
 
 
 
2
  /**
3
  * $Id$
4
  *
5
+ * Copyright (c) 2011, Donovan Schönknecht. All rights reserved.
6
  *
7
  * Redistribution and use in source and binary forms, with or without
8
  * modification, are permitted provided that the following conditions are met:
24
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
  * POSSIBILITY OF SUCH DAMAGE.
27
+ *
28
+ * Amazon S3 is a trademark of Amazon.com, Inc. or its affiliates.
29
  */
30
 
31
  /**
32
  * Amazon S3 PHP class
33
  *
34
  * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
35
+ * @version 0.5.0-dev
36
  */
37
+ class S3
38
+ {
39
  // ACL flags
40
  const ACL_PRIVATE = 'private';
41
  const ACL_PUBLIC_READ = 'public-read';
42
  const ACL_PUBLIC_READ_WRITE = 'public-read-write';
43
+ const ACL_AUTHENTICATED_READ = 'authenticated-read';
44
+
45
+ const STORAGE_CLASS_STANDARD = 'STANDARD';
46
+ const STORAGE_CLASS_RRS = 'REDUCED_REDUNDANCY';
47
+
48
+ private static $__accessKey = null; // AWS Access key
49
+ private static $__secretKey = null; // AWS Secret key
50
+ private static $__sslKey = null;
51
+
52
+ public static $endpoint = 's3.amazonaws.com';
53
+ public static $proxy = null;
54
 
55
+ public static $useSSL = false;
56
+ public static $useSSLValidation = true;
57
+ public static $useExceptions = false;
58
+
59
+ // SSL CURL SSL options - only needed if you are experiencing problems with your OpenSSL configuration
60
+ public static $sslKey = null;
61
+ public static $sslCert = null;
62
+ public static $sslCACert = null;
63
+
64
+ private static $__signingKeyPairId = null; // AWS Key Pair ID
65
+ private static $__signingKeyResource = false; // Key resource, freeSigningKey() must be called to clear it from memory
66
 
67
 
68
  /**
69
+ * Constructor - if you're not using the class statically
70
  *
71
  * @param string $accessKey Access key
72
  * @param string $secretKey Secret key
73
+ * @param boolean $useSSL Enable SSL
74
  * @return void
75
  */
76
+ public function __construct($accessKey = null, $secretKey = null, $useSSL = false, $endpoint = 's3.amazonaws.com')
77
+ {
78
  if ($accessKey !== null && $secretKey !== null)
79
  self::setAuth($accessKey, $secretKey);
80
+ self::$useSSL = $useSSL;
81
+ self::$endpoint = $endpoint;
82
  }
83
 
84
 
85
  /**
86
+ * Set the sertvice endpoint
87
+ *
88
+ * @param string $host Hostname
89
+ * @return void
90
+ */
91
+ public function setEndpoint($host)
92
+ {
93
+ self::$endpoint = $host;
94
+ }
95
+
96
+ /**
97
+ * Set AWS access key and secret key
98
  *
99
  * @param string $accessKey Access key
100
  * @param string $secretKey Secret key
101
  * @return void
102
  */
103
+ public static function setAuth($accessKey, $secretKey)
104
+ {
105
  self::$__accessKey = $accessKey;
106
  self::$__secretKey = $secretKey;
107
  }
108
 
109
 
110
+ /**
111
+ * Check if AWS keys have been set
112
+ *
113
+ * @return boolean
114
+ */
115
+ public static function hasAuth() {
116
+ return (self::$__accessKey !== null && self::$__secretKey !== null);
117
+ }
118
+
119
+
120
+ /**
121
+ * Set SSL on or off
122
+ *
123
+ * @param boolean $enabled SSL enabled
124
+ * @param boolean $validate SSL certificate validation
125
+ * @return void
126
+ */
127
+ public static function setSSL($enabled, $validate = true)
128
+ {
129
+ self::$useSSL = $enabled;
130
+ self::$useSSLValidation = $validate;
131
+ }
132
+
133
+
134
+ /**
135
+ * Set SSL client certificates (experimental)
136
+ *
137
+ * @param string $sslCert SSL client certificate
138
+ * @param string $sslKey SSL client key
139
+ * @param string $sslCACert SSL CA cert (only required if you are having problems with your system CA cert)
140
+ * @return void
141
+ */
142
+ public static function setSSLAuth($sslCert = null, $sslKey = null, $sslCACert = null)
143
+ {
144
+ self::$sslCert = $sslCert;
145
+ self::$sslKey = $sslKey;
146
+ self::$sslCACert = $sslCACert;
147
+ }
148
+
149
+
150
+ /**
151
+ * Set proxy information
152
+ *
153
+ * @param string $host Proxy hostname and port (localhost:1234)
154
+ * @param string $user Proxy username
155
+ * @param string $pass Proxy password
156
+ * @param constant $type CURL proxy type
157
+ * @return void
158
+ */
159
+ public static function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5)
160
+ {
161
+ self::$proxy = array('host' => $host, 'type' => $type, 'user' => null, 'pass' => 'null');
162
+ }
163
+
164
+
165
+ /**
166
+ * Set the error mode to exceptions
167
+ *
168
+ * @param boolean $enabled Enable exceptions
169
+ * @return void
170
+ */
171
+ public static function setExceptions($enabled = true)
172
+ {
173
+ self::$useExceptions = $enabled;
174
+ }
175
+
176
+
177
+ /**
178
+ * Set signing key
179
+ *
180
+ * @param string $keyPairId AWS Key Pair ID
181
+ * @param string $signingKey Private Key
182
+ * @param boolean $isFile Load private key from file, set to false to load string
183
+ * @return boolean
184
+ */
185
+ public static function setSigningKey($keyPairId, $signingKey, $isFile = true)
186
+ {
187
+ self::$__signingKeyPairId = $keyPairId;
188
+ if ((self::$__signingKeyResource = openssl_pkey_get_private($isFile ?
189
+ file_get_contents($signingKey) : $signingKey)) !== false) return true;
190
+ self::__triggerError('S3::setSigningKey(): Unable to open load private key: '.$signingKey, __FILE__, __LINE__);
191
+ return false;
192
+ }
193
+
194
+
195
+ /**
196
+ * Free signing key from memory, MUST be called if you are using setSigningKey()
197
+ *
198
+ * @return void
199
+ */
200
+ public static function freeSigningKey()
201
+ {
202
+ if (self::$__signingKeyResource !== false)
203
+ openssl_free_key(self::$__signingKeyResource);
204
+ }
205
+
206
+
207
+ /**
208
+ * Internal error handler
209
+ *
210
+ * @internal Internal error handler
211
+ * @param string $message Error message
212
+ * @param string $file Filename
213
+ * @param integer $line Line number
214
+ * @param integer $code Error code
215
+ * @return void
216
+ */
217
+ private static function __triggerError($message, $file, $line, $code = 0)
218
+ {
219
+ if (self::$useExceptions)
220
+ throw new S3Exception($message, $file, $line, $code);
221
+ else
222
+ trigger_error($message, E_USER_WARNING);
223
+ }
224
+
225
+
226
  /**
227
  * Get a list of buckets
228
  *
229
  * @param boolean $detailed Returns detailed bucket list when true
230
  * @return array | false
231
  */
232
+ public static function listBuckets($detailed = false)
233
+ {
234
+ $rest = new S3Request('GET', '', '', self::$endpoint);
235
  $rest = $rest->getResponse();
236
  if ($rest->error === false && $rest->code !== 200)
237
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
238
+ if ($rest->error !== false)
239
+ {
240
+ self::__triggerError(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'],
241
+ $rest->error['message']), __FILE__, __LINE__);
242
  return false;
243
  }
244
+ $results = array();
245
  if (!isset($rest->body->Buckets)) return $results;
246
 
247
+ if ($detailed)
248
+ {
249
  if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
250
  $results['owner'] = array(
251
  'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID
271
  * @param string $prefix Prefix
272
  * @param string $marker Marker (last file listed)
273
  * @param string $maxKeys Max keys (maximum number of keys to return)
274
+ * @param string $delimiter Delimiter
275
+ * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes
276
  * @return array | false
277
  */
278
+ public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false)
279
+ {
280
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint);
281
+ if ($maxKeys == 0) $maxKeys = null;
282
  if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
283
+ if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker);
284
+ if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys);
285
+ if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
286
  $response = $rest->getResponse();
287
  if ($response->error === false && $response->code !== 200)
288
  $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
289
+ if ($response->error !== false)
290
+ {
291
+ self::__triggerError(sprintf("S3::getBucket(): [%s] %s",
292
+ $response->error['code'], $response->error['message']), __FILE__, __LINE__);
293
  return false;
294
  }
295
 
296
  $results = array();
297
 
298
+ $nextMarker = null;
299
  if (isset($response->body, $response->body->Contents))
300
+ foreach ($response->body->Contents as $c)
301
+ {
302
+ $results[(string)$c->Key] = array(
303
+ 'name' => (string)$c->Key,
304
+ 'time' => strtotime((string)$c->LastModified),
305
+ 'size' => (int)$c->Size,
306
+ 'hash' => substr((string)$c->ETag, 1, -1)
307
+ );
308
+ $nextMarker = (string)$c->Key;
309
+ }
310
+
311
+ if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
312
+ foreach ($response->body->CommonPrefixes as $c)
313
+ $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
314
+
315
+ if (isset($response->body, $response->body->IsTruncated) &&
316
+ (string)$response->body->IsTruncated == 'false') return $results;
317
+
318
+ if (isset($response->body, $response->body->NextMarker))
319
+ $nextMarker = (string)$response->body->NextMarker;
320
+
321
+ // Loop through truncated results if maxKeys isn't specified
322
+ if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true')
323
+ do
324
+ {
325
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint);
326
+ if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
327
+ $rest->setParameter('marker', $nextMarker);
328
+ if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
329
+
330
+ if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break;
331
+
332
+ if (isset($response->body, $response->body->Contents))
333
+ foreach ($response->body->Contents as $c)
334
+ {
335
  $results[(string)$c->Key] = array(
336
  'name' => (string)$c->Key,
337
+ 'time' => strtotime((string)$c->LastModified),
338
  'size' => (int)$c->Size,
339
  'hash' => substr((string)$c->ETag, 1, -1)
340
  );
341
+ $nextMarker = (string)$c->Key;
 
342
  }
343
 
344
+ if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
345
+ foreach ($response->body->CommonPrefixes as $c)
346
+ $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
347
 
348
+ if (isset($response->body, $response->body->NextMarker))
349
+ $nextMarker = (string)$response->body->NextMarker;
 
 
 
 
 
 
 
350
 
 
 
 
 
 
 
 
 
 
 
 
351
  } while ($response !== false && (string)$response->body->IsTruncated == 'true');
352
 
353
  return $results;
359
  *
360
  * @param string $bucket Bucket name
361
  * @param constant $acl ACL flag
362
+ * @param string $location Set as "EU" to create buckets hosted in Europe
363
  * @return boolean
364
  */
365
+ public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false)
366
+ {
367
+ $rest = new S3Request('PUT', $bucket, '', self::$endpoint);
368
  $rest->setAmzHeader('x-amz-acl', $acl);
369
+
370
+ if ($location !== false)
371
+ {
372
+ $dom = new DOMDocument;
373
+ $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration');
374
+ $locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location));
375
+ $createBucketConfiguration->appendChild($locationConstraint);
376
+ $dom->appendChild($createBucketConfiguration);
377
+ $rest->data = $dom->saveXML();
378
+ $rest->size = strlen($rest->data);
379
+ $rest->setHeader('Content-Type', 'application/xml');
380
+ }
381
  $rest = $rest->getResponse();
382
+
383
  if ($rest->error === false && $rest->code !== 200)
384
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
385
+ if ($rest->error !== false)
386
+ {
387
+ self::__triggerError(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s",
388
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
389
  return false;
390
  }
391
  return true;
398
  * @param string $bucket Bucket name
399
  * @return boolean
400
  */
401
+ public static function deleteBucket($bucket)
402
+ {
403
+ $rest = new S3Request('DELETE', $bucket, '', self::$endpoint);
404
  $rest = $rest->getResponse();
405
  if ($rest->error === false && $rest->code !== 204)
406
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
407
+ if ($rest->error !== false)
408
+ {
409
+ self::__triggerError(sprintf("S3::deleteBucket({$bucket}): [%s] %s",
410
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
411
  return false;
412
  }
413
  return true;
421
  * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
422
  * @return array | false
423
  */
424
+ public static function inputFile($file, $md5sum = true)
425
+ {
426
+ if (!file_exists($file) || !is_file($file) || !is_readable($file))
427
+ {
428
+ self::__triggerError('S3::inputFile(): Unable to open input file: '.$file, __FILE__, __LINE__);
429
  return false;
430
  }
431
+ return array('file' => $file, 'size' => filesize($file), 'md5sum' => $md5sum !== false ?
432
+ (is_string($md5sum) ? $md5sum : base64_encode(md5_file($file, true))) : '');
 
433
  }
434
 
435
 
436
  /**
437
+ * Create input array info for putObject() with a resource
438
  *
439
+ * @param string $resource Input resource to read from
440
  * @param integer $bufferSize Input byte size
441
  * @param string $md5sum MD5 hash to send (optional)
442
  * @return array | false
443
  */
444
+ public static function inputResource(&$resource, $bufferSize, $md5sum = '')
445
+ {
446
+ if (!is_resource($resource) || $bufferSize < 0)
447
+ {
448
+ self::__triggerError('S3::inputResource(): Invalid resource or buffer size', __FILE__, __LINE__);
449
  return false;
450
  }
451
  $input = array('size' => $bufferSize, 'md5sum' => $md5sum);
462
  * @param string $uri Object URI
463
  * @param constant $acl ACL constant
464
  * @param array $metaHeaders Array of x-amz-meta-* headers
465
+ * @param array $requestHeaders Array of request headers or content type as a string
466
+ * @param constant $storageClass Storage class constant
467
  * @return boolean
468
  */
469
+ public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD)
470
+ {
471
+ if ($input === false) return false;
472
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
473
 
474
  if (is_string($input)) $input = array(
475
  'data' => $input, 'size' => strlen($input),
485
  $rest->data = $input['data'];
486
 
487
  // Content-Length (required)
488
+ if (isset($input['size']) && $input['size'] >= 0)
489
  $rest->size = $input['size'];
490
  else {
491
  if (isset($input['file']))
494
  $rest->size = strlen($input['data']);
495
  }
496
 
497
+ // Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
498
+ if (is_array($requestHeaders))
499
+ foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
500
+ elseif (is_string($requestHeaders)) // Support for legacy contentType parameter
501
+ $input['type'] = $requestHeaders;
502
+
503
  // Content-Type
504
+ if (!isset($input['type']))
505
+ {
506
+ if (isset($requestHeaders['Content-Type']))
507
+ $input['type'] =& $requestHeaders['Content-Type'];
508
+ elseif (isset($input['file']))
509
+ $input['type'] = self::__getMimeType($input['file']);
510
+ else
511
+ $input['type'] = 'application/octet-stream';
512
+ }
513
+
514
+ if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class
515
+ $rest->setAmzHeader('x-amz-storage-class', $storageClass);
516
 
517
+ // We need to post with Content-Length and Content-Type, MD5 is optional
518
+ if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false))
519
+ {
520
  $rest->setHeader('Content-Type', $input['type']);
521
  if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);
522
 
528
 
529
  if ($rest->response->error === false && $rest->response->code !== 200)
530
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
531
+ if ($rest->response->error !== false)
532
+ {
533
+ self::__triggerError(sprintf("S3::putObject(): [%s] %s",
534
+ $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
535
  return false;
536
  }
537
  return true;
539
 
540
 
541
  /**
542
+ * Put an object from a file (legacy function)
543
  *
544
  * @param string $file Input file path
545
  * @param string $bucket Bucket name
549
  * @param string $contentType Content type
550
  * @return boolean
551
  */
552
+ public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null)
553
+ {
554
+ return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType);
555
  }
556
 
557
 
566
  * @param string $contentType Content type
567
  * @return boolean
568
  */
569
+ public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain')
570
+ {
571
  return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
572
  }
573
 
577
  *
578
  * @param string $bucket Bucket name
579
  * @param string $uri Object URI
580
+ * @param mixed $saveTo Filename or resource to write to
581
  * @return mixed
582
  */
583
+ public static function getObject($bucket, $uri, $saveTo = false)
584
+ {
585
+ $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
586
+ if ($saveTo !== false)
587
+ {
588
  if (is_resource($saveTo))
589
  $rest->fp =& $saveTo;
590
  else
591
+ if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
592
+ $rest->file = realpath($saveTo);
593
+ else
594
+ $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
595
  }
596
  if ($rest->response->error === false) $rest->getResponse();
597
 
598
  if ($rest->response->error === false && $rest->response->code !== 200)
599
  $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
600
+ if ($rest->response->error !== false)
601
+ {
602
+ self::__triggerError(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s",
603
+ $rest->response->error['code'], $rest->response->error['message']), __FILE__, __LINE__);
604
  return false;
605
  }
 
606
  return $rest->response;
607
  }
608
 
615
  * @param boolean $returnInfo Return response information
616
  * @return mixed | false
617
  */
618
+ public static function getObjectInfo($bucket, $uri, $returnInfo = true)
619
+ {
620
+ $rest = new S3Request('HEAD', $bucket, $uri, self::$endpoint);
621
  $rest = $rest->getResponse();
622
  if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
623
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
624
+ if ($rest->error !== false)
625
+ {
626
+ self::__triggerError(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s",
627
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
628
  return false;
629
  }
630
  return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
631
  }
632
 
633
 
634
+ /**
635
+ * Copy an object
636
+ *
637
+ * @param string $bucket Source bucket name
638
+ * @param string $uri Source object URI
639
+ * @param string $bucket Destination bucket name
640
+ * @param string $uri Destination object URI
641
+ * @param constant $acl ACL constant
642
+ * @param array $metaHeaders Optional array of x-amz-meta-* headers
643
+ * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.)
644
+ * @param constant $storageClass Storage class constant
645
+ * @return mixed | false
646
+ */
647
+ public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD)
648
+ {
649
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
650
+ $rest->setHeader('Content-Length', 0);
651
+ foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
652
+ foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
653
+ if ($storageClass !== self::STORAGE_CLASS_STANDARD) // Storage class
654
+ $rest->setAmzHeader('x-amz-storage-class', $storageClass);
655
+ $rest->setAmzHeader('x-amz-acl', $acl); // Added rawurlencode() for $srcUri (thanks a.yamanoi)
656
+ $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, rawurlencode($srcUri)));
657
+ if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0)
658
+ $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE');
659
+
660
+ $rest = $rest->getResponse();
661
+ if ($rest->error === false && $rest->code !== 200)
662
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
663
+ if ($rest->error !== false)
664
+ {
665
+ self::__triggerError(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s",
666
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
667
+ return false;
668
+ }
669
+ return isset($rest->body->LastModified, $rest->body->ETag) ? array(
670
+ 'time' => strtotime((string)$rest->body->LastModified),
671
+ 'hash' => substr((string)$rest->body->ETag, 1, -1)
672
+ ) : false;
673
+ }
674
+
675
+
676
  /**
677
  * Set logging for a bucket
678
  *
681
  * @param string $targetPrefix Log prefix (e,g; domain.com-)
682
  * @return boolean
683
  */
684
+ public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null)
685
+ {
686
+ // The S3 log delivery group has to be added to the target bucket's ACP
687
+ if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false)
688
+ {
689
+ // Only add permissions to the target bucket when they do not exist
690
+ $aclWriteSet = false;
691
+ $aclReadSet = false;
692
+ foreach ($acp['acl'] as $acl)
693
+ if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery')
694
+ {
695
+ if ($acl['permission'] == 'WRITE') $aclWriteSet = true;
696
+ elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true;
697
+ }
698
+ if (!$aclWriteSet) $acp['acl'][] = array(
699
+ 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE'
700
+ );
701
+ if (!$aclReadSet) $acp['acl'][] = array(
702
+ 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP'
703
+ );
704
+ if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp);
705
+ }
706
+
707
  $dom = new DOMDocument;
708
  $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus');
709
  $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/');
710
+ if ($targetBucket !== null)
711
+ {
712
+ if ($targetPrefix == null) $targetPrefix = $bucket . '-';
713
+ $loggingEnabled = $dom->createElement('LoggingEnabled');
714
+ $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket));
715
+ $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix));
716
+ // TODO: Add TargetGrants?
717
+ $bucketLoggingStatus->appendChild($loggingEnabled);
718
+ }
719
  $dom->appendChild($bucketLoggingStatus);
720
 
721
+ $rest = new S3Request('PUT', $bucket, '', self::$endpoint);
722
  $rest->setParameter('logging', null);
723
  $rest->data = $dom->saveXML();
724
  $rest->size = strlen($rest->data);
726
  $rest = $rest->getResponse();
727
  if ($rest->error === false && $rest->code !== 200)
728
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
729
+ if ($rest->error !== false)
730
+ {
731
+ self::__triggerError(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s",
732
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
733
  return false;
734
  }
735
  return true;
745
  * @param string $bucket Bucket name
746
  * @return array | false
747
  */
748
+ public static function getBucketLogging($bucket)
749
+ {
750
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint);
751
  $rest->setParameter('logging', null);
752
  $rest = $rest->getResponse();
753
  if ($rest->error === false && $rest->code !== 200)
754
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
755
+ if ($rest->error !== false)
756
+ {
757
+ self::__triggerError(sprintf("S3::getBucketLogging({$bucket}): [%s] %s",
758
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
759
  return false;
760
  }
761
  if (!isset($rest->body->LoggingEnabled)) return false; // No logging
766
  }
767
 
768
 
769
+ /**
770
+ * Disable bucket logging
771
+ *
772
+ * @param string $bucket Bucket name
773
+ * @return boolean
774
+ */
775
+ public static function disableBucketLogging($bucket)
776
+ {
777
+ return self::setBucketLogging($bucket, null);
778
+ }
779
+
780
+
781
+ /**
782
+ * Get a bucket's location
783
+ *
784
+ * @param string $bucket Bucket name
785
+ * @return string | false
786
+ */
787
+ public static function getBucketLocation($bucket)
788
+ {
789
+ $rest = new S3Request('GET', $bucket, '', self::$endpoint);
790
+ $rest->setParameter('location', null);
791
+ $rest = $rest->getResponse();
792
+ if ($rest->error === false && $rest->code !== 200)
793
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
794
+ if ($rest->error !== false)
795
+ {
796
+ self::__triggerError(sprintf("S3::getBucketLocation({$bucket}): [%s] %s",
797
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
798
+ return false;
799
+ }
800
+ return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US';
801
+ }
802
+
803
+
804
  /**
805
  * Set object or bucket Access Control Policy
806
  *
809
  * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
810
  * @return boolean
811
  */
812
+ public static function setAccessControlPolicy($bucket, $uri = '', $acp = array())
813
+ {
814
  $dom = new DOMDocument;
815
  $dom->formatOutput = true;
816
  $accessControlPolicy = $dom->createElement('AccessControlPolicy');
822
  $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name']));
823
  $accessControlPolicy->appendChild($owner);
824
 
825
+ foreach ($acp['acl'] as $g)
826
+ {
827
  $grant = $dom->createElement('Grant');
828
  $grantee = $dom->createElement('Grantee');
829
  $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
830
+ if (isset($g['id']))
831
+ { // CanonicalUser (DisplayName is omitted)
832
  $grantee->setAttribute('xsi:type', 'CanonicalUser');
833
  $grantee->appendChild($dom->createElement('ID', $g['id']));
834
+ }
835
+ elseif (isset($g['email']))
836
+ { // AmazonCustomerByEmail
837
  $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail');
838
  $grantee->appendChild($dom->createElement('EmailAddress', $g['email']));
839
+ }
840
+ elseif ($g['type'] == 'Group')
841
+ { // Group
842
  $grantee->setAttribute('xsi:type', 'Group');
843
  $grantee->appendChild($dom->createElement('URI', $g['uri']));
844
  }
850
  $accessControlPolicy->appendChild($accessControlList);
851
  $dom->appendChild($accessControlPolicy);
852
 
853
+ $rest = new S3Request('PUT', $bucket, $uri, self::$endpoint);
854
  $rest->setParameter('acl', null);
855
  $rest->data = $dom->saveXML();
856
  $rest->size = strlen($rest->data);
858
  $rest = $rest->getResponse();
859
  if ($rest->error === false && $rest->code !== 200)
860
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
861
+ if ($rest->error !== false)
862
+ {
863
+ self::__triggerError(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
864
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
865
  return false;
866
  }
867
  return true;
871
  /**
872
  * Get object or bucket Access Control Policy
873
  *
 
 
874
  * @param string $bucket Bucket name
875
  * @param string $uri Object URI
876
  * @return mixed | false
877
  */
878
+ public static function getAccessControlPolicy($bucket, $uri = '')
879
+ {
880
+ $rest = new S3Request('GET', $bucket, $uri, self::$endpoint);
881
  $rest->setParameter('acl', null);
882
  $rest = $rest->getResponse();
883
  if ($rest->error === false && $rest->code !== 200)
884
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
885
+ if ($rest->error !== false)
886
+ {
887
+ self::__triggerError(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
888
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
889
  return false;
890
  }
891
 
892
  $acp = array();
893
+ if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
894
  $acp['owner'] = array(
895
  'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
896
  );
897
+
898
+ if (isset($rest->body->AccessControlList))
899
+ {
900
  $acp['acl'] = array();
901
+ foreach ($rest->body->AccessControlList->Grant as $grant)
902
+ {
903
+ foreach ($grant->Grantee as $grantee)
904
+ {
905
  if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser
906
  $acp['acl'][] = array(
907
  'type' => 'CanonicalUser',
934
  *
935
  * @param string $bucket Bucket name
936
  * @param string $uri Object URI
937
+ * @return boolean
938
  */
939
+ public static function deleteObject($bucket, $uri)
940
+ {
941
+ $rest = new S3Request('DELETE', $bucket, $uri, self::$endpoint);
942
  $rest = $rest->getResponse();
943
  if ($rest->error === false && $rest->code !== 204)
944
  $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
945
+ if ($rest->error !== false)
946
+ {
947
+ self::__triggerError(sprintf("S3::deleteObject(): [%s] %s",
948
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
949
+ return false;
950
+ }
951
+ return true;
952
+ }
953
+
954
+
955
+ /**
956
+ * Get a query string authenticated URL
957
+ *
958
+ * @param string $bucket Bucket name
959
+ * @param string $uri Object URI
960
+ * @param integer $lifetime Lifetime in seconds
961
+ * @param boolean $hostBucket Use the bucket name as the hostname
962
+ * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification)
963
+ * @return string
964
+ */
965
+ public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false)
966
+ {
967
+ $expires = time() + $lifetime;
968
+ $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea)
969
+ return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s',
970
+ $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires,
971
+ urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}")));
972
+ }
973
+
974
+
975
+ /**
976
+ * Get a CloudFront signed policy URL
977
+ *
978
+ * @param array $policy Policy
979
+ * @return string
980
+ */
981
+ public static function getSignedPolicyURL($policy)
982
+ {
983
+ $data = json_encode($policy);
984
+ $signature = '';
985
+ if (!openssl_sign($data, $signature, self::$__signingKeyResource)) return false;
986
+
987
+ $encoded = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($data));
988
+ $signature = str_replace(array('+', '='), array('-', '_', '~'), base64_encode($signature));
989
+
990
+ $url = $policy['Statement'][0]['Resource'] . '?';
991
+
992
+ foreach (array('Policy' => $encoded, 'Signature' => $signature, 'Key-Pair-Id' => self::$__signingKeyPairId) as $k => $v)
993
+ $url .= $k.'='.str_replace('%2F', '/', rawurlencode($v)).'&';
994
+ return substr($url, 0, -1);
995
+ }
996
+
997
+
998
+ /**
999
+ * Get a CloudFront canned policy URL
1000
+ *
1001
+ * @param string $string URL to sign
1002
+ * @param integer $lifetime URL lifetime
1003
+ * @return string
1004
+ */
1005
+ public static function getSignedCannedURL($url, $lifetime)
1006
+ {
1007
+ return self::getSignedPolicyURL(array(
1008
+ 'Statement' => array(
1009
+ array('Resource' => $url, 'Condition' => array(
1010
+ 'DateLessThan' => array('AWS:EpochTime' => time() + $lifetime)
1011
+ ))
1012
+ )
1013
+ ));
1014
+ }
1015
+
1016
+
1017
+ /**
1018
+ * Get upload POST parameters for form uploads
1019
+ *
1020
+ * @param string $bucket Bucket name
1021
+ * @param string $uriPrefix Object URI prefix
1022
+ * @param constant $acl ACL constant
1023
+ * @param integer $lifetime Lifetime in seconds
1024
+ * @param integer $maxFileSize Maximum filesize in bytes (default 5MB)
1025
+ * @param string $successRedirect Redirect URL or 200 / 201 status code
1026
+ * @param array $amzHeaders Array of x-amz-meta-* headers
1027
+ * @param array $headers Array of request headers or content type as a string
1028
+ * @param boolean $flashVars Includes additional "Filename" variable posted by Flash
1029
+ * @return object
1030
+ */
1031
+ public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600,
1032
+ $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false)
1033
+ {
1034
+ // Create policy object
1035
+ $policy = new stdClass;
1036
+ $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime));
1037
+ $policy->conditions = array();
1038
+ $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj);
1039
+ $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj);
1040
+
1041
+ $obj = new stdClass; // 200 for non-redirect uploads
1042
+ if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
1043
+ $obj->success_action_status = (string)$successRedirect;
1044
+ else // URL
1045
+ $obj->success_action_redirect = $successRedirect;
1046
+ array_push($policy->conditions, $obj);
1047
+
1048
+ if ($acl !== self::ACL_PUBLIC_READ)
1049
+ array_push($policy->conditions, array('eq', '$acl', $acl));
1050
+
1051
+ array_push($policy->conditions, array('starts-with', '$key', $uriPrefix));
1052
+ if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', ''));
1053
+ foreach (array_keys($headers) as $headerKey)
1054
+ array_push($policy->conditions, array('starts-with', '$'.$headerKey, ''));
1055
+ foreach ($amzHeaders as $headerKey => $headerVal)
1056
+ {
1057
+ $obj = new stdClass;
1058
+ $obj->{$headerKey} = (string)$headerVal;
1059
+ array_push($policy->conditions, $obj);
1060
+ }
1061
+ array_push($policy->conditions, array('content-length-range', 0, $maxFileSize));
1062
+ $policy = base64_encode(str_replace('\/', '/', json_encode($policy)));
1063
+
1064
+ // Create parameters
1065
+ $params = new stdClass;
1066
+ $params->AWSAccessKeyId = self::$__accessKey;
1067
+ $params->key = $uriPrefix.'${filename}';
1068
+ $params->acl = $acl;
1069
+ $params->policy = $policy; unset($policy);
1070
+ $params->signature = self::__getHash($params->policy);
1071
+ if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
1072
+ $params->success_action_status = (string)$successRedirect;
1073
+ else
1074
+ $params->success_action_redirect = $successRedirect;
1075
+ foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
1076
+ foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
1077
+ return $params;
1078
+ }
1079
+
1080
+
1081
+ /**
1082
+ * Create a CloudFront distribution
1083
+ *
1084
+ * @param string $bucket Bucket name
1085
+ * @param boolean $enabled Enabled (true/false)
1086
+ * @param array $cnames Array containing CNAME aliases
1087
+ * @param string $comment Use the bucket name as the hostname
1088
+ * @param string $defaultRootObject Default root object
1089
+ * @param string $originAccessIdentity Origin access identity
1090
+ * @param array $trustedSigners Array of trusted signers
1091
+ * @return array | false
1092
+ */
1093
+ public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = null, $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array())
1094
+ {
1095
+ if (!extension_loaded('openssl'))
1096
+ {
1097
+ self::__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): %s",
1098
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1099
+ return false;
1100
+ }
1101
+ $useSSL = self::$useSSL;
1102
+
1103
+ self::$useSSL = true; // CloudFront requires SSL
1104
+ $rest = new S3Request('POST', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com');
1105
+ $rest->data = self::__getCloudFrontDistributionConfigXML(
1106
+ $bucket.'.s3.amazonaws.com',
1107
+ $enabled,
1108
+ (string)$comment,
1109
+ (string)microtime(true),
1110
+ $cnames,
1111
+ $defaultRootObject,
1112
+ $originAccessIdentity,
1113
+ $trustedSigners
1114
+ );
1115
+
1116
+ $rest->size = strlen($rest->data);
1117
+ $rest->setHeader('Content-Type', 'application/xml');
1118
+ $rest = self::__getCloudFrontResponse($rest);
1119
+
1120
+ self::$useSSL = $useSSL;
1121
+
1122
+ if ($rest->error === false && $rest->code !== 201)
1123
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1124
+ if ($rest->error !== false)
1125
+ {
1126
+ self::__triggerError(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", [], '$comment'): [%s] %s",
1127
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
1128
+ return false;
1129
+ } elseif ($rest->body instanceof SimpleXMLElement)
1130
+ return self::__parseCloudFrontDistributionConfig($rest->body);
1131
+ return false;
1132
+ }
1133
+
1134
+
1135
+ /**
1136
+ * Get CloudFront distribution info
1137
+ *
1138
+ * @param string $distributionId Distribution ID from listDistributions()
1139
+ * @return array | false
1140
+ */
1141
+ public static function getDistribution($distributionId)
1142
+ {
1143
+ if (!extension_loaded('openssl'))
1144
+ {
1145
+ self::__triggerError(sprintf("S3::getDistribution($distributionId): %s",
1146
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1147
+ return false;
1148
+ }
1149
+ $useSSL = self::$useSSL;
1150
+
1151
+ self::$useSSL = true; // CloudFront requires SSL
1152
+ $rest = new S3Request('GET', '', '2010-11-01/distribution/'.$distributionId, 'cloudfront.amazonaws.com');
1153
+ $rest = self::__getCloudFrontResponse($rest);
1154
+
1155
+ self::$useSSL = $useSSL;
1156
+
1157
+ if ($rest->error === false && $rest->code !== 200)
1158
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1159
+ if ($rest->error !== false)
1160
+ {
1161
+ self::__triggerError(sprintf("S3::getDistribution($distributionId): [%s] %s",
1162
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
1163
+ return false;
1164
+ }
1165
+ elseif ($rest->body instanceof SimpleXMLElement)
1166
+ {
1167
+ $dist = self::__parseCloudFrontDistributionConfig($rest->body);
1168
+ $dist['hash'] = $rest->headers['hash'];
1169
+ $dist['id'] = $distributionId;
1170
+ return $dist;
1171
+ }
1172
+ return false;
1173
+ }
1174
+
1175
+
1176
+ /**
1177
+ * Update a CloudFront distribution
1178
+ *
1179
+ * @param array $dist Distribution array info identical to output of getDistribution()
1180
+ * @return array | false
1181
+ */
1182
+ public static function updateDistribution($dist)
1183
+ {
1184
+ if (!extension_loaded('openssl'))
1185
+ {
1186
+ self::__triggerError(sprintf("S3::updateDistribution({$dist['id']}): %s",
1187
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1188
+ return false;
1189
+ }
1190
+
1191
+ $useSSL = self::$useSSL;
1192
+
1193
+ self::$useSSL = true; // CloudFront requires SSL
1194
+ $rest = new S3Request('PUT', '', '2010-11-01/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com');
1195
+ $rest->data = self::__getCloudFrontDistributionConfigXML(
1196
+ $dist['origin'],
1197
+ $dist['enabled'],
1198
+ $dist['comment'],
1199
+ $dist['callerReference'],
1200
+ $dist['cnames'],
1201
+ $dist['defaultRootObject'],
1202
+ $dist['originAccessIdentity'],
1203
+ $dist['trustedSigners']
1204
+ );
1205
+
1206
+ $rest->size = strlen($rest->data);
1207
+ $rest->setHeader('If-Match', $dist['hash']);
1208
+ $rest = self::__getCloudFrontResponse($rest);
1209
+
1210
+ self::$useSSL = $useSSL;
1211
+
1212
+ if ($rest->error === false && $rest->code !== 200)
1213
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1214
+ if ($rest->error !== false)
1215
+ {
1216
+ self::__triggerError(sprintf("S3::updateDistribution({$dist['id']}): [%s] %s",
1217
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
1218
+ return false;
1219
+ } else {
1220
+ $dist = self::__parseCloudFrontDistributionConfig($rest->body);
1221
+ $dist['hash'] = $rest->headers['hash'];
1222
+ return $dist;
1223
+ }
1224
+ return false;
1225
+ }
1226
+
1227
+
1228
+ /**
1229
+ * Delete a CloudFront distribution
1230
+ *
1231
+ * @param array $dist Distribution array info identical to output of getDistribution()
1232
+ * @return boolean
1233
+ */
1234
+ public static function deleteDistribution($dist)
1235
+ {
1236
+ if (!extension_loaded('openssl'))
1237
+ {
1238
+ self::__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): %s",
1239
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1240
+ return false;
1241
+ }
1242
+
1243
+ $useSSL = self::$useSSL;
1244
+
1245
+ self::$useSSL = true; // CloudFront requires SSL
1246
+ $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com');
1247
+ $rest->setHeader('If-Match', $dist['hash']);
1248
+ $rest = self::__getCloudFrontResponse($rest);
1249
+
1250
+ self::$useSSL = $useSSL;
1251
+
1252
+ if ($rest->error === false && $rest->code !== 204)
1253
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1254
+ if ($rest->error !== false)
1255
+ {
1256
+ self::__triggerError(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s",
1257
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
1258
  return false;
1259
  }
1260
  return true;
1261
  }
1262
 
1263
 
1264
+ /**
1265
+ * Get a list of CloudFront distributions
1266
+ *
1267
+ * @return array
1268
+ */
1269
+ public static function listDistributions()
1270
+ {
1271
+ if (!extension_loaded('openssl'))
1272
+ {
1273
+ self::__triggerError(sprintf("S3::listDistributions(): [%s] %s",
1274
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1275
+ return false;
1276
+ }
1277
+
1278
+ $useSSL = self::$useSSL;
1279
+ self::$useSSL = true; // CloudFront requires SSL
1280
+ $rest = new S3Request('GET', '', '2010-11-01/distribution', 'cloudfront.amazonaws.com');
1281
+ $rest = self::__getCloudFrontResponse($rest);
1282
+ self::$useSSL = $useSSL;
1283
+
1284
+ if ($rest->error === false && $rest->code !== 200)
1285
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1286
+ if ($rest->error !== false)
1287
+ {
1288
+ self::__triggerError(sprintf("S3::listDistributions(): [%s] %s",
1289
+ $rest->error['code'], $rest->error['message']), __FILE__, __LINE__);
1290
+ return false;
1291
+ }
1292
+ elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary))
1293
+ {
1294
+ $list = array();
1295
+ if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated))
1296
+ {
1297
+ //$info['marker'] = (string)$rest->body->Marker;
1298
+ //$info['maxItems'] = (int)$rest->body->MaxItems;
1299
+ //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false;
1300
+ }
1301
+ foreach ($rest->body->DistributionSummary as $summary)
1302
+ $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary);
1303
+
1304
+ return $list;
1305
+ }
1306
+ return array();
1307
+ }
1308
+
1309
+ /**
1310
+ * List CloudFront Origin Access Identities
1311
+ *
1312
+ * @return array
1313
+ */
1314
+ public static function listOriginAccessIdentities()
1315
+ {
1316
+ if (!extension_loaded('openssl'))
1317
+ {
1318
+ self::__triggerError(sprintf("S3::listOriginAccessIdentities(): [%s] %s",
1319
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1320
+ return false;
1321
+ }
1322
+
1323
+ self::$useSSL = true; // CloudFront requires SSL
1324
+ $rest = new S3Request('GET', '', '2010-11-01/origin-access-identity/cloudfront', 'cloudfront.amazonaws.com');
1325
+ $rest = self::__getCloudFrontResponse($rest);
1326
+ $useSSL = self::$useSSL;
1327
+
1328
+ if ($rest->error === false && $rest->code !== 200)
1329
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1330
+ if ($rest->error !== false)
1331
+ {
1332
+ trigger_error(sprintf("S3::listOriginAccessIdentities(): [%s] %s",
1333
+ $rest->error['code'], $rest->error['message']), E_USER_WARNING);
1334
+ return false;
1335
+ }
1336
+
1337
+ if (isset($rest->body->CloudFrontOriginAccessIdentitySummary))
1338
+ {
1339
+ $identities = array();
1340
+ foreach ($rest->body->CloudFrontOriginAccessIdentitySummary as $identity)
1341
+ if (isset($identity->S3CanonicalUserId))
1342
+ $identities[(string)$identity->Id] = array('id' => (string)$identity->Id, 's3CanonicalUserId' => (string)$identity->S3CanonicalUserId);
1343
+ return $identities;
1344
+ }
1345
+ return false;
1346
+ }
1347
+
1348
+
1349
+ /**
1350
+ * Invalidate objects in a CloudFront distribution
1351
+ *
1352
+ * Thanks to Martin Lindkvist for S3::invalidateDistribution()
1353
+ *
1354
+ * @param string $distributionId Distribution ID from listDistributions()
1355
+ * @param array $paths Array of object paths to invalidate
1356
+ * @return boolean
1357
+ */
1358
+ public static function invalidateDistribution($distributionId, $paths)
1359
+ {
1360
+ if (!extension_loaded('openssl'))
1361
+ {
1362
+ self::__triggerError(sprintf("S3::invalidateDistribution(): [%s] %s",
1363
+ "CloudFront functionality requires SSL"), __FILE__, __LINE__);
1364
+ return false;
1365
+ }
1366
+
1367
+ $useSSL = self::$useSSL;
1368
+ self::$useSSL = true; // CloudFront requires SSL
1369
+ $rest = new S3Request('POST', '', '2010-08-01/distribution/'.$distributionId.'/invalidation', 'cloudfront.amazonaws.com');
1370
+ $rest->data = self::__getCloudFrontInvalidationBatchXML($paths, (string)microtime(true));
1371
+ $rest->size = strlen($rest->data);
1372
+ $rest = self::__getCloudFrontResponse($rest);
1373
+ self::$useSSL = $useSSL;
1374
+
1375
+ if ($rest->error === false && $rest->code !== 201)
1376
+ $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
1377
+ if ($rest->error !== false)
1378
+ {
1379
+ trigger_error(sprintf("S3::invalidate('{$distributionId}',{$paths}): [%s] %s",
1380
+ $rest->error['code'], $rest->error['message']), E_USER_WARNING);
1381
+ return false;
1382
+ }
1383
+ return true;
1384
+ }
1385
+
1386
+
1387
+ /**
1388
+ * Get a InvalidationBatch DOMDocument
1389
+ *
1390
+ * @internal Used to create XML in invalidateDistribution()
1391
+ * @param array $paths Paths to objects to invalidateDistribution
1392
+ * @return string
1393
+ */
1394
+ private static function __getCloudFrontInvalidationBatchXML($paths, $callerReference = '0') {
1395
+ $dom = new DOMDocument('1.0', 'UTF-8');
1396
+ $dom->formatOutput = true;
1397
+ $invalidationBatch = $dom->createElement('InvalidationBatch');
1398
+ foreach ($paths as $path)
1399
+ $invalidationBatch->appendChild($dom->createElement('Path', $path));
1400
+
1401
+ $invalidationBatch->appendChild($dom->createElement('CallerReference', $callerReference));
1402
+ $dom->appendChild($invalidationBatch);
1403
+ return $dom->saveXML();
1404
+ }
1405
+
1406
+
1407
+ /**
1408
+ * Get a DistributionConfig DOMDocument
1409
+ *
1410
+ * http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?PutConfig.html
1411
+ *
1412
+ * @internal Used to create XML in createDistribution() and updateDistribution()
1413
+ * @param string $bucket S3 Origin bucket
1414
+ * @param boolean $enabled Enabled (true/false)
1415
+ * @param string $comment Comment to append
1416
+ * @param string $callerReference Caller reference
1417
+ * @param array $cnames Array of CNAME aliases
1418
+ * @param string $defaultRootObject Default root object
1419
+ * @param string $originAccessIdentity Origin access identity
1420
+ * @param array $trustedSigners Array of trusted signers
1421
+ * @return string
1422
+ */
1423
+ private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array(), $defaultRootObject = null, $originAccessIdentity = null, $trustedSigners = array())
1424
+ {
1425
+ $dom = new DOMDocument('1.0', 'UTF-8');
1426
+ $dom->formatOutput = true;
1427
+ $distributionConfig = $dom->createElement('DistributionConfig');
1428
+ $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2010-11-01/');
1429
+
1430
+ $origin = $dom->createElement('S3Origin');
1431
+ $origin->appendChild($dom->createElement('DNSName', $bucket));
1432
+ if ($originAccessIdentity !== null) $origin->appendChild($dom->createElement('OriginAccessIdentity', $originAccessIdentity));
1433
+ $distributionConfig->appendChild($origin);
1434
+
1435
+ if ($defaultRootObject !== null) $distributionConfig->appendChild($dom->createElement('DefaultRootObject', $defaultRootObject));
1436
+
1437
+ $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference));
1438
+ foreach ($cnames as $cname)
1439
+ $distributionConfig->appendChild($dom->createElement('CNAME', $cname));
1440
+ if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment));
1441
+ $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false'));
1442
+
1443
+ $trusted = $dom->createElement('TrustedSigners');
1444
+ foreach ($trustedSigners as $id => $type)
1445
+ $trusted->appendChild($id !== '' ? $dom->createElement($type, $id) : $dom->createElement($type));
1446
+ $distributionConfig->appendChild($trusted);
1447
+
1448
+ $dom->appendChild($distributionConfig);
1449
+ //var_dump($dom->saveXML());
1450
+ return $dom->saveXML();
1451
+ }
1452
+
1453
+
1454
+ /**
1455
+ * Parse a CloudFront distribution config
1456
+ *
1457
+ * See http://docs.amazonwebservices.com/AmazonCloudFront/latest/APIReference/index.html?GetDistribution.html
1458
+ *
1459
+ * @internal Used to parse the CloudFront DistributionConfig node to an array
1460
+ * @param object &$node DOMNode
1461
+ * @return array
1462
+ */
1463
+ private static function __parseCloudFrontDistributionConfig(&$node)
1464
+ {
1465
+ if (isset($node->DistributionConfig))
1466
+ return self::__parseCloudFrontDistributionConfig($node->DistributionConfig);
1467
+
1468
+ $dist = array();
1469
+ if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName))
1470
+ {
1471
+ $dist['id'] = (string)$node->Id;
1472
+ $dist['status'] = (string)$node->Status;
1473
+ $dist['time'] = strtotime((string)$node->LastModifiedTime);
1474
+ $dist['domain'] = (string)$node->DomainName;
1475
+ }
1476
+
1477
+ if (isset($node->CallerReference))
1478
+ $dist['callerReference'] = (string)$node->CallerReference;
1479
+
1480
+ if (isset($node->Enabled))
1481
+ $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false;
1482
+
1483
+ if (isset($node->S3Origin))
1484
+ {
1485
+ if (isset($node->S3Origin->DNSName))
1486
+ $dist['origin'] = (string)$node->S3Origin->DNSName;
1487
+
1488
+ $dist['originAccessIdentity'] = isset($node->S3Origin->OriginAccessIdentity) ?
1489
+ (string)$node->S3Origin->OriginAccessIdentity : null;
1490
+ }
1491
+
1492
+ $dist['defaultRootObject'] = isset($node->DefaultRootObject) ? (string)$node->DefaultRootObject : null;
1493
+
1494
+ $dist['cnames'] = array();
1495
+ if (isset($node->CNAME))
1496
+ foreach ($node->CNAME as $cname)
1497
+ $dist['cnames'][(string)$cname] = (string)$cname;
1498
+
1499
+ $dist['trustedSigners'] = array();
1500
+ if (isset($node->TrustedSigners))
1501
+ foreach ($node->TrustedSigners as $signer)
1502
+ {
1503
+ if (isset($signer->Self))
1504
+ $dist['trustedSigners'][''] = 'Self';
1505
+ elseif (isset($signer->KeyPairId))
1506
+ $dist['trustedSigners'][(string)$signer->KeyPairId] = 'KeyPairId';
1507
+ elseif (isset($signer->AwsAccountNumber))
1508
+ $dist['trustedSigners'][(string)$signer->AwsAccountNumber] = 'AwsAccountNumber';
1509
+ }
1510
+
1511
+ $dist['comment'] = isset($node->Comment) ? (string)$node->Comment : null;
1512
+ return $dist;
1513
+ }
1514
+
1515
+
1516
+ /**
1517
+ * Grab CloudFront response
1518
+ *
1519
+ * @internal Used to parse the CloudFront S3Request::getResponse() output
1520
+ * @param object &$rest S3Request instance
1521
+ * @return object
1522
+ */
1523
+ private static function __getCloudFrontResponse(&$rest)
1524
+ {
1525
+ $rest->getResponse();
1526
+ if ($rest->response->error === false && isset($rest->response->body) &&
1527
+ is_string($rest->response->body) && substr($rest->response->body, 0, 5) == '<?xml')
1528
+ {
1529
+ $rest->response->body = simplexml_load_string($rest->response->body);
1530
+ // Grab CloudFront errors
1531
+ if (isset($rest->response->body->Error, $rest->response->body->Error->Code,
1532
+ $rest->response->body->Error->Message))
1533
+ {
1534
+ $rest->response->error = array(
1535
+ 'code' => (string)$rest->response->body->Error->Code,
1536
+ 'message' => (string)$rest->response->body->Error->Message
1537
+ );
1538
+ unset($rest->response->body);
1539
+ }
1540
+ }
1541
+ return $rest->response;
1542
+ }
1543
+
1544
+
1545
  /**
1546
  * Get MIME type for file
1547
  *
1549
  * @param string &$file File path
1550
  * @return string
1551
  */
1552
+ public static function __getMimeType(&$file)
1553
+ {
1554
  $type = false;
1555
  // Fileinfo documentation says fileinfo_open() will use the
1556
  // MAGIC env var for the magic file
1557
  if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
1558
+ ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false)
1559
+ {
1560
+ if (($type = finfo_file($finfo, $file)) !== false)
1561
+ {
1562
  // Remove the charset and grab the last content-type
1563
  $type = explode(' ', str_replace('; charset=', ';charset=', $type));
1564
  $type = array_pop($type);
1565
  $type = explode(';', $type);
1566
+ $type = trim(array_shift($type));
1567
  }
1568
  finfo_close($finfo);
1569
 
1570
  // If anyone is still using mime_content_type()
1571
  } elseif (function_exists('mime_content_type'))
1572
+ $type = trim(mime_content_type($file));
1573
 
1574
  if ($type !== false && strlen($type) > 0) return $type;
1575
 
1582
  'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
1583
  'bz2' => 'application/x-bzip2', 'txt' => 'text/plain',
1584
  'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
1585
+ 'css' => 'text/css', 'js' => 'text/javascript',
1586
  'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
1587
  'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
1588
  'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
1589
  'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
1590
  );
1591
+ $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION));
1592
  return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream';
1593
  }
1594
 
1596
  /**
1597
  * Generate the auth string: "AWS AccessKey:Signature"
1598
  *
1599
+ * @internal Used by S3Request::getResponse()
1600
+ * @param string $string String to sign
1601
+ * @return string
1602
+ */
1603
+ public static function __getSignature($string)
1604
+ {
1605
+ return 'AWS '.self::$__accessKey.':'.self::__getHash($string);
1606
+ }
1607
+
1608
+
1609
+ /**
1610
+ * Creates a HMAC-SHA1 hash
1611
+ *
1612
  * This uses the hash extension if loaded
1613
  *
1614
+ * @internal Used by __getSignature()
1615
  * @param string $string String to sign
1616
  * @return string
1617
  */
1618
+ private static function __getHash($string)
1619
+ {
1620
+ return base64_encode(extension_loaded('hash') ?
1621
  hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1(
1622
  (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
1623
  pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^
1624
  (str_repeat(chr(0x36), 64))) . $string)))));
1625
  }
1626
 
 
1627
  }
1628
 
1629
+ final class S3Request
1630
+ {
1631
+ private $endpoint, $verb, $bucket, $uri, $resource = '', $parameters = array(),
1632
  $amzHeaders = array(), $headers = array(
1633
  'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
1634
  );
1643
  * @param string $uri Object URI
1644
  * @return mixed
1645
  */
1646
+ function __construct($verb, $bucket = '', $uri = '', $endpoint = 's3.amazonaws.com')
1647
+ {
1648
+ $this->endpoint = $endpoint;
1649
  $this->verb = $verb;
1650
+ $this->bucket = $bucket;
1651
+ $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/';
1652
+
1653
+ if ($this->bucket !== '')
1654
+ {
1655
+ $this->headers['Host'] = $this->bucket.'.'.$this->endpoint;
1656
+ $this->resource = '/'.$this->bucket.$this->uri;
1657
+ }
1658
+ else
1659
+ {
1660
+ $this->headers['Host'] = $this->endpoint;
1661
+ $this->resource = $this->uri;
 
1662
  }
1663
  $this->headers['Date'] = gmdate('D, d M Y H:i:s T');
1664
 
1674
  * @param string $value Value
1675
  * @return void
1676
  */
1677
+ public function setParameter($key, $value)
1678
+ {
1679
  $this->parameters[$key] = $value;
1680
  }
1681
 
1687
  * @param string $value Value
1688
  * @return void
1689
  */
1690
+ public function setHeader($key, $value)
1691
+ {
1692
  $this->headers[$key] = $value;
1693
  }
1694
 
1700
  * @param string $value Value
1701
  * @return void
1702
  */
1703
+ public function setAmzHeader($key, $value)
1704
+ {
1705
  $this->amzHeaders[$key] = $value;
1706
  }
1707
 
1711
  *
1712
  * @return object | false
1713
  */
1714
+ public function getResponse()
1715
+ {
1716
  $query = '';
1717
+ if (sizeof($this->parameters) > 0)
1718
+ {
1719
  $query = substr($this->uri, -1) !== '?' ? '?' : '&';
1720
  foreach ($this->parameters as $var => $value)
1721
  if ($value == null || $value == '') $query .= $var.'&';
1722
+ // Parameters should be encoded (thanks Sean O'Dea)
1723
+ else $query .= $var.'='.rawurlencode($value).'&';
1724
  $query = substr($query, 0, -1);
1725
  $this->uri .= $query;
1726
+
1727
+ if (array_key_exists('acl', $this->parameters) ||
1728
+ array_key_exists('location', $this->parameters) ||
1729
+ array_key_exists('torrent', $this->parameters) ||
1730
+ array_key_exists('logging', $this->parameters))
1731
  $this->resource .= $query;
1732
  }
1733
+ $url = (S3::$useSSL ? 'https://' : 'http://') . $this->headers['Host'].$this->uri;
1734
  //var_dump($this->bucket, $this->uri, $this->resource, $url);
1735
 
1736
  // Basic setup
1737
  $curl = curl_init();
1738
  curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');
1739
+
1740
+ if (S3::$useSSL)
1741
+ {
1742
+ // SSL Validation can now be optional for those with broken OpenSSL installations
1743
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, S3::$useSSLValidation ? 1 : 0);
1744
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, S3::$useSSLValidation ? 1 : 0);
1745
+
1746
+ if (S3::$sslKey !== null) curl_setopt($curl, CURLOPT_SSLKEY, S3::$sslKey);
1747
+ if (S3::$sslCert !== null) curl_setopt($curl, CURLOPT_SSLCERT, S3::$sslCert);
1748
+ if (S3::$sslCACert !== null) curl_setopt($curl, CURLOPT_CAINFO, S3::$sslCACert);
1749
+ }
1750
+
1751
  curl_setopt($curl, CURLOPT_URL, $url);
1752
 
1753
+ if (S3::$proxy != null && isset(S3::$proxy['host']))
1754
+ {
1755
+ curl_setopt($curl, CURLOPT_PROXY, S3::$proxy['host']);
1756
+ curl_setopt($curl, CURLOPT_PROXYTYPE, S3::$proxy['type']);
1757
+ if (isset(S3::$proxy['user'], S3::$proxy['pass']) && $proxy['user'] != null && $proxy['pass'] != null)
1758
+ curl_setopt($curl, CURLOPT_PROXYUSERPWD, sprintf('%s:%s', S3::$proxy['user'], S3::$proxy['pass']));
1759
+ }
1760
+
1761
  // Headers
1762
  $headers = array(); $amz = array();
1763
  foreach ($this->amzHeaders as $header => $value)
1764
  if (strlen($value) > 0) $headers[] = $header.': '.$value;
1765
  foreach ($this->headers as $header => $value)
1766
  if (strlen($value) > 0) $headers[] = $header.': '.$value;
1767
+
1768
+ // Collect AMZ headers for signature
1769
  foreach ($this->amzHeaders as $header => $value)
1770
+ if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value;
1771
+
1772
+ // AMZ headers must be sorted
1773
+ if (sizeof($amz) > 0)
1774
+ {
1775
+ sort($amz);
1776
+ $amz = "\n".implode("\n", $amz);
1777
+ } else $amz = '';
1778
+
1779
+ if (S3::hasAuth())
1780
+ {
1781
+ // Authorization string (CloudFront stringToSign should only contain a date)
1782
+ $headers[] = 'Authorization: ' . S3::__getSignature(
1783
+ $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] :
1784
+ $this->verb."\n".$this->headers['Content-MD5']."\n".
1785
+ $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource
1786
+ );
1787
+ }
1788
 
1789
  curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1790
  curl_setopt($curl, CURLOPT_HEADER, false);
1791
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
1792
  curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
1793
  curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
1794
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
1795
 
1796
  // Request types
1797
+ switch ($this->verb)
1798
+ {
1799
  case 'GET': break;
1800
+ case 'PUT': case 'POST': // POST only used for CloudFront
1801
+ if ($this->fp !== false)
1802
+ {
1803
  curl_setopt($curl, CURLOPT_PUT, true);
1804
  curl_setopt($curl, CURLOPT_INFILE, $this->fp);
1805
+ if ($this->size >= 0)
1806
  curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
1807
+ }
1808
+ elseif ($this->data !== false)
1809
+ {
1810
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
1811
  curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
1812
+ }
1813
+ else
1814
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
 
1815
  break;
1816
  case 'HEAD':
1817
  curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
1837
 
1838
  // Parse body into XML
1839
  if ($this->response->error === false && isset($this->response->headers['type']) &&
1840
+ $this->response->headers['type'] == 'application/xml' && isset($this->response->body))
1841
+ {
1842
  $this->response->body = simplexml_load_string($this->response->body);
1843
 
1844
  // Grab S3 errors
1845
+ if (!in_array($this->response->code, array(200, 204, 206)) &&
1846
+ isset($this->response->body->Code, $this->response->body->Message))
1847
+ {
1848
  $this->response->error = array(
1849
  'code' => (string)$this->response->body->Code,
1850
  'message' => (string)$this->response->body->Message
1869
  * @param string &$data Data
1870
  * @return integer
1871
  */
1872
+ private function __responseWriteCallback(&$curl, &$data)
1873
+ {
1874
+ if (in_array($this->response->code, array(200, 206)) && $this->fp !== false)
1875
  return fwrite($this->fp, $data);
1876
  else
1877
  $this->response->body .= $data;
1886
  * @param string &$data Data
1887
  * @return integer
1888
  */
1889
+ private function __responseHeaderCallback(&$curl, &$data)
1890
+ {
1891
  if (($strlen = strlen($data)) <= 2) return $strlen;
1892
  if (substr($data, 0, 4) == 'HTTP')
1893
  $this->response->code = (int)substr($data, 9, 3);
1894
+ else
1895
+ {
1896
+ $data = trim($data);
1897
+ if (strpos($data, ': ') === false) return $strlen;
1898
+ list($header, $value) = explode(': ', $data, 2);
1899
  if ($header == 'Last-Modified')
1900
  $this->response->headers['time'] = strtotime($value);
1901
  elseif ($header == 'Content-Length')
1903
  elseif ($header == 'Content-Type')
1904
  $this->response->headers['type'] = $value;
1905
  elseif ($header == 'ETag')
1906
+ $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value;
1907
  elseif (preg_match('/^x-amz-meta-.*$/', $header))
1908
  $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value;
1909
  }
1911
  }
1912
 
1913
  }
1914
+
1915
+ class S3Exception extends Exception {
1916
+ function __construct($message, $file, $line, $code = 0)
1917
+ {
1918
+ parent::__construct($message, $code);
1919
+ $this->file = $file;
1920
+ $this->line = $line;
1921
+ }
1922
+ }
post.class.php CHANGED
@@ -246,8 +246,8 @@ class MMB_Post extends MMB_Core
246
  }
247
  }
248
 
249
-
250
- // create post
251
  $post_id = wp_insert_post($post_data);
252
 
253
  if (count($attachments)) {
246
  }
247
  }
248
 
249
+ //Prepare post data and temporarily remove content filters before insert post
250
+ remove_filter('content_save_pre', 'wp_filter_post_kses');
251
  $post_id = wp_insert_post($post_data);
252
 
253
  if (count($attachments)) {
readme.txt CHANGED
@@ -1,35 +1,45 @@
1
  === ManageWP Worker ===
2
  Contributors: freediver
3
  Donate link: https://www.networkforgood.org/donation/MakeDonation.aspx?ORGID2=520781390
4
- Tags: managewp, managewp worker, admin, manage blogs, multi blog manager, manage multiple blogs, remote blog management
5
  Requires at least: 3.0
6
- Tested up to: 3.2
7
  Stable tag: trunk
8
 
9
  ManageWP Worker plugin allows you to remotely manage your blogs from one dashboard.
10
 
11
  == Description ==
12
 
13
- ManageWP is a revolutionary plugin that allows you to manage multiple WordPress blogs from one dashboard.
14
 
15
  Main features:
16
 
17
- * Secure! No passwords for sites required, uses OpenSSL encrypted protocol
18
  * One click upgrades of WordPress, plugin and themes across all your sites
19
- * One click to access administration dashboard for any site
20
- * Add sub-users (writes, staff..) to your account
 
 
21
  * Bulk publish posts to multiple sites at once
22
- * Bulk upload themes and plugins to multiple sites at once
23
- * Automatic backups of your sites
24
- * Clone or migrate site to another domain
25
  * Much, much more...
26
 
27
- Check out [ManageWP.com](http://managewp.com/ "Manage Multiple Blogs")
28
 
29
- API for developers available at [ManageWP.com/API](http://managewp.com/api "ManageWP API")
 
 
30
 
31
  == Changelog ==
32
 
 
 
 
 
 
 
 
 
33
  = 3.9.7 =
34
  * Fixed problem with cron schedules
35
 
1
  === ManageWP Worker ===
2
  Contributors: freediver
3
  Donate link: https://www.networkforgood.org/donation/MakeDonation.aspx?ORGID2=520781390
4
+ Tags: admin, administration, amazon, api, authentication, automatic, dashboard, dropbox, events, integration, manage, multsite, notification, performance, s3, security, seo, stats, tracking, managewp
5
  Requires at least: 3.0
6
+ Tested up to: 3.2.1
7
  Stable tag: trunk
8
 
9
  ManageWP Worker plugin allows you to remotely manage your blogs from one dashboard.
10
 
11
  == Description ==
12
 
13
+ [ManageWP](http://managewp.com/ "Manage Multiple Blogs") is a revolutionary plugin that helps users manage multiple WordPress blogs from one dashboard.
14
 
15
  Main features:
16
 
17
+ * Secure and fast solution for managing your WordPress sites
18
  * One click upgrades of WordPress, plugin and themes across all your sites
19
+ * Schedule automatic backups of your websites (Amazon S3 and Dropbox supported)
20
+ * One click access to access WP admin of any site
21
+ * Bulk install themes and plugins to multiple sites at once
22
+ * Add sub-users (writers, staff..) to your account
23
  * Bulk publish posts to multiple sites at once
24
+ * Install WordPress, clone or migrate a website to another domain
 
 
25
  * Much, much more...
26
 
27
+ Check out the [ManageWP Tour video](http://vimeo.com/22099014).
28
 
29
+ http://vimeo.com/22099014
30
+
31
+ Check out [ManageWP.com](http://managewp.com/ "Manage Multiple Blogs").
32
 
33
  == Changelog ==
34
 
35
+ = 3.9.8 =
36
+ * Conversion goals integration
37
+ * Update notifications
38
+ * Enhanced security for your account
39
+ * Better backups
40
+ * Better update interface
41
+ * [Full changelog](http://managewp.com/update-goals-and-adsense-analytics-integration-update-notifications-login-by-ip-better-backups "Full changelog")
42
+
43
  = 3.9.7 =
44
  * Fixed problem with cron schedules
45
 
stats.class.php CHANGED
@@ -106,7 +106,7 @@ class MMB_Stats extends MMB_Core
106
  $recent->post_date = $recent_post->post_date;
107
  $recent->post_title = $recent_post->post_title;
108
  $recent->post_modified = $recent_post->post_modified;
109
- $recent->comment_count = $recent_post->comment_count;
110
  $recent_posts[] = $recent;
111
  }
112
 
@@ -133,7 +133,6 @@ class MMB_Stats extends MMB_Core
133
  $recent->post_date = $scheduled->post_date;
134
  $recent->post_title = $scheduled->post_title;
135
  $recent->post_modified = $scheduled->post_modified;
136
-
137
  $scheduled_posts[] = $recent;
138
  }
139
 
@@ -151,7 +150,7 @@ class MMB_Stats extends MMB_Core
151
 
152
  $recent_posts[] = $recent;
153
  }
154
- usort($recent_posts, 'cmp_posts_worker');
155
  $stats['posts'] = array_slice($recent_posts, 0, 20);
156
 
157
  $all_pages_drafts = get_pages('post_status=draft&numberposts=20&orderby=modified&order=desc');
@@ -166,7 +165,7 @@ class MMB_Stats extends MMB_Core
166
 
167
  $recent_drafts[] = $recent;
168
  }
169
- usort($recent_drafts, 'cmp_posts_worker');
170
  $stats['drafts'] = array_slice($recent_drafts, 0, 20);
171
 
172
 
@@ -182,7 +181,7 @@ class MMB_Stats extends MMB_Core
182
 
183
  $scheduled_posts[] = $recent;
184
  }
185
- usort($scheduled_posts, 'cmp_posts_worker');
186
  $stats['scheduled'] = array_slice($scheduled_posts, 0, 20);
187
 
188
 
@@ -194,64 +193,25 @@ class MMB_Stats extends MMB_Core
194
 
195
  //Backup Results
196
  $stats['mwp_backups'] = $this->get_backup_instance()->get_backup_stats();
 
 
 
197
 
 
 
 
 
 
 
 
198
  //Backup requirements
199
  $stats['mwp_backup_req'] = $this->get_backup_instance()->check_backup_compat();
200
 
201
- $stats['sheduled_backup'] = '12.07.2011 13:00';
202
- $stats['sheduled_next'] = '12.08.2011 13:00';
203
-
204
  $stats = apply_filters('mmb_stats_filter', $stats);
205
 
206
  return $stats;
207
  }
208
 
209
- function get_stats_notification($params)
210
- {
211
-
212
- global $mmb_wp_version, $mmb_plugin_dir;
213
- $stats = array();
214
-
215
- //define constants
216
- $num_pending_comments = 1000;
217
-
218
-
219
- require_once(ABSPATH . '/wp-admin/includes/update.php');
220
-
221
- $stats['worker_version'] = MMB_WORKER_VERSION;
222
- $stats['wordpress_version'] = $mmb_wp_version;
223
-
224
- $updates = $this->mmb_get_transient('update_core');
225
-
226
- if ($updates->updates[0]->response == 'development' || version_compare($mmb_wp_version, $updates->updates[0]->current, '<')) {
227
- $updates->updates[0]->current_version = $mmb_wp_version;
228
- $stats['core_updates'] = $updates->updates[0];
229
- } else
230
- $stats['core_updates'] = false;
231
-
232
- $mmb_user_hits = get_option('user_hit_count');
233
- if (is_array($mmb_user_hits)) {
234
- end($mmb_user_hits);
235
- $last_key_date = key($mmb_user_hits);
236
- $current_date = date('Y-m-d');
237
- if ($last_key_date != $curent_date)
238
- $this->set_hit_count(true);
239
- }
240
-
241
-
242
- $this->get_theme_instance();
243
- $this->get_plugin_instance();
244
- $stats['upgradable_themes'] = $this->theme_instance->get_upgradable_themes();
245
- $stats['upgradable_plugins'] = $this->plugin_instance->get_upgradable_plugins();
246
-
247
- $pending_comments = get_comments('status=hold&number=' . $num_pending_comments);
248
-
249
- $stats['comments_pending'] = count($pending_comments);
250
-
251
- return $stats;
252
- }
253
-
254
-
255
  function get_comments_stats(){
256
  $num_pending_comments = 3;
257
  $num_approved_comments = 3;
@@ -272,6 +232,7 @@ class MMB_Stats extends MMB_Core
272
 
273
  return $stats;
274
  }
 
275
  function get_initial_stats()
276
  {
277
  global $mmb_plugin_dir;
@@ -419,13 +380,86 @@ class MMB_Stats extends MMB_Core
419
  return false;
420
  }
421
 
422
-
423
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
- function cmp_posts_worker($a, $b)
426
- {
427
- return ($a->post_modified < $b->post_modified);
428
  }
429
-
430
-
431
  ?>
106
  $recent->post_date = $recent_post->post_date;
107
  $recent->post_title = $recent_post->post_title;
108
  $recent->post_modified = $recent_post->post_modified;
109
+ $recent->comment_count = (int)$recent_post->comment_count;
110
  $recent_posts[] = $recent;
111
  }
112
 
133
  $recent->post_date = $scheduled->post_date;
134
  $recent->post_title = $scheduled->post_title;
135
  $recent->post_modified = $scheduled->post_modified;
 
136
  $scheduled_posts[] = $recent;
137
  }
138
 
150
 
151
  $recent_posts[] = $recent;
152
  }
153
+ usort($recent_posts, array($this, 'cmp_posts_worker'));
154
  $stats['posts'] = array_slice($recent_posts, 0, 20);
155
 
156
  $all_pages_drafts = get_pages('post_status=draft&numberposts=20&orderby=modified&order=desc');
165
 
166
  $recent_drafts[] = $recent;
167
  }
168
+ usort($recent_drafts, array($this, 'cmp_posts_worker'));
169
  $stats['drafts'] = array_slice($recent_drafts, 0, 20);
170
 
171
 
181
 
182
  $scheduled_posts[] = $recent;
183
  }
184
+ usort($scheduled_posts, array($this, 'cmp_posts_worker'));
185
  $stats['scheduled'] = array_slice($scheduled_posts, 0, 20);
186
 
187
 
193
 
194
  //Backup Results
195
  $stats['mwp_backups'] = $this->get_backup_instance()->get_backup_stats();
196
+ $stats['mwp_next_backups'] = $this->get_backup_instance()->get_next_schedules();
197
+
198
+ $clone_backup = get_option('mwp_manual_backup');
199
 
200
+ if(!is_array($clone_backup) || !file_exists($clone_backup['file_path'])){
201
+ $clone_backup = '';
202
+ } else {
203
+ $clone_backup = $clone_backup['file_url'];
204
+ }
205
+ $stats['clone_backup'] = $clone_backup;
206
+
207
  //Backup requirements
208
  $stats['mwp_backup_req'] = $this->get_backup_instance()->check_backup_compat();
209
 
 
 
 
210
  $stats = apply_filters('mmb_stats_filter', $stats);
211
 
212
  return $stats;
213
  }
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  function get_comments_stats(){
216
  $num_pending_comments = 3;
217
  $num_approved_comments = 3;
232
 
233
  return $stats;
234
  }
235
+
236
  function get_initial_stats()
237
  {
238
  global $mmb_plugin_dir;
380
  return false;
381
  }
382
 
383
+
384
+ function set_notifications($params)
385
+ {
386
+ if(empty($params))
387
+ return false;
388
+
389
+ extract($params);
390
+
391
+ if(!isset($delete)){
392
+ $mwp_notifications = array('plugins' => $plugins, 'themes' => $themes, 'wp' => $wp,'url' => $url, 'notification_key' => $notification_key);
393
+ update_option('mwp_notifications',$mwp_notifications);
394
+ } else {
395
+ delete_option('mwp_notifications');
396
+ }
397
+
398
+ return true;
399
+
400
+ }
401
+
402
+ //Cron update check for notifications
403
+ function check_notifications(){
404
+ global $wpdb, $mmb_wp_version, $mmb_plugin_dir, $wp_version, $wp_local_package;
405
+
406
+ $mwp_notifications = get_option('mwp_notifications',true);
407
+ $updates = array();
408
+
409
+ if(is_array($mwp_notifications) && $mwp_notifications != false){
410
+ include_once(ABSPATH . 'wp-includes/update.php');
411
+ include_once(ABSPATH . '/wp-admin/includes/update.php');
412
+ extract($mwp_notifications);
413
+
414
+ //Check wordpress core updates
415
+ if($wp){
416
+ @wp_version_check();
417
+ if (function_exists('get_core_updates')) {
418
+ $wp_updates = get_core_updates();
419
+ if (!empty($wp_updates)) {
420
+ $current_transient = $wp_updates[0];
421
+ if ($current_transient->response == "development" || version_compare($wp_version, $current_transient->current, '<')) {
422
+ $current_transient->current_version = $wp_version;
423
+ $updates['core_updates'] = $current_transient;
424
+ } else
425
+ $updates['core_updates'] = array();
426
+ } else
427
+ $updates['core_updates'] = array();
428
+ }
429
+
430
+ //Check plugin updates
431
+ if($plugins){
432
+ @wp_update_plugins();
433
+ $this->get_installer_instance();
434
+ $updates['upgradable_plugins'] = $this->installer_instance->get_upgradable_plugins();
435
+ }
436
+
437
+ //Check theme updates
438
+ if($themes)
439
+ {
440
+ @wp_update_themes();
441
+ $this->get_installer_instance();
442
+
443
+ $updates['upgradable_themes'] = $this->installer_instance->get_upgradable_themes();
444
+ }
445
+
446
+ }
447
+
448
+ if( !class_exists( 'WP_Http' ) ){
449
+ include_once( ABSPATH . WPINC. '/class-http.php' );
450
+ }
451
+
452
+ $args = array();
453
+ $args['body'] = array('updates' => $updates, 'notification_key' => $notification_key);
454
+ $result= wp_remote_post($url, $args);
455
+ }
456
+ }
457
+
458
+
459
+ function cmp_posts_worker($a, $b)
460
+ {
461
+ return ($a->post_modified < $b->post_modified);
462
+ }
463
 
 
 
 
464
  }
 
 
465
  ?>
version CHANGED
@@ -1 +1 @@
1
- 3.9.7
1
+ 3.9.8