Version Description
- 05/21/2012 =
- Removed CloudFront method; I have no way of testing this
- Backup all tables found in the database that have this site's table prefix
- If encryption fails, then abort (don't revert to not encrypting)
- Added ability to decrypt encrypted database backups
- Added ability to opt out of backing up each file group
- Now adds database character set, the lack of which before made database backups unusable without modifications
- Version number bump to make sure that this is an improvement on the original Updraft, and is now tried and tested
Download this release
Release Info
Developer | DavidAnderson |
Plugin | UpdraftPlus WordPress Backup Plugin |
Version | 0.7.4 |
Comparing to | |
See all releases |
Version 0.7.4
- example-decrypt.php +41 -0
- includes/Rijndael.php +1424 -0
- includes/S3.php +1365 -0
- includes/ftp.class.php +193 -0
- includes/updraft-restorer.php +62 -0
- readme.txt +94 -0
- screenshot-1.png +0 -0
- updraftplus.php +1501 -0
example-decrypt.php
ADDED
@@ -0,0 +1,41 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
|
5 |
+
To dump the decrypted file using the given key on stdout, call:
|
6 |
+
|
7 |
+
rijndael_decrypt_file( '../path/to/file.crypt' , 'mykey' );
|
8 |
+
|
9 |
+
Thus, here are the easy instructions:
|
10 |
+
|
11 |
+
1) Add a line like the above into this PHP file (not inside these comments, but outside)
|
12 |
+
e.g.
|
13 |
+
rijndael_decrypt_file( '/home/myself/myfile.crypt' , 'MYKEY' );
|
14 |
+
|
15 |
+
2) Run this file (and make sure that includes/Rijndael.php is available, if you are moving this file around)
|
16 |
+
e.g.
|
17 |
+
php /home/myself/example-decrypt.php >output.sql.gz
|
18 |
+
|
19 |
+
3) You may then want to gunzip the resulting file to have a standard SQL file.
|
20 |
+
e.g.
|
21 |
+
gunzip output.sql.gz
|
22 |
+
|
23 |
+
*/
|
24 |
+
|
25 |
+
function rijndael_decrypt_file($file, $key) {
|
26 |
+
|
27 |
+
require_once(dirname(__FILE__).'/includes/Rijndael.php');
|
28 |
+
|
29 |
+
$rijndael = new Crypt_Rijndael();
|
30 |
+
$rijndael->setKey($key);
|
31 |
+
$in_handle = fopen($file,'r');
|
32 |
+
$ciphertext = "";
|
33 |
+
while (!feof ($in_handle)) {
|
34 |
+
$ciphertext .= fread($in_handle, 16384);
|
35 |
+
}
|
36 |
+
fclose ($in_handle);
|
37 |
+
print $rijndael->decrypt($ciphertext);
|
38 |
+
|
39 |
+
}
|
40 |
+
|
41 |
+
?>
|
includes/Rijndael.php
ADDED
@@ -0,0 +1,1424 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Pure-PHP implementation of Rijndael.
|
6 |
+
*
|
7 |
+
* Does not use mcrypt, even when available, for reasons that are explained below.
|
8 |
+
*
|
9 |
+
* PHP versions 4 and 5
|
10 |
+
*
|
11 |
+
* If {@link Crypt_Rijndael::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits. If
|
12 |
+
* {@link Crypt_Rijndael::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
13 |
+
* {@link Crypt_Rijndael::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's
|
14 |
+
* 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until
|
15 |
+
* {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated.
|
16 |
+
*
|
17 |
+
* Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example,
|
18 |
+
* does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
|
19 |
+
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
|
20 |
+
* algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224
|
21 |
+
* are first defined as valid key / block lengths in
|
22 |
+
* {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
|
23 |
+
* Extensions: Other block and Cipher Key lengths.
|
24 |
+
*
|
25 |
+
* {@internal The variable names are the same as those in
|
26 |
+
* {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
|
27 |
+
*
|
28 |
+
* Here's a short example of how to use this library:
|
29 |
+
* <code>
|
30 |
+
* <?php
|
31 |
+
* include('Crypt/Rijndael.php');
|
32 |
+
*
|
33 |
+
* $rijndael = new Crypt_Rijndael();
|
34 |
+
*
|
35 |
+
* $rijndael->setKey('abcdefghijklmnop');
|
36 |
+
*
|
37 |
+
* $size = 10 * 1024;
|
38 |
+
* $plaintext = '';
|
39 |
+
* for ($i = 0; $i < $size; $i++) {
|
40 |
+
* $plaintext.= 'a';
|
41 |
+
* }
|
42 |
+
*
|
43 |
+
* echo $rijndael->decrypt($rijndael->encrypt($plaintext));
|
44 |
+
* ?>
|
45 |
+
* </code>
|
46 |
+
*
|
47 |
+
* LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
|
48 |
+
* of this software and associated documentation files (the "Software"), to deal
|
49 |
+
* in the Software without restriction, including without limitation the rights
|
50 |
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
51 |
+
* copies of the Software, and to permit persons to whom the Software is
|
52 |
+
* furnished to do so, subject to the following conditions:
|
53 |
+
*
|
54 |
+
* The above copyright notice and this permission notice shall be included in
|
55 |
+
* all copies or substantial portions of the Software.
|
56 |
+
*
|
57 |
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
58 |
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
59 |
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
60 |
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
61 |
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
62 |
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
63 |
+
* THE SOFTWARE.
|
64 |
+
*
|
65 |
+
* @category Crypt
|
66 |
+
* @package Crypt_Rijndael
|
67 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
68 |
+
* @copyright MMVIII Jim Wigginton
|
69 |
+
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
70 |
+
* @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
|
71 |
+
* @link http://phpseclib.sourceforge.net
|
72 |
+
*/
|
73 |
+
|
74 |
+
/**#@+
|
75 |
+
* @access public
|
76 |
+
* @see Crypt_Rijndael::encrypt()
|
77 |
+
* @see Crypt_Rijndael::decrypt()
|
78 |
+
*/
|
79 |
+
/**
|
80 |
+
* Encrypt / decrypt using the Counter mode.
|
81 |
+
*
|
82 |
+
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
|
83 |
+
*
|
84 |
+
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
|
85 |
+
*/
|
86 |
+
define('CRYPT_RIJNDAEL_MODE_CTR', -1);
|
87 |
+
/**
|
88 |
+
* Encrypt / decrypt using the Electronic Code Book mode.
|
89 |
+
*
|
90 |
+
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
|
91 |
+
*/
|
92 |
+
define('CRYPT_RIJNDAEL_MODE_ECB', 1);
|
93 |
+
/**
|
94 |
+
* Encrypt / decrypt using the Code Book Chaining mode.
|
95 |
+
*
|
96 |
+
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
|
97 |
+
*/
|
98 |
+
define('CRYPT_RIJNDAEL_MODE_CBC', 2);
|
99 |
+
/**
|
100 |
+
* Encrypt / decrypt using the Cipher Feedback mode.
|
101 |
+
*
|
102 |
+
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
|
103 |
+
*/
|
104 |
+
define('CRYPT_RIJNDAEL_MODE_CFB', 3);
|
105 |
+
/**
|
106 |
+
* Encrypt / decrypt using the Cipher Feedback mode.
|
107 |
+
*
|
108 |
+
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
|
109 |
+
*/
|
110 |
+
define('CRYPT_RIJNDAEL_MODE_OFB', 4);
|
111 |
+
/**#@-*/
|
112 |
+
|
113 |
+
/**#@+
|
114 |
+
* @access private
|
115 |
+
* @see Crypt_Rijndael::Crypt_Rijndael()
|
116 |
+
*/
|
117 |
+
/**
|
118 |
+
* Toggles the internal implementation
|
119 |
+
*/
|
120 |
+
define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1);
|
121 |
+
/**
|
122 |
+
* Toggles the mcrypt implementation
|
123 |
+
*/
|
124 |
+
define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2);
|
125 |
+
/**#@-*/
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Pure-PHP implementation of Rijndael.
|
129 |
+
*
|
130 |
+
* @author Jim Wigginton <terrafrost@php.net>
|
131 |
+
* @version 0.1.0
|
132 |
+
* @access public
|
133 |
+
* @package Crypt_Rijndael
|
134 |
+
*/
|
135 |
+
class Crypt_Rijndael {
|
136 |
+
/**
|
137 |
+
* The Encryption Mode
|
138 |
+
*
|
139 |
+
* @see Crypt_Rijndael::Crypt_Rijndael()
|
140 |
+
* @var Integer
|
141 |
+
* @access private
|
142 |
+
*/
|
143 |
+
var $mode;
|
144 |
+
|
145 |
+
/**
|
146 |
+
* The Key
|
147 |
+
*
|
148 |
+
* @see Crypt_Rijndael::setKey()
|
149 |
+
* @var String
|
150 |
+
* @access private
|
151 |
+
*/
|
152 |
+
var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
153 |
+
|
154 |
+
/**
|
155 |
+
* The Initialization Vector
|
156 |
+
*
|
157 |
+
* @see Crypt_Rijndael::setIV()
|
158 |
+
* @var String
|
159 |
+
* @access private
|
160 |
+
*/
|
161 |
+
var $iv = '';
|
162 |
+
|
163 |
+
/**
|
164 |
+
* A "sliding" Initialization Vector
|
165 |
+
*
|
166 |
+
* @see Crypt_Rijndael::enableContinuousBuffer()
|
167 |
+
* @var String
|
168 |
+
* @access private
|
169 |
+
*/
|
170 |
+
var $encryptIV = '';
|
171 |
+
|
172 |
+
/**
|
173 |
+
* A "sliding" Initialization Vector
|
174 |
+
*
|
175 |
+
* @see Crypt_Rijndael::enableContinuousBuffer()
|
176 |
+
* @var String
|
177 |
+
* @access private
|
178 |
+
*/
|
179 |
+
var $decryptIV = '';
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Continuous Buffer status
|
183 |
+
*
|
184 |
+
* @see Crypt_Rijndael::enableContinuousBuffer()
|
185 |
+
* @var Boolean
|
186 |
+
* @access private
|
187 |
+
*/
|
188 |
+
var $continuousBuffer = false;
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Padding status
|
192 |
+
*
|
193 |
+
* @see Crypt_Rijndael::enablePadding()
|
194 |
+
* @var Boolean
|
195 |
+
* @access private
|
196 |
+
*/
|
197 |
+
var $padding = true;
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Does the key schedule need to be (re)calculated?
|
201 |
+
*
|
202 |
+
* @see setKey()
|
203 |
+
* @see setBlockLength()
|
204 |
+
* @see setKeyLength()
|
205 |
+
* @var Boolean
|
206 |
+
* @access private
|
207 |
+
*/
|
208 |
+
var $changed = true;
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Has the key length explicitly been set or should it be derived from the key, itself?
|
212 |
+
*
|
213 |
+
* @see setKeyLength()
|
214 |
+
* @var Boolean
|
215 |
+
* @access private
|
216 |
+
*/
|
217 |
+
var $explicit_key_length = false;
|
218 |
+
|
219 |
+
/**
|
220 |
+
* The Key Schedule
|
221 |
+
*
|
222 |
+
* @see _setup()
|
223 |
+
* @var Array
|
224 |
+
* @access private
|
225 |
+
*/
|
226 |
+
var $w;
|
227 |
+
|
228 |
+
/**
|
229 |
+
* The Inverse Key Schedule
|
230 |
+
*
|
231 |
+
* @see _setup()
|
232 |
+
* @var Array
|
233 |
+
* @access private
|
234 |
+
*/
|
235 |
+
var $dw;
|
236 |
+
|
237 |
+
/**
|
238 |
+
* The Block Length
|
239 |
+
*
|
240 |
+
* @see setBlockLength()
|
241 |
+
* @var Integer
|
242 |
+
* @access private
|
243 |
+
* @internal The max value is 32, the min value is 16. All valid values are multiples of 4. Exists in conjunction with
|
244 |
+
* $Nb because we need this value and not $Nb to pad strings appropriately.
|
245 |
+
*/
|
246 |
+
var $block_size = 16;
|
247 |
+
|
248 |
+
/**
|
249 |
+
* The Block Length divided by 32
|
250 |
+
*
|
251 |
+
* @see setBlockLength()
|
252 |
+
* @var Integer
|
253 |
+
* @access private
|
254 |
+
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
|
255 |
+
* because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
|
256 |
+
* derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
257 |
+
* of that, we'll just precompute it once.
|
258 |
+
*
|
259 |
+
*/
|
260 |
+
var $Nb = 4;
|
261 |
+
|
262 |
+
/**
|
263 |
+
* The Key Length
|
264 |
+
*
|
265 |
+
* @see setKeyLength()
|
266 |
+
* @var Integer
|
267 |
+
* @access private
|
268 |
+
* @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $key_size
|
269 |
+
* because the encryption / decryption / key schedule creation requires this number and not $key_size. We could
|
270 |
+
* derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
|
271 |
+
* of that, we'll just precompute it once.
|
272 |
+
*/
|
273 |
+
var $key_size = 16;
|
274 |
+
|
275 |
+
/**
|
276 |
+
* The Key Length divided by 32
|
277 |
+
*
|
278 |
+
* @see setKeyLength()
|
279 |
+
* @var Integer
|
280 |
+
* @access private
|
281 |
+
* @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
|
282 |
+
*/
|
283 |
+
var $Nk = 4;
|
284 |
+
|
285 |
+
/**
|
286 |
+
* The Number of Rounds
|
287 |
+
*
|
288 |
+
* @var Integer
|
289 |
+
* @access private
|
290 |
+
* @internal The max value is 14, the min value is 10.
|
291 |
+
*/
|
292 |
+
var $Nr;
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Shift offsets
|
296 |
+
*
|
297 |
+
* @var Array
|
298 |
+
* @access private
|
299 |
+
*/
|
300 |
+
var $c;
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Precomputed mixColumns table
|
304 |
+
*
|
305 |
+
* @see Crypt_Rijndael()
|
306 |
+
* @var Array
|
307 |
+
* @access private
|
308 |
+
*/
|
309 |
+
var $t0;
|
310 |
+
|
311 |
+
/**
|
312 |
+
* Precomputed mixColumns table
|
313 |
+
*
|
314 |
+
* @see Crypt_Rijndael()
|
315 |
+
* @var Array
|
316 |
+
* @access private
|
317 |
+
*/
|
318 |
+
var $t1;
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Precomputed mixColumns table
|
322 |
+
*
|
323 |
+
* @see Crypt_Rijndael()
|
324 |
+
* @var Array
|
325 |
+
* @access private
|
326 |
+
*/
|
327 |
+
var $t2;
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Precomputed mixColumns table
|
331 |
+
*
|
332 |
+
* @see Crypt_Rijndael()
|
333 |
+
* @var Array
|
334 |
+
* @access private
|
335 |
+
*/
|
336 |
+
var $t3;
|
337 |
+
|
338 |
+
/**
|
339 |
+
* Precomputed invMixColumns table
|
340 |
+
*
|
341 |
+
* @see Crypt_Rijndael()
|
342 |
+
* @var Array
|
343 |
+
* @access private
|
344 |
+
*/
|
345 |
+
var $dt0;
|
346 |
+
|
347 |
+
/**
|
348 |
+
* Precomputed invMixColumns table
|
349 |
+
*
|
350 |
+
* @see Crypt_Rijndael()
|
351 |
+
* @var Array
|
352 |
+
* @access private
|
353 |
+
*/
|
354 |
+
var $dt1;
|
355 |
+
|
356 |
+
/**
|
357 |
+
* Precomputed invMixColumns table
|
358 |
+
*
|
359 |
+
* @see Crypt_Rijndael()
|
360 |
+
* @var Array
|
361 |
+
* @access private
|
362 |
+
*/
|
363 |
+
var $dt2;
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Precomputed invMixColumns table
|
367 |
+
*
|
368 |
+
* @see Crypt_Rijndael()
|
369 |
+
* @var Array
|
370 |
+
* @access private
|
371 |
+
*/
|
372 |
+
var $dt3;
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Is the mode one that is paddable?
|
376 |
+
*
|
377 |
+
* @see Crypt_Rijndael::Crypt_Rijndael()
|
378 |
+
* @var Boolean
|
379 |
+
* @access private
|
380 |
+
*/
|
381 |
+
var $paddable = false;
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Encryption buffer for CTR, OFB and CFB modes
|
385 |
+
*
|
386 |
+
* @see Crypt_Rijndael::encrypt()
|
387 |
+
* @var String
|
388 |
+
* @access private
|
389 |
+
*/
|
390 |
+
var $enbuffer = array('encrypted' => '', 'xor' => '');
|
391 |
+
|
392 |
+
/**
|
393 |
+
* Decryption buffer for CTR, OFB and CFB modes
|
394 |
+
*
|
395 |
+
* @see Crypt_Rijndael::decrypt()
|
396 |
+
* @var String
|
397 |
+
* @access private
|
398 |
+
*/
|
399 |
+
var $debuffer = array('ciphertext' => '');
|
400 |
+
|
401 |
+
/**
|
402 |
+
* Default Constructor.
|
403 |
+
*
|
404 |
+
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
|
405 |
+
* CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used.
|
406 |
+
*
|
407 |
+
* @param optional Integer $mode
|
408 |
+
* @return Crypt_Rijndael
|
409 |
+
* @access public
|
410 |
+
*/
|
411 |
+
function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC)
|
412 |
+
{
|
413 |
+
switch ($mode) {
|
414 |
+
case CRYPT_RIJNDAEL_MODE_ECB:
|
415 |
+
case CRYPT_RIJNDAEL_MODE_CBC:
|
416 |
+
$this->paddable = true;
|
417 |
+
$this->mode = $mode;
|
418 |
+
break;
|
419 |
+
case CRYPT_RIJNDAEL_MODE_CTR:
|
420 |
+
case CRYPT_RIJNDAEL_MODE_CFB:
|
421 |
+
case CRYPT_RIJNDAEL_MODE_OFB:
|
422 |
+
$this->mode = $mode;
|
423 |
+
break;
|
424 |
+
default:
|
425 |
+
$this->paddable = true;
|
426 |
+
$this->mode = CRYPT_RIJNDAEL_MODE_CBC;
|
427 |
+
}
|
428 |
+
|
429 |
+
$t3 = &$this->t3;
|
430 |
+
$t2 = &$this->t2;
|
431 |
+
$t1 = &$this->t1;
|
432 |
+
$t0 = &$this->t0;
|
433 |
+
|
434 |
+
$dt3 = &$this->dt3;
|
435 |
+
$dt2 = &$this->dt2;
|
436 |
+
$dt1 = &$this->dt1;
|
437 |
+
$dt0 = &$this->dt0;
|
438 |
+
|
439 |
+
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
|
440 |
+
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
|
441 |
+
// those are the names we'll use.
|
442 |
+
$t3 = array(
|
443 |
+
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
|
444 |
+
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
|
445 |
+
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
|
446 |
+
0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
|
447 |
+
0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
|
448 |
+
0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
|
449 |
+
0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
|
450 |
+
0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
|
451 |
+
0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
|
452 |
+
0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
|
453 |
+
0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
|
454 |
+
0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
|
455 |
+
0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
|
456 |
+
0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
|
457 |
+
0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
|
458 |
+
0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
|
459 |
+
0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
|
460 |
+
0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
|
461 |
+
0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
|
462 |
+
0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
|
463 |
+
0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
|
464 |
+
0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
|
465 |
+
0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
|
466 |
+
0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
|
467 |
+
0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
|
468 |
+
0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
|
469 |
+
0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
|
470 |
+
0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
|
471 |
+
0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
|
472 |
+
0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
|
473 |
+
0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
|
474 |
+
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
|
475 |
+
);
|
476 |
+
|
477 |
+
$dt3 = array(
|
478 |
+
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
|
479 |
+
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
|
480 |
+
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
|
481 |
+
0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
|
482 |
+
0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
|
483 |
+
0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
|
484 |
+
0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
|
485 |
+
0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
|
486 |
+
0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
|
487 |
+
0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
|
488 |
+
0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
|
489 |
+
0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
|
490 |
+
0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
|
491 |
+
0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
|
492 |
+
0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
|
493 |
+
0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
|
494 |
+
0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
|
495 |
+
0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
|
496 |
+
0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
|
497 |
+
0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
|
498 |
+
0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
|
499 |
+
0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
|
500 |
+
0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
|
501 |
+
0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
|
502 |
+
0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
|
503 |
+
0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
|
504 |
+
0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
|
505 |
+
0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
|
506 |
+
0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
|
507 |
+
0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
|
508 |
+
0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
|
509 |
+
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
|
510 |
+
);
|
511 |
+
|
512 |
+
for ($i = 0; $i < 256; $i++) {
|
513 |
+
$t2[$i << 8] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF);
|
514 |
+
$t1[$i << 16] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF);
|
515 |
+
$t0[$i << 24] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF);
|
516 |
+
|
517 |
+
$dt2[$i << 8] = (($this->dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF);
|
518 |
+
$dt1[$i << 16] = (($this->dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF);
|
519 |
+
$dt0[$i << 24] = (($this->dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF);
|
520 |
+
}
|
521 |
+
}
|
522 |
+
|
523 |
+
/**
|
524 |
+
* Sets the key.
|
525 |
+
*
|
526 |
+
* Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
|
527 |
+
* whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length
|
528 |
+
* up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the
|
529 |
+
* excess bits.
|
530 |
+
*
|
531 |
+
* If the key is not explicitly set, it'll be assumed to be all null bytes.
|
532 |
+
*
|
533 |
+
* @access public
|
534 |
+
* @param String $key
|
535 |
+
*/
|
536 |
+
function setKey($key)
|
537 |
+
{
|
538 |
+
$this->key = $key;
|
539 |
+
$this->changed = true;
|
540 |
+
}
|
541 |
+
|
542 |
+
/**
|
543 |
+
* Sets the initialization vector. (optional)
|
544 |
+
*
|
545 |
+
* SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used. If not explictly set, it'll be assumed
|
546 |
+
* to be all zero's.
|
547 |
+
*
|
548 |
+
* @access public
|
549 |
+
* @param String $iv
|
550 |
+
*/
|
551 |
+
function setIV($iv)
|
552 |
+
{
|
553 |
+
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0));;
|
554 |
+
}
|
555 |
+
|
556 |
+
/**
|
557 |
+
* Sets the key length
|
558 |
+
*
|
559 |
+
* Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
560 |
+
* 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
|
561 |
+
*
|
562 |
+
* @access public
|
563 |
+
* @param Integer $length
|
564 |
+
*/
|
565 |
+
function setKeyLength($length)
|
566 |
+
{
|
567 |
+
$length >>= 5;
|
568 |
+
if ($length > 8) {
|
569 |
+
$length = 8;
|
570 |
+
} else if ($length < 4) {
|
571 |
+
$length = 4;
|
572 |
+
}
|
573 |
+
$this->Nk = $length;
|
574 |
+
$this->key_size = $length << 2;
|
575 |
+
|
576 |
+
$this->explicit_key_length = true;
|
577 |
+
$this->changed = true;
|
578 |
+
}
|
579 |
+
|
580 |
+
/**
|
581 |
+
* Sets the block length
|
582 |
+
*
|
583 |
+
* Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
|
584 |
+
* 128. If the length is greater then 128 and invalid, it will be rounded down to the closest valid amount.
|
585 |
+
*
|
586 |
+
* @access public
|
587 |
+
* @param Integer $length
|
588 |
+
*/
|
589 |
+
function setBlockLength($length)
|
590 |
+
{
|
591 |
+
$length >>= 5;
|
592 |
+
if ($length > 8) {
|
593 |
+
$length = 8;
|
594 |
+
} else if ($length < 4) {
|
595 |
+
$length = 4;
|
596 |
+
}
|
597 |
+
$this->Nb = $length;
|
598 |
+
$this->block_size = $length << 2;
|
599 |
+
$this->changed = true;
|
600 |
+
}
|
601 |
+
|
602 |
+
/**
|
603 |
+
* Generate CTR XOR encryption key
|
604 |
+
*
|
605 |
+
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
|
606 |
+
* plaintext / ciphertext in CTR mode.
|
607 |
+
*
|
608 |
+
* @see Crypt_Rijndael::decrypt()
|
609 |
+
* @see Crypt_Rijndael::encrypt()
|
610 |
+
* @access public
|
611 |
+
* @param Integer $length
|
612 |
+
* @param String $iv
|
613 |
+
*/
|
614 |
+
function _generate_xor($length, &$iv)
|
615 |
+
{
|
616 |
+
$xor = '';
|
617 |
+
$block_size = $this->block_size;
|
618 |
+
$num_blocks = floor(($length + ($block_size - 1)) / $block_size);
|
619 |
+
for ($i = 0; $i < $num_blocks; $i++) {
|
620 |
+
$xor.= $iv;
|
621 |
+
for ($j = 4; $j <= $block_size; $j+=4) {
|
622 |
+
$temp = substr($iv, -$j, 4);
|
623 |
+
switch ($temp) {
|
624 |
+
case "\xFF\xFF\xFF\xFF":
|
625 |
+
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
|
626 |
+
break;
|
627 |
+
case "\x7F\xFF\xFF\xFF":
|
628 |
+
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
|
629 |
+
break 2;
|
630 |
+
default:
|
631 |
+
extract(unpack('Ncount', $temp));
|
632 |
+
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
|
633 |
+
break 2;
|
634 |
+
}
|
635 |
+
}
|
636 |
+
}
|
637 |
+
|
638 |
+
return $xor;
|
639 |
+
}
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Encrypts a message.
|
643 |
+
*
|
644 |
+
* $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other Rjindael
|
645 |
+
* implementations may or may not pad in the same manner. Other common approaches to padding and the reasons why it's
|
646 |
+
* necessary are discussed in the following
|
647 |
+
* URL:
|
648 |
+
*
|
649 |
+
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
|
650 |
+
*
|
651 |
+
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
|
652 |
+
* strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
|
653 |
+
* length.
|
654 |
+
*
|
655 |
+
* @see Crypt_Rijndael::decrypt()
|
656 |
+
* @access public
|
657 |
+
* @param String $plaintext
|
658 |
+
*/
|
659 |
+
function encrypt($plaintext)
|
660 |
+
{
|
661 |
+
$this->_setup();
|
662 |
+
if ($this->paddable) {
|
663 |
+
$plaintext = $this->_pad($plaintext);
|
664 |
+
}
|
665 |
+
|
666 |
+
$block_size = $this->block_size;
|
667 |
+
$buffer = &$this->enbuffer;
|
668 |
+
$continuousBuffer = $this->continuousBuffer;
|
669 |
+
$ciphertext = '';
|
670 |
+
switch ($this->mode) {
|
671 |
+
case CRYPT_RIJNDAEL_MODE_ECB:
|
672 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
673 |
+
$ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
|
674 |
+
}
|
675 |
+
break;
|
676 |
+
case CRYPT_RIJNDAEL_MODE_CBC:
|
677 |
+
$xor = $this->encryptIV;
|
678 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
679 |
+
$block = substr($plaintext, $i, $block_size);
|
680 |
+
$block = $this->_encryptBlock($block ^ $xor);
|
681 |
+
$xor = $block;
|
682 |
+
$ciphertext.= $block;
|
683 |
+
}
|
684 |
+
if ($this->continuousBuffer) {
|
685 |
+
$this->encryptIV = $xor;
|
686 |
+
}
|
687 |
+
break;
|
688 |
+
case CRYPT_RIJNDAEL_MODE_CTR:
|
689 |
+
$xor = $this->encryptIV;
|
690 |
+
if (!empty($buffer)) {
|
691 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
692 |
+
$block = substr($plaintext, $i, $block_size);
|
693 |
+
$buffer.= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
|
694 |
+
$key = $this->_string_shift($buffer, $block_size);
|
695 |
+
$ciphertext.= $block ^ $key;
|
696 |
+
}
|
697 |
+
} else {
|
698 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
699 |
+
$block = substr($plaintext, $i, $block_size);
|
700 |
+
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
|
701 |
+
$ciphertext.= $block ^ $key;
|
702 |
+
}
|
703 |
+
}
|
704 |
+
if ($this->continuousBuffer) {
|
705 |
+
$this->encryptIV = $xor;
|
706 |
+
if ($start = strlen($plaintext) % $block_size) {
|
707 |
+
$buffer = substr($key, $start) . $buffer;
|
708 |
+
}
|
709 |
+
}
|
710 |
+
break;
|
711 |
+
case CRYPT_RIJNDAEL_MODE_CFB:
|
712 |
+
if (!empty($buffer['xor'])) {
|
713 |
+
$ciphertext = $plaintext ^ $buffer['xor'];
|
714 |
+
$iv = $buffer['encrypted'] . $ciphertext;
|
715 |
+
$start = strlen($ciphertext);
|
716 |
+
$buffer['encrypted'].= $ciphertext;
|
717 |
+
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
|
718 |
+
} else {
|
719 |
+
$ciphertext = '';
|
720 |
+
$iv = $this->encryptIV;
|
721 |
+
$start = 0;
|
722 |
+
}
|
723 |
+
|
724 |
+
for ($i = $start; $i < strlen($plaintext); $i+=$block_size) {
|
725 |
+
$block = substr($plaintext, $i, $block_size);
|
726 |
+
$xor = $this->_encryptBlock($iv);
|
727 |
+
$iv = $block ^ $xor;
|
728 |
+
if ($continuousBuffer && strlen($iv) != $block_size) {
|
729 |
+
$buffer = array(
|
730 |
+
'encrypted' => $iv,
|
731 |
+
'xor' => substr($xor, strlen($iv))
|
732 |
+
);
|
733 |
+
}
|
734 |
+
$ciphertext.= $iv;
|
735 |
+
}
|
736 |
+
|
737 |
+
if ($this->continuousBuffer) {
|
738 |
+
$this->encryptIV = $iv;
|
739 |
+
}
|
740 |
+
break;
|
741 |
+
case CRYPT_RIJNDAEL_MODE_OFB:
|
742 |
+
$xor = $this->encryptIV;
|
743 |
+
if (strlen($buffer)) {
|
744 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
745 |
+
$xor = $this->_encryptBlock($xor);
|
746 |
+
$buffer.= $xor;
|
747 |
+
$key = $this->_string_shift($buffer, $block_size);
|
748 |
+
$ciphertext.= substr($plaintext, $i, $block_size) ^ $key;
|
749 |
+
}
|
750 |
+
} else {
|
751 |
+
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
|
752 |
+
$xor = $this->_encryptBlock($xor);
|
753 |
+
$ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
|
754 |
+
}
|
755 |
+
$key = $xor;
|
756 |
+
}
|
757 |
+
if ($this->continuousBuffer) {
|
758 |
+
$this->encryptIV = $xor;
|
759 |
+
if ($start = strlen($plaintext) % $block_size) {
|
760 |
+
$buffer = substr($key, $start) . $buffer;
|
761 |
+
}
|
762 |
+
}
|
763 |
+
}
|
764 |
+
|
765 |
+
return $ciphertext;
|
766 |
+
}
|
767 |
+
|
768 |
+
/**
|
769 |
+
* Decrypts a message.
|
770 |
+
*
|
771 |
+
* If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
|
772 |
+
* it is.
|
773 |
+
*
|
774 |
+
* @see Crypt_Rijndael::encrypt()
|
775 |
+
* @access public
|
776 |
+
* @param String $ciphertext
|
777 |
+
*/
|
778 |
+
function decrypt($ciphertext)
|
779 |
+
{
|
780 |
+
$this->_setup();
|
781 |
+
|
782 |
+
if ($this->paddable) {
|
783 |
+
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
|
784 |
+
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
|
785 |
+
$ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
|
786 |
+
}
|
787 |
+
|
788 |
+
$block_size = $this->block_size;
|
789 |
+
$buffer = &$this->debuffer;
|
790 |
+
$continuousBuffer = $this->continuousBuffer;
|
791 |
+
$plaintext = '';
|
792 |
+
switch ($this->mode) {
|
793 |
+
case CRYPT_RIJNDAEL_MODE_ECB:
|
794 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
795 |
+
$plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
|
796 |
+
}
|
797 |
+
break;
|
798 |
+
case CRYPT_RIJNDAEL_MODE_CBC:
|
799 |
+
$xor = $this->decryptIV;
|
800 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
801 |
+
$block = substr($ciphertext, $i, $block_size);
|
802 |
+
$plaintext.= $this->_decryptBlock($block) ^ $xor;
|
803 |
+
$xor = $block;
|
804 |
+
}
|
805 |
+
if ($this->continuousBuffer) {
|
806 |
+
$this->decryptIV = $xor;
|
807 |
+
}
|
808 |
+
break;
|
809 |
+
case CRYPT_RIJNDAEL_MODE_CTR:
|
810 |
+
$xor = $this->decryptIV;
|
811 |
+
if (strlen($buffer)) {
|
812 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
813 |
+
$block = substr($ciphertext, $i, $block_size);
|
814 |
+
$buffer.= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
|
815 |
+
$key = $this->_string_shift($buffer, $block_size);
|
816 |
+
$plaintext.= $block ^ $key;
|
817 |
+
}
|
818 |
+
} else {
|
819 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
820 |
+
$block = substr($ciphertext, $i, $block_size);
|
821 |
+
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
|
822 |
+
$plaintext.= $block ^ $key;
|
823 |
+
}
|
824 |
+
}
|
825 |
+
if ($this->continuousBuffer) {
|
826 |
+
$this->decryptIV = $xor;
|
827 |
+
if ($start = strlen($ciphertext) % $block_size) {
|
828 |
+
$buffer = substr($key, $start) . $buffer;
|
829 |
+
}
|
830 |
+
}
|
831 |
+
break;
|
832 |
+
case CRYPT_RIJNDAEL_MODE_CFB:
|
833 |
+
if (!empty($buffer['ciphertext'])) {
|
834 |
+
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
|
835 |
+
$buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
|
836 |
+
if (strlen($buffer['ciphertext']) == $block_size) {
|
837 |
+
$xor = $this->_encryptBlock($buffer['ciphertext']);
|
838 |
+
$buffer['ciphertext'] = '';
|
839 |
+
}
|
840 |
+
$start = strlen($plaintext);
|
841 |
+
$block = $this->decryptIV;
|
842 |
+
} else {
|
843 |
+
$plaintext = '';
|
844 |
+
$xor = $this->_encryptBlock($this->decryptIV);
|
845 |
+
$start = 0;
|
846 |
+
}
|
847 |
+
|
848 |
+
for ($i = $start; $i < strlen($ciphertext); $i+=$block_size) {
|
849 |
+
$block = substr($ciphertext, $i, $block_size);
|
850 |
+
$plaintext.= $block ^ $xor;
|
851 |
+
if ($continuousBuffer && strlen($block) != $block_size) {
|
852 |
+
$buffer['ciphertext'].= $block;
|
853 |
+
$block = $xor;
|
854 |
+
} else if (strlen($block) == $block_size) {
|
855 |
+
$xor = $this->_encryptBlock($block);
|
856 |
+
}
|
857 |
+
}
|
858 |
+
if ($this->continuousBuffer) {
|
859 |
+
$this->decryptIV = $block;
|
860 |
+
}
|
861 |
+
break;
|
862 |
+
case CRYPT_RIJNDAEL_MODE_OFB:
|
863 |
+
$xor = $this->decryptIV;
|
864 |
+
if (strlen($buffer)) {
|
865 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
866 |
+
$xor = $this->_encryptBlock($xor);
|
867 |
+
$buffer.= $xor;
|
868 |
+
$key = $this->_string_shift($buffer, $block_size);
|
869 |
+
$plaintext.= substr($ciphertext, $i, $block_size) ^ $key;
|
870 |
+
}
|
871 |
+
} else {
|
872 |
+
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
|
873 |
+
$xor = $this->_encryptBlock($xor);
|
874 |
+
$plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
|
875 |
+
}
|
876 |
+
$key = $xor;
|
877 |
+
}
|
878 |
+
if ($this->continuousBuffer) {
|
879 |
+
$this->decryptIV = $xor;
|
880 |
+
if ($start = strlen($ciphertext) % $block_size) {
|
881 |
+
$buffer = substr($key, $start) . $buffer;
|
882 |
+
}
|
883 |
+
}
|
884 |
+
}
|
885 |
+
|
886 |
+
return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
|
887 |
+
}
|
888 |
+
|
889 |
+
/**
|
890 |
+
* Encrypts a block
|
891 |
+
*
|
892 |
+
* @access private
|
893 |
+
* @param String $in
|
894 |
+
* @return String
|
895 |
+
*/
|
896 |
+
function _encryptBlock($in)
|
897 |
+
{
|
898 |
+
$state = array();
|
899 |
+
$words = unpack('N*word', $in);
|
900 |
+
|
901 |
+
$w = $this->w;
|
902 |
+
$t0 = $this->t0;
|
903 |
+
$t1 = $this->t1;
|
904 |
+
$t2 = $this->t2;
|
905 |
+
$t3 = $this->t3;
|
906 |
+
$Nb = $this->Nb;
|
907 |
+
$Nr = $this->Nr;
|
908 |
+
$c = $this->c;
|
909 |
+
|
910 |
+
// addRoundKey
|
911 |
+
$i = 0;
|
912 |
+
foreach ($words as $word) {
|
913 |
+
$state[] = $word ^ $w[0][$i++];
|
914 |
+
}
|
915 |
+
|
916 |
+
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
|
917 |
+
// subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
|
918 |
+
// Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
|
919 |
+
// Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
|
920 |
+
// Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
|
921 |
+
// equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
|
922 |
+
|
923 |
+
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
|
924 |
+
$temp = array();
|
925 |
+
for ($round = 1; $round < $Nr; $round++) {
|
926 |
+
$i = 0; // $c[0] == 0
|
927 |
+
$j = $c[1];
|
928 |
+
$k = $c[2];
|
929 |
+
$l = $c[3];
|
930 |
+
|
931 |
+
while ($i < $this->Nb) {
|
932 |
+
$temp[$i] = $t0[$state[$i] & 0xFF000000] ^
|
933 |
+
$t1[$state[$j] & 0x00FF0000] ^
|
934 |
+
$t2[$state[$k] & 0x0000FF00] ^
|
935 |
+
$t3[$state[$l] & 0x000000FF] ^
|
936 |
+
$w[$round][$i];
|
937 |
+
$i++;
|
938 |
+
$j = ($j + 1) % $Nb;
|
939 |
+
$k = ($k + 1) % $Nb;
|
940 |
+
$l = ($l + 1) % $Nb;
|
941 |
+
}
|
942 |
+
|
943 |
+
for ($i = 0; $i < $Nb; $i++) {
|
944 |
+
$state[$i] = $temp[$i];
|
945 |
+
}
|
946 |
+
}
|
947 |
+
|
948 |
+
// subWord
|
949 |
+
for ($i = 0; $i < $Nb; $i++) {
|
950 |
+
$state[$i] = $this->_subWord($state[$i]);
|
951 |
+
}
|
952 |
+
|
953 |
+
// shiftRows + addRoundKey
|
954 |
+
$i = 0; // $c[0] == 0
|
955 |
+
$j = $c[1];
|
956 |
+
$k = $c[2];
|
957 |
+
$l = $c[3];
|
958 |
+
while ($i < $this->Nb) {
|
959 |
+
$temp[$i] = ($state[$i] & 0xFF000000) ^
|
960 |
+
($state[$j] & 0x00FF0000) ^
|
961 |
+
($state[$k] & 0x0000FF00) ^
|
962 |
+
($state[$l] & 0x000000FF) ^
|
963 |
+
$w[$Nr][$i];
|
964 |
+
$i++;
|
965 |
+
$j = ($j + 1) % $Nb;
|
966 |
+
$k = ($k + 1) % $Nb;
|
967 |
+
$l = ($l + 1) % $Nb;
|
968 |
+
}
|
969 |
+
$state = $temp;
|
970 |
+
|
971 |
+
array_unshift($state, 'N*');
|
972 |
+
|
973 |
+
return call_user_func_array('pack', $state);
|
974 |
+
}
|
975 |
+
|
976 |
+
/**
|
977 |
+
* Decrypts a block
|
978 |
+
*
|
979 |
+
* @access private
|
980 |
+
* @param String $in
|
981 |
+
* @return String
|
982 |
+
*/
|
983 |
+
function _decryptBlock($in)
|
984 |
+
{
|
985 |
+
$state = array();
|
986 |
+
$words = unpack('N*word', $in);
|
987 |
+
|
988 |
+
$num_states = count($state);
|
989 |
+
$dw = $this->dw;
|
990 |
+
$dt0 = $this->dt0;
|
991 |
+
$dt1 = $this->dt1;
|
992 |
+
$dt2 = $this->dt2;
|
993 |
+
$dt3 = $this->dt3;
|
994 |
+
$Nb = $this->Nb;
|
995 |
+
$Nr = $this->Nr;
|
996 |
+
$c = $this->c;
|
997 |
+
|
998 |
+
// addRoundKey
|
999 |
+
$i = 0;
|
1000 |
+
foreach ($words as $word) {
|
1001 |
+
$state[] = $word ^ $dw[$Nr][$i++];
|
1002 |
+
}
|
1003 |
+
|
1004 |
+
$temp = array();
|
1005 |
+
for ($round = $Nr - 1; $round > 0; $round--) {
|
1006 |
+
$i = 0; // $c[0] == 0
|
1007 |
+
$j = $Nb - $c[1];
|
1008 |
+
$k = $Nb - $c[2];
|
1009 |
+
$l = $Nb - $c[3];
|
1010 |
+
|
1011 |
+
while ($i < $Nb) {
|
1012 |
+
$temp[$i] = $dt0[$state[$i] & 0xFF000000] ^
|
1013 |
+
$dt1[$state[$j] & 0x00FF0000] ^
|
1014 |
+
$dt2[$state[$k] & 0x0000FF00] ^
|
1015 |
+
$dt3[$state[$l] & 0x000000FF] ^
|
1016 |
+
$dw[$round][$i];
|
1017 |
+
$i++;
|
1018 |
+
$j = ($j + 1) % $Nb;
|
1019 |
+
$k = ($k + 1) % $Nb;
|
1020 |
+
$l = ($l + 1) % $Nb;
|
1021 |
+
}
|
1022 |
+
|
1023 |
+
for ($i = 0; $i < $Nb; $i++) {
|
1024 |
+
$state[$i] = $temp[$i];
|
1025 |
+
}
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
// invShiftRows + invSubWord + addRoundKey
|
1029 |
+
$i = 0; // $c[0] == 0
|
1030 |
+
$j = $Nb - $c[1];
|
1031 |
+
$k = $Nb - $c[2];
|
1032 |
+
$l = $Nb - $c[3];
|
1033 |
+
|
1034 |
+
while ($i < $Nb) {
|
1035 |
+
$temp[$i] = $dw[0][$i] ^
|
1036 |
+
$this->_invSubWord(($state[$i] & 0xFF000000) |
|
1037 |
+
($state[$j] & 0x00FF0000) |
|
1038 |
+
($state[$k] & 0x0000FF00) |
|
1039 |
+
($state[$l] & 0x000000FF));
|
1040 |
+
$i++;
|
1041 |
+
$j = ($j + 1) % $Nb;
|
1042 |
+
$k = ($k + 1) % $Nb;
|
1043 |
+
$l = ($l + 1) % $Nb;
|
1044 |
+
}
|
1045 |
+
|
1046 |
+
$state = $temp;
|
1047 |
+
|
1048 |
+
array_unshift($state, 'N*');
|
1049 |
+
|
1050 |
+
return call_user_func_array('pack', $state);
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
/**
|
1054 |
+
* Setup Rijndael
|
1055 |
+
*
|
1056 |
+
* Validates all the variables and calculates $Nr - the number of rounds that need to be performed - and $w - the key
|
1057 |
+
* key schedule.
|
1058 |
+
*
|
1059 |
+
* @access private
|
1060 |
+
*/
|
1061 |
+
function _setup()
|
1062 |
+
{
|
1063 |
+
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
|
1064 |
+
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
|
1065 |
+
static $rcon = array(0,
|
1066 |
+
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
|
1067 |
+
0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
|
1068 |
+
0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
|
1069 |
+
0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
|
1070 |
+
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
|
1071 |
+
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
|
1072 |
+
);
|
1073 |
+
|
1074 |
+
if (!$this->changed) {
|
1075 |
+
return;
|
1076 |
+
}
|
1077 |
+
|
1078 |
+
if (!$this->explicit_key_length) {
|
1079 |
+
// we do >> 2, here, and not >> 5, as we do above, since strlen($this->key) tells us the number of bytes - not bits
|
1080 |
+
$length = strlen($this->key) >> 2;
|
1081 |
+
if ($length > 8) {
|
1082 |
+
$length = 8;
|
1083 |
+
} else if ($length < 4) {
|
1084 |
+
$length = 4;
|
1085 |
+
}
|
1086 |
+
$this->Nk = $length;
|
1087 |
+
$this->key_size = $length << 2;
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
$this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
|
1091 |
+
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, chr(0));
|
1092 |
+
|
1093 |
+
// see Rijndael-ammended.pdf#page=44
|
1094 |
+
$this->Nr = max($this->Nk, $this->Nb) + 6;
|
1095 |
+
|
1096 |
+
// shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
|
1097 |
+
// "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
|
1098 |
+
// shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
|
1099 |
+
// "Table 2: Shift offsets for different block lengths"
|
1100 |
+
switch ($this->Nb) {
|
1101 |
+
case 4:
|
1102 |
+
case 5:
|
1103 |
+
case 6:
|
1104 |
+
$this->c = array(0, 1, 2, 3);
|
1105 |
+
break;
|
1106 |
+
case 7:
|
1107 |
+
$this->c = array(0, 1, 2, 4);
|
1108 |
+
break;
|
1109 |
+
case 8:
|
1110 |
+
$this->c = array(0, 1, 3, 4);
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
$key = $this->key;
|
1114 |
+
|
1115 |
+
$w = array_values(unpack('N*words', $key));
|
1116 |
+
|
1117 |
+
$length = $this->Nb * ($this->Nr + 1);
|
1118 |
+
for ($i = $this->Nk; $i < $length; $i++) {
|
1119 |
+
$temp = $w[$i - 1];
|
1120 |
+
if ($i % $this->Nk == 0) {
|
1121 |
+
// according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
|
1122 |
+
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
|
1123 |
+
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
|
1124 |
+
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
|
1125 |
+
$temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
|
1126 |
+
$temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
|
1127 |
+
} else if ($this->Nk > 6 && $i % $this->Nk == 4) {
|
1128 |
+
$temp = $this->_subWord($temp);
|
1129 |
+
}
|
1130 |
+
$w[$i] = $w[$i - $this->Nk] ^ $temp;
|
1131 |
+
}
|
1132 |
+
|
1133 |
+
// convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
|
1134 |
+
// and generate the inverse key schedule. more specifically,
|
1135 |
+
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
|
1136 |
+
// "The key expansion for the Inverse Cipher is defined as follows:
|
1137 |
+
// 1. Apply the Key Expansion.
|
1138 |
+
// 2. Apply InvMixColumn to all Round Keys except the first and the last one."
|
1139 |
+
// also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
|
1140 |
+
$temp = array();
|
1141 |
+
for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
|
1142 |
+
if ($col == $this->Nb) {
|
1143 |
+
if ($row == 0) {
|
1144 |
+
$this->dw[0] = $this->w[0];
|
1145 |
+
} else {
|
1146 |
+
// subWord + invMixColumn + invSubWord = invMixColumn
|
1147 |
+
$j = 0;
|
1148 |
+
while ($j < $this->Nb) {
|
1149 |
+
$dw = $this->_subWord($this->w[$row][$j]);
|
1150 |
+
$temp[$j] = $this->dt0[$dw & 0xFF000000] ^
|
1151 |
+
$this->dt1[$dw & 0x00FF0000] ^
|
1152 |
+
$this->dt2[$dw & 0x0000FF00] ^
|
1153 |
+
$this->dt3[$dw & 0x000000FF];
|
1154 |
+
$j++;
|
1155 |
+
}
|
1156 |
+
$this->dw[$row] = $temp;
|
1157 |
+
}
|
1158 |
+
|
1159 |
+
$col = 0;
|
1160 |
+
$row++;
|
1161 |
+
}
|
1162 |
+
$this->w[$row][$col] = $w[$i];
|
1163 |
+
}
|
1164 |
+
|
1165 |
+
$this->dw[$row] = $this->w[$row];
|
1166 |
+
|
1167 |
+
$this->changed = false;
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
/**
|
1171 |
+
* Performs S-Box substitutions
|
1172 |
+
*
|
1173 |
+
* @access private
|
1174 |
+
*/
|
1175 |
+
function _subWord($word)
|
1176 |
+
{
|
1177 |
+
static $sbox0, $sbox1, $sbox2, $sbox3;
|
1178 |
+
|
1179 |
+
if (empty($sbox0)) {
|
1180 |
+
$sbox0 = array(
|
1181 |
+
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
1182 |
+
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
1183 |
+
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
1184 |
+
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
1185 |
+
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
1186 |
+
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
1187 |
+
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
1188 |
+
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
1189 |
+
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
1190 |
+
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
1191 |
+
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
1192 |
+
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
1193 |
+
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
1194 |
+
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
1195 |
+
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
1196 |
+
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
|
1197 |
+
);
|
1198 |
+
|
1199 |
+
$sbox1 = array();
|
1200 |
+
$sbox2 = array();
|
1201 |
+
$sbox3 = array();
|
1202 |
+
|
1203 |
+
for ($i = 0; $i < 256; $i++) {
|
1204 |
+
$sbox1[$i << 8] = $sbox0[$i] << 8;
|
1205 |
+
$sbox2[$i << 16] = $sbox0[$i] << 16;
|
1206 |
+
$sbox3[$i << 24] = $sbox0[$i] << 24;
|
1207 |
+
}
|
1208 |
+
}
|
1209 |
+
|
1210 |
+
return $sbox0[$word & 0x000000FF] |
|
1211 |
+
$sbox1[$word & 0x0000FF00] |
|
1212 |
+
$sbox2[$word & 0x00FF0000] |
|
1213 |
+
$sbox3[$word & 0xFF000000];
|
1214 |
+
}
|
1215 |
+
|
1216 |
+
/**
|
1217 |
+
* Performs inverse S-Box substitutions
|
1218 |
+
*
|
1219 |
+
* @access private
|
1220 |
+
*/
|
1221 |
+
function _invSubWord($word)
|
1222 |
+
{
|
1223 |
+
static $sbox0, $sbox1, $sbox2, $sbox3;
|
1224 |
+
|
1225 |
+
if (empty($sbox0)) {
|
1226 |
+
$sbox0 = array(
|
1227 |
+
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
1228 |
+
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
1229 |
+
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
1230 |
+
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
1231 |
+
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
1232 |
+
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
1233 |
+
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
1234 |
+
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
1235 |
+
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
1236 |
+
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
1237 |
+
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
1238 |
+
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
1239 |
+
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
1240 |
+
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
1241 |
+
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
1242 |
+
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
|
1243 |
+
);
|
1244 |
+
|
1245 |
+
$sbox1 = array();
|
1246 |
+
$sbox2 = array();
|
1247 |
+
$sbox3 = array();
|
1248 |
+
|
1249 |
+
for ($i = 0; $i < 256; $i++) {
|
1250 |
+
$sbox1[$i << 8] = $sbox0[$i] << 8;
|
1251 |
+
$sbox2[$i << 16] = $sbox0[$i] << 16;
|
1252 |
+
$sbox3[$i << 24] = $sbox0[$i] << 24;
|
1253 |
+
}
|
1254 |
+
}
|
1255 |
+
|
1256 |
+
return $sbox0[$word & 0x000000FF] |
|
1257 |
+
$sbox1[$word & 0x0000FF00] |
|
1258 |
+
$sbox2[$word & 0x00FF0000] |
|
1259 |
+
$sbox3[$word & 0xFF000000];
|
1260 |
+
}
|
1261 |
+
|
1262 |
+
/**
|
1263 |
+
* Pad "packets".
|
1264 |
+
*
|
1265 |
+
* Rijndael works by encrypting between sixteen and thirty-two bytes at a time, provided that number is also a multiple
|
1266 |
+
* of four. If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
|
1267 |
+
* pad the input so that it is of the proper length.
|
1268 |
+
*
|
1269 |
+
* Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH,
|
1270 |
+
* where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
|
1271 |
+
* away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
|
1272 |
+
* transmitted separately)
|
1273 |
+
*
|
1274 |
+
* @see Crypt_Rijndael::disablePadding()
|
1275 |
+
* @access public
|
1276 |
+
*/
|
1277 |
+
function enablePadding()
|
1278 |
+
{
|
1279 |
+
$this->padding = true;
|
1280 |
+
}
|
1281 |
+
|
1282 |
+
/**
|
1283 |
+
* Do not pad packets.
|
1284 |
+
*
|
1285 |
+
* @see Crypt_Rijndael::enablePadding()
|
1286 |
+
* @access public
|
1287 |
+
*/
|
1288 |
+
function disablePadding()
|
1289 |
+
{
|
1290 |
+
$this->padding = false;
|
1291 |
+
}
|
1292 |
+
|
1293 |
+
/**
|
1294 |
+
* Pads a string
|
1295 |
+
*
|
1296 |
+
* Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
|
1297 |
+
* $block_size - (strlen($text) % $block_size) bytes are added, each of which is equal to
|
1298 |
+
* chr($block_size - (strlen($text) % $block_size)
|
1299 |
+
*
|
1300 |
+
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
|
1301 |
+
* and padding will, hence forth, be enabled.
|
1302 |
+
*
|
1303 |
+
* @see Crypt_Rijndael::_unpad()
|
1304 |
+
* @access private
|
1305 |
+
*/
|
1306 |
+
function _pad($text)
|
1307 |
+
{
|
1308 |
+
$length = strlen($text);
|
1309 |
+
|
1310 |
+
if (!$this->padding) {
|
1311 |
+
if ($length % $this->block_size == 0) {
|
1312 |
+
return $text;
|
1313 |
+
} else {
|
1314 |
+
user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})", E_USER_NOTICE);
|
1315 |
+
$this->padding = true;
|
1316 |
+
}
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
$pad = $this->block_size - ($length % $this->block_size);
|
1320 |
+
|
1321 |
+
return str_pad($text, $length + $pad, chr($pad));
|
1322 |
+
}
|
1323 |
+
|
1324 |
+
/**
|
1325 |
+
* Unpads a string.
|
1326 |
+
*
|
1327 |
+
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
|
1328 |
+
* and false will be returned.
|
1329 |
+
*
|
1330 |
+
* @see Crypt_Rijndael::_pad()
|
1331 |
+
* @access private
|
1332 |
+
*/
|
1333 |
+
function _unpad($text)
|
1334 |
+
{
|
1335 |
+
if (!$this->padding) {
|
1336 |
+
return $text;
|
1337 |
+
}
|
1338 |
+
|
1339 |
+
$length = ord($text[strlen($text) - 1]);
|
1340 |
+
|
1341 |
+
if (!$length || $length > $this->block_size) {
|
1342 |
+
return false;
|
1343 |
+
}
|
1344 |
+
|
1345 |
+
return substr($text, 0, -$length);
|
1346 |
+
}
|
1347 |
+
|
1348 |
+
/**
|
1349 |
+
* Treat consecutive "packets" as if they are a continuous buffer.
|
1350 |
+
*
|
1351 |
+
* Say you have a 32-byte plaintext $plaintext. Using the default behavior, the two following code snippets
|
1352 |
+
* will yield different outputs:
|
1353 |
+
*
|
1354 |
+
* <code>
|
1355 |
+
* echo $rijndael->encrypt(substr($plaintext, 0, 16));
|
1356 |
+
* echo $rijndael->encrypt(substr($plaintext, 16, 16));
|
1357 |
+
* </code>
|
1358 |
+
* <code>
|
1359 |
+
* echo $rijndael->encrypt($plaintext);
|
1360 |
+
* </code>
|
1361 |
+
*
|
1362 |
+
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
|
1363 |
+
* another, as demonstrated with the following:
|
1364 |
+
*
|
1365 |
+
* <code>
|
1366 |
+
* $rijndael->encrypt(substr($plaintext, 0, 16));
|
1367 |
+
* echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
|
1368 |
+
* </code>
|
1369 |
+
* <code>
|
1370 |
+
* echo $rijndael->decrypt($des->encrypt(substr($plaintext, 16, 16)));
|
1371 |
+
* </code>
|
1372 |
+
*
|
1373 |
+
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
|
1374 |
+
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
|
1375 |
+
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
|
1376 |
+
*
|
1377 |
+
* Put another way, when the continuous buffer is enabled, the state of the Crypt_Rijndael() object changes after each
|
1378 |
+
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
|
1379 |
+
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
|
1380 |
+
* however, they are also less intuitive and more likely to cause you problems.
|
1381 |
+
*
|
1382 |
+
* @see Crypt_Rijndael::disableContinuousBuffer()
|
1383 |
+
* @access public
|
1384 |
+
*/
|
1385 |
+
function enableContinuousBuffer()
|
1386 |
+
{
|
1387 |
+
$this->continuousBuffer = true;
|
1388 |
+
}
|
1389 |
+
|
1390 |
+
/**
|
1391 |
+
* Treat consecutive packets as if they are a discontinuous buffer.
|
1392 |
+
*
|
1393 |
+
* The default behavior.
|
1394 |
+
*
|
1395 |
+
* @see Crypt_Rijndael::enableContinuousBuffer()
|
1396 |
+
* @access public
|
1397 |
+
*/
|
1398 |
+
function disableContinuousBuffer()
|
1399 |
+
{
|
1400 |
+
$this->continuousBuffer = false;
|
1401 |
+
$this->encryptIV = $this->iv;
|
1402 |
+
$this->decryptIV = $this->iv;
|
1403 |
+
}
|
1404 |
+
|
1405 |
+
/**
|
1406 |
+
* String Shift
|
1407 |
+
*
|
1408 |
+
* Inspired by array_shift
|
1409 |
+
*
|
1410 |
+
* @param String $string
|
1411 |
+
* @param optional Integer $index
|
1412 |
+
* @return String
|
1413 |
+
* @access private
|
1414 |
+
*/
|
1415 |
+
function _string_shift(&$string, $index = 1)
|
1416 |
+
{
|
1417 |
+
$substr = substr($string, 0, $index);
|
1418 |
+
$string = substr($string, $index);
|
1419 |
+
return $substr;
|
1420 |
+
}
|
1421 |
+
}
|
1422 |
+
|
1423 |
+
// vim: ts=4:sw=4:et:
|
1424 |
+
// vim6: fdl=1:
|
includes/S3.php
ADDED
@@ -0,0 +1,1365 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* $Id: S3.php 47 2009-07-20 01:25:40Z don.schonknecht $
|
4 |
+
*
|
5 |
+
* Copyright (c) 2008, 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:
|
9 |
+
*
|
10 |
+
* - Redistributions of source code must retain the above copyright notice,
|
11 |
+
* this list of conditions and the following disclaimer.
|
12 |
+
* - Redistributions in binary form must reproduce the above copyright
|
13 |
+
* notice, this list of conditions and the following disclaimer in the
|
14 |
+
* documentation and/or other materials provided with the distribution.
|
15 |
+
*
|
16 |
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17 |
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18 |
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19 |
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
20 |
+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
21 |
+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
22 |
+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
23 |
+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
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.4.0
|
36 |
+
*/
|
37 |
+
class S3 {
|
38 |
+
// ACL flags
|
39 |
+
const ACL_PRIVATE = 'private';
|
40 |
+
const ACL_PUBLIC_READ = 'public-read';
|
41 |
+
const ACL_PUBLIC_READ_WRITE = 'public-read-write';
|
42 |
+
const ACL_AUTHENTICATED_READ = 'authenticated-read';
|
43 |
+
|
44 |
+
public static $useSSL = true;
|
45 |
+
|
46 |
+
private static $__accessKey; // AWS Access key
|
47 |
+
private static $__secretKey; // AWS Secret key
|
48 |
+
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Constructor - if you're not using the class statically
|
52 |
+
*
|
53 |
+
* @param string $accessKey Access key
|
54 |
+
* @param string $secretKey Secret key
|
55 |
+
* @param boolean $useSSL Enable SSL
|
56 |
+
* @return void
|
57 |
+
*/
|
58 |
+
public function __construct($accessKey = null, $secretKey = null, $useSSL = true) {
|
59 |
+
if ($accessKey !== null && $secretKey !== null)
|
60 |
+
self::setAuth($accessKey, $secretKey);
|
61 |
+
self::$useSSL = $useSSL;
|
62 |
+
}
|
63 |
+
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Set AWS access key and secret key
|
67 |
+
*
|
68 |
+
* @param string $accessKey Access key
|
69 |
+
* @param string $secretKey Secret key
|
70 |
+
* @return void
|
71 |
+
*/
|
72 |
+
public static function setAuth($accessKey, $secretKey) {
|
73 |
+
self::$__accessKey = $accessKey;
|
74 |
+
self::$__secretKey = $secretKey;
|
75 |
+
}
|
76 |
+
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Get a list of buckets
|
80 |
+
*
|
81 |
+
* @param boolean $detailed Returns detailed bucket list when true
|
82 |
+
* @return array | false
|
83 |
+
*/
|
84 |
+
public static function listBuckets($detailed = false) {
|
85 |
+
$rest = new S3Request('GET', '', '');
|
86 |
+
$rest = $rest->getResponse();
|
87 |
+
if ($rest->error === false && $rest->code !== 200)
|
88 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
89 |
+
if ($rest->error !== false) {
|
90 |
+
trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
91 |
+
return false;
|
92 |
+
}
|
93 |
+
$results = array();
|
94 |
+
if (!isset($rest->body->Buckets)) return $results;
|
95 |
+
|
96 |
+
if ($detailed) {
|
97 |
+
if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
|
98 |
+
$results['owner'] = array(
|
99 |
+
'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID
|
100 |
+
);
|
101 |
+
$results['buckets'] = array();
|
102 |
+
foreach ($rest->body->Buckets->Bucket as $b)
|
103 |
+
$results['buckets'][] = array(
|
104 |
+
'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate)
|
105 |
+
);
|
106 |
+
} else
|
107 |
+
foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name;
|
108 |
+
|
109 |
+
return $results;
|
110 |
+
}
|
111 |
+
|
112 |
+
|
113 |
+
/*
|
114 |
+
* Get contents for a bucket
|
115 |
+
*
|
116 |
+
* If maxKeys is null this method will loop through truncated result sets
|
117 |
+
*
|
118 |
+
* @param string $bucket Bucket name
|
119 |
+
* @param string $prefix Prefix
|
120 |
+
* @param string $marker Marker (last file listed)
|
121 |
+
* @param string $maxKeys Max keys (maximum number of keys to return)
|
122 |
+
* @param string $delimiter Delimiter
|
123 |
+
* @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes
|
124 |
+
* @return array | false
|
125 |
+
*/
|
126 |
+
public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) {
|
127 |
+
$rest = new S3Request('GET', $bucket, '');
|
128 |
+
if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
|
129 |
+
if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker);
|
130 |
+
if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys);
|
131 |
+
if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
|
132 |
+
$response = $rest->getResponse();
|
133 |
+
if ($response->error === false && $response->code !== 200)
|
134 |
+
$response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
|
135 |
+
if ($response->error !== false) {
|
136 |
+
trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING);
|
137 |
+
return false;
|
138 |
+
}
|
139 |
+
|
140 |
+
$results = array();
|
141 |
+
|
142 |
+
$nextMarker = null;
|
143 |
+
if (isset($response->body, $response->body->Contents))
|
144 |
+
foreach ($response->body->Contents as $c) {
|
145 |
+
$results[(string)$c->Key] = array(
|
146 |
+
'name' => (string)$c->Key,
|
147 |
+
'time' => strtotime((string)$c->LastModified),
|
148 |
+
'size' => (int)$c->Size,
|
149 |
+
'hash' => substr((string)$c->ETag, 1, -1)
|
150 |
+
);
|
151 |
+
$nextMarker = (string)$c->Key;
|
152 |
+
}
|
153 |
+
|
154 |
+
if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
|
155 |
+
foreach ($response->body->CommonPrefixes as $c)
|
156 |
+
$results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
|
157 |
+
|
158 |
+
if (isset($response->body, $response->body->IsTruncated) &&
|
159 |
+
(string)$response->body->IsTruncated == 'false') return $results;
|
160 |
+
|
161 |
+
if (isset($response->body, $response->body->NextMarker))
|
162 |
+
$nextMarker = (string)$response->body->NextMarker;
|
163 |
+
|
164 |
+
// Loop through truncated results if maxKeys isn't specified
|
165 |
+
if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true')
|
166 |
+
do {
|
167 |
+
$rest = new S3Request('GET', $bucket, '');
|
168 |
+
if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
|
169 |
+
$rest->setParameter('marker', $nextMarker);
|
170 |
+
if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter);
|
171 |
+
|
172 |
+
if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break;
|
173 |
+
|
174 |
+
if (isset($response->body, $response->body->Contents))
|
175 |
+
foreach ($response->body->Contents as $c) {
|
176 |
+
$results[(string)$c->Key] = array(
|
177 |
+
'name' => (string)$c->Key,
|
178 |
+
'time' => strtotime((string)$c->LastModified),
|
179 |
+
'size' => (int)$c->Size,
|
180 |
+
'hash' => substr((string)$c->ETag, 1, -1)
|
181 |
+
);
|
182 |
+
$nextMarker = (string)$c->Key;
|
183 |
+
}
|
184 |
+
|
185 |
+
if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
|
186 |
+
foreach ($response->body->CommonPrefixes as $c)
|
187 |
+
$results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
|
188 |
+
|
189 |
+
if (isset($response->body, $response->body->NextMarker))
|
190 |
+
$nextMarker = (string)$response->body->NextMarker;
|
191 |
+
|
192 |
+
} while ($response !== false && (string)$response->body->IsTruncated == 'true');
|
193 |
+
|
194 |
+
return $results;
|
195 |
+
}
|
196 |
+
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Put a bucket
|
200 |
+
*
|
201 |
+
* @param string $bucket Bucket name
|
202 |
+
* @param constant $acl ACL flag
|
203 |
+
* @param string $location Set as "EU" to create buckets hosted in Europe
|
204 |
+
* @return boolean
|
205 |
+
*/
|
206 |
+
public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) {
|
207 |
+
$rest = new S3Request('PUT', $bucket, '');
|
208 |
+
$rest->setAmzHeader('x-amz-acl', $acl);
|
209 |
+
|
210 |
+
if ($location !== false) {
|
211 |
+
$dom = new DOMDocument;
|
212 |
+
$createBucketConfiguration = $dom->createElement('CreateBucketConfiguration');
|
213 |
+
$locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location));
|
214 |
+
$createBucketConfiguration->appendChild($locationConstraint);
|
215 |
+
$dom->appendChild($createBucketConfiguration);
|
216 |
+
$rest->data = $dom->saveXML();
|
217 |
+
$rest->size = strlen($rest->data);
|
218 |
+
$rest->setHeader('Content-Type', 'application/xml');
|
219 |
+
}
|
220 |
+
$rest = $rest->getResponse();
|
221 |
+
|
222 |
+
if ($rest->error === false && $rest->code !== 200)
|
223 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
224 |
+
if ($rest->error !== false) {
|
225 |
+
trigger_error(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s",
|
226 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
227 |
+
return false;
|
228 |
+
}
|
229 |
+
return true;
|
230 |
+
}
|
231 |
+
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Delete an empty bucket
|
235 |
+
*
|
236 |
+
* @param string $bucket Bucket name
|
237 |
+
* @return boolean
|
238 |
+
*/
|
239 |
+
public static function deleteBucket($bucket) {
|
240 |
+
$rest = new S3Request('DELETE', $bucket);
|
241 |
+
$rest = $rest->getResponse();
|
242 |
+
if ($rest->error === false && $rest->code !== 204)
|
243 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
244 |
+
if ($rest->error !== false) {
|
245 |
+
trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s",
|
246 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
247 |
+
return false;
|
248 |
+
}
|
249 |
+
return true;
|
250 |
+
}
|
251 |
+
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Create input info array for putObject()
|
255 |
+
*
|
256 |
+
* @param string $file Input file
|
257 |
+
* @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
|
258 |
+
* @return array | false
|
259 |
+
*/
|
260 |
+
public static function inputFile($file, $md5sum = true) {
|
261 |
+
if (!file_exists($file) || !is_file($file) || !is_readable($file)) {
|
262 |
+
trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING);
|
263 |
+
return false;
|
264 |
+
}
|
265 |
+
return array('file' => $file, 'size' => filesize($file),
|
266 |
+
'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum :
|
267 |
+
base64_encode(md5_file($file, true))) : '');
|
268 |
+
}
|
269 |
+
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Create input array info for putObject() with a resource
|
273 |
+
*
|
274 |
+
* @param string $resource Input resource to read from
|
275 |
+
* @param integer $bufferSize Input byte size
|
276 |
+
* @param string $md5sum MD5 hash to send (optional)
|
277 |
+
* @return array | false
|
278 |
+
*/
|
279 |
+
public static function inputResource(&$resource, $bufferSize, $md5sum = '') {
|
280 |
+
if (!is_resource($resource) || $bufferSize < 0) {
|
281 |
+
trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING);
|
282 |
+
return false;
|
283 |
+
}
|
284 |
+
$input = array('size' => $bufferSize, 'md5sum' => $md5sum);
|
285 |
+
$input['fp'] =& $resource;
|
286 |
+
return $input;
|
287 |
+
}
|
288 |
+
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Put an object
|
292 |
+
*
|
293 |
+
* @param mixed $input Input data
|
294 |
+
* @param string $bucket Bucket name
|
295 |
+
* @param string $uri Object URI
|
296 |
+
* @param constant $acl ACL constant
|
297 |
+
* @param array $metaHeaders Array of x-amz-meta-* headers
|
298 |
+
* @param array $requestHeaders Array of request headers or content type as a string
|
299 |
+
* @return boolean
|
300 |
+
*/
|
301 |
+
public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) {
|
302 |
+
if ($input === false) return false;
|
303 |
+
$rest = new S3Request('PUT', $bucket, $uri);
|
304 |
+
|
305 |
+
if (is_string($input)) $input = array(
|
306 |
+
'data' => $input, 'size' => strlen($input),
|
307 |
+
'md5sum' => base64_encode(md5($input, true))
|
308 |
+
);
|
309 |
+
|
310 |
+
// Data
|
311 |
+
if (isset($input['fp']))
|
312 |
+
$rest->fp =& $input['fp'];
|
313 |
+
elseif (isset($input['file']))
|
314 |
+
$rest->fp = @fopen($input['file'], 'rb');
|
315 |
+
elseif (isset($input['data']))
|
316 |
+
$rest->data = $input['data'];
|
317 |
+
|
318 |
+
// Content-Length (required)
|
319 |
+
if (isset($input['size']) && $input['size'] >= 0)
|
320 |
+
$rest->size = $input['size'];
|
321 |
+
else {
|
322 |
+
if (isset($input['file']))
|
323 |
+
$rest->size = filesize($input['file']);
|
324 |
+
elseif (isset($input['data']))
|
325 |
+
$rest->size = strlen($input['data']);
|
326 |
+
}
|
327 |
+
|
328 |
+
// Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
|
329 |
+
if (is_array($requestHeaders))
|
330 |
+
foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
|
331 |
+
elseif (is_string($requestHeaders)) // Support for legacy contentType parameter
|
332 |
+
$input['type'] = $requestHeaders;
|
333 |
+
|
334 |
+
// Content-Type
|
335 |
+
if (!isset($input['type'])) {
|
336 |
+
if (isset($requestHeaders['Content-Type']))
|
337 |
+
$input['type'] =& $requestHeaders['Content-Type'];
|
338 |
+
elseif (isset($input['file']))
|
339 |
+
$input['type'] = self::__getMimeType($input['file']);
|
340 |
+
else
|
341 |
+
$input['type'] = 'application/octet-stream';
|
342 |
+
}
|
343 |
+
|
344 |
+
// We need to post with Content-Length and Content-Type, MD5 is optional
|
345 |
+
if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) {
|
346 |
+
$rest->setHeader('Content-Type', $input['type']);
|
347 |
+
if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);
|
348 |
+
|
349 |
+
$rest->setAmzHeader('x-amz-acl', $acl);
|
350 |
+
foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
|
351 |
+
$rest->getResponse();
|
352 |
+
} else
|
353 |
+
$rest->response->error = array('code' => 0, 'message' => 'Missing input parameters');
|
354 |
+
|
355 |
+
if ($rest->response->error === false && $rest->response->code !== 200)
|
356 |
+
$rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
|
357 |
+
if ($rest->response->error !== false) {
|
358 |
+
trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
|
359 |
+
return false;
|
360 |
+
}
|
361 |
+
return true;
|
362 |
+
}
|
363 |
+
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Put an object from a file (legacy function)
|
367 |
+
*
|
368 |
+
* @param string $file Input file path
|
369 |
+
* @param string $bucket Bucket name
|
370 |
+
* @param string $uri Object URI
|
371 |
+
* @param constant $acl ACL constant
|
372 |
+
* @param array $metaHeaders Array of x-amz-meta-* headers
|
373 |
+
* @param string $contentType Content type
|
374 |
+
* @return boolean
|
375 |
+
*/
|
376 |
+
public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) {
|
377 |
+
return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType);
|
378 |
+
}
|
379 |
+
|
380 |
+
|
381 |
+
/**
|
382 |
+
* Put an object from a string (legacy function)
|
383 |
+
*
|
384 |
+
* @param string $string Input data
|
385 |
+
* @param string $bucket Bucket name
|
386 |
+
* @param string $uri Object URI
|
387 |
+
* @param constant $acl ACL constant
|
388 |
+
* @param array $metaHeaders Array of x-amz-meta-* headers
|
389 |
+
* @param string $contentType Content type
|
390 |
+
* @return boolean
|
391 |
+
*/
|
392 |
+
public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') {
|
393 |
+
return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
|
394 |
+
}
|
395 |
+
|
396 |
+
|
397 |
+
/**
|
398 |
+
* Get an object
|
399 |
+
*
|
400 |
+
* @param string $bucket Bucket name
|
401 |
+
* @param string $uri Object URI
|
402 |
+
* @param mixed $saveTo Filename or resource to write to
|
403 |
+
* @return mixed
|
404 |
+
*/
|
405 |
+
public static function getObject($bucket, $uri, $saveTo = false) {
|
406 |
+
$rest = new S3Request('GET', $bucket, $uri);
|
407 |
+
if ($saveTo !== false) {
|
408 |
+
if (is_resource($saveTo))
|
409 |
+
$rest->fp =& $saveTo;
|
410 |
+
else
|
411 |
+
if (($rest->fp = @fopen($saveTo, 'wb')) !== false)
|
412 |
+
$rest->file = realpath($saveTo);
|
413 |
+
else
|
414 |
+
$rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
|
415 |
+
}
|
416 |
+
if ($rest->response->error === false) $rest->getResponse();
|
417 |
+
|
418 |
+
if ($rest->response->error === false && $rest->response->code !== 200)
|
419 |
+
$rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
|
420 |
+
if ($rest->response->error !== false) {
|
421 |
+
trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s",
|
422 |
+
$rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
|
423 |
+
return false;
|
424 |
+
}
|
425 |
+
return $rest->response;
|
426 |
+
}
|
427 |
+
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Get object information
|
431 |
+
*
|
432 |
+
* @param string $bucket Bucket name
|
433 |
+
* @param string $uri Object URI
|
434 |
+
* @param boolean $returnInfo Return response information
|
435 |
+
* @return mixed | false
|
436 |
+
*/
|
437 |
+
public static function getObjectInfo($bucket, $uri, $returnInfo = true) {
|
438 |
+
$rest = new S3Request('HEAD', $bucket, $uri);
|
439 |
+
$rest = $rest->getResponse();
|
440 |
+
if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
|
441 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
442 |
+
if ($rest->error !== false) {
|
443 |
+
trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s",
|
444 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
445 |
+
return false;
|
446 |
+
}
|
447 |
+
return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
|
448 |
+
}
|
449 |
+
|
450 |
+
|
451 |
+
/**
|
452 |
+
* Copy an object
|
453 |
+
*
|
454 |
+
* @param string $bucket Source bucket name
|
455 |
+
* @param string $uri Source object URI
|
456 |
+
* @param string $bucket Destination bucket name
|
457 |
+
* @param string $uri Destination object URI
|
458 |
+
* @param constant $acl ACL constant
|
459 |
+
* @param array $metaHeaders Optional array of x-amz-meta-* headers
|
460 |
+
* @param array $requestHeaders Optional array of request headers (content type, disposition, etc.)
|
461 |
+
* @return mixed | false
|
462 |
+
*/
|
463 |
+
public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) {
|
464 |
+
$rest = new S3Request('PUT', $bucket, $uri);
|
465 |
+
$rest->setHeader('Content-Length', 0);
|
466 |
+
foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v);
|
467 |
+
foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
|
468 |
+
$rest->setAmzHeader('x-amz-acl', $acl);
|
469 |
+
$rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, $srcUri));
|
470 |
+
if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0)
|
471 |
+
$rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE');
|
472 |
+
$rest = $rest->getResponse();
|
473 |
+
if ($rest->error === false && $rest->code !== 200)
|
474 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
475 |
+
if ($rest->error !== false) {
|
476 |
+
trigger_error(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s",
|
477 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
478 |
+
return false;
|
479 |
+
}
|
480 |
+
return isset($rest->body->LastModified, $rest->body->ETag) ? array(
|
481 |
+
'time' => strtotime((string)$rest->body->LastModified),
|
482 |
+
'hash' => substr((string)$rest->body->ETag, 1, -1)
|
483 |
+
) : false;
|
484 |
+
}
|
485 |
+
|
486 |
+
|
487 |
+
/**
|
488 |
+
* Set logging for a bucket
|
489 |
+
*
|
490 |
+
* @param string $bucket Bucket name
|
491 |
+
* @param string $targetBucket Target bucket (where logs are stored)
|
492 |
+
* @param string $targetPrefix Log prefix (e,g; domain.com-)
|
493 |
+
* @return boolean
|
494 |
+
*/
|
495 |
+
public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) {
|
496 |
+
// The S3 log delivery group has to be added to the target bucket's ACP
|
497 |
+
if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) {
|
498 |
+
// Only add permissions to the target bucket when they do not exist
|
499 |
+
$aclWriteSet = false;
|
500 |
+
$aclReadSet = false;
|
501 |
+
foreach ($acp['acl'] as $acl)
|
502 |
+
if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') {
|
503 |
+
if ($acl['permission'] == 'WRITE') $aclWriteSet = true;
|
504 |
+
elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true;
|
505 |
+
}
|
506 |
+
if (!$aclWriteSet) $acp['acl'][] = array(
|
507 |
+
'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE'
|
508 |
+
);
|
509 |
+
if (!$aclReadSet) $acp['acl'][] = array(
|
510 |
+
'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP'
|
511 |
+
);
|
512 |
+
if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp);
|
513 |
+
}
|
514 |
+
|
515 |
+
$dom = new DOMDocument;
|
516 |
+
$bucketLoggingStatus = $dom->createElement('BucketLoggingStatus');
|
517 |
+
$bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/');
|
518 |
+
if ($targetBucket !== null) {
|
519 |
+
if ($targetPrefix == null) $targetPrefix = $bucket . '-';
|
520 |
+
$loggingEnabled = $dom->createElement('LoggingEnabled');
|
521 |
+
$loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket));
|
522 |
+
$loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix));
|
523 |
+
// TODO: Add TargetGrants?
|
524 |
+
$bucketLoggingStatus->appendChild($loggingEnabled);
|
525 |
+
}
|
526 |
+
$dom->appendChild($bucketLoggingStatus);
|
527 |
+
|
528 |
+
$rest = new S3Request('PUT', $bucket, '');
|
529 |
+
$rest->setParameter('logging', null);
|
530 |
+
$rest->data = $dom->saveXML();
|
531 |
+
$rest->size = strlen($rest->data);
|
532 |
+
$rest->setHeader('Content-Type', 'application/xml');
|
533 |
+
$rest = $rest->getResponse();
|
534 |
+
if ($rest->error === false && $rest->code !== 200)
|
535 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
536 |
+
if ($rest->error !== false) {
|
537 |
+
trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s",
|
538 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
539 |
+
return false;
|
540 |
+
}
|
541 |
+
return true;
|
542 |
+
}
|
543 |
+
|
544 |
+
|
545 |
+
/**
|
546 |
+
* Get logging status for a bucket
|
547 |
+
*
|
548 |
+
* This will return false if logging is not enabled.
|
549 |
+
* Note: To enable logging, you also need to grant write access to the log group
|
550 |
+
*
|
551 |
+
* @param string $bucket Bucket name
|
552 |
+
* @return array | false
|
553 |
+
*/
|
554 |
+
public static function getBucketLogging($bucket) {
|
555 |
+
$rest = new S3Request('GET', $bucket, '');
|
556 |
+
$rest->setParameter('logging', null);
|
557 |
+
$rest = $rest->getResponse();
|
558 |
+
if ($rest->error === false && $rest->code !== 200)
|
559 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
560 |
+
if ($rest->error !== false) {
|
561 |
+
trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s",
|
562 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
563 |
+
return false;
|
564 |
+
}
|
565 |
+
if (!isset($rest->body->LoggingEnabled)) return false; // No logging
|
566 |
+
return array(
|
567 |
+
'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket,
|
568 |
+
'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix,
|
569 |
+
);
|
570 |
+
}
|
571 |
+
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Disable bucket logging
|
575 |
+
*
|
576 |
+
* @param string $bucket Bucket name
|
577 |
+
* @return boolean
|
578 |
+
*/
|
579 |
+
public static function disableBucketLogging($bucket) {
|
580 |
+
return self::setBucketLogging($bucket, null);
|
581 |
+
}
|
582 |
+
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Get a bucket's location
|
586 |
+
*
|
587 |
+
* @param string $bucket Bucket name
|
588 |
+
* @return string | false
|
589 |
+
*/
|
590 |
+
public static function getBucketLocation($bucket) {
|
591 |
+
$rest = new S3Request('GET', $bucket, '');
|
592 |
+
$rest->setParameter('location', null);
|
593 |
+
$rest = $rest->getResponse();
|
594 |
+
if ($rest->error === false && $rest->code !== 200)
|
595 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
596 |
+
if ($rest->error !== false) {
|
597 |
+
trigger_error(sprintf("S3::getBucketLocation({$bucket}): [%s] %s",
|
598 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
599 |
+
return false;
|
600 |
+
}
|
601 |
+
return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US';
|
602 |
+
}
|
603 |
+
|
604 |
+
|
605 |
+
/**
|
606 |
+
* Set object or bucket Access Control Policy
|
607 |
+
*
|
608 |
+
* @param string $bucket Bucket name
|
609 |
+
* @param string $uri Object URI
|
610 |
+
* @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
|
611 |
+
* @return boolean
|
612 |
+
*/
|
613 |
+
public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) {
|
614 |
+
$dom = new DOMDocument;
|
615 |
+
$dom->formatOutput = true;
|
616 |
+
$accessControlPolicy = $dom->createElement('AccessControlPolicy');
|
617 |
+
$accessControlList = $dom->createElement('AccessControlList');
|
618 |
+
|
619 |
+
// It seems the owner has to be passed along too
|
620 |
+
$owner = $dom->createElement('Owner');
|
621 |
+
$owner->appendChild($dom->createElement('ID', $acp['owner']['id']));
|
622 |
+
$owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name']));
|
623 |
+
$accessControlPolicy->appendChild($owner);
|
624 |
+
|
625 |
+
foreach ($acp['acl'] as $g) {
|
626 |
+
$grant = $dom->createElement('Grant');
|
627 |
+
$grantee = $dom->createElement('Grantee');
|
628 |
+
$grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
629 |
+
if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted)
|
630 |
+
$grantee->setAttribute('xsi:type', 'CanonicalUser');
|
631 |
+
$grantee->appendChild($dom->createElement('ID', $g['id']));
|
632 |
+
} elseif (isset($g['email'])) { // AmazonCustomerByEmail
|
633 |
+
$grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail');
|
634 |
+
$grantee->appendChild($dom->createElement('EmailAddress', $g['email']));
|
635 |
+
} elseif ($g['type'] == 'Group') { // Group
|
636 |
+
$grantee->setAttribute('xsi:type', 'Group');
|
637 |
+
$grantee->appendChild($dom->createElement('URI', $g['uri']));
|
638 |
+
}
|
639 |
+
$grant->appendChild($grantee);
|
640 |
+
$grant->appendChild($dom->createElement('Permission', $g['permission']));
|
641 |
+
$accessControlList->appendChild($grant);
|
642 |
+
}
|
643 |
+
|
644 |
+
$accessControlPolicy->appendChild($accessControlList);
|
645 |
+
$dom->appendChild($accessControlPolicy);
|
646 |
+
|
647 |
+
$rest = new S3Request('PUT', $bucket, $uri);
|
648 |
+
$rest->setParameter('acl', null);
|
649 |
+
$rest->data = $dom->saveXML();
|
650 |
+
$rest->size = strlen($rest->data);
|
651 |
+
$rest->setHeader('Content-Type', 'application/xml');
|
652 |
+
$rest = $rest->getResponse();
|
653 |
+
if ($rest->error === false && $rest->code !== 200)
|
654 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
655 |
+
if ($rest->error !== false) {
|
656 |
+
trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
|
657 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
658 |
+
return false;
|
659 |
+
}
|
660 |
+
return true;
|
661 |
+
}
|
662 |
+
|
663 |
+
|
664 |
+
/**
|
665 |
+
* Get object or bucket Access Control Policy
|
666 |
+
*
|
667 |
+
* @param string $bucket Bucket name
|
668 |
+
* @param string $uri Object URI
|
669 |
+
* @return mixed | false
|
670 |
+
*/
|
671 |
+
public static function getAccessControlPolicy($bucket, $uri = '') {
|
672 |
+
$rest = new S3Request('GET', $bucket, $uri);
|
673 |
+
$rest->setParameter('acl', null);
|
674 |
+
$rest = $rest->getResponse();
|
675 |
+
if ($rest->error === false && $rest->code !== 200)
|
676 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
677 |
+
if ($rest->error !== false) {
|
678 |
+
trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
|
679 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
680 |
+
return false;
|
681 |
+
}
|
682 |
+
|
683 |
+
$acp = array();
|
684 |
+
if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) {
|
685 |
+
$acp['owner'] = array(
|
686 |
+
'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
|
687 |
+
);
|
688 |
+
}
|
689 |
+
if (isset($rest->body->AccessControlList)) {
|
690 |
+
$acp['acl'] = array();
|
691 |
+
foreach ($rest->body->AccessControlList->Grant as $grant) {
|
692 |
+
foreach ($grant->Grantee as $grantee) {
|
693 |
+
if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser
|
694 |
+
$acp['acl'][] = array(
|
695 |
+
'type' => 'CanonicalUser',
|
696 |
+
'id' => (string)$grantee->ID,
|
697 |
+
'name' => (string)$grantee->DisplayName,
|
698 |
+
'permission' => (string)$grant->Permission
|
699 |
+
);
|
700 |
+
elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail
|
701 |
+
$acp['acl'][] = array(
|
702 |
+
'type' => 'AmazonCustomerByEmail',
|
703 |
+
'email' => (string)$grantee->EmailAddress,
|
704 |
+
'permission' => (string)$grant->Permission
|
705 |
+
);
|
706 |
+
elseif (isset($grantee->URI)) // Group
|
707 |
+
$acp['acl'][] = array(
|
708 |
+
'type' => 'Group',
|
709 |
+
'uri' => (string)$grantee->URI,
|
710 |
+
'permission' => (string)$grant->Permission
|
711 |
+
);
|
712 |
+
else continue;
|
713 |
+
}
|
714 |
+
}
|
715 |
+
}
|
716 |
+
return $acp;
|
717 |
+
}
|
718 |
+
|
719 |
+
|
720 |
+
/**
|
721 |
+
* Delete an object
|
722 |
+
*
|
723 |
+
* @param string $bucket Bucket name
|
724 |
+
* @param string $uri Object URI
|
725 |
+
* @return boolean
|
726 |
+
*/
|
727 |
+
public static function deleteObject($bucket, $uri) {
|
728 |
+
$rest = new S3Request('DELETE', $bucket, $uri);
|
729 |
+
$rest = $rest->getResponse();
|
730 |
+
if ($rest->error === false && $rest->code !== 204)
|
731 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
732 |
+
if ($rest->error !== false) {
|
733 |
+
trigger_error(sprintf("S3::deleteObject(): [%s] %s",
|
734 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
735 |
+
return false;
|
736 |
+
}
|
737 |
+
return true;
|
738 |
+
}
|
739 |
+
|
740 |
+
|
741 |
+
/**
|
742 |
+
* Get a query string authenticated URL
|
743 |
+
*
|
744 |
+
* @param string $bucket Bucket name
|
745 |
+
* @param string $uri Object URI
|
746 |
+
* @param integer $lifetime Lifetime in seconds
|
747 |
+
* @param boolean $hostBucket Use the bucket name as the hostname
|
748 |
+
* @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification)
|
749 |
+
* @return string
|
750 |
+
*/
|
751 |
+
public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) {
|
752 |
+
$expires = time() + $lifetime;
|
753 |
+
$uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea)
|
754 |
+
return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s',
|
755 |
+
$hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires,
|
756 |
+
urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}")));
|
757 |
+
}
|
758 |
+
|
759 |
+
/**
|
760 |
+
* Get upload POST parameters for form uploads
|
761 |
+
*
|
762 |
+
* @param string $bucket Bucket name
|
763 |
+
* @param string $uriPrefix Object URI prefix
|
764 |
+
* @param constant $acl ACL constant
|
765 |
+
* @param integer $lifetime Lifetime in seconds
|
766 |
+
* @param integer $maxFileSize Maximum filesize in bytes (default 5MB)
|
767 |
+
* @param string $successRedirect Redirect URL or 200 / 201 status code
|
768 |
+
* @param array $amzHeaders Array of x-amz-meta-* headers
|
769 |
+
* @param array $headers Array of request headers or content type as a string
|
770 |
+
* @param boolean $flashVars Includes additional "Filename" variable posted by Flash
|
771 |
+
* @return object
|
772 |
+
*/
|
773 |
+
public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) {
|
774 |
+
// Create policy object
|
775 |
+
$policy = new stdClass;
|
776 |
+
$policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime));
|
777 |
+
$policy->conditions = array();
|
778 |
+
$obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj);
|
779 |
+
$obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj);
|
780 |
+
|
781 |
+
$obj = new stdClass; // 200 for non-redirect uploads
|
782 |
+
if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
|
783 |
+
$obj->success_action_status = (string)$successRedirect;
|
784 |
+
else // URL
|
785 |
+
$obj->success_action_redirect = $successRedirect;
|
786 |
+
array_push($policy->conditions, $obj);
|
787 |
+
|
788 |
+
array_push($policy->conditions, array('starts-with', '$key', $uriPrefix));
|
789 |
+
if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', ''));
|
790 |
+
foreach (array_keys($headers) as $headerKey)
|
791 |
+
array_push($policy->conditions, array('starts-with', '$'.$headerKey, ''));
|
792 |
+
foreach ($amzHeaders as $headerKey => $headerVal) {
|
793 |
+
$obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj);
|
794 |
+
}
|
795 |
+
array_push($policy->conditions, array('content-length-range', 0, $maxFileSize));
|
796 |
+
$policy = base64_encode(str_replace('\/', '/', json_encode($policy)));
|
797 |
+
|
798 |
+
// Create parameters
|
799 |
+
$params = new stdClass;
|
800 |
+
$params->AWSAccessKeyId = self::$__accessKey;
|
801 |
+
$params->key = $uriPrefix.'${filename}';
|
802 |
+
$params->acl = $acl;
|
803 |
+
$params->policy = $policy; unset($policy);
|
804 |
+
$params->signature = self::__getHash($params->policy);
|
805 |
+
if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201)))
|
806 |
+
$params->success_action_status = (string)$successRedirect;
|
807 |
+
else
|
808 |
+
$params->success_action_redirect = $successRedirect;
|
809 |
+
foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
|
810 |
+
foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal;
|
811 |
+
return $params;
|
812 |
+
}
|
813 |
+
|
814 |
+
/**
|
815 |
+
* Create a CloudFront distribution
|
816 |
+
*
|
817 |
+
* @param string $bucket Bucket name
|
818 |
+
* @param boolean $enabled Enabled (true/false)
|
819 |
+
* @param array $cnames Array containing CNAME aliases
|
820 |
+
* @param string $comment Use the bucket name as the hostname
|
821 |
+
* @return array | false
|
822 |
+
*/
|
823 |
+
public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = '') {
|
824 |
+
self::$useSSL = true; // CloudFront requires SSL
|
825 |
+
$rest = new S3Request('POST', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com');
|
826 |
+
$rest->data = self::__getCloudFrontDistributionConfigXML($bucket.'.s3.amazonaws.com', $enabled, $comment, (string)microtime(true), $cnames);
|
827 |
+
$rest->size = strlen($rest->data);
|
828 |
+
$rest->setHeader('Content-Type', 'application/xml');
|
829 |
+
$rest = self::__getCloudFrontResponse($rest);
|
830 |
+
|
831 |
+
if ($rest->error === false && $rest->code !== 201)
|
832 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
833 |
+
if ($rest->error !== false) {
|
834 |
+
trigger_error(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", '$comment'): [%s] %s",
|
835 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
836 |
+
return false;
|
837 |
+
} elseif ($rest->body instanceof SimpleXMLElement)
|
838 |
+
return self::__parseCloudFrontDistributionConfig($rest->body);
|
839 |
+
return false;
|
840 |
+
}
|
841 |
+
|
842 |
+
|
843 |
+
/**
|
844 |
+
* Get CloudFront distribution info
|
845 |
+
*
|
846 |
+
* @param string $distributionId Distribution ID from listDistributions()
|
847 |
+
* @return array | false
|
848 |
+
*/
|
849 |
+
public static function getDistribution($distributionId) {
|
850 |
+
self::$useSSL = true; // CloudFront requires SSL
|
851 |
+
$rest = new S3Request('GET', '', '2008-06-30/distribution/'.$distributionId, 'cloudfront.amazonaws.com');
|
852 |
+
$rest = self::__getCloudFrontResponse($rest);
|
853 |
+
|
854 |
+
if ($rest->error === false && $rest->code !== 200)
|
855 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
856 |
+
if ($rest->error !== false) {
|
857 |
+
trigger_error(sprintf("S3::getDistribution($distributionId): [%s] %s",
|
858 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
859 |
+
return false;
|
860 |
+
} elseif ($rest->body instanceof SimpleXMLElement) {
|
861 |
+
$dist = self::__parseCloudFrontDistributionConfig($rest->body);
|
862 |
+
$dist['hash'] = $rest->headers['hash'];
|
863 |
+
return $dist;
|
864 |
+
}
|
865 |
+
return false;
|
866 |
+
}
|
867 |
+
|
868 |
+
|
869 |
+
/**
|
870 |
+
* Update a CloudFront distribution
|
871 |
+
*
|
872 |
+
* @param array $dist Distribution array info identical to output of getDistribution()
|
873 |
+
* @return array | false
|
874 |
+
*/
|
875 |
+
public static function updateDistribution($dist) {
|
876 |
+
self::$useSSL = true; // CloudFront requires SSL
|
877 |
+
$rest = new S3Request('PUT', '', '2008-06-30/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com');
|
878 |
+
$rest->data = self::__getCloudFrontDistributionConfigXML($dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']);
|
879 |
+
$rest->size = strlen($rest->data);
|
880 |
+
$rest->setHeader('If-Match', $dist['hash']);
|
881 |
+
$rest = self::__getCloudFrontResponse($rest);
|
882 |
+
|
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 |
+
trigger_error(sprintf("S3::updateDistribution({$dist['id']}, ".(int)$enabled.", '$comment'): [%s] %s",
|
887 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
888 |
+
return false;
|
889 |
+
} else {
|
890 |
+
$dist = self::__parseCloudFrontDistributionConfig($rest->body);
|
891 |
+
$dist['hash'] = $rest->headers['hash'];
|
892 |
+
return $dist;
|
893 |
+
}
|
894 |
+
return false;
|
895 |
+
}
|
896 |
+
|
897 |
+
|
898 |
+
/**
|
899 |
+
* Delete a CloudFront distribution
|
900 |
+
*
|
901 |
+
* @param array $dist Distribution array info identical to output of getDistribution()
|
902 |
+
* @return boolean
|
903 |
+
*/
|
904 |
+
public static function deleteDistribution($dist) {
|
905 |
+
self::$useSSL = true; // CloudFront requires SSL
|
906 |
+
$rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com');
|
907 |
+
$rest->setHeader('If-Match', $dist['hash']);
|
908 |
+
$rest = self::__getCloudFrontResponse($rest);
|
909 |
+
|
910 |
+
if ($rest->error === false && $rest->code !== 204)
|
911 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
912 |
+
if ($rest->error !== false) {
|
913 |
+
trigger_error(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s",
|
914 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
915 |
+
return false;
|
916 |
+
}
|
917 |
+
return true;
|
918 |
+
}
|
919 |
+
|
920 |
+
|
921 |
+
/**
|
922 |
+
* Get a list of CloudFront distributions
|
923 |
+
*
|
924 |
+
* @return array
|
925 |
+
*/
|
926 |
+
public static function listDistributions() {
|
927 |
+
self::$useSSL = true; // CloudFront requires SSL
|
928 |
+
$rest = new S3Request('GET', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com');
|
929 |
+
$rest = self::__getCloudFrontResponse($rest);
|
930 |
+
|
931 |
+
if ($rest->error === false && $rest->code !== 200)
|
932 |
+
$rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
|
933 |
+
if ($rest->error !== false) {
|
934 |
+
trigger_error(sprintf("S3::listDistributions(): [%s] %s",
|
935 |
+
$rest->error['code'], $rest->error['message']), E_USER_WARNING);
|
936 |
+
return false;
|
937 |
+
} elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) {
|
938 |
+
$list = array();
|
939 |
+
if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) {
|
940 |
+
//$info['marker'] = (string)$rest->body->Marker;
|
941 |
+
//$info['maxItems'] = (int)$rest->body->MaxItems;
|
942 |
+
//$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false;
|
943 |
+
}
|
944 |
+
foreach ($rest->body->DistributionSummary as $summary) {
|
945 |
+
$list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary);
|
946 |
+
}
|
947 |
+
return $list;
|
948 |
+
}
|
949 |
+
return array();
|
950 |
+
}
|
951 |
+
|
952 |
+
|
953 |
+
/**
|
954 |
+
* Get a DistributionConfig DOMDocument
|
955 |
+
*
|
956 |
+
* @internal Used to create XML in createDistribution() and updateDistribution()
|
957 |
+
* @param string $bucket Origin bucket
|
958 |
+
* @param boolean $enabled Enabled (true/false)
|
959 |
+
* @param string $comment Comment to append
|
960 |
+
* @param string $callerReference Caller reference
|
961 |
+
* @param array $cnames Array of CNAME aliases
|
962 |
+
* @return string
|
963 |
+
*/
|
964 |
+
private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array()) {
|
965 |
+
$dom = new DOMDocument('1.0', 'UTF-8');
|
966 |
+
$dom->formatOutput = true;
|
967 |
+
$distributionConfig = $dom->createElement('DistributionConfig');
|
968 |
+
$distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2008-06-30/');
|
969 |
+
$distributionConfig->appendChild($dom->createElement('Origin', $bucket));
|
970 |
+
$distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference));
|
971 |
+
foreach ($cnames as $cname)
|
972 |
+
$distributionConfig->appendChild($dom->createElement('CNAME', $cname));
|
973 |
+
if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment));
|
974 |
+
$distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false'));
|
975 |
+
$dom->appendChild($distributionConfig);
|
976 |
+
return $dom->saveXML();
|
977 |
+
}
|
978 |
+
|
979 |
+
|
980 |
+
/**
|
981 |
+
* Parse a CloudFront distribution config
|
982 |
+
*
|
983 |
+
* @internal Used to parse the CloudFront DistributionConfig node to an array
|
984 |
+
* @param object &$node DOMNode
|
985 |
+
* @return array
|
986 |
+
*/
|
987 |
+
private static function __parseCloudFrontDistributionConfig(&$node) {
|
988 |
+
$dist = array();
|
989 |
+
if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) {
|
990 |
+
$dist['id'] = (string)$node->Id;
|
991 |
+
$dist['status'] = (string)$node->Status;
|
992 |
+
$dist['time'] = strtotime((string)$node->LastModifiedTime);
|
993 |
+
$dist['domain'] = (string)$node->DomainName;
|
994 |
+
}
|
995 |
+
if (isset($node->CallerReference))
|
996 |
+
$dist['callerReference'] = (string)$node->CallerReference;
|
997 |
+
if (isset($node->Comment))
|
998 |
+
$dist['comment'] = (string)$node->Comment;
|
999 |
+
if (isset($node->Enabled, $node->Origin)) {
|
1000 |
+
$dist['origin'] = (string)$node->Origin;
|
1001 |
+
$dist['enabled'] = (string)$node->Enabled == 'true' ? true : false;
|
1002 |
+
} elseif (isset($node->DistributionConfig)) {
|
1003 |
+
$dist = array_merge($dist, self::__parseCloudFrontDistributionConfig($node->DistributionConfig));
|
1004 |
+
}
|
1005 |
+
if (isset($node->CNAME)) {
|
1006 |
+
$dist['cnames'] = array();
|
1007 |
+
foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname;
|
1008 |
+
}
|
1009 |
+
return $dist;
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
|
1013 |
+
/**
|
1014 |
+
* Grab CloudFront response
|
1015 |
+
*
|
1016 |
+
* @internal Used to parse the CloudFront S3Request::getResponse() output
|
1017 |
+
* @param object &$rest S3Request instance
|
1018 |
+
* @return object
|
1019 |
+
*/
|
1020 |
+
private static function __getCloudFrontResponse(&$rest) {
|
1021 |
+
$rest->getResponse();
|
1022 |
+
if ($rest->response->error === false && isset($rest->response->body) &&
|
1023 |
+
is_string($rest->response->body) && substr($rest->response->body, 0, 5) == '<?xml') {
|
1024 |
+
$rest->response->body = simplexml_load_string($rest->response->body);
|
1025 |
+
// Grab CloudFront errors
|
1026 |
+
if (isset($rest->response->body->Error, $rest->response->body->Error->Code,
|
1027 |
+
$rest->response->body->Error->Message)) {
|
1028 |
+
$rest->response->error = array(
|
1029 |
+
'code' => (string)$rest->response->body->Error->Code,
|
1030 |
+
'message' => (string)$rest->response->body->Error->Message
|
1031 |
+
);
|
1032 |
+
unset($rest->response->body);
|
1033 |
+
}
|
1034 |
+
}
|
1035 |
+
return $rest->response;
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
|
1039 |
+
/**
|
1040 |
+
* Get MIME type for file
|
1041 |
+
*
|
1042 |
+
* @internal Used to get mime types
|
1043 |
+
* @param string &$file File path
|
1044 |
+
* @return string
|
1045 |
+
*/
|
1046 |
+
public static function __getMimeType(&$file) {
|
1047 |
+
$type = false;
|
1048 |
+
// Fileinfo documentation says fileinfo_open() will use the
|
1049 |
+
// MAGIC env var for the magic file
|
1050 |
+
if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
|
1051 |
+
($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) {
|
1052 |
+
if (($type = finfo_file($finfo, $file)) !== false) {
|
1053 |
+
// Remove the charset and grab the last content-type
|
1054 |
+
$type = explode(' ', str_replace('; charset=', ';charset=', $type));
|
1055 |
+
$type = array_pop($type);
|
1056 |
+
$type = explode(';', $type);
|
1057 |
+
$type = trim(array_shift($type));
|
1058 |
+
}
|
1059 |
+
finfo_close($finfo);
|
1060 |
+
|
1061 |
+
// If anyone is still using mime_content_type()
|
1062 |
+
} elseif (function_exists('mime_content_type'))
|
1063 |
+
$type = trim(mime_content_type($file));
|
1064 |
+
|
1065 |
+
if ($type !== false && strlen($type) > 0) return $type;
|
1066 |
+
|
1067 |
+
// Otherwise do it the old fashioned way
|
1068 |
+
static $exts = array(
|
1069 |
+
'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png',
|
1070 |
+
'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon',
|
1071 |
+
'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf',
|
1072 |
+
'zip' => 'application/zip', 'gz' => 'application/x-gzip',
|
1073 |
+
'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
|
1074 |
+
'bz2' => 'application/x-bzip2', 'txt' => 'text/plain',
|
1075 |
+
'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
|
1076 |
+
'css' => 'text/css', 'js' => 'text/javascript',
|
1077 |
+
'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
|
1078 |
+
'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
|
1079 |
+
'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
|
1080 |
+
'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
|
1081 |
+
);
|
1082 |
+
$ext = strtolower(pathInfo($file, PATHINFO_EXTENSION));
|
1083 |
+
return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream';
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
|
1087 |
+
/**
|
1088 |
+
* Generate the auth string: "AWS AccessKey:Signature"
|
1089 |
+
*
|
1090 |
+
* @internal Used by S3Request::getResponse()
|
1091 |
+
* @param string $string String to sign
|
1092 |
+
* @return string
|
1093 |
+
*/
|
1094 |
+
public static function __getSignature($string) {
|
1095 |
+
return 'AWS '.self::$__accessKey.':'.self::__getHash($string);
|
1096 |
+
}
|
1097 |
+
|
1098 |
+
|
1099 |
+
/**
|
1100 |
+
* Creates a HMAC-SHA1 hash
|
1101 |
+
*
|
1102 |
+
* This uses the hash extension if loaded
|
1103 |
+
*
|
1104 |
+
* @internal Used by __getSignature()
|
1105 |
+
* @param string $string String to sign
|
1106 |
+
* @return string
|
1107 |
+
*/
|
1108 |
+
private static function __getHash($string) {
|
1109 |
+
return base64_encode(extension_loaded('hash') ?
|
1110 |
+
hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1(
|
1111 |
+
(str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
|
1112 |
+
pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^
|
1113 |
+
(str_repeat(chr(0x36), 64))) . $string)))));
|
1114 |
+
}
|
1115 |
+
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
final class S3Request {
|
1119 |
+
private $verb, $bucket, $uri, $resource = '', $parameters = array(),
|
1120 |
+
$amzHeaders = array(), $headers = array(
|
1121 |
+
'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
|
1122 |
+
);
|
1123 |
+
public $fp = false, $size = 0, $data = false, $response;
|
1124 |
+
|
1125 |
+
|
1126 |
+
/**
|
1127 |
+
* Constructor
|
1128 |
+
*
|
1129 |
+
* @param string $verb Verb
|
1130 |
+
* @param string $bucket Bucket name
|
1131 |
+
* @param string $uri Object URI
|
1132 |
+
* @return mixed
|
1133 |
+
*/
|
1134 |
+
function __construct($verb, $bucket = '', $uri = '', $defaultHost = 's3.amazonaws.com') {
|
1135 |
+
$this->verb = $verb;
|
1136 |
+
$this->bucket = strtolower($bucket);
|
1137 |
+
$this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/';
|
1138 |
+
|
1139 |
+
if ($this->bucket !== '') {
|
1140 |
+
$this->headers['Host'] = $this->bucket.'.'.$defaultHost;
|
1141 |
+
$this->resource = '/'.$this->bucket.$this->uri;
|
1142 |
+
} else {
|
1143 |
+
$this->headers['Host'] = $defaultHost;
|
1144 |
+
//$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri;
|
1145 |
+
$this->resource = $this->uri;
|
1146 |
+
}
|
1147 |
+
$this->headers['Date'] = gmdate('D, d M Y H:i:s T');
|
1148 |
+
|
1149 |
+
$this->response = new STDClass;
|
1150 |
+
$this->response->error = false;
|
1151 |
+
}
|
1152 |
+
|
1153 |
+
|
1154 |
+
/**
|
1155 |
+
* Set request parameter
|
1156 |
+
*
|
1157 |
+
* @param string $key Key
|
1158 |
+
* @param string $value Value
|
1159 |
+
* @return void
|
1160 |
+
*/
|
1161 |
+
public function setParameter($key, $value) {
|
1162 |
+
$this->parameters[$key] = $value;
|
1163 |
+
}
|
1164 |
+
|
1165 |
+
|
1166 |
+
/**
|
1167 |
+
* Set request header
|
1168 |
+
*
|
1169 |
+
* @param string $key Key
|
1170 |
+
* @param string $value Value
|
1171 |
+
* @return void
|
1172 |
+
*/
|
1173 |
+
public function setHeader($key, $value) {
|
1174 |
+
$this->headers[$key] = $value;
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
|
1178 |
+
/**
|
1179 |
+
* Set x-amz-meta-* header
|
1180 |
+
*
|
1181 |
+
* @param string $key Key
|
1182 |
+
* @param string $value Value
|
1183 |
+
* @return void
|
1184 |
+
*/
|
1185 |
+
public function setAmzHeader($key, $value) {
|
1186 |
+
$this->amzHeaders[$key] = $value;
|
1187 |
+
}
|
1188 |
+
|
1189 |
+
|
1190 |
+
/**
|
1191 |
+
* Get the S3 response
|
1192 |
+
*
|
1193 |
+
* @return object | false
|
1194 |
+
*/
|
1195 |
+
public function getResponse() {
|
1196 |
+
$query = '';
|
1197 |
+
if (sizeof($this->parameters) > 0) {
|
1198 |
+
$query = substr($this->uri, -1) !== '?' ? '?' : '&';
|
1199 |
+
foreach ($this->parameters as $var => $value)
|
1200 |
+
if ($value == null || $value == '') $query .= $var.'&';
|
1201 |
+
// Parameters should be encoded (thanks Sean O'Dea)
|
1202 |
+
else $query .= $var.'='.rawurlencode($value).'&';
|
1203 |
+
$query = substr($query, 0, -1);
|
1204 |
+
$this->uri .= $query;
|
1205 |
+
|
1206 |
+
if (array_key_exists('acl', $this->parameters) ||
|
1207 |
+
array_key_exists('location', $this->parameters) ||
|
1208 |
+
array_key_exists('torrent', $this->parameters) ||
|
1209 |
+
array_key_exists('logging', $this->parameters))
|
1210 |
+
$this->resource .= $query;
|
1211 |
+
}
|
1212 |
+
$url = ((S3::$useSSL && extension_loaded('openssl')) ?
|
1213 |
+
'https://':'http://').$this->headers['Host'].$this->uri;
|
1214 |
+
//var_dump($this->bucket, $this->uri, $this->resource, $url);
|
1215 |
+
|
1216 |
+
// Basic setup
|
1217 |
+
$curl = curl_init();
|
1218 |
+
curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');
|
1219 |
+
|
1220 |
+
if (S3::$useSSL) {
|
1221 |
+
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
|
1222 |
+
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
curl_setopt($curl, CURLOPT_URL, $url);
|
1226 |
+
|
1227 |
+
// Headers
|
1228 |
+
$headers = array(); $amz = array();
|
1229 |
+
foreach ($this->amzHeaders as $header => $value)
|
1230 |
+
if (strlen($value) > 0) $headers[] = $header.': '.$value;
|
1231 |
+
foreach ($this->headers as $header => $value)
|
1232 |
+
if (strlen($value) > 0) $headers[] = $header.': '.$value;
|
1233 |
+
|
1234 |
+
// Collect AMZ headers for signature
|
1235 |
+
foreach ($this->amzHeaders as $header => $value)
|
1236 |
+
if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value;
|
1237 |
+
|
1238 |
+
// AMZ headers must be sorted
|
1239 |
+
if (sizeof($amz) > 0) {
|
1240 |
+
sort($amz);
|
1241 |
+
$amz = "\n".implode("\n", $amz);
|
1242 |
+
} else $amz = '';
|
1243 |
+
|
1244 |
+
// Authorization string (CloudFront stringToSign should only contain a date)
|
1245 |
+
$headers[] = 'Authorization: ' . S3::__getSignature(
|
1246 |
+
$this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] :
|
1247 |
+
$this->verb."\n".$this->headers['Content-MD5']."\n".
|
1248 |
+
$this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource
|
1249 |
+
);
|
1250 |
+
|
1251 |
+
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
1252 |
+
curl_setopt($curl, CURLOPT_HEADER, false);
|
1253 |
+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
|
1254 |
+
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
|
1255 |
+
curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
|
1256 |
+
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
1257 |
+
|
1258 |
+
// Request types
|
1259 |
+
switch ($this->verb) {
|
1260 |
+
case 'GET': break;
|
1261 |
+
case 'PUT': case 'POST': // POST only used for CloudFront
|
1262 |
+
if ($this->fp !== false) {
|
1263 |
+
curl_setopt($curl, CURLOPT_PUT, true);
|
1264 |
+
curl_setopt($curl, CURLOPT_INFILE, $this->fp);
|
1265 |
+
if ($this->size >= 0)
|
1266 |
+
curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
|
1267 |
+
} elseif ($this->data !== false) {
|
1268 |
+
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
|
1269 |
+
curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
|
1270 |
+
if ($this->size >= 0)
|
1271 |
+
curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size);
|
1272 |
+
} else
|
1273 |
+
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
|
1274 |
+
break;
|
1275 |
+
case 'HEAD':
|
1276 |
+
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
|
1277 |
+
curl_setopt($curl, CURLOPT_NOBODY, true);
|
1278 |
+
break;
|
1279 |
+
case 'DELETE':
|
1280 |
+
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
1281 |
+
break;
|
1282 |
+
default: break;
|
1283 |
+
}
|
1284 |
+
|
1285 |
+
// Execute, grab errors
|
1286 |
+
if (curl_exec($curl))
|
1287 |
+
$this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
1288 |
+
else
|
1289 |
+
$this->response->error = array(
|
1290 |
+
'code' => curl_errno($curl),
|
1291 |
+
'message' => curl_error($curl),
|
1292 |
+
'resource' => $this->resource
|
1293 |
+
);
|
1294 |
+
|
1295 |
+
@curl_close($curl);
|
1296 |
+
|
1297 |
+
// Parse body into XML
|
1298 |
+
if ($this->response->error === false && isset($this->response->headers['type']) &&
|
1299 |
+
$this->response->headers['type'] == 'application/xml' && isset($this->response->body)) {
|
1300 |
+
$this->response->body = simplexml_load_string($this->response->body);
|
1301 |
+
|
1302 |
+
// Grab S3 errors
|
1303 |
+
if (!in_array($this->response->code, array(200, 204)) &&
|
1304 |
+
isset($this->response->body->Code, $this->response->body->Message)) {
|
1305 |
+
$this->response->error = array(
|
1306 |
+
'code' => (string)$this->response->body->Code,
|
1307 |
+
'message' => (string)$this->response->body->Message
|
1308 |
+
);
|
1309 |
+
if (isset($this->response->body->Resource))
|
1310 |
+
$this->response->error['resource'] = (string)$this->response->body->Resource;
|
1311 |
+
unset($this->response->body);
|
1312 |
+
}
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
// Clean up file resources
|
1316 |
+
if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp);
|
1317 |
+
|
1318 |
+
return $this->response;
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
|
1322 |
+
/**
|
1323 |
+
* CURL write callback
|
1324 |
+
*
|
1325 |
+
* @param resource &$curl CURL resource
|
1326 |
+
* @param string &$data Data
|
1327 |
+
* @return integer
|
1328 |
+
*/
|
1329 |
+
private function __responseWriteCallback(&$curl, &$data) {
|
1330 |
+
if ($this->response->code == 200 && $this->fp !== false)
|
1331 |
+
return fwrite($this->fp, $data);
|
1332 |
+
else
|
1333 |
+
$this->response->body .= $data;
|
1334 |
+
return strlen($data);
|
1335 |
+
}
|
1336 |
+
|
1337 |
+
|
1338 |
+
/**
|
1339 |
+
* CURL header callback
|
1340 |
+
*
|
1341 |
+
* @param resource &$curl CURL resource
|
1342 |
+
* @param string &$data Data
|
1343 |
+
* @return integer
|
1344 |
+
*/
|
1345 |
+
private function __responseHeaderCallback(&$curl, &$data) {
|
1346 |
+
if (($strlen = strlen($data)) <= 2) return $strlen;
|
1347 |
+
if (substr($data, 0, 4) == 'HTTP')
|
1348 |
+
$this->response->code = (int)substr($data, 9, 3);
|
1349 |
+
else {
|
1350 |
+
list($header, $value) = explode(': ', trim($data), 2);
|
1351 |
+
if ($header == 'Last-Modified')
|
1352 |
+
$this->response->headers['time'] = strtotime($value);
|
1353 |
+
elseif ($header == 'Content-Length')
|
1354 |
+
$this->response->headers['size'] = (int)$value;
|
1355 |
+
elseif ($header == 'Content-Type')
|
1356 |
+
$this->response->headers['type'] = $value;
|
1357 |
+
elseif ($header == 'ETag')
|
1358 |
+
$this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value;
|
1359 |
+
elseif (preg_match('/^x-amz-meta-.*$/', $header))
|
1360 |
+
$this->response->headers[$header] = is_numeric($value) ? (int)$value : $value;
|
1361 |
+
}
|
1362 |
+
return $strlen;
|
1363 |
+
}
|
1364 |
+
|
1365 |
+
}
|
includes/ftp.class.php
ADDED
@@ -0,0 +1,193 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
/* from http://www.solutionbot.com/2009/01/02/php-ftp-class/ */
|
3 |
+
class ftp_wrapper
|
4 |
+
{
|
5 |
+
private $conn_id;
|
6 |
+
private $host;
|
7 |
+
private $username;
|
8 |
+
private $password;
|
9 |
+
private $port;
|
10 |
+
public $timeout = 90;
|
11 |
+
public $passive = false;
|
12 |
+
public $ssl = false;
|
13 |
+
public $system_type = '';
|
14 |
+
|
15 |
+
|
16 |
+
public function __construct($host, $username, $password, $port = 21)
|
17 |
+
{
|
18 |
+
$this->host = $host;
|
19 |
+
$this->username = $username;
|
20 |
+
$this->password = $password;
|
21 |
+
$this->port = $port;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function connect()
|
25 |
+
{
|
26 |
+
if ($this->ssl == false)
|
27 |
+
{
|
28 |
+
$this->conn_id = ftp_connect($this->host, $this->port);
|
29 |
+
}
|
30 |
+
else
|
31 |
+
{
|
32 |
+
if (function_exists('ftp_ssl_connect'))
|
33 |
+
{
|
34 |
+
$this->conn_id = ftp_ssl_connect($this->host, $this->port);
|
35 |
+
}
|
36 |
+
else
|
37 |
+
{
|
38 |
+
return false;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
$result = ftp_login($this->conn_id, $this->username, $this->password);
|
43 |
+
|
44 |
+
if ($result == true)
|
45 |
+
{
|
46 |
+
ftp_set_option($this->conn_id, FTP_TIMEOUT_SEC, $this->timeout);
|
47 |
+
|
48 |
+
if ($this->passive == true)
|
49 |
+
{
|
50 |
+
ftp_pasv($this->conn_id, true);
|
51 |
+
}
|
52 |
+
else
|
53 |
+
{
|
54 |
+
ftp_pasv($this->conn_id, false);
|
55 |
+
}
|
56 |
+
|
57 |
+
$this->system_type = ftp_systype($this->conn_id);
|
58 |
+
|
59 |
+
return true;
|
60 |
+
}
|
61 |
+
else
|
62 |
+
{
|
63 |
+
return false;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
public function put($local_file_path, $remote_file_path, $mode = FTP_ASCII)
|
68 |
+
{
|
69 |
+
if (ftp_put($this->conn_id, $remote_file_path, $local_file_path, $mode))
|
70 |
+
{
|
71 |
+
return true;
|
72 |
+
}
|
73 |
+
else
|
74 |
+
{
|
75 |
+
return false;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
public function get($local_file_path, $remote_file_path, $mode = FTP_ASCII)
|
80 |
+
{
|
81 |
+
if (ftp_get($this->conn_id, $local_file_path, $remote_file_path, $mode))
|
82 |
+
{
|
83 |
+
return true;
|
84 |
+
}
|
85 |
+
else
|
86 |
+
{
|
87 |
+
return false;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
public function chmod($permissions, $remote_filename)
|
92 |
+
{
|
93 |
+
if ($this->is_octal($permissions))
|
94 |
+
{
|
95 |
+
$result = ftp_chmod($this->conn_id, $permissions, $remote_filename);
|
96 |
+
if ($result)
|
97 |
+
{
|
98 |
+
return true;
|
99 |
+
}
|
100 |
+
else
|
101 |
+
{
|
102 |
+
return false;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
else
|
106 |
+
{
|
107 |
+
throw new Exception('$permissions must be an octal number');
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
public function chdir($directory)
|
112 |
+
{
|
113 |
+
ftp_chdir($this->conn_id, $directory);
|
114 |
+
}
|
115 |
+
|
116 |
+
public function delete($remote_file_path)
|
117 |
+
{
|
118 |
+
if (ftp_delete($this->conn_id, $remote_file_path))
|
119 |
+
{
|
120 |
+
return true;
|
121 |
+
}
|
122 |
+
else
|
123 |
+
{
|
124 |
+
return false;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
public function make_dir($directory)
|
129 |
+
{
|
130 |
+
if (ftp_mkdir($this->conn_id, $directory))
|
131 |
+
{
|
132 |
+
return true;
|
133 |
+
}
|
134 |
+
else
|
135 |
+
{
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
public function rename($old_name, $new_name)
|
141 |
+
{
|
142 |
+
if (ftp_rename($this->conn_id, $old_name, $new_name))
|
143 |
+
{
|
144 |
+
return true;
|
145 |
+
}
|
146 |
+
else
|
147 |
+
{
|
148 |
+
return false;
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
public function remove_dir($directory)
|
153 |
+
{
|
154 |
+
if (ftp_rmdir($this->conn_id, $directory))
|
155 |
+
{
|
156 |
+
return true;
|
157 |
+
}
|
158 |
+
else
|
159 |
+
{
|
160 |
+
return false;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
public function dir_list($directory)
|
165 |
+
{
|
166 |
+
$contents = ftp_nlist($this->conn_id, $directory);
|
167 |
+
return $contents;
|
168 |
+
}
|
169 |
+
|
170 |
+
public function cdup()
|
171 |
+
{
|
172 |
+
ftp_cdup($this->conn_id);
|
173 |
+
}
|
174 |
+
|
175 |
+
public function current_dir()
|
176 |
+
{
|
177 |
+
return ftp_pwd($this->conn_id);
|
178 |
+
}
|
179 |
+
|
180 |
+
private function is_octal($i)
|
181 |
+
{
|
182 |
+
return decoct(octdec($i)) == $i;
|
183 |
+
}
|
184 |
+
|
185 |
+
public function __destruct()
|
186 |
+
{
|
187 |
+
if ($this->conn_id)
|
188 |
+
{
|
189 |
+
ftp_close($this->conn_id);
|
190 |
+
}
|
191 |
+
}
|
192 |
+
}
|
193 |
+
?>
|
includes/updraft-restorer.php
ADDED
@@ -0,0 +1,62 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
class Updraft_Restorer extends WP_Upgrader {
|
3 |
+
|
4 |
+
function backup_strings() {
|
5 |
+
$this->strings['no_package'] = __('Backup file not available.');
|
6 |
+
$this->strings['unpack_package'] = __('Unpacking backup...');
|
7 |
+
$this->strings['moving_old'] = __('Moving old directory out of the way...');
|
8 |
+
$this->strings['moving_backup'] = __('Moving unpackaged backup in place...');
|
9 |
+
$this->strings['cleaning_up'] = __('Cleaning up detritus...');
|
10 |
+
$this->strings['old_move_failed'] = __('Could not move old dir out of the way.');
|
11 |
+
$this->strings['new_move_failed'] = __('Could not move new dir into place. Check your wp-content/upgrade folder.');
|
12 |
+
$this->strings['delete_failed'] = __('Failed to delete working directory after restoring.');
|
13 |
+
}
|
14 |
+
|
15 |
+
function restore_backup($backup_file,$type) {
|
16 |
+
global $wp_filesystem;
|
17 |
+
$this->init();
|
18 |
+
$this->backup_strings();
|
19 |
+
|
20 |
+
$res = $this->fs_connect( array(ABSPATH, WP_CONTENT_DIR) );
|
21 |
+
if(!$res) {
|
22 |
+
exit;
|
23 |
+
}
|
24 |
+
|
25 |
+
$wp_dir = trailingslashit($wp_filesystem->abspath());
|
26 |
+
|
27 |
+
$download = $this->download_package( $backup_file );
|
28 |
+
if ( is_wp_error($download) )
|
29 |
+
return $download;
|
30 |
+
|
31 |
+
$delete = (get_option('updraft_delete_local'))?true:false;
|
32 |
+
|
33 |
+
$working_dir = $this->unpack_package( $download , $delete );
|
34 |
+
if ( is_wp_error($working_dir) )
|
35 |
+
return $working_dir;
|
36 |
+
|
37 |
+
show_message($this->strings['moving_old']);
|
38 |
+
if ( !$wp_filesystem->move($wp_dir . "wp-content/$type", $wp_dir . "wp-content/$type-old", true) ) {
|
39 |
+
return new WP_Error('old_move_failed', $this->strings['old_move_failed']);
|
40 |
+
}
|
41 |
+
|
42 |
+
show_message($this->strings['moving_backup']);
|
43 |
+
if ( !$wp_filesystem->move($working_dir . "/$type", $wp_dir . "wp-content/$type", true) ) {
|
44 |
+
return new WP_Error('new_move_failed', $this->strings['new_move_failed']);
|
45 |
+
}
|
46 |
+
|
47 |
+
show_message($this->strings['cleaning_up']);
|
48 |
+
if ( !$wp_filesystem->delete($working_dir) ) {
|
49 |
+
return new WP_Error('delete_failed', $this->strings['delete_failed']);
|
50 |
+
}
|
51 |
+
|
52 |
+
switch($type) {
|
53 |
+
case 'uploads':
|
54 |
+
$wp_filesystem->chmod($wp_dir . "wp-content/$type", 0777, true);
|
55 |
+
break;
|
56 |
+
default:
|
57 |
+
$wp_filesystem->chmod($wp_dir . "wp-content/$type", FS_CHMOD_DIR);
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
}
|
62 |
+
?>
|
readme.txt
ADDED
@@ -0,0 +1,94 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
=== UpdraftPlus ===
|
2 |
+
Contributors: David Anderson
|
3 |
+
Tags: backup, restore, database, cloud, amazon, s3, ftp, cloud
|
4 |
+
Requires at least: 3.2
|
5 |
+
Tested up to: 3.3.2
|
6 |
+
Stable tag: 0.7.4
|
7 |
+
License: GPLv2 or later
|
8 |
+
|
9 |
+
== Description ==
|
10 |
+
|
11 |
+
UpdraftPlus simplifies backups (and restoration) for your blog. Backup into the cloud (S3, FTP, and email) and restore with a single click.
|
12 |
+
|
13 |
+
== Installation ==
|
14 |
+
|
15 |
+
Standard WordPress plugin installation:
|
16 |
+
|
17 |
+
1. Upload updraftplus/ into wp-content/plugins/ (or use the built-in installers)
|
18 |
+
2. Activate the plugin via the 'Plugins' menu.
|
19 |
+
3. Go to the 'UpdraftPlus' option under settings.
|
20 |
+
4. Follow the instructions.
|
21 |
+
|
22 |
+
== Frequently Asked Questions ==
|
23 |
+
|
24 |
+
= How is this better than the original Updraft? =
|
25 |
+
|
26 |
+
You can check the changelog for changes; but the original Updraft, before I forked it, had two major problems. Firstly, it only backed up WP core tables from the database; if any of your plugins stored data in extra tables, then they were not backed up. Secondly, the database backup did not include charset information, which meant that you needed to know some SQL wizardry to actually be able to use the backup. I made UpdraftPlus out of my experience of trying to back up several sites with Updraft. Then, I added encryption for the database file for extra peace of mind, and future-proofed by getting rid of some deprecated aspects.
|
27 |
+
|
28 |
+
= I like automating WordPress, and using the command-line. Please advertise to me. =
|
29 |
+
|
30 |
+
That's very good of you, thank you. You are looking for WordShell, <a href="http://wordshell.net">http://wordshell.net</a>.
|
31 |
+
|
32 |
+
= I encrypted my database - how do I decrypt it? =
|
33 |
+
|
34 |
+
If you have the encryption key entered in your settings and you are restoring from the settings interface, then it will automatically decrypt. Otherwise, use the file example-decrypt.php found in the plugin directory.
|
35 |
+
|
36 |
+
= I lost my encryption key - what can I do? =
|
37 |
+
|
38 |
+
Nothing, probably. That's the point of an encryption key - people who don't have it can't get the data. Hire an encryption expert to build a super computer to try to break the encryption by brute force, at a tremendous price.
|
39 |
+
|
40 |
+
= I found a bug. What do I do? =
|
41 |
+
|
42 |
+
Contact me! This is a complex plugin and the only way I can ensure it's robust is to get bug reports and fix the problems that crop up. Please include as much information as you can when reporting (PHP version, your blog's site, the error you saw and how you got to the page that caused it, etcetera). If you can send a patch, that's even better.
|
43 |
+
|
44 |
+
== Upgrade Notice ==
|
45 |
+
New fork of Updraft, fixing some serious bugs and adding new features
|
46 |
+
|
47 |
+
== Changelog ==
|
48 |
+
|
49 |
+
= 0.7.4 - 05/21/2012 =
|
50 |
+
* Removed CloudFront method; I have no way of testing this
|
51 |
+
* Backup all tables found in the database that have this site's table prefix
|
52 |
+
* If encryption fails, then abort (don't revert to not encrypting)
|
53 |
+
* Added ability to decrypt encrypted database backups
|
54 |
+
* Added ability to opt out of backing up each file group
|
55 |
+
* Now adds database character set, the lack of which before made database backups unusable without modifications
|
56 |
+
* Version number bump to make sure that this is an improvement on the original Updraft, and is now tried and tested
|
57 |
+
|
58 |
+
= 0.1.3 - 01/16/2012 =
|
59 |
+
* Force backup of all tables found in database (vanilla Updraft only backed up WP core tables)
|
60 |
+
* Tweak notification email to include site name
|
61 |
+
|
62 |
+
= 0.1 - 08/10/2011 =
|
63 |
+
|
64 |
+
* A fork of Updraft 0.6.1 by Paul Kehrer with the following improvements
|
65 |
+
* Replaced deprecated function calls (in WordPress 3.2.1)
|
66 |
+
* Removed all warnings from basic admin page with WP_DEBUG on
|
67 |
+
* Implemented encrypted backup (but not yet automatic restoration) on database
|
68 |
+
* Some de-uglification of admin interface
|
69 |
+
|
70 |
+
|
71 |
+
== Screenshots ==
|
72 |
+
|
73 |
+
1. Configuration page
|
74 |
+
|
75 |
+
|
76 |
+
== License ==
|
77 |
+
|
78 |
+
Portions copyright 2011-2 David Anderson
|
79 |
+
Portions copyright 2010 Paul Kehrer
|
80 |
+
|
81 |
+
This program is free software; you can redistribute it and/or modify
|
82 |
+
it under the terms of the GNU General Public License as published by
|
83 |
+
the Free Software Foundation; either version 2 of the License, or
|
84 |
+
(at your option) any later version.
|
85 |
+
|
86 |
+
This program is distributed in the hope that it will be useful,
|
87 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
88 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
89 |
+
GNU General Public License for more details.
|
90 |
+
|
91 |
+
You should have received a copy of the GNU General Public License
|
92 |
+
along with this program; if not, write to the Free Software
|
93 |
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
94 |
+
|
screenshot-1.png
ADDED
Binary file
|
updraftplus.php
ADDED
@@ -0,0 +1,1501 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin Name: UpdraftPlus - Backup/Restore
|
4 |
+
Plugin URI: http://wordpress.org/extend/plugins/updraftplus
|
5 |
+
Description: UpdraftPlus - Backup/Restore is a plugin designed to back up your WordPress blog. Uploads, themes, plugins, and your DB can be backed up to Amazon S3, sent to an FTP server, or even emailed to you on a scheduled basis.
|
6 |
+
Author: David Anderson.
|
7 |
+
Version: 0.7.4
|
8 |
+
Author URI: http://wordshell.net
|
9 |
+
*/
|
10 |
+
|
11 |
+
//TODO:
|
12 |
+
//Struggles with large uploads - runs out of time before finishing. Break into chunks? Resume download on later run? (Add a new scheduled event to check on progress? Separate the upload from the creation?). Add in some logging (in a .php file that exists first).
|
13 |
+
//More logging
|
14 |
+
//improve error reporting. s3 and dir backup have decent reporting now, but not sure i know what to do from here
|
15 |
+
//better implementation of retain. one that isn't dependent on being inside the cloud_backup method
|
16 |
+
//list backups that aren't tracked (helps with double backup problem)
|
17 |
+
//refactor db backup methods a bit. give full credit to wp-db-backup
|
18 |
+
//investigate $php_errormsg further
|
19 |
+
//pretty up return messages in admin area
|
20 |
+
//check s3/ftp download
|
21 |
+
//allow upload of backup files too. (specify 1-4 files to restore)
|
22 |
+
//Add back donate link in readme.txt header. Donate link: URL
|
23 |
+
//user permissions for WP users if ( function_exists('is_site_admin') && ! is_site_admin() ) around backups?
|
24 |
+
|
25 |
+
/* More TODO:
|
26 |
+
Are all directories in wp-content covered? No; only plugins, themes, content. We should check for others and allow the user the chance to choose which ones he wants
|
27 |
+
Add turn-off-foreign-key-checks stuff into mysql dump (does WP even use these?)
|
28 |
+
Put DB and file backups onto separate schedules
|
29 |
+
Use only one entry in WP options database
|
30 |
+
Encrypt filesystem, if memory allows (and have option for abort if not); split up into multiple zips when needed
|
31 |
+
More verbose debug reports, send debug report in the email
|
32 |
+
*/
|
33 |
+
|
34 |
+
/* Portions copyright 2010 Paul Kehrer
|
35 |
+
Portions copyright 2011-12 David Anderson
|
36 |
+
|
37 |
+
This program is free software; you can redistribute it and/or modify
|
38 |
+
it under the terms of the GNU General Public License as published by
|
39 |
+
the Free Software Foundation; either version 2 of the License, or
|
40 |
+
(at your option) any later version.
|
41 |
+
|
42 |
+
This program is distributed in the hope that it will be useful,
|
43 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
44 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
45 |
+
GNU General Public License for more details.
|
46 |
+
|
47 |
+
You should have received a copy of the GNU General Public License
|
48 |
+
along with this program; if not, write to the Free Software
|
49 |
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
50 |
+
*/
|
51 |
+
// TODO: Note this might *lower* the limit - should check first.
|
52 |
+
|
53 |
+
@set_time_limit(900); //15 minutes max. i'm not sure how long a really big blog could take to back up?
|
54 |
+
|
55 |
+
$updraft = new UpdraftPlus();
|
56 |
+
|
57 |
+
if(!$updraft->memory_check(192)) {
|
58 |
+
# TODO: Better solution is to split the backup set into manageable chunks based on this limit
|
59 |
+
@ini_set('memory_limit', '192M'); //up the memory limit for large backup files... should split the backup set into manageable chunks based on the limit
|
60 |
+
}
|
61 |
+
|
62 |
+
class UpdraftPlus {
|
63 |
+
|
64 |
+
var $version = '0.7.4';
|
65 |
+
|
66 |
+
var $dbhandle;
|
67 |
+
var $errors = array();
|
68 |
+
var $nonce;
|
69 |
+
var $backup_time;
|
70 |
+
|
71 |
+
function __construct() {
|
72 |
+
// Initialisation actions
|
73 |
+
# Create admin page
|
74 |
+
add_action('admin_menu', array($this,'add_admin_pages'));
|
75 |
+
add_action('admin_init', array($this,'admin_init'));
|
76 |
+
add_action('updraft_backup', array($this,'backup'));
|
77 |
+
add_action('wp_ajax_updraft_download_backup', array($this, 'updraft_download_backup'));
|
78 |
+
add_filter('cron_schedules', array($this,'modify_cron_schedules'));
|
79 |
+
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
80 |
+
}
|
81 |
+
|
82 |
+
# Adds the settings link under the plugin on the plugin screen.
|
83 |
+
function plugin_action_links($links, $file) {
|
84 |
+
if ($file == plugin_basename(__FILE__)){
|
85 |
+
$settings_link = '<a href="'.site_url().'/wp-admin/options-general.php?page=updraft-backuprestore.php">'.__("Settings", "wp-updates-notifier").'</a>';
|
86 |
+
array_unshift($links, $settings_link);
|
87 |
+
}
|
88 |
+
return $links;
|
89 |
+
}
|
90 |
+
|
91 |
+
function backup_time_nonce() {
|
92 |
+
$this->backup_time = time();
|
93 |
+
$this->nonce = substr(md5(time().rand()),20);
|
94 |
+
}
|
95 |
+
|
96 |
+
//scheduled wp-cron events can have a race condition here if page loads are coming fast enough, but there's nothing we can do about it.
|
97 |
+
function backup() {
|
98 |
+
//generate backup information
|
99 |
+
$this->backup_time_nonce();
|
100 |
+
|
101 |
+
//backup directories and return a numerically indexed array of file paths to the backup files
|
102 |
+
$backup_array = $this->backup_dirs();
|
103 |
+
//backup DB and return string of file path
|
104 |
+
$db_backup = $this->backup_db();
|
105 |
+
//add db path to rest of files
|
106 |
+
if(is_array($backup_array)) { $backup_array['db'] = $db_backup; }
|
107 |
+
//save this to our history so we can track backups for the retain feature
|
108 |
+
$this->save_backup_history($backup_array);
|
109 |
+
|
110 |
+
//cloud operations (S3,FTP,email,nothing)
|
111 |
+
//this also calls the retain feature at the end (done in this method to reuse existing cloud connections)
|
112 |
+
if(is_array($backup_array) && count($backup_array) >0) {
|
113 |
+
$this->cloud_backup($backup_array);
|
114 |
+
}
|
115 |
+
//delete local files if the pref is set
|
116 |
+
foreach($backup_array as $file) {
|
117 |
+
$this->delete_local($file);
|
118 |
+
}
|
119 |
+
|
120 |
+
//save the last backup info, including errors, if any
|
121 |
+
$this->save_last_backup($backup_array);
|
122 |
+
|
123 |
+
if(get_option('updraft_email') != "" && get_option('updraft_service') != 'email') {
|
124 |
+
wp_mail(get_option('updraft_email'),'Backed up: '.get_bloginfo('name').' (UpdraftPlus) '.date('Y-m-d H:i',time()),'Site: '.site_url()."\r\nUpdraftPlus WordPress backup is complete.");
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
function save_last_backup($backup_array) {
|
129 |
+
$success = (empty($this->errors))?1:0;
|
130 |
+
$last_backup = array('backup_time'=>$this->backup_time,'backup_array'=>$backup_array,'success'=>$success,'errors'=>$this->errors);
|
131 |
+
update_option('updraft_last_backup',$last_backup);
|
132 |
+
}
|
133 |
+
|
134 |
+
function cloud_backup($backup_array) {
|
135 |
+
switch(get_option('updraft_service')) {
|
136 |
+
case 's3':
|
137 |
+
if (count($backup_array) >0) { $this->s3_backup($backup_array); }
|
138 |
+
break;
|
139 |
+
case 'ftp':
|
140 |
+
if (count($backup_array) >0) { $this->ftp_backup($backup_array); }
|
141 |
+
break;
|
142 |
+
case 'email':
|
143 |
+
//files can easily get way too big for this...
|
144 |
+
foreach($backup_array as $type=>$file) {
|
145 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
146 |
+
wp_mail(get_option('updraft_email'),"WordPress Backup ".date('Y-m-d H:i',$this->backup_time),"Backup is of the $type. Be wary; email backups may fail because of file size limitations on mail servers.",null,array($fullpath));
|
147 |
+
}
|
148 |
+
//we don't break here so it goes and executes all the default behavior below as well. this gives us retain behavior for email
|
149 |
+
default:
|
150 |
+
/*retain behavior*/
|
151 |
+
$updraft_retain = get_option('updraft_retain');
|
152 |
+
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
|
153 |
+
$backup_history = $this->get_backup_history();
|
154 |
+
while (count($backup_history) > $retain) {
|
155 |
+
$backup_to_delete = array_pop($backup_history);
|
156 |
+
foreach($backup_to_delete as $file) {
|
157 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
158 |
+
@unlink($fullpath); //delete it if it's locally available
|
159 |
+
}
|
160 |
+
}
|
161 |
+
update_option('updraft_backup_history',$backup_history);
|
162 |
+
/*retain behavior*/
|
163 |
+
break;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
function s3_backup($backup_array) {
|
168 |
+
if(!class_exists('S3')) {
|
169 |
+
require_once(dirname(__FILE__).'/includes/S3.php');
|
170 |
+
}
|
171 |
+
$s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
|
172 |
+
$bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
|
173 |
+
if (@$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) {
|
174 |
+
foreach($backup_array as $file) {
|
175 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
176 |
+
if (!$s3->putObjectFile($fullpath, $bucket_name, $file)) {
|
177 |
+
$this->error("S3 Error: Failed to upload $fullpath. Error was ".$php_errormsg);
|
178 |
+
}
|
179 |
+
}
|
180 |
+
} else {
|
181 |
+
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
182 |
+
}
|
183 |
+
/*retain behavior*/
|
184 |
+
$updraft_retain = get_option('updraft_retain');
|
185 |
+
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
|
186 |
+
$backup_history = $this->get_backup_history();
|
187 |
+
while (count($backup_history) > $retain) {
|
188 |
+
$backup_to_delete = array_pop($backup_history);
|
189 |
+
foreach($backup_to_delete as $file) {
|
190 |
+
//if for some reason one of the backup files is an empty string let's skip it.
|
191 |
+
if($file == '') {
|
192 |
+
continue;
|
193 |
+
}
|
194 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
195 |
+
@unlink($fullpath); //delete it if it's locally available
|
196 |
+
if (!$s3->deleteObject($bucket_name, $file)) {
|
197 |
+
$this->error("S3 Error: Failed to delete object $file. Error was ".$php_errormsg);
|
198 |
+
}
|
199 |
+
}
|
200 |
+
}
|
201 |
+
update_option('updraft_backup_history',$backup_history);
|
202 |
+
/*retain behavior*/
|
203 |
+
}
|
204 |
+
|
205 |
+
function ftp_backup($backup_array) {
|
206 |
+
if( !class_exists('ftp_wrapper')) {
|
207 |
+
require_once(dirname(__FILE__).'/includes/ftp.class.php');
|
208 |
+
}
|
209 |
+
//handle SSL and errors at some point TODO
|
210 |
+
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
|
211 |
+
$ftp->passive = true;
|
212 |
+
$ftp->connect();
|
213 |
+
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
214 |
+
|
215 |
+
$ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
|
216 |
+
foreach($backup_array as $file) {
|
217 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
218 |
+
$ftp->put($fullpath,$ftp_remote_path.$file,FTP_BINARY);
|
219 |
+
}
|
220 |
+
|
221 |
+
/*retain behavior*/
|
222 |
+
$updraft_retain = get_option('updraft_retain');
|
223 |
+
$retain = (isset($updraft_retain))?get_option('updraft_retain'):1;
|
224 |
+
$backup_history = $this->get_backup_history();
|
225 |
+
while (count($backup_history) > $retain) {
|
226 |
+
$backup_to_delete = array_pop($backup_history);
|
227 |
+
foreach($backup_to_delete as $file) {
|
228 |
+
//if for some reason one of the backup files is an empty string let's skip it.
|
229 |
+
if($file == '') {
|
230 |
+
continue;
|
231 |
+
}
|
232 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
233 |
+
@unlink($fullpath); //delete it if it's locally available
|
234 |
+
@$ftp->delete($ftp_remote_path.$file);
|
235 |
+
}
|
236 |
+
}
|
237 |
+
update_option('updraft_backup_history',$backup_history);
|
238 |
+
/*retain behavior*/
|
239 |
+
}
|
240 |
+
|
241 |
+
function delete_local($file) {
|
242 |
+
if(get_option('updraft_delete_local')) {
|
243 |
+
//need error checking so we don't delete what isn't successfully uploaded?
|
244 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
245 |
+
return unlink($fullpath);
|
246 |
+
}
|
247 |
+
return true;
|
248 |
+
}
|
249 |
+
|
250 |
+
function backup_dirs() {
|
251 |
+
if(!$this->backup_time) {
|
252 |
+
$this->backup_time_nonce();
|
253 |
+
}
|
254 |
+
$wp_themes_dir = WP_CONTENT_DIR.'/themes';
|
255 |
+
$wp_upload_dir = wp_upload_dir();
|
256 |
+
$wp_upload_dir = $wp_upload_dir['basedir'];
|
257 |
+
$wp_plugins_dir = WP_PLUGIN_DIR;
|
258 |
+
if(!class_exists('PclZip')) {
|
259 |
+
if (file_exists(ABSPATH.'/wp-admin/includes/class-pclzip.php')) {
|
260 |
+
require_once(ABSPATH.'/wp-admin/includes/class-pclzip.php');
|
261 |
+
}
|
262 |
+
}
|
263 |
+
$updraft_dir = $this->backups_dir_location();
|
264 |
+
if(!is_writable($updraft_dir)) {
|
265 |
+
$this->error('Backup directory is not writable.','fatal');
|
266 |
+
}
|
267 |
+
//get the blog name and rip out all non-alphanumeric chars other than _
|
268 |
+
$blog_name = str_replace(' ','_',get_bloginfo());
|
269 |
+
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name);
|
270 |
+
if(!$blog_name) {
|
271 |
+
$blog_name = 'non_alpha_name';
|
272 |
+
}
|
273 |
+
|
274 |
+
$backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce;
|
275 |
+
|
276 |
+
$backup_array = array();
|
277 |
+
|
278 |
+
# Plugins
|
279 |
+
if (get_option('updraft_include_plugins', true)) {
|
280 |
+
$plugins = new PclZip($backup_file_base.'-plugins.zip');
|
281 |
+
if (!$plugins->create($wp_plugins_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
282 |
+
$this->error('Could not create plugins zip. Error was '.$php_errmsg,'fatal');
|
283 |
+
}
|
284 |
+
$backup_array['plugins'] = basename($backup_file_base.'-plugins.zip');
|
285 |
+
}
|
286 |
+
|
287 |
+
# Themes
|
288 |
+
if (get_option('updraft_include_themes', true)) {
|
289 |
+
$themes = new PclZip($backup_file_base.'-themes.zip');
|
290 |
+
if (!$themes->create($wp_themes_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
291 |
+
$this->error('Could not create themes zip. Error was '.$php_errmsg,'fatal');
|
292 |
+
}
|
293 |
+
$backup_array['themes'] = basename($backup_file_base.'-themes.zip');
|
294 |
+
}
|
295 |
+
|
296 |
+
# Uploads
|
297 |
+
if (get_option('updraft_include_uploads', true)) {
|
298 |
+
$uploads = new PclZip($backup_file_base.'-uploads.zip');
|
299 |
+
if (!$uploads->create($wp_upload_dir,PCLZIP_OPT_REMOVE_PATH,WP_CONTENT_DIR)) {
|
300 |
+
$this->error('Could not create uploads zip. Error was '.$php_errmsg,'fatal');
|
301 |
+
}
|
302 |
+
$backup_array['uploads'] = basename($backup_file_base.'-uploads.zip');
|
303 |
+
}
|
304 |
+
|
305 |
+
return $backup_array;
|
306 |
+
}
|
307 |
+
|
308 |
+
function save_backup_history($backup_array) {
|
309 |
+
//this stores full paths right now. should probably concatenate with ABSPATH to make it easier to move blogs
|
310 |
+
$backup_history = get_option('updraft_backup_history');
|
311 |
+
$backup_history = (!is_array($backup_history))?array():$backup_history;
|
312 |
+
if(is_array($backup_array)) {
|
313 |
+
$backup_history[$this->backup_time] = $backup_array;
|
314 |
+
update_option('updraft_backup_history',$backup_history);
|
315 |
+
} else {
|
316 |
+
$this->error('Could not save backup history because we have no backup array. Backup probably failed.');
|
317 |
+
}
|
318 |
+
}
|
319 |
+
|
320 |
+
function get_backup_history() {
|
321 |
+
//$backup_history = get_option('updraft_backup_history');
|
322 |
+
//by doing a raw DB query to get the most up-to-date data from this option we slightly narrow the window for the multiple-cron race condition
|
323 |
+
global $wpdb;
|
324 |
+
$backup_history = @unserialize($wpdb->get_var($wpdb->prepare("SELECT option_value from $wpdb->options WHERE option_name='updraft_backup_history'")));
|
325 |
+
if(is_array($backup_history)) {
|
326 |
+
krsort($backup_history); //reverse sort so earliest backup is last on the array. this way we can array_pop
|
327 |
+
} else {
|
328 |
+
$backup_history = array();
|
329 |
+
}
|
330 |
+
return $backup_history;
|
331 |
+
}
|
332 |
+
|
333 |
+
|
334 |
+
/*START OF WB-DB-BACKUP BLOCK*/
|
335 |
+
|
336 |
+
function backup_db() {
|
337 |
+
global $table_prefix, $wpdb;
|
338 |
+
if(!$this->backup_time) {
|
339 |
+
$this->backup_time_nonce();
|
340 |
+
}
|
341 |
+
|
342 |
+
$all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N);
|
343 |
+
$all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables);
|
344 |
+
|
345 |
+
$updraft_dir = $this->backups_dir_location();
|
346 |
+
//get the blog name and rip out all non-alphanumeric chars other than _
|
347 |
+
$blog_name = str_replace(' ','_',get_bloginfo());
|
348 |
+
$blog_name = preg_replace('/[^A-Za-z0-9_]/','', $blog_name);
|
349 |
+
if(!$blog_name) {
|
350 |
+
$blog_name = 'non_alpha_name';
|
351 |
+
}
|
352 |
+
|
353 |
+
$backup_file_base = $updraft_dir.'/backup_'.date('Y-m-d-Hi',$this->backup_time).'_'.$blog_name.'_'.$this->nonce;
|
354 |
+
if (is_writable($updraft_dir)) {
|
355 |
+
if (function_exists('gzopen')) {
|
356 |
+
$this->dbhandle = @gzopen($backup_file_base.'-db.gz','w');
|
357 |
+
} else {
|
358 |
+
$this->dbhandle = @fopen($backup_file_base.'-db.gz', 'w');
|
359 |
+
}
|
360 |
+
if(!$this->dbhandle) {
|
361 |
+
//$this->error(__('Could not open the backup file for writing!','wp-db-backup'));
|
362 |
+
}
|
363 |
+
} else {
|
364 |
+
//$this->error(__('The backup directory is not writable!','wp-db-backup'));
|
365 |
+
}
|
366 |
+
|
367 |
+
|
368 |
+
//Begin new backup of MySql
|
369 |
+
$this->stow("# " . __('WordPress MySQL database backup','wp-db-backup') . "\n");
|
370 |
+
$this->stow("#\n");
|
371 |
+
$this->stow("# " . sprintf(__('Generated: %s','wp-db-backup'),date("l j. F Y H:i T")) . "\n");
|
372 |
+
$this->stow("# " . sprintf(__('Hostname: %s','wp-db-backup'),DB_HOST) . "\n");
|
373 |
+
$this->stow("# " . sprintf(__('Database: %s','wp-db-backup'),$this->backquote(DB_NAME)) . "\n");
|
374 |
+
$this->stow("# --------------------------------------------------------\n");
|
375 |
+
|
376 |
+
|
377 |
+
if (defined("DB_CHARSET")) {
|
378 |
+
$this->stow("/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n");
|
379 |
+
$this->stow("/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n");
|
380 |
+
$this->stow("/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n");
|
381 |
+
$this->stow("/*!40101 SET NAMES " . DB_CHARSET . " */;\n");
|
382 |
+
}
|
383 |
+
|
384 |
+
foreach ($all_tables as $table) {
|
385 |
+
// Increase script execution time-limit to 15 min for every table.
|
386 |
+
if ( !ini_get('safe_mode')) @set_time_limit(15*60);
|
387 |
+
if ( strpos($table, $table_prefix) == 0 ) {
|
388 |
+
// Create the SQL statements
|
389 |
+
$this->stow("# --------------------------------------------------------\n");
|
390 |
+
$this->stow("# " . sprintf(__('Table: %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
391 |
+
$this->stow("# --------------------------------------------------------\n");
|
392 |
+
$this->backup_table($table);
|
393 |
+
} else {
|
394 |
+
$this->stow("# --------------------------------------------------------\n");
|
395 |
+
$this->stow("# " . sprintf(__('Skipping non-WP table: %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
396 |
+
$this->stow("# --------------------------------------------------------\n");
|
397 |
+
}
|
398 |
+
}
|
399 |
+
|
400 |
+
if (defined("DB_CHARSET")) {
|
401 |
+
$this->stow("/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n");
|
402 |
+
$this->stow("/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n");
|
403 |
+
$this->stow("/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
|
404 |
+
}
|
405 |
+
|
406 |
+
$this->close($this->dbhandle);
|
407 |
+
|
408 |
+
if (count($this->errors)) {
|
409 |
+
return false;
|
410 |
+
} else {
|
411 |
+
# Encrypt, if requested
|
412 |
+
$encryption = get_option('updraft_encryptionphrase');
|
413 |
+
if (strlen($encryption) > 0) {
|
414 |
+
$encryption_error = 0;
|
415 |
+
require_once(dirname(__FILE__).'/includes/Rijndael.php');
|
416 |
+
$rijndael = new Crypt_Rijndael();
|
417 |
+
$rijndael->setKey($encryption);
|
418 |
+
$in_handle = @fopen($backup_file_base.'-db.gz','r');
|
419 |
+
$buffer = "";
|
420 |
+
while (!feof ($in_handle)) {
|
421 |
+
$buffer .= fread($in_handle, 16384);
|
422 |
+
}
|
423 |
+
fclose ($in_handle);
|
424 |
+
$out_handle = @fopen($backup_file_base.'-db.gz.crypt','w');
|
425 |
+
if (!fwrite($out_handle, $rijndael->encrypt($buffer))) {$encryption_error = 1;}
|
426 |
+
fclose ($out_handle);
|
427 |
+
if (0 == $encryption_error) {
|
428 |
+
# Delete unencrypted file
|
429 |
+
@unlink($backup_file_base.'-db.gz');
|
430 |
+
return basename($backup_file_base.'-db.gz.crypt');
|
431 |
+
} else {
|
432 |
+
$this->error("Encryption error occurred when encrypting database. Aborted.");
|
433 |
+
}
|
434 |
+
} else {
|
435 |
+
return basename($backup_file_base.'-db.gz');
|
436 |
+
}
|
437 |
+
}
|
438 |
+
|
439 |
+
} //wp_db_backup
|
440 |
+
|
441 |
+
/**
|
442 |
+
* Taken partially from phpMyAdmin and partially from
|
443 |
+
* Alain Wolf, Zurich - Switzerland
|
444 |
+
* Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
|
445 |
+
* Modified by Scott Merrill (http://www.skippy.net/)
|
446 |
+
* to use the WordPress $wpdb object
|
447 |
+
* @param string $table
|
448 |
+
* @param string $segment
|
449 |
+
* @return void
|
450 |
+
*/
|
451 |
+
function backup_table($table, $segment = 'none') {
|
452 |
+
global $wpdb;
|
453 |
+
|
454 |
+
$table_structure = $wpdb->get_results("DESCRIBE $table");
|
455 |
+
if (! $table_structure) {
|
456 |
+
//$this->error(__('Error getting table details','wp-db-backup') . ": $table");
|
457 |
+
return false;
|
458 |
+
}
|
459 |
+
|
460 |
+
if(($segment == 'none') || ($segment == 0)) {
|
461 |
+
// Add SQL statement to drop existing table
|
462 |
+
$this->stow("\n\n");
|
463 |
+
$this->stow("#\n");
|
464 |
+
$this->stow("# " . sprintf(__('Delete any existing table %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
465 |
+
$this->stow("#\n");
|
466 |
+
$this->stow("\n");
|
467 |
+
$this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n");
|
468 |
+
|
469 |
+
// Table structure
|
470 |
+
// Comment in SQL-file
|
471 |
+
$this->stow("\n\n");
|
472 |
+
$this->stow("#\n");
|
473 |
+
$this->stow("# " . sprintf(__('Table structure of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
474 |
+
$this->stow("#\n");
|
475 |
+
$this->stow("\n");
|
476 |
+
|
477 |
+
$create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N);
|
478 |
+
if (false === $create_table) {
|
479 |
+
$err_msg = sprintf(__('Error with SHOW CREATE TABLE for %s.','wp-db-backup'), $table);
|
480 |
+
//$this->error($err_msg);
|
481 |
+
$this->stow("#\n# $err_msg\n#\n");
|
482 |
+
}
|
483 |
+
$this->stow($create_table[0][1] . ' ;');
|
484 |
+
|
485 |
+
if (false === $table_structure) {
|
486 |
+
$err_msg = sprintf(__('Error getting table structure of %s','wp-db-backup'), $table);
|
487 |
+
//$this->error($err_msg);
|
488 |
+
$this->stow("#\n# $err_msg\n#\n");
|
489 |
+
}
|
490 |
+
|
491 |
+
// Comment in SQL-file
|
492 |
+
$this->stow("\n\n");
|
493 |
+
$this->stow("#\n");
|
494 |
+
$this->stow('# ' . sprintf(__('Data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
495 |
+
$this->stow("#\n");
|
496 |
+
}
|
497 |
+
|
498 |
+
if(($segment == 'none') || ($segment >= 0)) {
|
499 |
+
$defs = array();
|
500 |
+
$ints = array();
|
501 |
+
foreach ($table_structure as $struct) {
|
502 |
+
if ( (0 === strpos($struct->Type, 'tinyint')) ||
|
503 |
+
(0 === strpos(strtolower($struct->Type), 'smallint')) ||
|
504 |
+
(0 === strpos(strtolower($struct->Type), 'mediumint')) ||
|
505 |
+
(0 === strpos(strtolower($struct->Type), 'int')) ||
|
506 |
+
(0 === strpos(strtolower($struct->Type), 'bigint')) ) {
|
507 |
+
$defs[strtolower($struct->Field)] = ( null === $struct->Default ) ? 'NULL' : $struct->Default;
|
508 |
+
$ints[strtolower($struct->Field)] = "1";
|
509 |
+
}
|
510 |
+
}
|
511 |
+
|
512 |
+
|
513 |
+
// Batch by $row_inc
|
514 |
+
if ( ! defined('ROWS_PER_SEGMENT') ) {
|
515 |
+
define('ROWS_PER_SEGMENT', 100);
|
516 |
+
}
|
517 |
+
|
518 |
+
if($segment == 'none') {
|
519 |
+
$row_start = 0;
|
520 |
+
$row_inc = ROWS_PER_SEGMENT;
|
521 |
+
} else {
|
522 |
+
$row_start = $segment * ROWS_PER_SEGMENT;
|
523 |
+
$row_inc = ROWS_PER_SEGMENT;
|
524 |
+
}
|
525 |
+
do {
|
526 |
+
// don't include extra stuff, if so requested
|
527 |
+
$excs = array('revisions' => 0, 'spam' => 1); //TODO, FIX THIS
|
528 |
+
$where = '';
|
529 |
+
if ( is_array($excs['spam'] ) && in_array($table, $excs['spam']) ) {
|
530 |
+
$where = ' WHERE comment_approved != "spam"';
|
531 |
+
} elseif ( is_array($excs['revisions'] ) && in_array($table, $excs['revisions']) ) {
|
532 |
+
$where = ' WHERE post_type != "revision"';
|
533 |
+
}
|
534 |
+
|
535 |
+
if ( !ini_get('safe_mode')) @set_time_limit(15*60);
|
536 |
+
$table_data = $wpdb->get_results("SELECT * FROM $table $where LIMIT {$row_start}, {$row_inc}", ARRAY_A);
|
537 |
+
$entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES (';
|
538 |
+
// \x08\\x09, not required
|
539 |
+
$search = array("\x00", "\x0a", "\x0d", "\x1a");
|
540 |
+
$replace = array('\0', '\n', '\r', '\Z');
|
541 |
+
if($table_data) {
|
542 |
+
foreach ($table_data as $row) {
|
543 |
+
$values = array();
|
544 |
+
foreach ($row as $key => $value) {
|
545 |
+
if ($ints[strtolower($key)]) {
|
546 |
+
// make sure there are no blank spots in the insert syntax,
|
547 |
+
// yet try to avoid quotation marks around integers
|
548 |
+
$value = ( null === $value || '' === $value) ? $defs[strtolower($key)] : $value;
|
549 |
+
$values[] = ( '' === $value ) ? "''" : $value;
|
550 |
+
} else {
|
551 |
+
$values[] = "'" . str_replace($search, $replace, $this->sql_addslashes($value)) . "'";
|
552 |
+
}
|
553 |
+
}
|
554 |
+
$this->stow(" \n" . $entries . implode(', ', $values) . ');');
|
555 |
+
}
|
556 |
+
$row_start += $row_inc;
|
557 |
+
}
|
558 |
+
} while((count($table_data) > 0) and ($segment=='none'));
|
559 |
+
}
|
560 |
+
|
561 |
+
if(($segment == 'none') || ($segment < 0)) {
|
562 |
+
// Create footer/closing comment in SQL-file
|
563 |
+
$this->stow("\n");
|
564 |
+
$this->stow("#\n");
|
565 |
+
$this->stow("# " . sprintf(__('End of data contents of table %s','wp-db-backup'),$this->backquote($table)) . "\n");
|
566 |
+
$this->stow("# --------------------------------------------------------\n");
|
567 |
+
$this->stow("\n");
|
568 |
+
}
|
569 |
+
} // end backup_table()
|
570 |
+
|
571 |
+
|
572 |
+
function stow($query_line) {
|
573 |
+
if (function_exists('gzopen')) {
|
574 |
+
if(! @gzwrite($this->dbhandle, $query_line)) {
|
575 |
+
//$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg);
|
576 |
+
}
|
577 |
+
} else {
|
578 |
+
if(false === @fwrite($this->dbhandle, $query_line)) {
|
579 |
+
//$this->error(__('There was an error writing a line to the backup script:','wp-db-backup') . ' ' . $query_line . ' ' . $php_errormsg);
|
580 |
+
}
|
581 |
+
}
|
582 |
+
}
|
583 |
+
|
584 |
+
|
585 |
+
function close($handle) {
|
586 |
+
if (function_exists('gzopen')) {
|
587 |
+
gzclose($handle);
|
588 |
+
} else {
|
589 |
+
fclose($handle);
|
590 |
+
}
|
591 |
+
}
|
592 |
+
|
593 |
+
/**
|
594 |
+
* Logs any error messages
|
595 |
+
* @param array $args
|
596 |
+
* @return bool
|
597 |
+
*/
|
598 |
+
function error($error,$severity='') {
|
599 |
+
$this->errors[] = array('error'=>$error,'severity'=>$severity);
|
600 |
+
if ($severity == 'fatal') {
|
601 |
+
//do something...
|
602 |
+
}
|
603 |
+
return true;
|
604 |
+
}
|
605 |
+
|
606 |
+
|
607 |
+
|
608 |
+
/**
|
609 |
+
* Add backquotes to tables and db-names in
|
610 |
+
* SQL queries. Taken from phpMyAdmin.
|
611 |
+
*/
|
612 |
+
function backquote($a_name) {
|
613 |
+
if (!empty($a_name) && $a_name != '*') {
|
614 |
+
if (is_array($a_name)) {
|
615 |
+
$result = array();
|
616 |
+
reset($a_name);
|
617 |
+
while(list($key, $val) = each($a_name))
|
618 |
+
$result[$key] = '`' . $val . '`';
|
619 |
+
return $result;
|
620 |
+
} else {
|
621 |
+
return '`' . $a_name . '`';
|
622 |
+
}
|
623 |
+
} else {
|
624 |
+
return $a_name;
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
/**
|
629 |
+
* Better addslashes for SQL queries.
|
630 |
+
* Taken from phpMyAdmin.
|
631 |
+
*/
|
632 |
+
function sql_addslashes($a_string = '', $is_like = false) {
|
633 |
+
if ($is_like) $a_string = str_replace('\\', '\\\\\\\\', $a_string);
|
634 |
+
else $a_string = str_replace('\\', '\\\\', $a_string);
|
635 |
+
return str_replace('\'', '\\\'', $a_string);
|
636 |
+
}
|
637 |
+
|
638 |
+
/*END OF WP-DB-BACKUP BLOCK */
|
639 |
+
/*END OF WP-DB-BACKUP BLOCK */
|
640 |
+
/*END OF WP-DB-BACKUP BLOCK */
|
641 |
+
/*END OF WP-DB-BACKUP BLOCK */
|
642 |
+
/*END OF WP-DB-BACKUP BLOCK */
|
643 |
+
|
644 |
+
/*
|
645 |
+
this function is both the backup scheduler and ostensibly a filter callback for saving the option.
|
646 |
+
it is called in the register_setting for the updraft_interval, which means when the admin settings
|
647 |
+
are saved it is called. it returns the actual result from wp_filter_nohtml_kses (a sanitization filter)
|
648 |
+
so the option can be properly saved. this is an UGLY HACK and there must be a better way.
|
649 |
+
*/
|
650 |
+
function schedule_backup($interval) {
|
651 |
+
//clear schedule and add new so we don't stack up scheduled backups
|
652 |
+
wp_clear_scheduled_hook('updraft_backup');
|
653 |
+
switch($interval) {
|
654 |
+
case 'daily':
|
655 |
+
case 'weekly':
|
656 |
+
case 'monthly':
|
657 |
+
wp_schedule_event(time()+300, $interval, 'updraft_backup');
|
658 |
+
break;
|
659 |
+
}
|
660 |
+
return wp_filter_nohtml_kses($interval);
|
661 |
+
}
|
662 |
+
|
663 |
+
//wp-cron only has hourly, daily and twicedaily, so we need to add weekly and monthly.
|
664 |
+
function modify_cron_schedules($schedules) {
|
665 |
+
$schedules['weekly'] = array(
|
666 |
+
'interval' => 604800,
|
667 |
+
'display' => 'Once Weekly'
|
668 |
+
);
|
669 |
+
$schedules['monthly'] = array(
|
670 |
+
'interval' => 2592000,
|
671 |
+
'display' => 'Once Monthly'
|
672 |
+
);
|
673 |
+
return $schedules;
|
674 |
+
}
|
675 |
+
|
676 |
+
function backups_dir_location() {
|
677 |
+
$updraft_dir = untrailingslashit(get_option('updraft_dir'));
|
678 |
+
$default_backup_dir = WP_CONTENT_DIR.'/updraft';
|
679 |
+
//if the option isn't set, default it to /backups inside the upload dir
|
680 |
+
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
|
681 |
+
//check for the existence of the dir and an enumeration preventer.
|
682 |
+
if(!is_dir($updraft_dir) || !is_file($updraft_dir.'/index.html') || !is_file($updraft_dir.'/.htaccess')) {
|
683 |
+
@mkdir($updraft_dir,0777,true); //recursively create the dir with 0777 permissions. 0777 is default for php creation. not ideal, but I'll get back to this
|
684 |
+
@file_put_contents($updraft_dir.'/index.html','Nothing to see here.');
|
685 |
+
@file_put_contents($updraft_dir.'/.htaccess','deny from all');
|
686 |
+
}
|
687 |
+
return $updraft_dir;
|
688 |
+
}
|
689 |
+
|
690 |
+
function updraft_download_backup() {
|
691 |
+
$type = $_POST['type'];
|
692 |
+
$timestamp = (int)$_POST['timestamp'];
|
693 |
+
$backup_history = $this->get_backup_history();
|
694 |
+
$file = $backup_history[$timestamp][$type];
|
695 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
696 |
+
if(!is_readable($fullpath)) {
|
697 |
+
//if the file doesn't exist and they're using one of the cloud options, fetch it down from the cloud.
|
698 |
+
$this->download_backup($file);
|
699 |
+
}
|
700 |
+
if(@is_readable($fullpath) && is_file($fullpath)) {
|
701 |
+
$len = filesize($fullpath);
|
702 |
+
|
703 |
+
$filearr = explode('.',$file);
|
704 |
+
//we've only got zip and gz...for now
|
705 |
+
$file_ext = array_pop($filearr);
|
706 |
+
if($file_ext == 'zip') {
|
707 |
+
header('Content-type: application/zip');
|
708 |
+
} else {
|
709 |
+
// This catches both when what was popped was 'crypt' (*-db.gz.crypt) and when it was 'gz' (unencrypted)
|
710 |
+
header('Content-type: application/x-gzip');
|
711 |
+
}
|
712 |
+
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
|
713 |
+
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
|
714 |
+
header("Content-Length: $len;");
|
715 |
+
if ($file_ext == 'crypt') {
|
716 |
+
header("Content-Disposition: attachment; filename=\"".substr($file,0,-6)."\";");
|
717 |
+
} else {
|
718 |
+
header("Content-Disposition: attachment; filename=\"$file\";");
|
719 |
+
}
|
720 |
+
ob_end_flush();
|
721 |
+
if ($file_ext == 'crypt') {
|
722 |
+
$encryption = get_option('updraft_encryptionphrase');
|
723 |
+
if ($encryption == "") {
|
724 |
+
$this->error('Decryption of database failed: the database file is encrypted, but you have no encryption key entered.');
|
725 |
+
} else {
|
726 |
+
require_once(dirname(__FILE__).'/includes/Rijndael.php');
|
727 |
+
$rijndael = new Crypt_Rijndael();
|
728 |
+
$rijndael->setKey($encryption);
|
729 |
+
$in_handle = fopen($fullpath,'r');
|
730 |
+
$ciphertext = "";
|
731 |
+
while (!feof ($in_handle)) {
|
732 |
+
$ciphertext .= fread($in_handle, 16384);
|
733 |
+
}
|
734 |
+
fclose ($in_handle);
|
735 |
+
print $rijndael->decrypt($ciphertext);
|
736 |
+
}
|
737 |
+
} else {
|
738 |
+
readfile($fullpath);
|
739 |
+
}
|
740 |
+
$this->delete_local($file);
|
741 |
+
exit; //we exit immediately because otherwise admin-ajax appends an additional zero to the end for some reason I don't understand. seriously, why die('0')?
|
742 |
+
} else {
|
743 |
+
echo 'Download failed. File '.$fullpath.' did not exist or was unreadable. If you delete local backups then S3 or FTP retrieval may have failed.';
|
744 |
+
}
|
745 |
+
}
|
746 |
+
|
747 |
+
function download_backup($file) {
|
748 |
+
switch(get_option('updraft_service')) {
|
749 |
+
case 's3':
|
750 |
+
$this->download_s3_backup($file);
|
751 |
+
break;
|
752 |
+
case 'ftp':
|
753 |
+
$this->download_ftp_backup($file);
|
754 |
+
break;
|
755 |
+
default:
|
756 |
+
$this->error('Automatic backup restoration is only available via S3, FTP, and local. Email and downloaded backup restoration must be performed manually.');
|
757 |
+
}
|
758 |
+
}
|
759 |
+
|
760 |
+
function download_s3_backup($file) {
|
761 |
+
if(!class_exists('S3')) {
|
762 |
+
require_once(dirname(__FILE__).'/includes/S3.php');
|
763 |
+
}
|
764 |
+
$s3 = new S3(get_option('updraft_s3_login'), get_option('updraft_s3_pass'));
|
765 |
+
$bucket_name = untrailingslashit(get_option('updraft_s3_remote_path'));
|
766 |
+
if (@$s3->putBucket($bucket_name, S3::ACL_PRIVATE)) {
|
767 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
768 |
+
if (!$s3->getObject($bucket_name, $file, $fullpath)) {
|
769 |
+
$this->error("S3 Error: Failed to download $fullpath. Error was ".$php_errormsg);
|
770 |
+
}
|
771 |
+
} else {
|
772 |
+
$this->error("S3 Error: Failed to create bucket $bucket_name. Error was ".$php_errormsg);
|
773 |
+
}
|
774 |
+
}
|
775 |
+
|
776 |
+
function download_ftp_backup($file) {
|
777 |
+
if( !class_exists('ftp_wrapper')) {
|
778 |
+
require_once(dirname(__FILE__).'/includes/ftp.class.php');
|
779 |
+
}
|
780 |
+
//handle SSL and errors at some point TODO
|
781 |
+
$ftp = new ftp_wrapper(get_option('updraft_server_address'),get_option('updraft_ftp_login'),get_option('updraft_ftp_pass'));
|
782 |
+
$ftp->passive = true;
|
783 |
+
$ftp->connect();
|
784 |
+
//$ftp->make_dir(); we may need to recursively create dirs? TODO
|
785 |
+
|
786 |
+
$ftp_remote_path = trailingslashit(get_option('updraft_ftp_remote_path'));
|
787 |
+
$fullpath = trailingslashit(get_option('updraft_dir')).$file;
|
788 |
+
$ftp->get($fullpath,$ftp_remote_path.$file,FTP_BINARY);
|
789 |
+
}
|
790 |
+
|
791 |
+
function restore_backup($timestamp) {
|
792 |
+
global $wp_filesystem;
|
793 |
+
$backup_history = get_option('updraft_backup_history');
|
794 |
+
if(!is_array($backup_history[$timestamp])) {
|
795 |
+
echo '<p>This backup does not exist in the backup history -- restoration aborted! timestamp: '.$timestamp.'</p><br/>';
|
796 |
+
return false;
|
797 |
+
}
|
798 |
+
|
799 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_restore&backup_timestamp=$timestamp");
|
800 |
+
WP_Filesystem($credentials);
|
801 |
+
if ( $wp_filesystem->errors->get_error_code() ) {
|
802 |
+
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
803 |
+
show_message($message);
|
804 |
+
exit;
|
805 |
+
}
|
806 |
+
|
807 |
+
//if we make it this far then WP_Filesystem has been instantiated and is functional (tested with ftpext, what about suPHP and other situations where direct may work?)
|
808 |
+
echo '<span style="font-weight:bold">Restoration Progress </span><div id="updraft-restore-progress">';
|
809 |
+
|
810 |
+
$updraft_dir = trailingslashit(get_option('updraft_dir'));
|
811 |
+
foreach($backup_history[$timestamp] as $type=>$file) {
|
812 |
+
$fullpath = $updraft_dir.$file;
|
813 |
+
if(!is_readable($fullpath) && $type != 'db') {
|
814 |
+
$this->download_backup($file);
|
815 |
+
}
|
816 |
+
if(is_readable($fullpath) && $type != 'db') {
|
817 |
+
if(!class_exists('WP_Upgrader')) {
|
818 |
+
require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
|
819 |
+
}
|
820 |
+
require_once('includes/updraft-restorer.php');
|
821 |
+
$restorer = new Updraft_Restorer();
|
822 |
+
$val = $restorer->restore_backup($fullpath,$type);
|
823 |
+
if(is_wp_error($val)) {
|
824 |
+
print_r($val);
|
825 |
+
echo '</div>'; //close the updraft_restore_progress div even if we error
|
826 |
+
return false;
|
827 |
+
}
|
828 |
+
}
|
829 |
+
}
|
830 |
+
echo '</div>'; //close the updraft_restore_progress div
|
831 |
+
if(ini_get('safe_mode')) {
|
832 |
+
echo "<p>DB could not be restored because safe_mode is active on your server. You will need to manually restore the file via phpMyAdmin or another method.</p><br/>";
|
833 |
+
return false;
|
834 |
+
}
|
835 |
+
return true;
|
836 |
+
}
|
837 |
+
|
838 |
+
|
839 |
+
//deletes the -old directories that are created when a backup is restored.
|
840 |
+
function delete_old_dirs() {
|
841 |
+
global $wp_filesystem;
|
842 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_delete_old_dirs");
|
843 |
+
WP_Filesystem($credentials);
|
844 |
+
if ( $wp_filesystem->errors->get_error_code() ) {
|
845 |
+
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
846 |
+
show_message($message);
|
847 |
+
exit;
|
848 |
+
}
|
849 |
+
|
850 |
+
$to_delete = array('themes-old','plugins-old','uploads-old');
|
851 |
+
|
852 |
+
foreach($to_delete as $name) {
|
853 |
+
//recursively delete
|
854 |
+
if(!$wp_filesystem->delete(WP_CONTENT_DIR.'/'.$name, true)) {
|
855 |
+
return false;
|
856 |
+
}
|
857 |
+
}
|
858 |
+
return true;
|
859 |
+
}
|
860 |
+
|
861 |
+
//scans the content dir to see if any -old dirs are present
|
862 |
+
function scan_old_dirs() {
|
863 |
+
$dirArr = scandir(WP_CONTENT_DIR);
|
864 |
+
foreach($dirArr as $dir) {
|
865 |
+
if(strpos($dir,'-old') !== false) {
|
866 |
+
return true;
|
867 |
+
}
|
868 |
+
}
|
869 |
+
return false;
|
870 |
+
}
|
871 |
+
|
872 |
+
|
873 |
+
function retain_range($input) {
|
874 |
+
$input = (int)$input;
|
875 |
+
if($input > 0 && $input < 3650) {
|
876 |
+
return $input;
|
877 |
+
} else {
|
878 |
+
return 1;
|
879 |
+
}
|
880 |
+
}
|
881 |
+
|
882 |
+
function create_backup_dir() {
|
883 |
+
global $wp_filesystem;
|
884 |
+
$credentials = request_filesystem_credentials("options-general.php?page=updraft-backuprestore.php&action=updraft_create_backup_dir");
|
885 |
+
WP_Filesystem($credentials);
|
886 |
+
if ( $wp_filesystem->errors->get_error_code() ) {
|
887 |
+
foreach ( $wp_filesystem->errors->get_error_messages() as $message )
|
888 |
+
show_message($message);
|
889 |
+
exit;
|
890 |
+
}
|
891 |
+
|
892 |
+
$updraft_dir = untrailingslashit(get_option('updraft_dir'));
|
893 |
+
$default_backup_dir = WP_CONTENT_DIR.'/updraft';
|
894 |
+
$updraft_dir = ($updraft_dir)?$updraft_dir:$default_backup_dir;
|
895 |
+
|
896 |
+
//chmod the backup dir to 0777. ideally we'd rather chgrp it but i'm not sure if it's possible to detect the group apache is running under (or what if it's not apache...)
|
897 |
+
if(!$wp_filesystem->mkdir($updraft_dir, 0777)) {
|
898 |
+
return false;
|
899 |
+
}
|
900 |
+
return true;
|
901 |
+
}
|
902 |
+
|
903 |
+
|
904 |
+
function memory_check_current() {
|
905 |
+
# Returns in megabytes
|
906 |
+
$memory_limit = ini_get('memory_limit');
|
907 |
+
$memory_unit = $memory_limit[strlen($memory_limit)-1];
|
908 |
+
$memory_limit = substr($memory_limit,0,strlen($memory_limit)-1);
|
909 |
+
switch($memory_unit) {
|
910 |
+
case 'K':
|
911 |
+
$memory_limit = $memory_limit/1024;
|
912 |
+
break;
|
913 |
+
case 'G':
|
914 |
+
$memory_limit = $memory_limit*1024;
|
915 |
+
break;
|
916 |
+
case 'M':
|
917 |
+
//assumed size, no change needed
|
918 |
+
break;
|
919 |
+
}
|
920 |
+
return $memory_limit;
|
921 |
+
}
|
922 |
+
|
923 |
+
function memory_check($memory) {
|
924 |
+
$memory_limit = $this->memory_check_current();
|
925 |
+
return ($memory_limit >= $memory)?true:false;
|
926 |
+
}
|
927 |
+
|
928 |
+
function execution_time_check($time) {
|
929 |
+
return (ini_get('max_execution_time') >= $time)?true:false;
|
930 |
+
}
|
931 |
+
|
932 |
+
function admin_init() {
|
933 |
+
if(get_option('updraft_debug_mode')) {
|
934 |
+
ini_set('display_errors',1);
|
935 |
+
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
|
936 |
+
ini_set('track_errors',1);
|
937 |
+
}
|
938 |
+
wp_enqueue_script('jquery');
|
939 |
+
register_setting( 'updraft-options-group', 'updraft_interval', array($this,'schedule_backup') );
|
940 |
+
register_setting( 'updraft-options-group', 'updraft_retain', array($this,'retain_range') );
|
941 |
+
register_setting( 'updraft-options-group', 'updraft_encryptionphrase', 'wp_filter_nohtml_kses' );
|
942 |
+
register_setting( 'updraft-options-group', 'updraft_service', 'wp_filter_nohtml_kses' );
|
943 |
+
register_setting( 'updraft-options-group', 'updraft_s3_login', 'wp_filter_nohtml_kses' );
|
944 |
+
register_setting( 'updraft-options-group', 'updraft_s3_pass', 'wp_filter_nohtml_kses' );
|
945 |
+
register_setting( 'updraft-options-group', 'updraft_ftp_login', 'wp_filter_nohtml_kses' );
|
946 |
+
register_setting( 'updraft-options-group', 'updraft_ftp_pass', 'wp_filter_nohtml_kses' );
|
947 |
+
register_setting( 'updraft-options-group', 'updraft_dir', 'wp_filter_nohtml_kses' );
|
948 |
+
register_setting( 'updraft-options-group', 'updraft_email', 'wp_filter_nohtml_kses' );
|
949 |
+
register_setting( 'updraft-options-group', 'updraft_s3_remote_path', 'wp_filter_nohtml_kses' );
|
950 |
+
register_setting( 'updraft-options-group', 'updraft_ftp_remote_path', 'wp_filter_nohtml_kses' );
|
951 |
+
register_setting( 'updraft-options-group', 'updraft_server_address', 'wp_filter_nohtml_kses' );
|
952 |
+
register_setting( 'updraft-options-group', 'updraft_delete_local', 'absint' );
|
953 |
+
register_setting( 'updraft-options-group', 'updraft_debug_mode', 'absint' );
|
954 |
+
register_setting( 'updraft-options-group', 'updraft_include_plugins', 'absint' );
|
955 |
+
register_setting( 'updraft-options-group', 'updraft_include_themes', 'absint' );
|
956 |
+
register_setting( 'updraft-options-group', 'updraft_include_uploads', 'absint' );
|
957 |
+
|
958 |
+
if (current_user_can('manage_options')) {
|
959 |
+
$updraft_dir = $this->backups_dir_location();
|
960 |
+
if(strpos($updraft_dir,WP_CONTENT_DIR) !== false) {
|
961 |
+
$relative_dir = str_replace(WP_CONTENT_DIR,'',$updraft_dir);
|
962 |
+
$possible_updraft_url = WP_CONTENT_URL.$relative_dir;
|
963 |
+
$resp = wp_remote_request($possible_updraft_url, array('timeout' => 15));
|
964 |
+
if ( is_wp_error($resp) ) {
|
965 |
+
add_action('admin_notices', array($this,'show_admin_warning_accessible_unknownresult') );
|
966 |
+
} else {
|
967 |
+
if(strpos($resp['response']['code'],'403') === false) {
|
968 |
+
add_action('admin_notices', array($this,'show_admin_warning_accessible') );
|
969 |
+
}
|
970 |
+
}
|
971 |
+
if (isset($dir_protection_info)) {
|
972 |
+
}
|
973 |
+
}
|
974 |
+
}
|
975 |
+
}
|
976 |
+
|
977 |
+
|
978 |
+
function add_admin_pages() {
|
979 |
+
add_submenu_page('options-general.php', "UpdraftPlus", "UpdraftPlus", "manage_options", "updraft-backuprestore.php",
|
980 |
+
array($this,"settings_output"));
|
981 |
+
}
|
982 |
+
|
983 |
+
function wordshell_random_advert() {
|
984 |
+
if (rand(0,1) == 0) {
|
985 |
+
return 'Like automating WordPress operations? Use the CLI? <a href="http://wordshell.net">You will love WordShell</a> - saves time and money fast.';
|
986 |
+
} else {
|
987 |
+
return '<a href="http://wordshell.net">Check out WordShell</a> - manage WordPress from the command line - huge time-saver';
|
988 |
+
}
|
989 |
+
}
|
990 |
+
|
991 |
+
function settings_output() {
|
992 |
+
|
993 |
+
$ws_advert = $this->wordshell_random_advert();
|
994 |
+
echo <<<ENDHERE
|
995 |
+
<div class="updated fade" style="font-size:140%; padding:14px;">${ws_advert}</div>
|
996 |
+
ENDHERE;
|
997 |
+
|
998 |
+
/*
|
999 |
+
we use request here because the initial restore is triggered by a POSTed form. we then may need to obtain credentials
|
1000 |
+
for the WP_Filesystem. to do this WP outputs a form that we can't insert variables into (apparently). So the values are
|
1001 |
+
passed back in as GET parameters. REQUEST covers both GET and POST so this weird logic works.
|
1002 |
+
*/
|
1003 |
+
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_restore' && isset($_REQUEST['backup_timestamp'])) {
|
1004 |
+
$backup_success = $this->restore_backup($_REQUEST['backup_timestamp']);
|
1005 |
+
if(empty($this->errors) && $backup_success == true) {
|
1006 |
+
echo '<p>Restore successful!</p><br/>';
|
1007 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php&updraft_restore_success=true">Return to Updraft Configuration</a>.';
|
1008 |
+
return;
|
1009 |
+
} else {
|
1010 |
+
echo '<p>Restore failed...</p><br/>';
|
1011 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.';
|
1012 |
+
return;
|
1013 |
+
}
|
1014 |
+
//uncomment the below once i figure out how i want the flow of a restoration to work.
|
1015 |
+
//echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.';
|
1016 |
+
}
|
1017 |
+
$deleted_old_dirs = false;
|
1018 |
+
if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'updraft_delete_old_dirs') {
|
1019 |
+
if($this->delete_old_dirs()) {
|
1020 |
+
$deleted_old_dirs = true;
|
1021 |
+
} else {
|
1022 |
+
echo '<p>Old directory removal failed for some reason. You may want to do this manually.</p><br/>';
|
1023 |
+
}
|
1024 |
+
echo '<p>Old directories successfully removed.</p><br/>';
|
1025 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.';
|
1026 |
+
return;
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
if(isset($_GET['action']) && $_GET['action'] == 'updraft_create_backup_dir') {
|
1030 |
+
if(!$this->create_backup_dir()) {
|
1031 |
+
echo '<p>Backup directory could not be created...</p><br/>';
|
1032 |
+
}
|
1033 |
+
echo '<p>Backup directory successfully created.</p><br/>';
|
1034 |
+
echo '<b>Actions:</b> <a href="options-general.php?page=updraft-backuprestore.php">Return to Updraft Configuration</a>.';
|
1035 |
+
return;
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup') {
|
1039 |
+
wp_schedule_single_event(time()+3, 'updraft_backup');
|
1040 |
+
}
|
1041 |
+
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_all') {
|
1042 |
+
$this->backup();
|
1043 |
+
}
|
1044 |
+
if(isset($_POST['action']) && $_POST['action'] == 'updraft_backup_debug_db') {
|
1045 |
+
$this->backup_db();
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
?>
|
1049 |
+
<div class="wrap">
|
1050 |
+
<h2>UpdraftPlus - Backup/Restore</h2>
|
1051 |
+
|
1052 |
+
Version: <b><?php echo $this->version; ?></b><br />
|
1053 |
+
Maintained by <b>David Anderson</b> (<a href="http://david.dw-perspective.org.uk">Homepage</a> | <a href="http://wordshell.net">WordShell - WordPress command line</a> )
|
1054 |
+
<br />
|
1055 |
+
Based on Updraft by <b>Paul Kehrer</b> (<a href="http://langui.sh" target="_blank">Blog</a> | <a href="http://twitter.com/reaperhulk" target="_blank">Twitter</a> )
|
1056 |
+
<br />
|
1057 |
+
<?php
|
1058 |
+
if(isset($_GET['updraft_restore_success'])) {
|
1059 |
+
echo "<div style=\"color:blue\">Your backup has been restored. Your old themes, uploads, and plugins directories have been retained with \"-old\" appended to their name. Remove them when you are satisfied that the backup worked properly. At this time Updraft does not automatically restore your DB. You will need to use an external tool like phpMyAdmin to perform that task.</div>";
|
1060 |
+
}
|
1061 |
+
if($deleted_old_dirs) {
|
1062 |
+
echo "<div style=\"color:blue\">Old directories successfully deleted.</div>";
|
1063 |
+
}
|
1064 |
+
if(!$this->memory_check(96)) {?>
|
1065 |
+
<div style="color:orange">Your PHP memory limit is too low. Updraft attempted to raise it but was unsuccessful. This plugin may not work properly with a memory limit of less than 96 Mb. Current limit is: <?php echo $this->memory_check_current(); ?> Mb</div>
|
1066 |
+
<?php
|
1067 |
+
}
|
1068 |
+
if(!$this->execution_time_check(300)) {?>
|
1069 |
+
<div style="color:orange">Your PHP max_execution_time is less than 300 seconds. This probably means you're running in safe_mode. Either disable safe_mode or modify your php.ini to set max_execution_time to a higher number. If you do not, there is a chance Updraft will be unable to complete a backup. Present limit is: <?php echo ini_get('max_execution_time'); ?> seconds.</div>
|
1070 |
+
<?php
|
1071 |
+
}
|
1072 |
+
|
1073 |
+
if($this->scan_old_dirs()) {?>
|
1074 |
+
<div style="color:orange">You have old directories from a previous backup. Click to delete them after you have verified that the restoration worked.</div>
|
1075 |
+
<form method="post" action="<?php echo remove_query_arg(array('updraft_restore_success','action')) ?>">
|
1076 |
+
<input type="hidden" name="action" value="updraft_delete_old_dirs" />
|
1077 |
+
<input type="submit" class="button-primary" value="Delete Old Dirs" onclick="return(confirm('Are you sure you want to delete the old directories? This cannot be undone.'))" />
|
1078 |
+
</form>
|
1079 |
+
<?php
|
1080 |
+
}
|
1081 |
+
if(!empty($this->errors)) {
|
1082 |
+
foreach($this->errors as $error) {
|
1083 |
+
//ignoring severity here right now
|
1084 |
+
echo '<div style="color:red">'.$error['error'].'</div>';
|
1085 |
+
}
|
1086 |
+
}
|
1087 |
+
?>
|
1088 |
+
<table class="form-table" style="float:left;width:475px">
|
1089 |
+
<tr>
|
1090 |
+
<?php
|
1091 |
+
$next_scheduled_backup = wp_next_scheduled('updraft_backup');
|
1092 |
+
if($next_scheduled_backup) {
|
1093 |
+
$next_scheduled_backup = date('D, F j, Y H:i T',$next_scheduled_backup);
|
1094 |
+
} else {
|
1095 |
+
$next_scheduled_backup = 'No backups are scheduled at this time.';
|
1096 |
+
}
|
1097 |
+
$current_time = date('D, F j, Y H:i T',time());
|
1098 |
+
$updraft_last_backup = get_option('updraft_last_backup');
|
1099 |
+
if($updraft_last_backup) {
|
1100 |
+
if($updraft_last_backup['success']) {
|
1101 |
+
$last_backup = date('D, F j, Y H:i T',$updraft_last_backup['backup_time']);
|
1102 |
+
$last_backup_color = 'green';
|
1103 |
+
} else {
|
1104 |
+
$last_backup = print_r($updraft_last_backup['errors'],true);
|
1105 |
+
$last_backup_color = 'red';
|
1106 |
+
}
|
1107 |
+
} else {
|
1108 |
+
$last_backup = 'No backup has been completed.';
|
1109 |
+
$last_backup_color = 'blue';
|
1110 |
+
}
|
1111 |
+
|
1112 |
+
$updraft_dir = $this->backups_dir_location();
|
1113 |
+
if(is_writable($updraft_dir)) {
|
1114 |
+
$dir_info = '<span style="color:green">Backup directory specified is writable, which is good.</span>';
|
1115 |
+
$backup_disabled = "";
|
1116 |
+
} else {
|
1117 |
+
$backup_disabled = 'disabled="disabled"';
|
1118 |
+
$dir_info = '<span style="color:red">Backup directory specified is <b>not</b> writable. <span style="font-size:110%;font-weight:bold"><a href="options-general.php?page=updraft-backuprestore.php&action=updraft_create_backup_dir">Click here</a></span> to attempt to create the directory and set the permissions. If that is unsuccessful check the permissions on your server or change it to another directory that is writable by your web server process.</span>';
|
1119 |
+
}
|
1120 |
+
?>
|
1121 |
+
<th>Current Time:</th>
|
1122 |
+
<td style="color:blue"><?php echo $current_time?></td>
|
1123 |
+
</tr>
|
1124 |
+
<tr>
|
1125 |
+
<th>Next Scheduled Backup:</th>
|
1126 |
+
<td style="color:blue"><?php echo $next_scheduled_backup?></td>
|
1127 |
+
</tr>
|
1128 |
+
<tr>
|
1129 |
+
<th>Last Backup:</th>
|
1130 |
+
<td style="color:<?php echo $last_backup_color ?>"><?php echo $last_backup?></td>
|
1131 |
+
</tr>
|
1132 |
+
</table>
|
1133 |
+
<div style="float:left;width:200px">
|
1134 |
+
<form method="post" action="">
|
1135 |
+
<input type="hidden" name="action" value="updraft_backup" />
|
1136 |
+
<p><input type="submit" <?php echo $backup_disabled ?> class="button-primary" value="Backup Now!" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('This will schedule a one time backup. To trigger the backup immediately you may need to load a page on your blog.'))" /></p>
|
1137 |
+
</form>
|
1138 |
+
<div style="position:relative">
|
1139 |
+
<div style="position:absolute;top:0;left:0">
|
1140 |
+
<?php
|
1141 |
+
$backup_history = get_option('updraft_backup_history');
|
1142 |
+
$backup_history = (is_array($backup_history))?$backup_history:array();
|
1143 |
+
$restore_disabled = (count($backup_history) == 0) ? 'disabled="disabled"' : "";
|
1144 |
+
?>
|
1145 |
+
<input type="button" class="button-primary" <?php echo $restore_disabled ?> value="Restore" style="padding-top:7px;padding-bottom:7px;font-size:24px !important" onclick="jQuery('#backup-restore').fadeIn('slow');jQuery(this).parent().fadeOut('slow')" />
|
1146 |
+
</div>
|
1147 |
+
<div style="display:none;position:absolute;top:0;left:0" id="backup-restore">
|
1148 |
+
<form method="post" action="">
|
1149 |
+
<b>Choose: </b>
|
1150 |
+
<select name="backup_timestamp" style="display:inline">
|
1151 |
+
<?php
|
1152 |
+
foreach($backup_history as $key=>$value) {
|
1153 |
+
echo "<option value='$key'>".date('Y-m-d G:i',$key)."</option>\n";
|
1154 |
+
}
|
1155 |
+
?>
|
1156 |
+
</select>
|
1157 |
+
|
1158 |
+
<input type="hidden" name="action" value="updraft_restore" />
|
1159 |
+
<input type="submit" <?php echo $restore_disabled ?> class="button-primary" value="Restore Now!" style="padding-top:7px;margin-top:5px;padding-bottom:7px;font-size:24px !important" onclick="return(confirm('Restoring from backup will replace this blog\'s themes, plugins, and uploads directories. DB restoration must be done separately at this time. Continue with the restoration process?'))" />
|
1160 |
+
</form>
|
1161 |
+
</div>
|
1162 |
+
</div>
|
1163 |
+
</div>
|
1164 |
+
<br style="clear:both" />
|
1165 |
+
<table class="form-table">
|
1166 |
+
<tr>
|
1167 |
+
<th>Download Backups</th>
|
1168 |
+
<td><a href="#" title="Click to see available backups" onclick="jQuery('.download-backups').toggle();return false;"><?php echo count($backup_history)?> available</a></td>
|
1169 |
+
</tr>
|
1170 |
+
<tr>
|
1171 |
+
<td></td><td class="download-backups" style="display:none">
|
1172 |
+
<em>Click on a button to download the corresponding file to your computer. If you are using Opera, you should turn Turbo mode off.</em>
|
1173 |
+
<table>
|
1174 |
+
<?php
|
1175 |
+
foreach($backup_history as $key=>$value) {
|
1176 |
+
?>
|
1177 |
+
<tr>
|
1178 |
+
<td><b><?php echo date('Y-m-d G:i',$key)?></b></td>
|
1179 |
+
<td>
|
1180 |
+
<?php if (isset($value['db'])) { ?>
|
1181 |
+
<form action="admin-ajax.php" method="post">
|
1182 |
+
<input type="hidden" name="action" value="updraft_download_backup" />
|
1183 |
+
<input type="hidden" name="type" value="db" />
|
1184 |
+
<input type="hidden" name="timestamp" value="<?php echo $key?>" />
|
1185 |
+
<input type="submit" value="Database" />
|
1186 |
+
</form>
|
1187 |
+
<?php } else { echo "(No database in backup)"; } ?>
|
1188 |
+
</td>
|
1189 |
+
<td>
|
1190 |
+
<?php if (isset($value['plugins'])) { ?>
|
1191 |
+
<form action="admin-ajax.php" method="post">
|
1192 |
+
<input type="hidden" name="action" value="updraft_download_backup" />
|
1193 |
+
<input type="hidden" name="type" value="plugins" />
|
1194 |
+
<input type="hidden" name="timestamp" value="<?php echo $key?>" />
|
1195 |
+
<input type="submit" value="Plugins" />
|
1196 |
+
</form>
|
1197 |
+
<?php } else { echo "(No plugins in backup)"; } ?>
|
1198 |
+
</td>
|
1199 |
+
<td>
|
1200 |
+
<?php if (isset($value['themes'])) { ?>
|
1201 |
+
<form action="admin-ajax.php" method="post">
|
1202 |
+
<input type="hidden" name="action" value="updraft_download_backup" />
|
1203 |
+
<input type="hidden" name="type" value="themes" />
|
1204 |
+
<input type="hidden" name="timestamp" value="<?php echo $key?>" />
|
1205 |
+
<input type="submit" value="Themes" />
|
1206 |
+
</form>
|
1207 |
+
<?php } else { echo "(No themes in backup)"; } ?>
|
1208 |
+
</td>
|
1209 |
+
<td>
|
1210 |
+
<?php if (isset($value['uploads'])) { ?>
|
1211 |
+
<form action="admin-ajax.php" method="post">
|
1212 |
+
<input type="hidden" name="action" value="updraft_download_backup" />
|
1213 |
+
<input type="hidden" name="type" value="uploads" />
|
1214 |
+
<input type="hidden" name="timestamp" value="<?php echo $key?>" />
|
1215 |
+
<input type="submit" value="Uploads" />
|
1216 |
+
</form>
|
1217 |
+
<?php } else { echo "(No uploads in backup)"; } ?>
|
1218 |
+
</td>
|
1219 |
+
</tr>
|
1220 |
+
<?php }?>
|
1221 |
+
</table>
|
1222 |
+
</td>
|
1223 |
+
</tr>
|
1224 |
+
</table>
|
1225 |
+
<form method="post" action="options.php">
|
1226 |
+
<?php settings_fields('updraft-options-group'); ?>
|
1227 |
+
<table class="form-table">
|
1228 |
+
<tr>
|
1229 |
+
<th>Backup Directory:</th>
|
1230 |
+
<td><input type="text" name="updraft_dir" style="width:525px" value="<?php echo $updraft_dir ?>" /></td>
|
1231 |
+
</tr>
|
1232 |
+
<tr>
|
1233 |
+
<td></td><td><?php echo $dir_info ?> This is where Updraft Backup/Restore will write the zip files it creates initially. This directory must be writable by your web server. Typically you'll want to have it inside your wp-content folder (this is the default). <b>Do not</b> place it inside your uploads dir, as that will cause recursion issues (backups of backups of backups of...).</td>
|
1234 |
+
</tr>
|
1235 |
+
<tr>
|
1236 |
+
<th>Backup Intervals:</th>
|
1237 |
+
<td><select name="updraft_interval">
|
1238 |
+
<?php
|
1239 |
+
$intervals = array ("manual", "daily", "weekly", "monthly");
|
1240 |
+
foreach ($intervals as $ival) {
|
1241 |
+
echo "<option value=\"$ival\" ";
|
1242 |
+
if ($ival == get_option('updraft_interval')) { echo 'selected="selected"';}
|
1243 |
+
echo ">".ucfirst($ival)."</option>\n";
|
1244 |
+
}
|
1245 |
+
?>
|
1246 |
+
</select></td>
|
1247 |
+
</tr>
|
1248 |
+
<tr class="backup-interval-description">
|
1249 |
+
<td></td><td>If you would like to automatically schedule backups, choose a schedule from the dropdown above. Backups will occur at the interval specified starting five minutes after the current time. If you choose manual you must click the "Backup Now!" button to cause a backup to occur.</td>
|
1250 |
+
</tr>
|
1251 |
+
<?php
|
1252 |
+
# The true (default value if non-existent) here has the effect of forcing a default of on.
|
1253 |
+
$include_themes = (get_option('updraft_include_themes',true)) ? 'checked="checked"' : "";
|
1254 |
+
$include_plugins = (get_option('updraft_include_plugins',true)) ? 'checked="checked"' : "";
|
1255 |
+
$include_uploads = (get_option('updraft_include_uploads',true)) ? 'checked="checked"' : "";
|
1256 |
+
?>
|
1257 |
+
<tr>
|
1258 |
+
<th>Include in Backup:</th>
|
1259 |
+
<td>
|
1260 |
+
<input type="checkbox" name="updraft_include_plugins" value="1" <?php echo $include_plugins; ?> /> Plugins<br />
|
1261 |
+
<input type="checkbox" name="updraft_include_themes" value="1" <?php echo $include_themes; ?> /> Themes<br />
|
1262 |
+
<input type="checkbox" name="updraft_include_uploads" value="1" <?php echo $include_uploads; ?> /> Uploads<br />
|
1263 |
+
Include all of these, unless you are backing them up separately. Note that presently UpdraftPlus backs up these directories only - which is usually everything (except for WordPress core itself which you can download afresh from WordPress.org). But if you have made customised modifications outside of these directories, you need to back them up another way. The database is always included.<br />(<a href="http://wordshell.net">Use WordShell</a> for automatic backup, version control and patching).<br /></td>
|
1264 |
+
</td>
|
1265 |
+
</tr>
|
1266 |
+
<tr>
|
1267 |
+
<th>Retain Backups:</th>
|
1268 |
+
<?php
|
1269 |
+
$updraft_retain = get_option('updraft_retain');
|
1270 |
+
$retain = ((int)$updraft_retain > 0)?get_option('updraft_retain'):1;
|
1271 |
+
?>
|
1272 |
+
<td><input type="text" name="updraft_retain" value="<?php echo $retain ?>" style="width:50px" /></td>
|
1273 |
+
</tr>
|
1274 |
+
<tr class="backup-retain-description">
|
1275 |
+
<td></td><td>By default only the most recent backup is retained. If you'd like to preserve more, specify the number here.</td>
|
1276 |
+
</tr>
|
1277 |
+
<tr>
|
1278 |
+
<th>Encryption phrase:</th>
|
1279 |
+
<?php
|
1280 |
+
$updraft_encryptionphrase = get_option('updraft_encryptionphrase');
|
1281 |
+
?>
|
1282 |
+
<td><input type="text" name="updraft_encryptionphrase" value="<?php echo $updraft_encryptionphrase ?>" style="width:132px" /></td>
|
1283 |
+
</tr>
|
1284 |
+
<tr class="backup-crypt-description">
|
1285 |
+
<td></td><td>If you enter a string here, it is used to encrypt backups (Rijndael). Do not lose it, or all your backups will be useless. Presently, only the database file is encrypted. This is also the key used to decrypt backups from this admin interface (so if you change it, then automatic decryption will not work until you change it back). You can also use the file example-decrypt.php from inside the UpdraftPlus plugin directory to decrypt manually.</td>
|
1286 |
+
</tr>
|
1287 |
+
|
1288 |
+
<tr>
|
1289 |
+
<th>Remote backup:</th>
|
1290 |
+
<td><select name="updraft_service" id="updraft-service">
|
1291 |
+
<?php
|
1292 |
+
$delete_local = (get_option('updraft_delete_local')) ? 'checked="checked"' : "";
|
1293 |
+
$debug_mode = (get_option('updraft_debug_mode')) ? 'checked="checked"' : "";
|
1294 |
+
|
1295 |
+
$display_none = 'style="display:none"';
|
1296 |
+
$s3 = ""; $ftp = ""; $email = "";
|
1297 |
+
$email_display="";
|
1298 |
+
$display_email_complete = "";
|
1299 |
+
$set = 'selected="selected"';
|
1300 |
+
switch(get_option('updraft_service')) {
|
1301 |
+
case 's3':
|
1302 |
+
$s3 = $set;
|
1303 |
+
$ftp_display = $display_none;
|
1304 |
+
break;
|
1305 |
+
case 'ftp':
|
1306 |
+
$ftp = $set;
|
1307 |
+
$s3_display = $display_none;
|
1308 |
+
break;
|
1309 |
+
case 'email':
|
1310 |
+
$email = $set;
|
1311 |
+
$ftp_display = $display_none;
|
1312 |
+
$s3_display = $display_none;
|
1313 |
+
$display_email_complete = $display_none;
|
1314 |
+
break;
|
1315 |
+
default:
|
1316 |
+
$none = $set;
|
1317 |
+
$ftp_display = $display_none;
|
1318 |
+
$s3_display = $display_none;
|
1319 |
+
$display_delete_local = $display_none;
|
1320 |
+
break;
|
1321 |
+
}
|
1322 |
+
?>
|
1323 |
+
<option value="none" <?php echo $none?>>None</option>
|
1324 |
+
<option value="s3" <?php echo $s3?>>Amazon S3</option>
|
1325 |
+
<option value="ftp" <?php echo $ftp?>>FTP</option>
|
1326 |
+
<option value="email" <?php echo $email?>>E-mail</option>
|
1327 |
+
</select></td>
|
1328 |
+
</tr>
|
1329 |
+
<tr class="backup-service-description">
|
1330 |
+
<td></td><td>Choose which backup method you would like to employ. Be aware that email servers tend to have strict file size limitations and it is possible you will not receive your backup emails (>10MB is a typical threshold). Select none if you do not wish to send your backups anywhere. <b>Not recommended.</b></td>
|
1331 |
+
|
1332 |
+
</tr>
|
1333 |
+
<tr class="s3" <?php echo $s3_display?>>
|
1334 |
+
<th>S3 access key:</th>
|
1335 |
+
<td><input type="text" autocomplete="off" style="width:292px" name="updraft_s3_login" value="<?php echo get_option('updraft_s3_login') ?>" /></td>
|
1336 |
+
</tr>
|
1337 |
+
<tr class="s3" <?php echo $s3_display?>>
|
1338 |
+
<th>S3 secret key:</th>
|
1339 |
+
<td><input type="password" autocomplete="off" style="width:292px" name="updraft_s3_pass" value="<?php echo get_option('updraft_s3_pass'); ?>" /></td>
|
1340 |
+
</tr>
|
1341 |
+
<tr class="s3" <?php echo $s3_display?>>
|
1342 |
+
<th>S3 bucket:</th>
|
1343 |
+
<td><input type="text" style="width:292px" name="updraft_s3_remote_path" value="<?php echo get_option('updraft_s3_remote_path'); ?>" /></td>
|
1344 |
+
</tr>
|
1345 |
+
<tr class="s3" <?php echo $s3_display?>>
|
1346 |
+
<th></th>
|
1347 |
+
<td><p>Get your access key and secret key from your AWS page, then pick a (globally unique) bucket name (letters and numbers) to use for storage. (Do not enter the s3:// prefix).</p></td>
|
1348 |
+
</tr>
|
1349 |
+
<tr class="ftp" <?php echo $ftp_display?>>
|
1350 |
+
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Server:</a></th>
|
1351 |
+
<td><input type="text" style="width:260px" name="updraft_server_address" value="<?php echo get_option('updraft_server_address'); ?>" /></td>
|
1352 |
+
</tr>
|
1353 |
+
<tr class="ftp" <?php echo $ftp_display?>>
|
1354 |
+
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Login:</a></th>
|
1355 |
+
<td><input type="text" autocomplete="off" name="updraft_ftp_login" value="<?php echo get_option('updraft_ftp_login') ?>" /></td>
|
1356 |
+
</tr>
|
1357 |
+
<tr class="ftp" <?php echo $ftp_display?>>
|
1358 |
+
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">FTP Password:</a></th>
|
1359 |
+
<td><input type="password" autocomplete="off" style="width:260px" name="updraft_ftp_pass" value="<?php echo get_option('updraft_ftp_pass'); ?>" /></td>
|
1360 |
+
</tr>
|
1361 |
+
<tr class="ftp" <?php echo $ftp_display?>>
|
1362 |
+
<th><a href="#" title="Click for help!" onclick="jQuery('.ftp-description').toggle();return false;">Remote Path:</a></th>
|
1363 |
+
<td><input type="text" style="width:260px" name="updraft_ftp_remote_path" value="<?php echo get_option('updraft_ftp_remote_path'); ?>" /></td>
|
1364 |
+
</tr>
|
1365 |
+
<tr class="ftp-description" style="display:none">
|
1366 |
+
<td colspan="2">An FTP remote path will look like '/home/backup/some/folder'</td>
|
1367 |
+
</tr>
|
1368 |
+
<tr class="email" <?php echo $email_display?>>
|
1369 |
+
<th>Email:</th>
|
1370 |
+
<td><input type="text" style="width:260px" name="updraft_email" value="<?php echo get_option('updraft_email'); ?>" /> <br />Enter an address here to have a report sent (and the whole backup, if you choose) to it.</td>
|
1371 |
+
</tr>
|
1372 |
+
<tr class="deletelocal s3 ftp email" <?php echo $display_delete_local?>>
|
1373 |
+
<th>Delete local backup:</th>
|
1374 |
+
<td><input type="checkbox" name="updraft_delete_local" value="1" <?php echo $delete_local; ?> /> <br />Check this to delete the local backup file after it has been sent off the server.</td>
|
1375 |
+
</tr>
|
1376 |
+
<tr>
|
1377 |
+
<th>Debug mode:</th>
|
1378 |
+
<td><input type="checkbox" name="updraft_debug_mode" value="1" <?php echo $debug_mode; ?> /> <br />Check this for more information, if something is going wrong.</td>
|
1379 |
+
</tr>
|
1380 |
+
<tr>
|
1381 |
+
<td>
|
1382 |
+
<input type="hidden" name="action" value="update" />
|
1383 |
+
<input type="submit" class="button-primary" value="Save Changes" />
|
1384 |
+
</td>
|
1385 |
+
</tr>
|
1386 |
+
</table>
|
1387 |
+
</form>
|
1388 |
+
<?php
|
1389 |
+
if(get_option('updraft_debug_mode')) {
|
1390 |
+
?>
|
1391 |
+
<div>
|
1392 |
+
<h3>Debug Information</h3>
|
1393 |
+
<?php
|
1394 |
+
$peak_memory_usage = memory_get_peak_usage(true)/1024/1024;
|
1395 |
+
$memory_usage = memory_get_usage(true)/1024/1024;
|
1396 |
+
echo 'Peak memory usage: '.$peak_memory_usage.' MB<br/>';
|
1397 |
+
echo 'Current memory usage: '.$memory_usage.' MB<br/>';
|
1398 |
+
echo 'PHP memory limit: '.ini_get('memory_limit').' <br/>';
|
1399 |
+
?>
|
1400 |
+
<form method="post" action="">
|
1401 |
+
<input type="hidden" name="action" value="updraft_backup_debug_all" />
|
1402 |
+
<p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug Backup" onclick="return(confirm('This will cause an immediate backup. The page will stall loading until it finishes (ie, unscheduled). Use this if you\'re trying to see peak memory usage.'))" /></p>
|
1403 |
+
</form>
|
1404 |
+
<form method="post" action="">
|
1405 |
+
<input type="hidden" name="action" value="updraft_backup_debug_db" />
|
1406 |
+
<p><input type="submit" class="button-primary" <?php echo $backup_disabled ?> value="Debug DB Backup" onclick="return(confirm('This will cause an immediate DB backup. The page will stall loading until it finishes (ie, unscheduled). The backup will remain locally despite your prefs and will not go into the backup history or up into the cloud.'))" /></p>
|
1407 |
+
</form>
|
1408 |
+
</div>
|
1409 |
+
<?php } ?>
|
1410 |
+
<script type="text/javascript">
|
1411 |
+
jQuery(document).ready(function() {
|
1412 |
+
jQuery('#updraft-service').change(function() {
|
1413 |
+
switch(jQuery(this).val()) {
|
1414 |
+
case 'none':
|
1415 |
+
jQuery('.deletelocal,.s3,.ftp,.s3-description,.ftp-description').hide()
|
1416 |
+
jQuery('.email,.email-complete').show()
|
1417 |
+
break;
|
1418 |
+
case 's3':
|
1419 |
+
jQuery('.ftp,.ftp-description').hide()
|
1420 |
+
jQuery('.s3,.deletelocal,.email,.email-complete').show()
|
1421 |
+
break;
|
1422 |
+
case 'ftp':
|
1423 |
+
jQuery('.s3,.s3-description').hide()
|
1424 |
+
jQuery('.ftp,.deletelocal,.email,.email-complete').show()
|
1425 |
+
break;
|
1426 |
+
case 'email':
|
1427 |
+
jQuery('.s3,.ftp,.s3-description,.ftp-description,.email-complete').hide()
|
1428 |
+
jQuery('.email,.deletelocal').show()
|
1429 |
+
break;
|
1430 |
+
}
|
1431 |
+
})
|
1432 |
+
})
|
1433 |
+
jQuery(window).load(function() {
|
1434 |
+
//this is for hiding the restore progress at the top after it is done
|
1435 |
+
setTimeout('jQuery("#updraft-restore-progress").toggle(1000)',3000)
|
1436 |
+
jQuery('#updraft-restore-progress-toggle').click(function() {
|
1437 |
+
jQuery('#updraft-restore-progress').toggle(500)
|
1438 |
+
})
|
1439 |
+
})
|
1440 |
+
</script>
|
1441 |
+
<?php
|
1442 |
+
}
|
1443 |
+
|
1444 |
+
/*array2json provided by bin-co.com under BSD license*/
|
1445 |
+
function array2json($arr) {
|
1446 |
+
if(function_exists('json_encode')) return stripslashes(json_encode($arr)); //Latest versions of PHP already have this functionality.
|
1447 |
+
$parts = array();
|
1448 |
+
$is_list = false;
|
1449 |
+
|
1450 |
+
//Find out if the given array is a numerical array
|
1451 |
+
$keys = array_keys($arr);
|
1452 |
+
$max_length = count($arr)-1;
|
1453 |
+
if(($keys[0] == 0) and ($keys[$max_length] == $max_length)) {//See if the first key is 0 and last key is length - 1
|
1454 |
+
$is_list = true;
|
1455 |
+
for($i=0; $i<count($keys); $i++) { //See if each key correspondes to its position
|
1456 |
+
if($i != $keys[$i]) { //A key fails at position check.
|
1457 |
+
$is_list = false; //It is an associative array.
|
1458 |
+
break;
|
1459 |
+
}
|
1460 |
+
}
|
1461 |
+
}
|
1462 |
+
|
1463 |
+
foreach($arr as $key=>$value) {
|
1464 |
+
if(is_array($value)) { //Custom handling for arrays
|
1465 |
+
if($is_list) $parts[] = $this->array2json($value); /* :RECURSION: */
|
1466 |
+
else $parts[] = '"' . $key . '":' . $this->array2json($value); /* :RECURSION: */
|
1467 |
+
} else {
|
1468 |
+
$str = '';
|
1469 |
+
if(!$is_list) $str = '"' . $key . '":';
|
1470 |
+
|
1471 |
+
//Custom handling for multiple data types
|
1472 |
+
if(is_numeric($value)) $str .= $value; //Numbers
|
1473 |
+
elseif($value === false) $str .= 'false'; //The booleans
|
1474 |
+
elseif($value === true) $str .= 'true';
|
1475 |
+
else $str .= '"' . addslashes($value) . '"'; //All other things
|
1476 |
+
// :TODO: Is there any more datatype we should be in the lookout for? (Object?)
|
1477 |
+
|
1478 |
+
$parts[] = $str;
|
1479 |
+
}
|
1480 |
+
}
|
1481 |
+
$json = implode(',',$parts);
|
1482 |
+
|
1483 |
+
if($is_list) return '[' . $json . ']';//Return numerical JSON
|
1484 |
+
return '{' . $json . '}';//Return associative JSON
|
1485 |
+
}
|
1486 |
+
|
1487 |
+
function show_admin_warning($message) {
|
1488 |
+
echo '<div id="updraftmessage" class="updated fade">';
|
1489 |
+
echo "<p>$message</p></div>";
|
1490 |
+
}
|
1491 |
+
function show_admin_warning_accessible() {
|
1492 |
+
$this->show_admin_warning("UpdraftPlus backup directory specified is accessible via the web. This is a potential security problem (people may be able to download your backups - which is undesirable if your database is not encrypted and if you have non-public assets amongst the files). If using Apache, enable .htaccess support to allow web access to be denied; otherwise, you should deny access manually.");
|
1493 |
+
}
|
1494 |
+
function show_admin_warning_accessible_unknownresult() {
|
1495 |
+
$this->show_admin_warning("UpdraftPlus tried to check if the backup directory is accessible via web, but the result was unknown.");
|
1496 |
+
}
|
1497 |
+
|
1498 |
+
|
1499 |
+
}
|
1500 |
+
|
1501 |
+
?>
|