VaultPress - Version 1.4.6

Version Description

  • Bugfix: PHP 5.4 notices
  • Feature: Add the possibility to ignore frequent updates on some postmeta keys.
Download this release

Release Info

Developer xknown
Plugin Icon 128x128 VaultPress
Version 1.4.6
Comparing to
See all releases

Code changes from version 1.2.8 to 1.4.6

class.vaultpress-database.php CHANGED
@@ -12,8 +12,11 @@ class VaultPress_Database {
12
  function __construct() {
13
  }
14
 
15
- function attach( $table ) {
16
  $this->table=$table;
 
 
 
17
  }
18
 
19
  function get_tables( $filter=null ) {
@@ -49,7 +52,7 @@ class VaultPress_Database {
49
  if ( !is_array( $signatures ) || !count( $signatures ) )
50
  return false;
51
  if ( !$this->table )
52
- return false;
53
  $table = $wpdb->escape( $this->table );
54
  $diff = array();
55
  foreach ( $signatures as $where => $signature ) {
@@ -73,7 +76,7 @@ class VaultPress_Database {
73
  if ( !is_array( $columns ) || !count( $columns ) )
74
  return false;
75
  if ( !$this->table )
76
- return false;
77
  $table = $wpdb->escape( $this->table );
78
  $column = $wpdb->escape( array_shift( $columns ) );
79
  return $wpdb->get_var( "SELECT COUNT( $column ) FROM `$table`" );
@@ -124,17 +127,216 @@ class VaultPress_Database {
124
  // We don't need to actually record a real cron option value, just an empty array
125
  if ( isset( $row->option_name ) && $row->option_name == 'cron' )
126
  $row->option_value = serialize( array() );
127
- $keys = array();
128
- $vals = array();
129
- foreach ( get_object_vars( $row ) as $i => $v ) {
130
- $keys[] = sprintf( "`%s`", $wpdb->escape( $i ) );
131
- $vals[] = sprintf( "'%s'", $wpdb->escape( $v ) );
132
- if ( !in_array( $i, $columns ) )
133
- unset( $row->$i );
 
 
 
 
 
 
 
 
 
 
 
134
  }
135
- $row->hash = md5( sprintf( "(%s) VALUES(%s)", implode( ',',$keys ), implode( ',',$vals ) ) );
136
  $rval[]=$row;
137
  }
138
  return $rval;
139
  }
140
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  function __construct() {
13
  }
14
 
15
+ function attach( $table, $parse_create_table = false ) {
16
  $this->table=$table;
17
+ if ( $parse_create_table ) {
18
+ $this->structure = $this->parse_create_table( $this->show_create() );
19
+ }
20
  }
21
 
22
  function get_tables( $filter=null ) {
52
  if ( !is_array( $signatures ) || !count( $signatures ) )
53
  return false;
54
  if ( !$this->table )
55
+ return false;
56
  $table = $wpdb->escape( $this->table );
57
  $diff = array();
58
  foreach ( $signatures as $where => $signature ) {
76
  if ( !is_array( $columns ) || !count( $columns ) )
77
  return false;
78
  if ( !$this->table )
79
+ return false;
80
  $table = $wpdb->escape( $this->table );
81
  $column = $wpdb->escape( array_shift( $columns ) );
82
  return $wpdb->get_var( "SELECT COUNT( $column ) FROM `$table`" );
127
  // We don't need to actually record a real cron option value, just an empty array
128
  if ( isset( $row->option_name ) && $row->option_name == 'cron' )
129
  $row->option_value = serialize( array() );
130
+ if ( !empty( $this->structure ) ) {
131
+ $hash = md5( $this->convert_to_sql_string( $row, $this->structure->columns ) );
132
+ foreach ( get_object_vars( $row ) as $i => $v ) {
133
+ if ( !in_array( $i, $columns ) )
134
+ unset( $row->$i );
135
+ }
136
+
137
+ $row->hash = $hash;
138
+ } else {
139
+ $keys = array();
140
+ $vals = array();
141
+ foreach ( get_object_vars( $row ) as $i => $v ) {
142
+ $keys[] = sprintf( "`%s`", $wpdb->escape( $i ) );
143
+ $vals[] = sprintf( "'%s'", $wpdb->escape( $v ) );
144
+ if ( !in_array( $i, $columns ) )
145
+ unset( $row->$i );
146
+ }
147
+ $row->hash = md5( sprintf( "(%s) VALUES(%s)", implode( ',',$keys ), implode( ',',$vals ) ) );
148
  }
 
149
  $rval[]=$row;
150
  }
151
  return $rval;
152
  }
153
+
154
+ /**
155
+ * Convert a PHP object to a mysqldump compatible string, using the provided data type information.
156
+ **/
157
+ function convert_to_sql_string( $data, $datatypes ) {
158
+ global $wpdb;
159
+ if ( !is_object( $data ) || !is_object( $datatypes ) )
160
+ return false;
161
+
162
+ foreach ( array_keys( (array)$data ) as $key )
163
+ $keys[] = sprintf( "`%s`", $wpdb->escape( $key ) );
164
+ foreach ( (array)$data as $key => $val ) {
165
+ if ( null === $val ) {
166
+ $vals[] = 'NULL';
167
+ continue;
168
+ }
169
+ $type = 'text';
170
+ if ( isset( $datatypes->$key->type ) )
171
+ $type= strtolower( $datatypes->$key->type );
172
+ if ( preg_match( '/int|double|float|decimal|bool/i', $type ) )
173
+ $type = 'number';
174
+
175
+ if ( 'number' === $type ) {
176
+ // do not add quotes to numeric types.
177
+ $vals[] = $val;
178
+ } else {
179
+ $val = $wpdb->escape( $val );
180
+ // Escape characters that aren't escaped by $wpdb->escape(): \n, \r, etc.
181
+ $val = str_replace( array( "\x0a", "\x0d", "\x1a" ), array( '\n', '\r', '\Z' ), $val );
182
+ $vals[] = sprintf( "'%s'", $val );
183
+ }
184
+ }
185
+ if ( !count($keys) )
186
+ return false;
187
+ // format the row as a mysqldump line: (`column1`, `column2`) VALUES (numeric_value1,'text value 2')
188
+ return sprintf( "(%s) VALUES (%s)", implode( ', ',$keys ), implode( ',',$vals ) );
189
+ }
190
+
191
+
192
+
193
+ function parse_create_table( $sql ) {
194
+ $table = new stdClass();
195
+
196
+ $table->raw = $sql;
197
+ $table->columns = new stdClass();
198
+ $table->primary = null;
199
+ $table->uniques = new stdClass();
200
+ $table->keys = new stdClass();
201
+ $sql = explode( "\n", trim( $sql ) );
202
+ $table->engine = preg_replace( '/^.+ ENGINE=(\S+) .+$/i', "$1", $sql[(count($sql)-1)] );
203
+ $table->charset = preg_replace( '/^.+ DEFAULT CHARSET=(\S+)( .+)?$/i', "$1", $sql[(count($sql)-1)] );
204
+ $table->single_int_paging_column = null;
205
+
206
+ foreach ( $sql as $idx => $val )
207
+ $sql[$idx] = trim($val);
208
+ $columns = preg_grep( '/^\s*`[^`]+`\s*\S*/', $sql );
209
+ if ( !$columns )
210
+ return false;
211
+
212
+ $table->name = preg_replace( '/(^[^`]+`|`[^`]+$)/', '', array_shift( preg_grep( '/^CREATE\s+TABLE\s+/', $sql ) ) );
213
+
214
+ foreach ( $columns as $line ) {
215
+ preg_match( '/^`([^`]+)`\s+([a-z]+)(\(\d+\))?\s*/', $line, $m );
216
+ $name = $m[1];
217
+ $table->columns->$name = new stdClass();
218
+ $table->columns->$name->null = (bool)stripos( $line, ' NOT NULL ' );
219
+ $table->columns->$name->type = $m[2];
220
+ if ( isset($m[3]) ) {
221
+ if ( substr( $m[3], 0, 1 ) == '(' )
222
+ $table->columns->$name->length = substr( $m[3], 1, -1 );
223
+ else
224
+ $table->columns->$name->length = $m[3];
225
+ } else {
226
+ $table->columns->$name->length = null;
227
+ }
228
+ if ( preg_match( '/ character set (\S+)/i', $line, $m ) ) {
229
+ $table->columns->$name->charset = $m[1];
230
+ } else {
231
+ $table->columns->$name->charset = '';
232
+ }
233
+ if ( preg_match( '/ collate (\S+)/i', $line, $m ) ) {
234
+ $table->columns->$name->collate = $m[1];
235
+ } else {
236
+ $table->columns->$name->collate = '';
237
+ }
238
+ if ( preg_match( '/ DEFAULT (.+),$/i', $line, $m ) ) {
239
+ if ( substr( $m[1], 0, 1 ) == "'" )
240
+ $table->columns->$name->default = substr( $m[1], 1, -1 );
241
+ else
242
+ $table->columns->$name->default = $m[1];
243
+ } else {
244
+ $table->columns->$name->default = null;
245
+ }
246
+ $table->columns->$name->line = $line;
247
+ }
248
+ $pk = preg_grep( '/^PRIMARY\s+KEY\s+/i', $sql );
249
+ if ( count( $pk ) ) {
250
+ $pk = array_pop( $pk );
251
+ $pk = preg_replace( '/(^[^\(]+\(`|`\),?$)/', '', $pk );
252
+ $pk = preg_replace( '/\([0-9]+\)/', '', $pk );
253
+ $pk = explode( '`,`', $pk );
254
+ $table->primary = $pk;
255
+ }
256
+ if ( is_array( $table->primary ) && count( $table->primary ) == 1 ) {
257
+ $pk_column_name = $table->primary[0];
258
+ switch( strtolower( $table->columns->$pk_column_name->type ) ) {
259
+ // Integers, exact value
260
+ case 'tinyint':
261
+ case 'smallint':
262
+ case 'int':
263
+ case 'integer':
264
+ case 'bigint':
265
+ // Fixed point, exact value
266
+ case 'decimal':
267
+ case 'numeric':
268
+ // Floating point, approximate value
269
+ case 'float':
270
+ case 'double':
271
+ case 'real':
272
+ // Date and Time
273
+ case 'date':
274
+ case 'datetime':
275
+ case 'timestamp':
276
+ $table->single_int_paging_column = $pk_column_name;
277
+ break;
278
+ }
279
+ }
280
+ $keys = preg_grep( '/^((?:UNIQUE )?INDEX|(?:UNIQUE )?KEY)\s+/i', $sql );
281
+ if ( !count( $keys ) )
282
+ return $table;
283
+ foreach ( $keys as $idx => $key ) {
284
+ if ( 0 === strpos( $key, 'UNIQUE' ) )
285
+ $is_unique = false;
286
+ else
287
+ $is_unique = true;
288
+
289
+ // for KEY `refresh` (`ip`,`time_last`) USING BTREE,
290
+ $key = preg_replace( '/ USING \S+ ?(,?)$/', '$1', $key );
291
+
292
+ // for KEY `id` USING BTREE (`id`),
293
+ $key = preg_replace( '/` USING \S+ \(/i', '` (', $key );
294
+
295
+ $key = preg_replace( '/^((?:UNIQUE )?INDEX|(?:UNIQUE )?KEY)\s+/i', '', $key );
296
+ $key = preg_replace( '/\([0-9]+\)/', '', $key );
297
+ preg_match( '/^`([^`]+)`\s+\(`(.+)`\),?$/', $key, $m );
298
+ $key = $m[1]; //preg_replace( '/\([^)]+\)/', '', $m[1]);
299
+ if ( !$key )
300
+ continue;
301
+ if ( $is_unique )
302
+ $table->keys->$key = explode( '`,`', $m[2] );
303
+ else
304
+ $table->uniques->$key = explode( '`,`', $m[2] );
305
+ }
306
+
307
+ $uniques = get_object_vars( $table->uniques );
308
+ foreach( $uniques as $idx => $val ) {
309
+ if ( is_array( $val ) && count( $val ) == 1 ) {
310
+ $pk_column_name = $val[0];
311
+ switch( strtolower( $table->columns->$pk_column_name->type ) ) {
312
+ // Integers, exact value
313
+ case 'tinyint':
314
+ case 'smallint':
315
+ case 'int':
316
+ case 'integer':
317
+ case 'bigint':
318
+ // Fixed point, exact value
319
+ case 'decimal':
320
+ case 'numeric':
321
+ // Floating point, approximate value
322
+ case 'float':
323
+ case 'double':
324
+ case 'real':
325
+ // Date and Time
326
+ case 'date':
327
+ case 'datetime':
328
+ case 'timestamp':
329
+ $table->single_int_paging_column = $pk_column_name;
330
+ break;
331
+ }
332
+ }
333
+ }
334
+
335
+ if ( empty( $table->primary ) ) {
336
+ if ( !empty( $uniques ) )
337
+ $table->primary = array_shift( $uniques );
338
+ }
339
+
340
+ return $table;
341
+ }
342
+ }
class.vaultpress-filesystem.php CHANGED
@@ -48,8 +48,20 @@ class VaultPress_Filesystem {
48
  header("Content-Type: application/octet-stream;");
49
  header("Content-Transfer-Encoding: binary");
50
  @ob_end_clean();
51
- if ( !file_exists( $file ) || !is_readable( $file ) )
52
- die( "no such file" );
 
 
 
 
 
 
 
 
 
 
 
 
53
  if ( !is_file( $file ) && !is_link( $file ) )
54
  die( "can only dump files" );
55
  $fp = @fopen( $file, 'rb' );
@@ -75,19 +87,33 @@ class VaultPress_Filesystem {
75
  if ( $sha1 )
76
  $rval['sha1'] = sha1_file( $file );
77
  }
78
- $rval['path'] = str_replace( $this->dir, '', $file );
 
 
 
 
 
 
79
  return $rval;
80
  }
81
 
82
  function ls( $what, $md5=false, $sha1=false, $limit=null, $offset=null ) {
83
  clearstatcache();
84
  $path = realpath($this->dir . $what);
 
 
 
 
 
 
 
85
  if ( is_file($path) )
86
  return $this->stat( $path, $md5, $sha1 );
87
  if ( is_dir($path) ) {
88
  $entries = array();
89
  $current = 0;
90
  $offset = (int)$offset;
 
91
  $limit = $offset + (int)$limit;
92
  foreach ( (array)$this->scan_dir( $path ) as $i ) {
93
  $current++;
@@ -95,6 +121,11 @@ class VaultPress_Filesystem {
95
  continue;
96
  if ( $limit && $limit < $current )
97
  break;
 
 
 
 
 
98
  $entries[] = $this->stat( $i, $md5, $sha1 );
99
  }
100
  return $entries;
@@ -103,11 +134,18 @@ class VaultPress_Filesystem {
103
 
104
  function validate( $file ) {
105
  $rpath = realpath( $this->dir.$file );
 
 
 
 
 
 
 
106
  if ( !$rpath )
107
  die( serialize( array( 'type' => 'null', 'path' => $file ) ) );
108
  if ( is_dir( $rpath ) )
109
  $rpath = "$rpath/";
110
- if ( strpos( $rpath, $this->dir ) !== 0 )
111
  return false;
112
  return true;
113
  }
@@ -188,8 +226,17 @@ class VaultPress_Filesystem {
188
 
189
  function scan_dir( $path ) {
190
  $files = array();
 
 
 
 
 
191
  $dh = opendir( $path );
192
 
 
 
 
 
193
  while ( false !== ( $file = readdir( $dh ) ) ) {
194
  if ( $file == '.' || $file == '..' ) continue;
195
  $files[] = "$path/$file";
48
  header("Content-Type: application/octet-stream;");
49
  header("Content-Transfer-Encoding: binary");
50
  @ob_end_clean();
51
+ if ( !file_exists( $file ) || !is_readable( $file ) ) {
52
+ $file_name = basename( $file );
53
+ if ( 'wp-config.php' == $file_name ) {
54
+ $dir = dirname( $file );
55
+ $dir = explode( DIRECTORY_SEPARATOR, $dir );
56
+ array_pop( $dir );
57
+ $dir = implode( DIRECTORY_SEPARATOR, $dir );
58
+ $file = trailingslashit( $dir ) . $file_name;
59
+ if ( !file_exists( $file ) || !is_readable( $file ) )
60
+ die( "no such file" );
61
+ } else {
62
+ die( "no such file" );
63
+ }
64
+ }
65
  if ( !is_file( $file ) && !is_link( $file ) )
66
  die( "can only dump files" );
67
  $fp = @fopen( $file, 'rb' );
87
  if ( $sha1 )
88
  $rval['sha1'] = sha1_file( $file );
89
  }
90
+ $dir = $this->dir;
91
+ if ( 0 !== strpos( $file, $dir ) && 'wp-config.php' == basename( $file ) ) {
92
+ $dir = explode( DIRECTORY_SEPARATOR, $dir );
93
+ array_pop( $dir );
94
+ $dir = implode( DIRECTORY_SEPARATOR, $dir );
95
+ }
96
+ $rval['path'] = str_replace( $dir, '', $file );
97
  return $rval;
98
  }
99
 
100
  function ls( $what, $md5=false, $sha1=false, $limit=null, $offset=null ) {
101
  clearstatcache();
102
  $path = realpath($this->dir . $what);
103
+ $dir = $this->dir;
104
+ if ( !$path && '/wp-config.php' == $what ) {
105
+ $dir = explode( DIRECTORY_SEPARATOR, $dir );
106
+ array_pop( $dir );
107
+ $dir = implode( DIRECTORY_SEPARATOR, $dir );
108
+ $path = realpath( $dir . $what );
109
+ }
110
  if ( is_file($path) )
111
  return $this->stat( $path, $md5, $sha1 );
112
  if ( is_dir($path) ) {
113
  $entries = array();
114
  $current = 0;
115
  $offset = (int)$offset;
116
+ $orig_limit = (int)$limit;
117
  $limit = $offset + (int)$limit;
118
  foreach ( (array)$this->scan_dir( $path ) as $i ) {
119
  $current++;
121
  continue;
122
  if ( $limit && $limit < $current )
123
  break;
124
+
125
+ // don't sha1 files over 100MB if we are batching due to memory consumption
126
+ if ( $sha1 && $orig_limit > 1 && is_file( $i ) && (int)@filesize( $i ) > 104857600 )
127
+ $sha1 = false;
128
+
129
  $entries[] = $this->stat( $i, $md5, $sha1 );
130
  }
131
  return $entries;
134
 
135
  function validate( $file ) {
136
  $rpath = realpath( $this->dir.$file );
137
+ $dir = $this->dir;
138
+ if ( !$rpath && '/wp-config.php' == $file ) {
139
+ $dir = explode( DIRECTORY_SEPARATOR, $dir );
140
+ array_pop( $dir );
141
+ $dir = implode( DIRECTORY_SEPARATOR, $dir );
142
+ $rpath = realpath( $dir . $file );
143
+ }
144
  if ( !$rpath )
145
  die( serialize( array( 'type' => 'null', 'path' => $file ) ) );
146
  if ( is_dir( $rpath ) )
147
  $rpath = "$rpath/";
148
+ if ( strpos( $rpath, $dir ) !== 0 )
149
  return false;
150
  return true;
151
  }
226
 
227
  function scan_dir( $path ) {
228
  $files = array();
229
+
230
+ if ( false === is_readable( $path ) ) {
231
+ return array();
232
+ }
233
+
234
  $dh = opendir( $path );
235
 
236
+ if ( false === $dh ) {
237
+ return array();
238
+ }
239
+
240
  while ( false !== ( $file = readdir( $dh ) ) ) {
241
  if ( $file == '.' || $file == '..' ) continue;
242
  $files[] = "$path/$file";
class.vaultpress-hotfixes.php CHANGED
@@ -14,7 +14,7 @@ class VaultPress_Hotfixes {
14
  if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && version_compare( $wp_version, '3.0.3', '<' ) )
15
  add_action( 'xmlrpc_call', array( $this, 'r16803' ) );
16
 
17
- if ( version_compare( $wp_version, '3.0.4', '<' ) ) {
18
  add_filter( 'pre_kses', array( $this, 'r17172_wp_kses' ), 1, 3 );
19
  add_filter( 'clean_url', array( $this, 'r17172_esc_url' ), 1, 3 );
20
  }
@@ -47,6 +47,180 @@ class VaultPress_Hotfixes {
47
  add_filter( 'sanitize_option_new_admin_email', array( $this, 'r18346_sanitize_admin_email_on_save' ) );
48
  }
49
  add_filter( 'option_new_admin_email', array( $this, 'r18346_sanitize_admin_email' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
  function r16625( $query ) {
@@ -204,15 +378,21 @@ EOD;
204
 
205
  function r17172_esc_url( $url, $original_url, $_context ) {
206
  $url = $original_url;
 
207
  if ( '' == $url )
208
  return $url;
209
  $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
210
  $strip = array('%0d', '%0a', '%0D', '%0A');
211
  $url = _deep_replace($strip, $url);
212
  $url = str_replace(';//', '://', $url);
213
- if ( strpos($url, ':') === false &&
214
- substr( $url, 0, 1 ) != '/' && substr( $url, 0, 1 ) != '#' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) )
 
 
 
 
215
  $url = 'http://' . $url;
 
216
  // Replace ampersands and single quotes only when displaying.
217
  if ( 'display' == $_context ) {
218
  $url = wp_kses_normalize_entities( $url );
@@ -226,6 +406,8 @@ EOD;
226
  return $url;
227
  }
228
 
 
 
229
  function r17172_wp_kses( $string, $html, $protocols ) {
230
  return VaultPress_kses::wp_kses( $string, $html, $protocols );
231
  }
@@ -337,6 +519,7 @@ EOD;
337
  }
338
  }
339
 
 
340
  $needs_class_fix = version_compare( $wp_version, '3.1', '>=') && version_compare( $wp_version, '3.1.3', '<' );
341
  if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && $needs_class_fix ) {
342
  include_once( ABSPATH . WPINC . '/class-IXR.php' );
@@ -366,12 +549,12 @@ class VaultPress_kses {
366
  global $pass_allowed_html, $pass_allowed_protocols;
367
  $pass_allowed_html = $allowed_html;
368
  $pass_allowed_protocols = $allowed_protocols;
369
- return preg_replace_callback( '%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%', 'VaultPress_kses::_vp_kses_split_callback', $string );
370
  }
371
 
372
  static function _vp_kses_split_callback( $match ) {
373
  global $pass_allowed_html, $pass_allowed_protocols;
374
- return VaultPress_kses::wp_kses_split2( $match[1], $pass_allowed_html, $pass_allowed_protocols );
375
  }
376
 
377
  static function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
@@ -381,9 +564,9 @@ class VaultPress_kses {
381
  return '&gt;';
382
  # It matched a ">" character
383
 
384
- if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
385
- $string = str_replace(array('<!--', '-->'), '', $matches[1]);
386
- while ( $string != $newstring = VaultPress_kses::wp_kses($string, $allowed_html, $allowed_protocols) )
387
  $string = $newstring;
388
  if ( $string == '' )
389
  return '';
@@ -403,15 +586,15 @@ class VaultPress_kses {
403
  $elem = $matches[2];
404
  $attrlist = $matches[3];
405
 
406
- if (!@isset($allowed_html[strtolower($elem)]))
407
  return '';
408
  # They are using a not allowed HTML element
409
 
410
  if ($slash != '')
411
- return "<$slash$elem>";
412
  # No attributes are allowed for closing elements
413
 
414
- return VaultPress_kses::wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
415
  }
416
 
417
  static function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
@@ -422,60 +605,56 @@ class VaultPress_kses {
422
  $xhtml_slash = ' /';
423
 
424
  # Are any attributes allowed at all for this element?
425
-
426
- if (@ count($allowed_html[strtolower($element)]) == 0)
427
  return "<$element$xhtml_slash>";
428
 
429
  # Split it
430
-
431
  $attrarr = VaultPress_kses::wp_kses_hair($attr, $allowed_protocols);
432
 
433
  # Go through $attrarr, and save the allowed attributes for this element
434
  # in $attr2
435
-
436
  $attr2 = '';
437
 
 
438
  foreach ($attrarr as $arreach) {
439
- if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
440
  continue; # the attribute is not allowed
441
 
442
- $current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
443
- if ($current == '')
444
  continue; # the attribute is not allowed
445
 
446
- if (!is_array($current))
 
 
 
 
 
 
 
 
 
 
 
447
  $attr2 .= ' '.$arreach['whole'];
448
  # there are no checks
449
 
450
- else {
451
  # there are some checks
452
  $ok = true;
453
- foreach ($current as $currkey => $currval)
454
- if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
455
  $ok = false;
456
  break;
457
  }
458
-
459
- if ( strtolower($arreach['name']) == 'style' ) {
460
- $orig_value = $arreach['value'];
461
-
462
- $value = safecss_filter_attr($orig_value);
463
-
464
- if ( empty($value) )
465
- continue;
466
-
467
- $arreach['value'] = $value;
468
-
469
- $arreach['whole'] = str_replace($orig_value, $value, $arreach['whole']);
470
  }
471
 
472
- if ($ok)
473
  $attr2 .= ' '.$arreach['whole']; # it passed them
474
  } # if !is_array($current)
475
  } # foreach
476
 
477
  # Remove any "<" or ">" characters
478
-
479
  $attr2 = preg_replace('/[<>]/', '', $attr2);
480
 
481
  return "<$element$attr2$xhtml_slash>";
@@ -517,7 +696,7 @@ class VaultPress_kses {
517
  {
518
  $working = 1;
519
  $mode = 0;
520
- if(FALSE === array_key_exists($attrname, $attrarr)) {
521
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
522
  }
523
  $attr = preg_replace('/^\s+/', '', $attr);
@@ -534,7 +713,7 @@ class VaultPress_kses {
534
  if ( in_array(strtolower($attrname), $uris) )
535
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
536
 
537
- if(FALSE === array_key_exists($attrname, $attrarr)) {
538
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
539
  }
540
  $working = 1;
@@ -550,7 +729,7 @@ class VaultPress_kses {
550
  if ( in_array(strtolower($attrname), $uris) )
551
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
552
 
553
- if(FALSE === array_key_exists($attrname, $attrarr)) {
554
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
555
  }
556
  $working = 1;
@@ -566,7 +745,7 @@ class VaultPress_kses {
566
  if ( in_array(strtolower($attrname), $uris) )
567
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
568
 
569
- if(FALSE === array_key_exists($attrname, $attrarr)) {
570
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
571
  }
572
  # We add quotes to conform to W3C's HTML spec.
@@ -585,7 +764,7 @@ class VaultPress_kses {
585
  }
586
  } # while
587
 
588
- if ($mode == 1 && FALSE === array_key_exists($attrname, $attrarr))
589
  # special case, for when the attribute list ends with a valueless
590
  # attribute like "selected"
591
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
@@ -595,20 +774,33 @@ class VaultPress_kses {
595
 
596
  static function wp_kses_bad_protocol($string, $allowed_protocols) {
597
  $string = wp_kses_no_null($string);
598
- $string2 = $string.'a';
599
 
600
- while ($string != $string2) {
601
- $string2 = $string;
602
  $string = VaultPress_kses::wp_kses_bad_protocol_once($string, $allowed_protocols);
603
- } # while
 
 
 
604
 
605
  return $string;
606
  }
607
 
608
- static function wp_kses_bad_protocol_once($string, $allowed_protocols) {
609
  $string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
610
- if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) )
611
- $string = VaultPress_kses::wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols ) . trim( $string2[1] );
 
 
 
 
 
 
 
 
 
 
612
 
613
  return $string;
614
  }
@@ -620,12 +812,11 @@ class VaultPress_kses {
620
  $string2 = strtolower($string2);
621
 
622
  $allowed = false;
623
- foreach ( (array) $allowed_protocols as $one_protocol ) {
624
- if ( strtolower($one_protocol) != $string2 )
625
- continue;
626
- $allowed = true;
627
- break;
628
- }
629
 
630
  if ($allowed)
631
  return "$string2:";
@@ -634,3 +825,13 @@ class VaultPress_kses {
634
  }
635
 
636
  }
 
 
 
 
 
 
 
 
 
 
14
  if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && version_compare( $wp_version, '3.0.3', '<' ) )
15
  add_action( 'xmlrpc_call', array( $this, 'r16803' ) );
16
 
17
+ if ( version_compare( $wp_version, '3.3.2', '<' ) ) {
18
  add_filter( 'pre_kses', array( $this, 'r17172_wp_kses' ), 1, 3 );
19
  add_filter( 'clean_url', array( $this, 'r17172_esc_url' ), 1, 3 );
20
  }
47
  add_filter( 'sanitize_option_new_admin_email', array( $this, 'r18346_sanitize_admin_email_on_save' ) );
48
  }
49
  add_filter( 'option_new_admin_email', array( $this, 'r18346_sanitize_admin_email' ) );
50
+
51
+ if ( version_compare( $wp_version, '3.3.2', '<' ) ) {
52
+ remove_filter( 'comment_text', 'make_clickable' );
53
+ add_filter( 'comment_text', array( $this, 'r20493_make_clickable' ), 9 );
54
+
55
+ add_filter( 'comment_post_redirect', array( $this, 'r20486_comment_post_redirect' ) );
56
+ }
57
+
58
+ // WooThemes < 3.8.3, foxypress, asset-manager, wordpress-member-private-conversation.
59
+ $end_execution = false;
60
+ if ( isset( $_SERVER['SCRIPT_FILENAME'] ) )
61
+ foreach ( array( 'preview-shortcode-external.php', 'uploadify.php', 'doupload.php', 'cef-upload.php', 'upload.php' ) as $vulnerable_script )
62
+ if ( $vulnerable_script == basename( $_SERVER['SCRIPT_FILENAME'] ) ) {
63
+ switch( $vulnerable_script ) {
64
+ case 'upload.php':
65
+ $pma_config_file = realpath( dirname( $_SERVER['SCRIPT_FILENAME'] ) . DIRECTORY_SEPARATOR . 'paam-config-ajax.php' );
66
+ if ( !in_array( $pma_config_file, get_included_files() ) )
67
+ break;
68
+ default:
69
+ $end_execution = true;
70
+ break 2;
71
+ }
72
+ }
73
+ if ( $end_execution )
74
+ die( 'Disabled for security reasons' );
75
+
76
+ if ( version_compare( $wp_version, '3.3.2', '>') && version_compare( $wp_version, '3.4.1', '<' ) ) {
77
+ add_filter( 'map_meta_cap', array( $this, 'r21138_xmlrpc_edit_posts' ), 10, 4 );
78
+ add_action( 'map_meta_cap', array( $this, 'r21152_unfiltered_html' ), 10, 4 );
79
+ }
80
+
81
+ // https://core.trac.wordpress.org/changeset/21083
82
+ if ( version_compare( $wp_version, '3.3', '>=') && version_compare( $wp_version, '3.3.3', '<' ) )
83
+ add_filter( 'editable_slug', 'esc_textarea' );
84
+
85
+ add_filter( 'get_pagenum_link', array( $this, 'get_pagenum_link' ) );
86
+ }
87
+
88
+ function r21138_xmlrpc_edit_posts( $caps, $cap, $user_id, $args ) {
89
+ if ( ! isset( $args[0] ) || isset( $args[1] ) && $args[1] === 'hotfixed' )
90
+ return $caps;
91
+ foreach ( get_post_types( array(), 'objects' ) as $post_type_object ) {
92
+ if ( $cap === $post_type_object->cap->edit_posts )
93
+ return map_meta_cap( $post_type_object->cap->edit_post, $user_id, $args[0], 'hotfixed' );
94
+ }
95
+ return $caps;
96
+ }
97
+
98
+ function r21152_unfiltered_html( $caps, $cap, $user_id, $args ) {
99
+ if ( $cap !== 'unfiltered_html' )
100
+ return $caps;
101
+ if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
102
+ return $caps;
103
+ $key = array_search( 'do_not_allow', $caps );
104
+ if ( false !== $key )
105
+ return $caps;
106
+ if ( is_multisite() && ! is_super_admin( $user_id ) )
107
+ $caps[$key] = 'do_not_allow';
108
+ return $caps;
109
+ }
110
+
111
+ function get_pagenum_link( $url ) {
112
+ return esc_url_raw( $url );
113
+ }
114
+
115
+ function r20486_comment_post_redirect( $location ) {
116
+ $location = wp_sanitize_redirect( $location );
117
+ $location = wp_validate_redirect( $location, admin_url() );
118
+
119
+ return $location;
120
+ }
121
+
122
+ function r20493_make_clickable( $text ) {
123
+ $r = '';
124
+ $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
125
+ foreach ( $textarr as $piece ) {
126
+ if ( empty( $piece ) || ( $piece[0] == '<' && ! preg_match('|^<\s*[\w]{1,20}+://|', $piece) ) ) {
127
+ $r .= $piece;
128
+ continue;
129
+ }
130
+
131
+ // Long strings might contain expensive edge cases ...
132
+ if ( 10000 < strlen( $piece ) ) {
133
+ // ... break it up
134
+ foreach ( $this->r20493_split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
135
+ if ( 2101 < strlen( $chunk ) ) {
136
+ $r .= $chunk; // Too big, no whitespace: bail.
137
+ } else {
138
+ $r .= $this->r20493_make_clickable( $chunk );
139
+ }
140
+ }
141
+ } else {
142
+ $ret = " $piece "; // Pad with whitespace to simplify the regexes
143
+
144
+ $url_clickable = '~
145
+ ([\\s(<.,;:!?]) # 1: Leading whitespace, or punctuation
146
+ ( # 2: URL
147
+ [\\w]{1,20}+:// # Scheme and hier-part prefix
148
+ (?=\S{1,2000}\s) # Limit to URLs less than about 2000 characters long
149
+ [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+ # Non-punctuation URL character
150
+ (?: # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
151
+ [\'.,;:!?)] # Punctuation URL character
152
+ [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
153
+ )*
154
+ )
155
+ (\)?) # 3: Trailing closing parenthesis (for parethesis balancing post processing)
156
+ ~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
157
+ // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
158
+
159
+ $ret = preg_replace_callback( $url_clickable, array( $this, 'r20493_make_url_clickable_cb') , $ret );
160
+
161
+ $ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
162
+ $ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
163
+
164
+ $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
165
+ $r .= $ret;
166
+ }
167
+ }
168
+
169
+ // Cleanup of accidental links within links
170
+ $r = preg_replace( '#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
171
+ return $r;
172
+ }
173
+
174
+ function r20493_make_url_clickable_cb($matches) {
175
+ $url = $matches[2];
176
+
177
+ if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
178
+ // If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
179
+ // Then we can let the parenthesis balancer do its thing below.
180
+ $url .= $matches[3];
181
+ $suffix = '';
182
+ } else {
183
+ $suffix = $matches[3];
184
+ }
185
+
186
+ // Include parentheses in the URL only if paired
187
+ while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
188
+ $suffix = strrchr( $url, ')' ) . $suffix;
189
+ $url = substr( $url, 0, strrpos( $url, ')' ) );
190
+ }
191
+
192
+ $url = esc_url($url);
193
+ if ( empty($url) )
194
+ return $matches[0];
195
+
196
+ return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
197
+ }
198
+
199
+ function r20493_split_str_by_whitespace( $string, $goal ) {
200
+ $chunks = array();
201
+
202
+ $string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
203
+
204
+ while ( $goal < strlen( $string_nullspace ) ) {
205
+ $pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
206
+
207
+ if ( false === $pos ) {
208
+ $pos = strpos( $string_nullspace, "\000", $goal + 1 );
209
+ if ( false === $pos ) {
210
+ break;
211
+ }
212
+ }
213
+
214
+ $chunks[] = substr( $string, 0, $pos + 1 );
215
+ $string = substr( $string, $pos + 1 );
216
+ $string_nullspace = substr( $string_nullspace, $pos + 1 );
217
+ }
218
+
219
+ if ( $string ) {
220
+ $chunks[] = $string;
221
+ }
222
+
223
+ return $chunks;
224
  }
225
 
226
  function r16625( $query ) {
378
 
379
  function r17172_esc_url( $url, $original_url, $_context ) {
380
  $url = $original_url;
381
+
382
  if ( '' == $url )
383
  return $url;
384
  $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
385
  $strip = array('%0d', '%0a', '%0D', '%0A');
386
  $url = _deep_replace($strip, $url);
387
  $url = str_replace(';//', '://', $url);
388
+ /* If the URL doesn't appear to contain a scheme, we
389
+ * presume it needs http:// appended (unless a relative
390
+ * link starting with /, # or ? or a php file).
391
+ */
392
+ if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
393
+ ! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
394
  $url = 'http://' . $url;
395
+
396
  // Replace ampersands and single quotes only when displaying.
397
  if ( 'display' == $_context ) {
398
  $url = wp_kses_normalize_entities( $url );
406
  return $url;
407
  }
408
 
409
+ // http://core.trac.wordpress.org/changeset/17172
410
+ // http://core.trac.wordpress.org/changeset/20541
411
  function r17172_wp_kses( $string, $html, $protocols ) {
412
  return VaultPress_kses::wp_kses( $string, $html, $protocols );
413
  }
519
  }
520
  }
521
 
522
+ global $wp_version;
523
  $needs_class_fix = version_compare( $wp_version, '3.1', '>=') && version_compare( $wp_version, '3.1.3', '<' );
524
  if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && $needs_class_fix ) {
525
  include_once( ABSPATH . WPINC . '/class-IXR.php' );
549
  global $pass_allowed_html, $pass_allowed_protocols;
550
  $pass_allowed_html = $allowed_html;
551
  $pass_allowed_protocols = $allowed_protocols;
552
+ return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', 'VaultPress_kses::_vp_kses_split_callback', $string );
553
  }
554
 
555
  static function _vp_kses_split_callback( $match ) {
556
  global $pass_allowed_html, $pass_allowed_protocols;
557
+ return VaultPress_kses::wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols );
558
  }
559
 
560
  static function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
564
  return '&gt;';
565
  # It matched a ">" character
566
 
567
+ if ( '<!--' == substr( $string, 0, 4 ) ) {
568
+ $string = str_replace( array('<!--', '-->'), '', $string );
569
+ while ( $string != ($newstring = VaultPress_kses::wp_kses($string, $allowed_html, $allowed_protocols)) )
570
  $string = $newstring;
571
  if ( $string == '' )
572
  return '';
586
  $elem = $matches[2];
587
  $attrlist = $matches[3];
588
 
589
+ if ( ! isset($allowed_html[strtolower($elem)]) )
590
  return '';
591
  # They are using a not allowed HTML element
592
 
593
  if ($slash != '')
594
+ return "</$elem>";
595
  # No attributes are allowed for closing elements
596
 
597
+ return VaultPress_kses::wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
598
  }
599
 
600
  static function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
605
  $xhtml_slash = ' /';
606
 
607
  # Are any attributes allowed at all for this element?
608
+ if ( ! isset($allowed_html[strtolower($element)]) || count($allowed_html[strtolower($element)]) == 0 )
 
609
  return "<$element$xhtml_slash>";
610
 
611
  # Split it
 
612
  $attrarr = VaultPress_kses::wp_kses_hair($attr, $allowed_protocols);
613
 
614
  # Go through $attrarr, and save the allowed attributes for this element
615
  # in $attr2
 
616
  $attr2 = '';
617
 
618
+ $allowed_attr = $allowed_html[strtolower($element)];
619
  foreach ($attrarr as $arreach) {
620
+ if ( ! isset( $allowed_attr[strtolower($arreach['name'])] ) )
621
  continue; # the attribute is not allowed
622
 
623
+ $current = $allowed_attr[strtolower($arreach['name'])];
624
+ if ( $current == '' )
625
  continue; # the attribute is not allowed
626
 
627
+ if ( strtolower( $arreach['name'] ) == 'style' ) {
628
+ $orig_value = $arreach['value'];
629
+ $value = safecss_filter_attr( $orig_value );
630
+
631
+ if ( empty( $value ) )
632
+ continue;
633
+
634
+ $arreach['value'] = $value;
635
+ $arreach['whole'] = str_replace( $orig_value, $value, $arreach['whole'] );
636
+ }
637
+
638
+ if ( ! is_array($current) ) {
639
  $attr2 .= ' '.$arreach['whole'];
640
  # there are no checks
641
 
642
+ } else {
643
  # there are some checks
644
  $ok = true;
645
+ foreach ($current as $currkey => $currval) {
646
+ if ( ! wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval) ) {
647
  $ok = false;
648
  break;
649
  }
 
 
 
 
 
 
 
 
 
 
 
 
650
  }
651
 
652
+ if ( $ok )
653
  $attr2 .= ' '.$arreach['whole']; # it passed them
654
  } # if !is_array($current)
655
  } # foreach
656
 
657
  # Remove any "<" or ">" characters
 
658
  $attr2 = preg_replace('/[<>]/', '', $attr2);
659
 
660
  return "<$element$attr2$xhtml_slash>";
696
  {
697
  $working = 1;
698
  $mode = 0;
699
+ if(false === array_key_exists($attrname, $attrarr)) {
700
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
701
  }
702
  $attr = preg_replace('/^\s+/', '', $attr);
713
  if ( in_array(strtolower($attrname), $uris) )
714
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
715
 
716
+ if(false === array_key_exists($attrname, $attrarr)) {
717
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
718
  }
719
  $working = 1;
729
  if ( in_array(strtolower($attrname), $uris) )
730
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
731
 
732
+ if(false === array_key_exists($attrname, $attrarr)) {
733
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
734
  }
735
  $working = 1;
745
  if ( in_array(strtolower($attrname), $uris) )
746
  $thisval = VaultPress_kses::wp_kses_bad_protocol($thisval, $allowed_protocols);
747
 
748
+ if(false === array_key_exists($attrname, $attrarr)) {
749
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
750
  }
751
  # We add quotes to conform to W3C's HTML spec.
764
  }
765
  } # while
766
 
767
+ if ($mode == 1 && false === array_key_exists($attrname, $attrarr))
768
  # special case, for when the attribute list ends with a valueless
769
  # attribute like "selected"
770
  $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
774
 
775
  static function wp_kses_bad_protocol($string, $allowed_protocols) {
776
  $string = wp_kses_no_null($string);
777
+ $iterations = 0;
778
 
779
+ do {
780
+ $original_string = $string;
781
  $string = VaultPress_kses::wp_kses_bad_protocol_once($string, $allowed_protocols);
782
+ } while ( $original_string != $string && ++$iterations < 6 );
783
+
784
+ if ( $original_string != $string )
785
+ return '';
786
 
787
  return $string;
788
  }
789
 
790
+ static function wp_kses_bad_protocol_once($string, $allowed_protocols, $count = 1) {
791
  $string2 = preg_split( '/:|&#0*58;|&#x0*3a;/i', $string, 2 );
792
+ if ( isset($string2[1]) && ! preg_match('%/\?%', $string2[0]) ) {
793
+ $string = trim( $string2[1] );
794
+ $protocol = VaultPress_kses::wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
795
+ if ( 'feed:' == $protocol ) {
796
+ if ( $count > 2 )
797
+ return '';
798
+ $string = VaultPress_kses::wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
799
+ if ( empty( $string ) )
800
+ return $string;
801
+ }
802
+ $string = $protocol . $string;
803
+ }
804
 
805
  return $string;
806
  }
812
  $string2 = strtolower($string2);
813
 
814
  $allowed = false;
815
+ foreach ( (array) $allowed_protocols as $one_protocol )
816
+ if ( strtolower($one_protocol) == $string2 ) {
817
+ $allowed = true;
818
+ break;
819
+ }
 
820
 
821
  if ($allowed)
822
  return "$string2:";
825
  }
826
 
827
  }
828
+
829
+ if ( !function_exists( 'get_available_languages' ) ) {
830
+ function get_available_languages( $dir = null ) {
831
+ $languages = array();
832
+ foreach( glob( ( is_null( $dir) ? WP_LANG_DIR : $dir ) . '/*.mo' ) as $lang_file )
833
+ if ( false === strpos( $lang_file, 'continents-cities' ) )
834
+ $languages[] = basename($lang_file, '.mo');
835
+ return $languages;
836
+ }
837
+ }
class.vaultpress-ixr-ssl-client.php CHANGED
@@ -39,7 +39,7 @@ class VaultPress_IXR_SSL_Client extends IXR_Client {
39
  'method' => 'POST',
40
  'body' => $xml,
41
  'headers' => $this->headers,
42
- 'sslverify' => false,
43
  );
44
  if ( $this->timeout )
45
  $args['timeout'] = $this->timeout;
@@ -129,4 +129,4 @@ class VaultPress_IXR_SSL_Client extends IXR_Client {
129
  // Message must be OK
130
  return true;
131
  }
132
- }
39
  'method' => 'POST',
40
  'body' => $xml,
41
  'headers' => $this->headers,
42
+ 'sslverify' => true,
43
  );
44
  if ( $this->timeout )
45
  $args['timeout'] = $this->timeout;
129
  // Message must be OK
130
  return true;
131
  }
132
+ }
cron-tasks.php CHANGED
@@ -1,40 +1,116 @@
1
  <?php
2
  include_once dirname( __FILE__ ) . '/vp-scanner.php';
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  class VP_Site_Scanner {
6
  function VP_Site_Scanner() {
7
  self::__construct();
8
  }
9
  function __construct() {
10
- add_action( 'vp_scan_site', array( $this, '_scan_site') );
11
- add_filter( 'cron_schedules', array( $this, '_custom_cron' ) );
12
- add_action( 'vp_scan_next_batch', array( $this, '_scan_batch' ) );
 
 
 
13
 
14
  $signatures = get_option( '_vp_signatures' );
15
- if ( $signatures && ! wp_next_scheduled( 'vp_scan_site' ) )
16
- wp_schedule_event( time(), 'hourly', 'vp_scan_site' );
17
- if ( $signatures && ! wp_next_scheduled( 'vp_scan_next_batch' ) )
18
- wp_schedule_event( time(), '2_minutes', 'vp_scan_next_batch' );
19
  }
20
 
21
  function _custom_cron( $schedules ) {
22
- $schedules['2_minutes'] = array(
23
- 'interval' => 2 * 60,
24
- 'display' => __( 'Once every 2 minutes' ),
25
- );
26
  return $schedules;
27
  }
28
 
29
  function _scan_site() {
30
- if ( ! get_option( '_vp_current_scan' ) )
31
- update_option( '_vp_current_scan', new VP_FileScan( ABSPATH ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
33
 
34
  function _scan_batch() {
35
- $current = get_option( '_vp_current_scan' );
36
- if ( !is_object( $current ) )
37
- return;
 
 
 
 
 
38
 
39
  $default_batch_limit = 400;
40
  if ( function_exists( 'set_time_limit' ) )
@@ -42,36 +118,36 @@ class VP_Site_Scanner {
42
  else
43
  $default_batch_limit = 100; // avoid timeouts
44
 
45
- $limit = get_option( '_vp_batch_file_size', $default_batch_limit );
46
- $files = $current->get_files( $limit );
47
- $GLOBALS['vp_signatures'] = get_option( '_vp_signatures' );
48
- update_option( '_vp_current_scan', $current );
 
 
49
 
50
- if ( empty( $files ) || !$current->last_dir || empty( $GLOBALS['vp_signatures'] ) ) {
51
- delete_option('_vp_current_scan');
52
- return;
53
- }
54
 
 
55
  $results = array();
56
- foreach ( $files as $file ) {
57
  $verdict = vp_scan_file( $file );
58
  if ( !empty( $verdict ) )
59
- $results[$file] = @md5_file( $file );
60
- }
61
 
62
- if ( !empty( $results ) ) {
63
- $vaultpress = VaultPress::init();
64
- $vaultpress->add_ping( 'security', array( 'suspicious' => $results ) );
65
- }
66
  }
67
 
68
- function &init() {
69
- static $instance = false;
70
- if ( !$instance )
71
- $instance = new VP_Site_Scanner();
72
- return $instance;
73
- }
74
-
75
-
76
  }
77
  VP_Site_Scanner::init();
1
  <?php
2
  include_once dirname( __FILE__ ) . '/vp-scanner.php';
3
 
4
+ if ( !function_exists( 'apply_filters_ref_array' ) ) :
5
+
6
+ function apply_filters_ref_array($tag, $args) {
7
+ global $wp_filter, $merged_filters, $wp_current_filter;
8
+
9
+ // Do 'all' actions first
10
+ if ( isset($wp_filter['all']) ) {
11
+ $wp_current_filter[] = $tag;
12
+ $all_args = func_get_args();
13
+ _wp_call_all_hook($all_args);
14
+ }
15
+
16
+ if ( !isset($wp_filter[$tag]) ) {
17
+ if ( isset($wp_filter['all']) )
18
+ array_pop($wp_current_filter);
19
+ return $args[0];
20
+ }
21
+
22
+ if ( !isset($wp_filter['all']) )
23
+ $wp_current_filter[] = $tag;
24
+
25
+ // Sort
26
+ if ( !isset( $merged_filters[ $tag ] ) ) {
27
+ ksort($wp_filter[$tag]);
28
+ $merged_filters[ $tag ] = true;
29
+ }
30
+
31
+ reset( $wp_filter[ $tag ] );
32
+
33
+ do {
34
+ foreach( (array) current($wp_filter[$tag]) as $the_ )
35
+ if ( !is_null($the_['function']) )
36
+ $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
37
+
38
+ } while ( next($wp_filter[$tag]) !== false );
39
+
40
+ array_pop( $wp_current_filter );
41
+
42
+ return $args[0];
43
+ }
44
+
45
+ endif;
46
 
47
  class VP_Site_Scanner {
48
  function VP_Site_Scanner() {
49
  self::__construct();
50
  }
51
  function __construct() {
52
+ // Only scan once in multisites.
53
+ if( function_exists( 'is_main_site' ) && !is_main_site() )
54
+ return;
55
+ add_action( 'vp_scan_site' , array( $this, '_scan_site') );
56
+ add_filter( 'cron_schedules' , array( $this, '_custom_cron' ) );
57
+ add_action( 'vp_scan_next_batch', array( $this, '_scan_batch' ) );
58
 
59
  $signatures = get_option( '_vp_signatures' );
60
+ if ( $signatures && ! wp_next_scheduled( 'vp_scan_site' ) )
61
+ wp_schedule_event( time(), 'daily', 'vp_scan_site' );
62
+ if ( $signatures && ! wp_next_scheduled( 'vp_scan_next_batch' ) )
63
+ wp_schedule_event( time(), 'five_minutes_interval', 'vp_scan_next_batch' );
64
  }
65
 
66
  function _custom_cron( $schedules ) {
67
+ $schedules['five_minutes_interval'] = array(
68
+ 'interval' => 300,
69
+ 'display' => __( 'Once every five minutes' , 'vaultpress'),
70
+ );
71
  return $schedules;
72
  }
73
 
74
  function _scan_site() {
75
+ if ( !get_option( '_vp_current_scan' ) ) {
76
+ $ignore_symlinks = get_option( '_vp_ignore_symlinks', false );
77
+ $paths = array( 'root' => new VP_FileScan( ABSPATH, $ignore_symlinks ) );
78
+
79
+ // Is WP_CONTENT_DIR inside ABSPATH?
80
+ if ( is_dir( WP_CONTENT_DIR ) && strpos( realpath( WP_CONTENT_DIR ), realpath( ABSPATH ) . DIRECTORY_SEPARATOR ) !== 0 )
81
+ $paths['content'] = new VP_FileScan( WP_CONTENT_DIR, $ignore_symlinks );
82
+
83
+ // Is WP_PLUGIN_DIR inside ABSPATH or WP_CONTENT_DIR?
84
+ if ( is_dir( WP_PLUGIN_DIR ) && strpos( realpath( WP_PLUGIN_DIR ), realpath( WP_CONTENT_DIR ) . DIRECTORY_SEPARATOR ) !== 0 && strpos( realpath( WP_PLUGIN_DIR ), realpath( ABSPATH ) . DIRECTORY_SEPARATOR ) !== 0 )
85
+ $paths['plugins'] = new VP_FileScan( WP_PLUGIN_DIR, $ignore_symlinks );
86
+
87
+ // Is WPMU_PLUGIN_DIR inside ABSPATH or WP_CONTENT_DIR?
88
+ if ( is_dir( WPMU_PLUGIN_DIR ) && strpos( realpath( WPMU_PLUGIN_DIR ), realpath( WP_CONTENT_DIR ) . DIRECTORY_SEPARATOR ) !== 0 && strpos( realpath( WPMU_PLUGIN_DIR ), realpath( ABSPATH ) . DIRECTORY_SEPARATOR ) !== 0 )
89
+ $paths['mu-plugins'] = new VP_FileScan( WPMU_PLUGIN_DIR, $ignore_symlinks );
90
+
91
+ update_option( '_vp_current_scan', $paths );
92
+ }
93
+ }
94
+
95
+ function _scan_clean_up( &$paths, $type = null ) {
96
+ if( is_array( $paths ) )
97
+ unset( $paths[$type] );
98
+ if ( empty( $paths ) || !is_array( $paths ) ) {
99
+ delete_option( '_vp_current_scan' );
100
+ return true;
101
+ }
102
+ return false;
103
  }
104
 
105
  function _scan_batch() {
106
+ $paths = get_option( '_vp_current_scan' );
107
+ if ( empty( $paths ) || $this->_scan_clean_up( $paths ) )
108
+ return false;
109
+
110
+ reset( $paths );
111
+ list( $type, $current ) = each( $paths );
112
+ if ( !is_object( $current ) || empty( $current->last_dir ) )
113
+ return $this->_scan_clean_up( $paths, $type );
114
 
115
  $default_batch_limit = 400;
116
  if ( function_exists( 'set_time_limit' ) )
118
  else
119
  $default_batch_limit = 100; // avoid timeouts
120
 
121
+ $GLOBALS['vp_signatures'] = get_option( '_vp_signatures' );
122
+ if ( empty( $GLOBALS['vp_signatures'] ) )
123
+ return false;
124
+
125
+ $limit = get_option( '_vp_batch_file_size', $default_batch_limit );
126
+ $files = $current->get_files( $limit );
127
 
128
+ // No more files to scan.
129
+ if ( !$current->last_dir || count( $files ) < $limit )
130
+ unset( $paths[$type] );
 
131
 
132
+ update_option( '_vp_current_scan', $paths );
133
  $results = array();
134
+ foreach ( $files as $file ) {
135
  $verdict = vp_scan_file( $file );
136
  if ( !empty( $verdict ) )
137
+ $results[$file] = array( 'hash' => @md5_file( $file ), 'verdict' => $verdict );
138
+ }
139
 
140
+ if ( !empty( $results ) ) {
141
+ $vaultpress = VaultPress::init();
142
+ $vaultpress->add_ping( 'security', array( 'suspicious_v2' => $results ) );
143
+ }
144
  }
145
 
146
+ static function &init() {
147
+ static $instance = false;
148
+ if ( !$instance )
149
+ $instance = new VP_Site_Scanner();
150
+ return $instance;
151
+ }
 
 
152
  }
153
  VP_Site_Scanner::init();
readme.txt CHANGED
@@ -1,9 +1,10 @@
1
  === VaultPress ===
2
- Contributors: automattic, aldenta, apokalyptik, briancolinger, shaunandrews, xknown
3
  Tags: security, malware, virus, backups, scanning
4
  Requires at least: 2.9.2
5
- Tested up to: 3.2.1
6
- Stable tag: 1.2.8
 
7
 
8
  VaultPress is a subscription service offering realtime backup, automated security scanning, and support from WordPress experts.
9
 
@@ -21,21 +22,21 @@ For more information, check out [VaultPress.com](http://vaultpress.com/).
21
  2. Visit `wp-admin/plugins.php` and activate the VaultPress plugin.
22
  3. Head to `wp-admin/admin.php?page=vaultpress` and enter your site&rsquo;s registration key. You can purchase your registration key at [VaultPress.com](http://vaultpress.com/plugin/?utm_source=plugin-readme&utm_medium=installation&utm_campaign=1.0)
23
 
24
- You can find more detailed instructions at [http://vaultpress.com/help/](http://vaultpress.com/help/install-vaultpress/?utm_source=plugin-readme&utm_medium=description&utm_campaign=1.0)
25
 
26
  == Frequently Asked Questions ==
27
 
28
- View our full list of FAQs at [http://vaultpress.com/help/faq/](http://vaultpress.com/help/faq/?utm_source=plugin-readme&utm_medium=faq&utm_campaign=1.0)
29
 
30
  = What’s included in each VaultPress plan? =
31
 
32
- All plans include Realtime Backups, Downloadable Archives for Restoring, Vitality Statistics, and the Activity Log.
33
 
34
- The Basic plan provides access to disaster recovery and support services.
35
 
36
- The Premium plan provides priority recovery and support services, along with site migration assistance. The Premium plan provides automated security scanning of Core, Theme, and Plugin files.
37
 
38
- The Enterprise members receive top priority for support services and migration assistance. Along with security scanning, Enterprise members receive complimentary security consulting and performance auditing.
39
 
40
  Update-to-date pricing and features can always be found on the [Plans &amp; Pricing](http://vaultpress.com/plugin/?utm_source=plugin-readme&utm_medium=installation&utm_campaign=1.0) page.
41
 
@@ -45,9 +46,24 @@ A VaultPress subscription is for a single WordPress site. You can purchase addit
45
 
46
  = Does VaultPress work with WordPress 3.0 Multisite installs? =
47
 
48
- With our 1.0 release, VaultPress now supports Multisite installs. Each site will require its own subscription.
49
 
50
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  = 1.0 =
53
  * First public release!
1
  === VaultPress ===
2
+ Contributors: automattic, apokalyptik, briancolinger, josephscott, shaunandrews, xknown
3
  Tags: security, malware, virus, backups, scanning
4
  Requires at least: 2.9.2
5
+ Tested up to: 3.5.2
6
+ Stable tag: 1.4.6
7
+ License: GPLv2
8
 
9
  VaultPress is a subscription service offering realtime backup, automated security scanning, and support from WordPress experts.
10
 
22
  2. Visit `wp-admin/plugins.php` and activate the VaultPress plugin.
23
  3. Head to `wp-admin/admin.php?page=vaultpress` and enter your site&rsquo;s registration key. You can purchase your registration key at [VaultPress.com](http://vaultpress.com/plugin/?utm_source=plugin-readme&utm_medium=installation&utm_campaign=1.0)
24
 
25
+ You can find more detailed instructions at [http://vaultpress.com/](http://help.vaultpress.com/install-vaultpress/?utm_source=plugin-readme&utm_medium=description&utm_campaign=1.0)
26
 
27
  == Frequently Asked Questions ==
28
 
29
+ View our full list of FAQs at [http://help.vaultpress.com/faq/](http://help.vaultpress.com/faq/?utm_source=plugin-readme&utm_medium=faq&utm_campaign=1.0)
30
 
31
  = What’s included in each VaultPress plan? =
32
 
33
+ All plans include Daily or Realtime Backups, Downloadable Archives for Restoring, Vitality Statistics, and the Activity Log.
34
 
35
+ The Lite plan provides Daily Backups, a 30-day backup archive and automated restores.
36
 
37
+ The Basic plan provides Realtime Backups to protect your changes as they happen and support services.
38
 
39
+ The Premium plan provides priority recovery and support services, along with site migration assistance. The Premium plan provides automated security scanning of Core, Theme, and Plugin files.
40
 
41
  Update-to-date pricing and features can always be found on the [Plans &amp; Pricing](http://vaultpress.com/plugin/?utm_source=plugin-readme&utm_medium=installation&utm_campaign=1.0) page.
42
 
46
 
47
  = Does VaultPress work with WordPress 3.0 Multisite installs? =
48
 
49
+ Yes, VaultPress supports Multisite installs. Each site will require its own subscription.
50
 
51
  == Changelog ==
52
+ = 1.4.6 =
53
+ * Bugfix: PHP 5.4 notices
54
+ * Feature: Add the possibility to ignore frequent updates on some postmeta keys.
55
+
56
+ = 1.3.9 =
57
+ * Feature: Request decoding (base64/rot13)
58
+ * Feature: Response encoding (base64/rot13)
59
+
60
+ = 1.3.8 =
61
+ * Bugfix: Validate IPv4-mapped IPv6 addresses in the internal firewall.
62
+ * Bugfix: Fix hooks not being properly added under certain circumstances.
63
+
64
+ = 1.3.7 =
65
+ * Bugfix: Protect against infinite loop due to a PHP bug.
66
+ * Bugfix: Encode remote ping requests.
67
 
68
  = 1.0 =
69
  * First public release!
vaultpress.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: VaultPress
4
  * Plugin URI: http://vaultpress.com/?utm_source=plugin-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
5
  * Description: Protect your content, themes, plugins, and settings with <strong>realtime backup</strong> and <strong>automated security scanning</strong> from <a href="http://vaultpress.com/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">VaultPress</a>. Activate, enter your registration key, and never worry again. <a href="http://vaultpress.com/help/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">Need some help?</a>
6
- * Version: 1.2.8
7
  * Author: Automattic
8
  * Author URI: http://vaultpress.com/?utm_source=author-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
9
  * License: GPL2+
@@ -17,8 +17,8 @@ if ( !defined( 'ABSPATH' ) )
17
 
18
  class VaultPress {
19
  var $option_name = 'vaultpress';
20
- var $db_version = 2;
21
- var $plugin_version = '1.2.8';
22
 
23
  function VaultPress() {
24
  $this->__construct();
@@ -48,11 +48,16 @@ class VaultPress {
48
  if ( is_admin() )
49
  $this->add_admin_actions_and_filters();
50
 
51
- if ( $this->is_registered() )
52
- $this->add_listener_actions_and_filters();
 
 
 
 
 
53
  }
54
 
55
- function &init() {
56
  static $instance = false;
57
 
58
  if ( !$instance ) {
@@ -110,6 +115,12 @@ class VaultPress {
110
  $this->update_option( 'db_version', $this->db_version );
111
  $this->clear_connection();
112
  }
 
 
 
 
 
 
113
  }
114
 
115
  function get_option( $key ) {
@@ -192,6 +203,7 @@ class VaultPress {
192
  <style type="text/css">
193
  #toplevel_page_vaultpress div.wp-menu-image {
194
  background: url(<?php echo esc_url( $this->server_url() ); ?>images/vp-icon-sprite.png?20111216) center top no-repeat;
 
195
  }
196
 
197
  .admin-color-classic #toplevel_page_vaultpress div.wp-menu-image {
@@ -202,6 +214,12 @@ class VaultPress {
202
  #toplevel_page_vaultpress:hover div.wp-menu-image {
203
  background-position: center bottom;
204
  }
 
 
 
 
 
 
205
  </style>
206
 
207
  <?php
@@ -224,14 +242,68 @@ class VaultPress {
224
  }
225
 
226
  add_action( "load-$hook", array( $this, 'ui_load' ) );
227
- add_action( 'admin_print_styles', array( $this, 'admin_styles' ) );
228
  }
229
 
230
- function admin_styles() {
 
 
 
231
  // force the cache to bust every day
232
  wp_enqueue_style( 'vaultpress', $this->server_url() . 'css/plugin.css' , false, date( 'Ymd' ) );
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  function server_url() {
236
  if ( !isset( $this->_server_url ) ) {
237
  $scheme = is_ssl() ? 'https' : 'http';
@@ -281,7 +353,7 @@ class VaultPress {
281
 
282
  // link to the VaultPress page if we're not already there
283
  if ( !isset( $_GET['page'] ) || 'vaultpress' != $_GET['page'] )
284
- $error_message .= ' ' . sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=vaultpress' ), __( 'Visit&nbsp;the&nbsp;VaultPress&nbsp;page' ) );
285
 
286
  if ( !empty( $error_message ) )
287
  $this->ui_message( $error_message, 'error' );
@@ -468,6 +540,10 @@ class VaultPress {
468
  $val = $this->get_option_name_ignore( true );
469
  update_option( '_vp_config_option_name_ignore', $val );
470
  break;
 
 
 
 
471
  }
472
  return $val;
473
  }
@@ -489,6 +565,17 @@ class VaultPress {
489
  return array_unique( array_merge( $defaults, $ignore_names ) );
490
  }
491
 
 
 
 
 
 
 
 
 
 
 
 
492
  ###
493
  ### Section: Backup Notification Hooks
494
  ###
@@ -628,7 +715,7 @@ class VaultPress {
628
  $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
629
  }
630
 
631
- function commentmeta_modification_handler( $object_id, $meta_key, $meta_value, $meta_id ) {
632
  if ( !is_array( $meta_id ) )
633
  return $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
634
  foreach ( $meta_id as $id ) {
@@ -638,10 +725,16 @@ class VaultPress {
638
 
639
  // Handle Notifying VaultPress of PostMeta changes via newfangled metadata functions
640
  function postmeta_insert_handler( $meta_id, $post_id, $meta_key, $meta_value='' ) {
 
 
 
641
  $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
642
  }
643
 
644
  function postmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
 
 
 
645
  if ( !is_array( $meta_id ) )
646
  return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
647
  foreach ( $meta_id as $id ) {
@@ -650,7 +743,10 @@ class VaultPress {
650
  }
651
 
652
  // Handle Notifying VaultPress of PostMeta changes via old school cherypicked hooks
653
- function postmeta_action_handler( $meta_id ) {
 
 
 
654
  if ( !is_array($meta_id) )
655
  return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
656
  foreach ( $meta_id as $id )
@@ -810,9 +906,9 @@ class VaultPress {
810
  }
811
 
812
  function update_firewall() {
813
- $args = array( 'timeout' => $this->get_option( 'timeout' ) );
814
  $hostname = $this->get_option( 'hostname' );
815
- $data = wp_remote_get( "http://$hostname/service-ips", $args );
816
 
817
  if ( $data )
818
  $data = @unserialize( $data['body'] );
@@ -820,10 +916,34 @@ class VaultPress {
820
  if ( $data ) {
821
  $newval = array( 'updated' => time(), 'data' => $data );
822
  $this->update_option( 'service_ips', $newval );
823
- return $data;
824
  }
825
 
826
- return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
827
  }
828
 
829
  function check_connection( $force_check = false ) {
@@ -871,6 +991,13 @@ class VaultPress {
871
  return false;
872
  }
873
 
 
 
 
 
 
 
 
874
  // test connection between the site and the servers
875
  $connect = (string)$this->contact_service( 'test', array( 'type' => 'connect' ) );
876
  if ( 'ok' != $connect ) {
@@ -902,6 +1029,110 @@ class VaultPress {
902
  return true;
903
  }
904
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905
  function parse_request( $wp ) {
906
  if ( !isset( $_GET['vaultpress'] ) || $_GET['vaultpress'] !== 'true' )
907
  return $wp;
@@ -910,6 +1141,11 @@ class VaultPress {
910
 
911
  // just in case we have any plugins that decided to spit some data out already...
912
  @ob_end_clean();
 
 
 
 
 
913
 
914
  if ( isset( $_GET['ticker'] ) && function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) )
915
  die( (string)$this->contact_service( 'ticker' ) );
@@ -920,7 +1156,36 @@ class VaultPress {
920
  define( 'VAULTPRESS_API', true );
921
 
922
  if ( !$this->validate_api_signature() ) {
923
- die( 'invalid api call signature' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  }
925
 
926
  if ( !isset( $bdb ) ) {
@@ -965,10 +1230,10 @@ class VaultPress {
965
  $code = $_POST['code'];
966
  if ( !$code )
967
  $this->response( "No Code Found" );
968
- $syntax_check = @eval( 'return true;' . $_POST['code'] );
969
  if ( !$syntax_check )
970
  $this->response( "Code Failed Syntax Check" );
971
- $this->response( eval( $_POST['code'] ) );
972
  die();
973
  break;
974
  case 'catchup:get':
@@ -985,9 +1250,9 @@ class VaultPress {
985
  case 'general:ping':
986
  global $wp_version, $wp_db_version, $manifest_version;
987
  @error_reporting(0);
988
- $httpd_modules = array();
989
  $httpd = null;
990
- if ( !$httpd && function_exists( 'apache_get_modules' ) ) {
991
  if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
992
  $http_modules = apache_get_modules();
993
  else
@@ -1015,6 +1280,8 @@ class VaultPress {
1015
  foreach ( $wpdb->get_results( "SHOW VARIABLES" ) as $row )
1016
  $mvars["$row->Variable_name"] = $row->Value;
1017
  }
 
 
1018
 
1019
  $ms_global_tables = array_merge( $wpdb->global_tables, $wpdb->ms_global_tables );
1020
  $tinfo = array();
@@ -1069,10 +1336,12 @@ class VaultPress {
1069
  $loadavg = sys_getloadavg();
1070
  else
1071
  $loadavg = null;
1072
- if ( !function_exists( 'get_plugin_data' ) )
1073
- require_once ABSPATH . '/wp-admin/includes/plugin.php';
1074
 
1075
- $vaultpress_response_info = get_plugin_data( __FILE__ );
 
 
 
 
1076
  $vaultpress_response_info['deferred_pings'] = (int)$this->ai_ping_count();
1077
  $vaultpress_response_info['vaultpress_hostname'] = $this->get_option( 'hostname' );
1078
  $vaultpress_response_info['vaultpress_timeout'] = $this->get_option( 'timeout' );
@@ -1107,8 +1376,8 @@ class VaultPress {
1107
  'prefix' => $wpdb->prefix,
1108
  'is_multisite' => $this->is_multisite(),
1109
  'is_main_site' => $this->is_main_site(),
1110
- 'blog_id' => $current_blog->blog_id,
1111
- 'theme' => get_current_theme(),
1112
  'plugins' => preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ),
1113
  'tables' => $tinfo,
1114
  'name' => get_bloginfo( 'name' ),
@@ -1123,7 +1392,7 @@ class VaultPress {
1123
  'load' => $loadavg,
1124
  'info' => @php_uname( "a" ),
1125
  'time' => time(),
1126
- 'php' => array( 'version' => phpversion(), 'ini' => $ini_vals ),
1127
  'httpd' => array(
1128
  'type' => $httpd,
1129
  'modules' => $http_modules,
@@ -1175,8 +1444,10 @@ class VaultPress {
1175
  else
1176
  $where = null;
1177
 
1178
- if ( isset( $_POST['table'] ) )
1179
- $bdb->attach( base64_decode( $_POST['table'] ) );
 
 
1180
 
1181
  switch ( array_pop( explode( ':', $_GET['action'] ) ) ) {
1182
  case 'diff':
@@ -1324,7 +1595,8 @@ class VaultPress {
1324
  $args['args'] = '';
1325
  $old_timeout = ini_get( 'default_socket_timeout' );
1326
  $timeout = $this->get_option( 'timeout' );
1327
- ini_set( 'default_socket_timeout', $timeout );
 
1328
  $hostname = $this->get_option( 'hostname' );
1329
 
1330
  if ( !class_exists( 'VaultPress_IXR_SSL_Client' ) )
@@ -1356,9 +1628,10 @@ class VaultPress {
1356
  $this->_fix_ixr_null_to_string( $args );
1357
  $args['signature'] = $this->sign_string( serialize( $args ), $this->get_option( 'secret' ), $salt ).":$salt";
1358
 
1359
- $client->query( 'vaultpress.'.$action, $args );
1360
  $rval = $client->message ? $client->getResponse() : '';
1361
- ini_set( 'default_socket_timeout', $old_timeout );
 
1362
 
1363
  // we got an error from the servers
1364
  if ( is_array( $rval ) && isset( $rval['faultCode'] ) ) {
@@ -1372,18 +1645,28 @@ class VaultPress {
1372
 
1373
  function validate_api_signature() {
1374
  global $__vp_validate_error;
1375
- if ( isset( $_POST['signature']) && $_POST['signature'] )
1376
- $sig = $_POST['signature'];
1377
- else
 
 
 
 
 
 
1378
  return false;
 
1379
 
1380
  $secret = $this->get_option( 'secret' );
1381
  if ( !$secret ) {
1382
- $__vp_validate_error = "missing_secret";
1383
  return false;
1384
  }
1385
  if ( !$this->get_option( 'disable_firewall' ) ) {
1386
  $rxs = $this->get_option( 'service_ips' );
 
 
 
1387
  if ( $rxs ) {
1388
  $timeout = time() - 86400;
1389
  if ( $rxs ) {
@@ -1400,14 +1683,12 @@ class VaultPress {
1400
  if ( $data = $this->update_firewall() )
1401
  $rxs = $data;
1402
  }
1403
- if ( !$this->validate_ip_address( $rxs ) ) {
1404
- $__vp_validate_error = "firewall_fail";
1405
  return false;
1406
- }
1407
  }
1408
  $sig = explode( ':', $sig );
1409
- if ( !is_array( $sig ) || count( $sig ) != 2 || !$sig[0] || !$sig[1] ) {
1410
- $__vp_validate_error = "invalid_sig";
1411
  return false;
1412
  }
1413
 
@@ -1421,16 +1702,32 @@ class VaultPress {
1421
  ksort( $post );
1422
  $to_sign = serialize( array( 'uri' => $uri, 'post' => $post ) );
1423
  $signature = $this->sign_string( $to_sign, $secret, $sig[1] );
1424
- if ( $sig[0] == $signature )
1425
  return true;
1426
 
1427
- $__vp_validate_error = "was: {$sig[0]}, need: $signature";
1428
  return false;
1429
  }
1430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1431
  function validate_ip_address( $rxs ) {
1432
- if ( empty( $rxs ) )
 
 
1433
  return false;
 
1434
 
1435
  $remote_ips = array();
1436
 
@@ -1440,11 +1737,17 @@ class VaultPress {
1440
  if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
1441
  $remote_ips[] = $_SERVER['REMOTE_ADDR'];
1442
 
 
 
 
 
 
1443
  $iprx = '/^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$/';
1444
 
1445
- foreach ( $remote_ips as $remote_ip ) {
 
1446
  if ( !preg_match( $iprx, $remote_ip, $r ) ) {
1447
- $__vp_validate_error = "remote_addr_fail";
1448
  return false;
1449
  }
1450
 
@@ -1463,6 +1766,7 @@ class VaultPress {
1463
  }
1464
  }
1465
  }
 
1466
 
1467
  return false;
1468
  }
@@ -1472,8 +1776,19 @@ class VaultPress {
1472
  }
1473
 
1474
  function response( $response, $raw = false ) {
1475
- if ( $raw )
1476
- die( $response );
 
 
 
 
 
 
 
 
 
 
 
1477
  list( $usec, $sec ) = explode( " ", microtime() );
1478
  $r = new stdClass();
1479
  $r->req_vector = floatval( $_GET['vector'] );
@@ -1491,7 +1806,14 @@ class VaultPress {
1491
  else
1492
  $r->memory_usage = false;
1493
  $r->response = $response;
1494
- die( serialize( $r ) );
 
 
 
 
 
 
 
1495
  }
1496
 
1497
  function reset_pings() {
@@ -1694,11 +2016,14 @@ class VaultPress {
1694
 
1695
  function add_admin_actions_and_filters() {
1696
  add_action( 'admin_init', array( $this, 'admin_init' ) );
1697
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
1698
  add_action( 'admin_head', array( $this, 'admin_head' ) );
1699
  }
1700
 
1701
  function add_listener_actions_and_filters() {
 
 
 
1702
  // Comments
1703
  add_action( 'delete_comment', array( $this, 'comment_action_handler' ) );
1704
  add_action( 'wp_set_comment_status', array( $this, 'comment_action_handler' ) );
@@ -1748,9 +2073,9 @@ class VaultPress {
1748
  add_action( 'updated_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
1749
  add_action( 'delete_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
1750
  add_action( 'deleted_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
1751
- add_action( 'added_postmeta', array( $this, 'postmeta_action_handler' ) );
1752
- add_action( 'update_postmeta', array( $this, 'postmeta_action_handler' ) );
1753
- add_action( 'delete_postmeta', array( $this, 'postmeta_action_handler' ) );
1754
 
1755
  // Links
1756
  add_action( 'edit_link', array( $this, 'link_action_handler' ) );
@@ -1781,6 +2106,16 @@ class VaultPress {
1781
  add_action( 'updated_option', array( $this, 'option_handler' ), 1 );
1782
  add_action( 'added_option', array( $this, 'option_handler' ), 1 );
1783
 
 
 
 
 
 
 
 
 
 
 
1784
  // Report back to VaultPress
1785
  add_action( 'shutdown', array( $this, 'do_pings' ) );
1786
 
3
  * Plugin Name: VaultPress
4
  * Plugin URI: http://vaultpress.com/?utm_source=plugin-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
5
  * Description: Protect your content, themes, plugins, and settings with <strong>realtime backup</strong> and <strong>automated security scanning</strong> from <a href="http://vaultpress.com/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">VaultPress</a>. Activate, enter your registration key, and never worry again. <a href="http://vaultpress.com/help/?utm_source=wp-admin&amp;utm_medium=plugin-description&amp;utm_campaign=1.0" rel="nofollow">Need some help?</a>
6
+ * Version: 1.4.6
7
  * Author: Automattic
8
  * Author URI: http://vaultpress.com/?utm_source=author-uri&amp;utm_medium=plugin-description&amp;utm_campaign=1.0
9
  * License: GPL2+
17
 
18
  class VaultPress {
19
  var $option_name = 'vaultpress';
20
+ var $db_version = 3;
21
+ var $plugin_version = '1.4.6';
22
 
23
  function VaultPress() {
24
  $this->__construct();
48
  if ( is_admin() )
49
  $this->add_admin_actions_and_filters();
50
 
51
+ if ( $this->is_registered() ) {
52
+ $do_not_backup = $this->get_option( 'do_not_backup' ) || $this->get_option( 'do_not_send_backup_pings' );
53
+ if ( $do_not_backup )
54
+ $this->add_vp_required_filters();
55
+ else
56
+ $this->add_listener_actions_and_filters();
57
+ }
58
  }
59
 
60
+ static function &init() {
61
  static $instance = false;
62
 
63
  if ( !$instance ) {
115
  $this->update_option( 'db_version', $this->db_version );
116
  $this->clear_connection();
117
  }
118
+
119
+ if ( $current_db_version < 3 ) {
120
+ $this->update_firewall();
121
+ $this->update_option( 'db_version', $this->db_version );
122
+ $this->clear_connection();
123
+ }
124
  }
125
 
126
  function get_option( $key ) {
203
  <style type="text/css">
204
  #toplevel_page_vaultpress div.wp-menu-image {
205
  background: url(<?php echo esc_url( $this->server_url() ); ?>images/vp-icon-sprite.png?20111216) center top no-repeat;
206
+ background-size: 28px 84px;
207
  }
208
 
209
  .admin-color-classic #toplevel_page_vaultpress div.wp-menu-image {
214
  #toplevel_page_vaultpress:hover div.wp-menu-image {
215
  background-position: center bottom;
216
  }
217
+
218
+ @media only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
219
+ #toplevel_page_vaultpress div.wp-menu-image {
220
+ background-image: url(<?php echo esc_url( $this->server_url() ); ?>images/vp-icon-sprite-2x.png?20111216);
221
+ }
222
+ }
223
  </style>
224
 
225
  <?php
242
  }
243
 
244
  add_action( "load-$hook", array( $this, 'ui_load' ) );
245
+ add_action( 'admin_print_styles', array( $this, 'styles' ) );
246
  }
247
 
248
+ function styles() {
249
+ if ( !current_user_can( 'manage_options' ) || !is_admin() )
250
+ return;
251
+
252
  // force the cache to bust every day
253
  wp_enqueue_style( 'vaultpress', $this->server_url() . 'css/plugin.css' , false, date( 'Ymd' ) );
254
  }
255
 
256
+ // display a security threat notice if one exists
257
+ function toolbar( $wp_admin_bar ) {
258
+ global $wp_version;
259
+
260
+ // these new toolbar functions were introduced in 3.3
261
+ // http://codex.wordpress.org/Function_Reference/add_node
262
+ if ( version_compare( $wp_version, '3.3', '<') )
263
+ return;
264
+
265
+ if ( !current_user_can( 'manage_options' ) )
266
+ return;
267
+
268
+ $messages = $this->get_messages();
269
+ if ( !empty( $messages['security_notice_count'] ) ) {
270
+ $count = (int)$messages['security_notice_count'];
271
+ if ( $count > 0 ) {
272
+ $count = number_format( $count, 0 );
273
+ $wp_admin_bar->add_node( array(
274
+ 'id' => 'vp-notice',
275
+ 'title' => '<strong><span class="ab-icon"></span>' .
276
+ sprintf( _n( '%s Security Threat', '%s Security Threats', $count , 'vaultpress'), $count ) .
277
+ ' </strong>',
278
+ 'parent' => 'top-secondary',
279
+ 'href' => sprintf( 'https://dashboard.vaultpress.com/%d/security/', $messages['site_id'] ),
280
+ 'meta' => array(
281
+ 'title' => __( 'Visit VaultPress Security' , 'vaultpress'),
282
+ 'onclick' => 'window.open( this.href ); return false;',
283
+ 'class' => 'error'
284
+ ),
285
+ ) );
286
+ }
287
+ }
288
+ }
289
+
290
+ // get any messages from the VP servers
291
+ function get_messages( $force_reload = false ) {
292
+ $last_contact = $this->get_option( 'messages_last_contact' );
293
+
294
+ // only run the messages check every 30 minutes
295
+ if ( ( time() - (int)$last_contact ) > 1800 || $force_reload ) {
296
+ $messages = base64_decode( $this->contact_service( 'messages', array() ) );
297
+ $messages = unserialize( $messages );
298
+ $this->update_option( 'messages_last_contact', time() );
299
+ $this->update_option( 'messages', $messages );
300
+ } else {
301
+ $messages = $this->get_option( 'messages' );
302
+ }
303
+
304
+ return $messages;
305
+ }
306
+
307
  function server_url() {
308
  if ( !isset( $this->_server_url ) ) {
309
  $scheme = is_ssl() ? 'https' : 'http';
353
 
354
  // link to the VaultPress page if we're not already there
355
  if ( !isset( $_GET['page'] ) || 'vaultpress' != $_GET['page'] )
356
+ $error_message .= ' ' . sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=vaultpress' ), __( 'Visit&nbsp;the&nbsp;VaultPress&nbsp;page' , 'vaultpress') );
357
 
358
  if ( !empty( $error_message ) )
359
  $this->ui_message( $error_message, 'error' );
540
  $val = $this->get_option_name_ignore( true );
541
  update_option( '_vp_config_option_name_ignore', $val );
542
  break;
543
+ case '_vp_config_post_meta_name_ignore':
544
+ $val = $this->get_post_meta_name_ignore( true );
545
+ update_option( '_vp_config_post_meta_name_ignore', $val );
546
+ break;
547
  }
548
  return $val;
549
  }
565
  return array_unique( array_merge( $defaults, $ignore_names ) );
566
  }
567
 
568
+ // post meta name patterns to ignore
569
+ function get_post_meta_name_ignore( $return_defaults = false ) {
570
+ $defaults = array(
571
+ 'pvc_views'
572
+ );
573
+ if ( $return_defaults )
574
+ return $defaults;
575
+ $ignore_names = $this->get_config( '_vp_config_post_meta_name_ignore' );
576
+ return array_unique( array_merge( $defaults, $ignore_names ) );
577
+ }
578
+
579
  ###
580
  ### Section: Backup Notification Hooks
581
  ###
715
  $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
716
  }
717
 
718
+ function commentmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
719
  if ( !is_array( $meta_id ) )
720
  return $this->add_ping( 'db', array( 'commentmeta' => $meta_id ) );
721
  foreach ( $meta_id as $id ) {
725
 
726
  // Handle Notifying VaultPress of PostMeta changes via newfangled metadata functions
727
  function postmeta_insert_handler( $meta_id, $post_id, $meta_key, $meta_value='' ) {
728
+ if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
729
+ return;
730
+
731
  $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
732
  }
733
 
734
  function postmeta_modification_handler( $meta_id, $object_id, $meta_key, $meta_value ) {
735
+ if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
736
+ return;
737
+
738
  if ( !is_array( $meta_id ) )
739
  return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
740
  foreach ( $meta_id as $id ) {
743
  }
744
 
745
  // Handle Notifying VaultPress of PostMeta changes via old school cherypicked hooks
746
+ function postmeta_action_handler( $meta_id, $post_id = null, $meta_key = null ) {
747
+ if ( in_array( $meta_key, $this->get_post_meta_name_ignore() ) )
748
+ return;
749
+
750
  if ( !is_array($meta_id) )
751
  return $this->add_ping( 'db', array( 'postmeta' => $meta_id ) );
752
  foreach ( $meta_id as $id )
906
  }
907
 
908
  function update_firewall() {
909
+ $args = array( 'timeout' => $this->get_option( 'timeout' ), 'sslverify' => true );
910
  $hostname = $this->get_option( 'hostname' );
911
+ $data = wp_remote_get( "https://$hostname/service-ips", $args );
912
 
913
  if ( $data )
914
  $data = @unserialize( $data['body'] );
916
  if ( $data ) {
917
  $newval = array( 'updated' => time(), 'data' => $data );
918
  $this->update_option( 'service_ips', $newval );
 
919
  }
920
 
921
+ $external_data = wp_remote_get( "https://$hostname/service-ips-external", $args );
922
+ if ( $external_data )
923
+ $external_data = @unserialize( $external_data['body'] );
924
+
925
+ if ( $external_data ) {
926
+ $external_newval = array( 'updated' => time(), 'data' => $external_data );
927
+ update_option( 'vaultpress_service_ips_external', $external_newval );
928
+ }
929
+
930
+ if ( !empty( $data ) && !empty( $external_data ) )
931
+ $data = array_merge( $data, $external_data );
932
+
933
+ if ( $data ) {
934
+ return $data;
935
+ } else {
936
+ return null;
937
+ }
938
+ }
939
+
940
+ // Update local cache of VP plan settings, based on a ping or connection test result
941
+ function update_plan_settings( $message ) {
942
+ if ( array_key_exists( 'do_backups', $message ) )
943
+ $this->update_option( 'do_not_backup', ( false === $message['do_backups'] ) );
944
+
945
+ if ( array_key_exists( 'do_backup_pings', $message ) )
946
+ $this->update_option( 'do_not_send_backup_pings', ( false === $message['do_backup_pings'] ) );
947
  }
948
 
949
  function check_connection( $force_check = false ) {
991
  return false;
992
  }
993
 
994
+ $this->update_plan_settings( $connect );
995
+
996
+ if ( !empty( $connect['signatures'] ) ) {
997
+ delete_option( '_vp_signatures' );
998
+ add_option( '_vp_signatures', maybe_unserialize( $connect['signatures'] ), '', 'no' );
999
+ }
1000
+
1001
  // test connection between the site and the servers
1002
  $connect = (string)$this->contact_service( 'test', array( 'type' => 'connect' ) );
1003
  if ( 'ok' != $connect ) {
1029
  return true;
1030
  }
1031
 
1032
+ function get_login_tokens() {
1033
+ // By default the login token is valid for 30 minutes.
1034
+ $nonce_life = $this->get_option( 'nonce_life' ) ? $this->get_option( 'nonce_life' ) : 1800;
1035
+ $salt = wp_salt( 'nonce' ) . md5( $this->get_option( 'secret' ) );
1036
+ $nonce_life /= 2;
1037
+
1038
+ return array(
1039
+ 'previous' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life - 1 ), $salt ), -12, 10 ),
1040
+ 'current' => substr( hash_hmac( 'md5', 'vp-login' . ceil( time() / $nonce_life ), $salt ), -12, 10 ),
1041
+ );
1042
+ }
1043
+ function add_js_token() {
1044
+ $nonce = $this->get_login_tokens();
1045
+ $token = $nonce['current'];
1046
+
1047
+ // Uglyfies the JS code before sending it to the browser.
1048
+ $whitelist = array( 'charAt', 'all', 'setAttribute', 'document', 'createElement', 'appendChild', 'input', 'hidden', 'type', 'name', 'value', 'getElementById', 'loginform', '_vp' );
1049
+ shuffle( $whitelist );
1050
+ $whitelist = array_flip( $whitelist );
1051
+
1052
+ $set = array(
1053
+ 0 => array( '+[]', 'e^e' ),
1054
+ 1 => array( '+!![]', '2>>1', "e[{$whitelist['type']}].charCodeAt(3)>>6" ),
1055
+ 2 => array( '(+!![])<<1', "e[{$whitelist['_vp']}].replace(/_/,'').length" ),
1056
+ 3 => array( "(Math.log(2<<4)+[])[e[{$whitelist['charAt']}]](0)", "e[{$whitelist['_vp']}].length" ),
1057
+ 4 => array( '(+!![])<<2', "e[{$whitelist['input']}].length^1", "e[{$whitelist['name']}].length" ),
1058
+ 5 => array( '((1<<2)+1)', 'parseInt("f",0x10)/3' ),
1059
+ 6 => array( '(7^1)', "e[{$whitelist['hidden']}].length" ),
1060
+ 7 => array( '(3<<1)+1', "e[{$whitelist['hidden']}].length^1" ),
1061
+ 8 => array( '(0x101>>5)', "e[{$whitelist['document']}].length" ),
1062
+ 9 => array( '(0x7^4)*(3+[])', "e[{$whitelist['loginform']}].length", "(1<<e[{$whitelist['_vp']}].length)^1" ),
1063
+ 'a' => array( "(![]+\"\")[e[{$whitelist['charAt']}]](1)", "e[{$whitelist['appendChild']}][e[{$whitelist['charAt']}]](0)", "e[{$whitelist['name']}][e[{$whitelist['charAt']}]](1)" ),
1064
+ 'b' => array( "([]+{})[e[{$whitelist['charAt']}]](2)", "({}+[])[e[{$whitelist['charAt']}]](2)" ),
1065
+ 'c' => array( "([]+{})[e[{$whitelist['charAt']}]](5)", "e[{$whitelist['createElement']}][e[{$whitelist['charAt']}]](0)" ),
1066
+ 'd' => array( "([][0]+\"\")[e[{$whitelist['charAt']}]](2)", "([][0]+[])[e[{$whitelist['charAt']}]](2)" ),
1067
+ 'e' => array( "(!![]+[])[e[{$whitelist['charAt']}]](3)", "(!![]+\"\")[e[{$whitelist['charAt']}]](3)" ),
1068
+ 'f' => array( "(![]+[])[e[{$whitelist['charAt']}]](0)", "([]+![])[e[{$whitelist['charAt']}]](e^e)", "([]+![])[e[{$whitelist['charAt']}]](0)" ),
1069
+ );
1070
+
1071
+ $js_code = <<<JS
1072
+ <script type="text/javascript">
1073
+ /* <![CDATA[ */
1074
+ (function(){
1075
+ var i,e='%s'.split('|'),_=[%s],s=function(a,b,c){a[b]=c};
1076
+ if(this[e[{$whitelist['document']}]][e[{$whitelist['all']}]]){
1077
+ try {
1078
+ i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]]('<'+e[{$whitelist['input']}]+' '+e[{$whitelist['name']}]+'='+(e[{$whitelist['_vp']}]+(!![]))+' />');
1079
+ }catch(e){}
1080
+ }
1081
+ if(!i){
1082
+ i=this[e[{$whitelist['document']}]][e[{$whitelist['createElement']}]](e[{$whitelist['input']}]);
1083
+ s(i,e[{$whitelist['name']}],e[{$whitelist['_vp']}]+(!![]));
1084
+ }
1085
+ s(i,e[{$whitelist['type']}],e[{$whitelist['hidden']}]).
1086
+ s(i,e[{$whitelist['value']}],(%s+""));
1087
+ try {
1088
+ var __=this[e[{$whitelist['document']}]][e[{$whitelist['getElementById']}]](e[{$whitelist['loginform']}]);
1089
+ __[e[{$whitelist['appendChild']}]](i);
1090
+ } catch(e){}
1091
+ })();
1092
+ /* ]]> */
1093
+ </script>
1094
+ JS;
1095
+ $chars = array();
1096
+ for ( $i = 0; $i < strlen( $token ); $i++ ) {
1097
+ if ( isset( $set[$token{$i}] ) ) {
1098
+ $k = array_rand( $set[$token{$i}], 1 );
1099
+ $chars[] = $set[$token{$i}][$k];
1100
+ } else {
1101
+ $chars[] = $token{$i};
1102
+ }
1103
+ }
1104
+ $random = array_unique( $chars );
1105
+ shuffle( $random );
1106
+ $random = array_flip( $random );
1107
+
1108
+ foreach( $chars as $i => $v )
1109
+ $chars[$i] = sprintf( '_[%d]', $random[$v] );
1110
+
1111
+ $code = preg_replace(
1112
+ "#[\n\r\t]#",
1113
+ '',
1114
+ sprintf( $js_code,
1115
+ join( '|', array_keys( $whitelist ) ),
1116
+ join( ',', array_keys( $random ) ),
1117
+ join( '+"")+(', $chars )
1118
+ )
1119
+ );
1120
+ echo $code;
1121
+ }
1122
+
1123
+ function authenticate( $user, $username, $password ) {
1124
+ if ( is_wp_error( $user ) )
1125
+ return $user;
1126
+ if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST || defined( 'APP_REQUEST' ) && APP_REQUEST ) {
1127
+ // Try to log in with the username and password.
1128
+ }
1129
+ $retval = $user;
1130
+ if ( empty( $_POST['_vptrue'] ) || !in_array( $_POST['_vptrue'], $this->get_login_tokens(), true ) )
1131
+ $retval = new WP_Error( 'invalid_token', __( 'Invalid token. Please try to log in again.' ) );
1132
+
1133
+ return $retval;
1134
+ }
1135
+
1136
  function parse_request( $wp ) {
1137
  if ( !isset( $_GET['vaultpress'] ) || $_GET['vaultpress'] !== 'true' )
1138
  return $wp;
1141
 
1142
  // just in case we have any plugins that decided to spit some data out already...
1143
  @ob_end_clean();
1144
+ // Headers to avoid search engines indexing "invalid api call signature" pages.
1145
+ if ( !headers_sent() ) {
1146
+ header( 'X-Robots-Tag: none' );
1147
+ header( 'X-Robots-Tag: unavailable_after: 1 Oct 2012 00:00:00 PST', false );
1148
+ }
1149
 
1150
  if ( isset( $_GET['ticker'] ) && function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) )
1151
  die( (string)$this->contact_service( 'ticker' ) );
1156
  define( 'VAULTPRESS_API', true );
1157
 
1158
  if ( !$this->validate_api_signature() ) {
1159
+ global $__vp_validate_error;
1160
+ die( 'invalid api call signature [' . base64_encode( serialize( $__vp_validate_error ) ) . ']' );
1161
+ }
1162
+
1163
+ if ( !empty( $_GET['ge'] ) ) {
1164
+ // "ge" -- "GET encoding"
1165
+ if ( '1' === $_GET['ge'] )
1166
+ $_GET['action'] = base64_decode( $_GET['action'] );
1167
+ if ( '2' === $_GET['ge'] )
1168
+ $_GET['action'] = str_rot13( $_GET['action'] );
1169
+ }
1170
+
1171
+ if ( !empty( $_GET['pe'] ) ) {
1172
+ // "pe" -- POST encoding
1173
+ if ( '1' === $_GET['pe'] ) {
1174
+ foreach( $_POST as $idx => $val ) {
1175
+ if ( $idx === 'signature' )
1176
+ continue;
1177
+ $_POST[ base64_decode( $idx ) ] = base64_decode( $val );
1178
+ unset( $_POST[$idx] );
1179
+ }
1180
+ }
1181
+ if ( '2' === $_GET['pe'] ) {
1182
+ foreach( $_POST as $idx => $val ) {
1183
+ if ( $idx === 'signature' )
1184
+ continue;
1185
+ $_POST[ base64_decode( $idx ) ] = str_rot13( $val );
1186
+ unset( $_POST[$idx] );
1187
+ }
1188
+ }
1189
  }
1190
 
1191
  if ( !isset( $bdb ) ) {
1230
  $code = $_POST['code'];
1231
  if ( !$code )
1232
  $this->response( "No Code Found" );
1233
+ $syntax_check = @eval( 'return true;' . $code );
1234
  if ( !$syntax_check )
1235
  $this->response( "Code Failed Syntax Check" );
1236
+ $this->response( eval( $code ) );
1237
  die();
1238
  break;
1239
  case 'catchup:get':
1250
  case 'general:ping':
1251
  global $wp_version, $wp_db_version, $manifest_version;
1252
  @error_reporting(0);
1253
+ $http_modules = array();
1254
  $httpd = null;
1255
+ if ( function_exists( 'apache_get_modules' ) ) {
1256
  if ( isset( $_POST['apache_modules'] ) && $_POST['apache_modules'] == 1 )
1257
  $http_modules = apache_get_modules();
1258
  else
1280
  foreach ( $wpdb->get_results( "SHOW VARIABLES" ) as $row )
1281
  $mvars["$row->Variable_name"] = $row->Value;
1282
  }
1283
+
1284
+ $this->update_plan_settings( $_POST );
1285
 
1286
  $ms_global_tables = array_merge( $wpdb->global_tables, $wpdb->ms_global_tables );
1287
  $tinfo = array();
1336
  $loadavg = sys_getloadavg();
1337
  else
1338
  $loadavg = null;
 
 
1339
 
1340
+ require_once ABSPATH . '/wp-admin/includes/plugin.php';
1341
+ if ( function_exists( 'get_plugin_data' ) )
1342
+ $vaultpress_response_info = get_plugin_data( __FILE__ );
1343
+ else
1344
+ $vaultpress_response_info = array( 'Version' => $this->plugin_version );
1345
  $vaultpress_response_info['deferred_pings'] = (int)$this->ai_ping_count();
1346
  $vaultpress_response_info['vaultpress_hostname'] = $this->get_option( 'hostname' );
1347
  $vaultpress_response_info['vaultpress_timeout'] = $this->get_option( 'timeout' );
1376
  'prefix' => $wpdb->prefix,
1377
  'is_multisite' => $this->is_multisite(),
1378
  'is_main_site' => $this->is_main_site(),
1379
+ 'blog_id' => isset( $current_blog ) ? $current_blog->blog_id : null,
1380
+ 'theme' => (string) ( function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_current_theme() ),
1381
  'plugins' => preg_replace( '#/.*$#', '', get_option( 'active_plugins' ) ),
1382
  'tables' => $tinfo,
1383
  'name' => get_bloginfo( 'name' ),
1392
  'load' => $loadavg,
1393
  'info' => @php_uname( "a" ),
1394
  'time' => time(),
1395
+ 'php' => array( 'version' => phpversion(), 'ini' => $ini_vals, 'directory_separator' => DIRECTORY_SEPARATOR ),
1396
  'httpd' => array(
1397
  'type' => $httpd,
1398
  'modules' => $http_modules,
1444
  else
1445
  $where = null;
1446
 
1447
+ if ( isset( $_POST['table'] ) ) {
1448
+ $parse_create_table = isset( $_POST['use_new_hash'] ) && $_POST['use_new_hash'] ? true : false;
1449
+ $bdb->attach( base64_decode( $_POST['table'] ), $parse_create_table );
1450
+ }
1451
 
1452
  switch ( array_pop( explode( ':', $_GET['action'] ) ) ) {
1453
  case 'diff':
1595
  $args['args'] = '';
1596
  $old_timeout = ini_get( 'default_socket_timeout' );
1597
  $timeout = $this->get_option( 'timeout' );
1598
+ if ( function_exists( 'ini_set' ) )
1599
+ ini_set( 'default_socket_timeout', $timeout );
1600
  $hostname = $this->get_option( 'hostname' );
1601
 
1602
  if ( !class_exists( 'VaultPress_IXR_SSL_Client' ) )
1628
  $this->_fix_ixr_null_to_string( $args );
1629
  $args['signature'] = $this->sign_string( serialize( $args ), $this->get_option( 'secret' ), $salt ).":$salt";
1630
 
1631
+ $client->query( 'vaultpress.'.$action, new IXR_Base64( serialize( $args ) ) );
1632
  $rval = $client->message ? $client->getResponse() : '';
1633
+ if ( function_exists( 'ini_set' ) )
1634
+ ini_set( 'default_socket_timeout', $old_timeout );
1635
 
1636
  // we got an error from the servers
1637
  if ( is_array( $rval ) && isset( $rval['faultCode'] ) ) {
1645
 
1646
  function validate_api_signature() {
1647
  global $__vp_validate_error;
1648
+ if ( !empty( $_POST['signature'] ) ) {
1649
+ if ( is_string( $_POST['signature'] ) ) {
1650
+ $sig = $_POST['signature'];
1651
+ } else {
1652
+ $__vp_validate_error = array( 'error' => 'invalid_signature_format' );
1653
+ return false;
1654
+ }
1655
+ } else {
1656
+ $__vp_validate_error = array( 'error' => 'no_signature' );
1657
  return false;
1658
+ }
1659
 
1660
  $secret = $this->get_option( 'secret' );
1661
  if ( !$secret ) {
1662
+ $__vp_validate_error = array( 'error' => 'missing_secret' );
1663
  return false;
1664
  }
1665
  if ( !$this->get_option( 'disable_firewall' ) ) {
1666
  $rxs = $this->get_option( 'service_ips' );
1667
+ $service_ips_external = get_option( 'vaultpress_service_ips_external' );
1668
+ if ( !empty( $rxs['data'] ) && !empty( $service_ips_external['data'] ) )
1669
+ $rxs['data'] = array_merge( $rxs['data'], $service_ips_external['data'] );
1670
  if ( $rxs ) {
1671
  $timeout = time() - 86400;
1672
  if ( $rxs ) {
1683
  if ( $data = $this->update_firewall() )
1684
  $rxs = $data;
1685
  }
1686
+ if ( !$this->validate_ip_address( $rxs ) )
 
1687
  return false;
 
1688
  }
1689
  $sig = explode( ':', $sig );
1690
+ if ( !is_array( $sig ) || count( $sig ) != 2 || !isset( $sig[0] ) || !isset( $sig[1] ) ) {
1691
+ $__vp_validate_error = array( 'error' => 'invalid_signature_format' );
1692
  return false;
1693
  }
1694
 
1702
  ksort( $post );
1703
  $to_sign = serialize( array( 'uri' => $uri, 'post' => $post ) );
1704
  $signature = $this->sign_string( $to_sign, $secret, $sig[1] );
1705
+ if ( $sig[0] === $signature )
1706
  return true;
1707
 
1708
+ $__vp_validate_error = array( 'error' => 'invalid_signed_data', 'detail' => array( 'actual' => $sig[0], 'needed' => $signature ) );
1709
  return false;
1710
  }
1711
 
1712
+ function ip_in_cidr( $ip, $cidr ) {
1713
+ list ($net, $mask) = explode( '/', $cidr );
1714
+ return ( ip2long( $ip ) & ~((1 << (32 - $mask)) - 1) ) == ( ip2long( $net ) & ~((1 << (32 - $mask)) - 1) );
1715
+ }
1716
+
1717
+ function ip_in_cidrs( $ip, $cidrs ) {
1718
+ foreach ( (array)$cidrs as $cidr ) {
1719
+ if ( $this->ip_in_cidr( $ip, $cidr ) ) {
1720
+ return $cidr;
1721
+ }
1722
+ }
1723
+ }
1724
+
1725
  function validate_ip_address( $rxs ) {
1726
+ global $__vp_validate_error;
1727
+ if ( empty( $rxs ) ) {
1728
+ $__vp_validate_error = array( 'error' => 'empty_vp_ip_range' );
1729
  return false;
1730
+ }
1731
 
1732
  $remote_ips = array();
1733
 
1737
  if ( !empty( $_SERVER['REMOTE_ADDR'] ) )
1738
  $remote_ips[] = $_SERVER['REMOTE_ADDR'];
1739
 
1740
+ if ( empty( $remote_ips ) ) {
1741
+ $__vp_validate_error = array( 'error' => 'no_remote_addr', 'detail' => (int) $this->get_option( 'allow_forwarded_for' ) ); // shouldn't happen
1742
+ return false;
1743
+ }
1744
+
1745
  $iprx = '/^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$/';
1746
 
1747
+ foreach ( $remote_ips as $_remote_ip ) {
1748
+ $remote_ip = preg_replace( '#^::(ffff:)?#', '', $_remote_ip );
1749
  if ( !preg_match( $iprx, $remote_ip, $r ) ) {
1750
+ $__vp_validate_error = array( 'error' => "remote_addr_fail", 'detail' => $_remote_ip );
1751
  return false;
1752
  }
1753
 
1766
  }
1767
  }
1768
  }
1769
+ $__vp_validate_error = array( 'error' => 'remote_addr_fail', 'detail' => $remote_ips );
1770
 
1771
  return false;
1772
  }
1776
  }
1777
 
1778
  function response( $response, $raw = false ) {
1779
+ // "re" -- "Response Encoding"
1780
+ if ( !empty( $_GET['re'] ) )
1781
+ header( sprintf( 'X-VP-Encoded: X%d', abs( intval( $_GET['re'] ) ) ) );
1782
+ if ( $raw ) {
1783
+ if ( !isset( $_GET['re'] ) )
1784
+ die( $response );
1785
+ else if ( '1' === $_GET['re'] )
1786
+ die( base64_encode( $response ) );
1787
+ else if ( '2' === $_GET['re'] )
1788
+ die( str_rot13( $response ) );
1789
+ else
1790
+ die( $response );
1791
+ }
1792
  list( $usec, $sec ) = explode( " ", microtime() );
1793
  $r = new stdClass();
1794
  $r->req_vector = floatval( $_GET['vector'] );
1806
  else
1807
  $r->memory_usage = false;
1808
  $r->response = $response;
1809
+ if ( !isset( $_GET['re'] ) )
1810
+ die( serialize( $r ) );
1811
+ else if ( '1' === $_GET['re'] )
1812
+ die( base64_encode( serialize( $r ) ) );
1813
+ else if ( '2' === $_GET['re'] )
1814
+ die( str_rot13( serialize( $r ) ) );
1815
+ else
1816
+ die( serialize( $r ) );
1817
  }
1818
 
1819
  function reset_pings() {
2016
 
2017
  function add_admin_actions_and_filters() {
2018
  add_action( 'admin_init', array( $this, 'admin_init' ) );
2019
+ add_action( 'admin_menu', array( $this, 'admin_menu' ), 5 ); # Priority 5, so it's called before Jetpack's admin_menu.
2020
  add_action( 'admin_head', array( $this, 'admin_head' ) );
2021
  }
2022
 
2023
  function add_listener_actions_and_filters() {
2024
+ add_action( 'admin_bar_menu', array( $this, 'toolbar' ), 999 );
2025
+ add_action( 'admin_bar_init', array( $this, 'styles' ) );
2026
+
2027
  // Comments
2028
  add_action( 'delete_comment', array( $this, 'comment_action_handler' ) );
2029
  add_action( 'wp_set_comment_status', array( $this, 'comment_action_handler' ) );
2073
  add_action( 'updated_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2074
  add_action( 'delete_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2075
  add_action( 'deleted_post_meta', array( $this, 'postmeta_modification_handler' ), 10, 4 );
2076
+ add_action( 'added_postmeta', array( $this, 'postmeta_action_handler' ), 10, 3 );
2077
+ add_action( 'update_postmeta', array( $this, 'postmeta_action_handler' ), 10, 3 );
2078
+ add_action( 'delete_postmeta', array( $this, 'postmeta_action_handler' ), 10, 3 );
2079
 
2080
  // Links
2081
  add_action( 'edit_link', array( $this, 'link_action_handler' ) );
2106
  add_action( 'updated_option', array( $this, 'option_handler' ), 1 );
2107
  add_action( 'added_option', array( $this, 'option_handler' ), 1 );
2108
 
2109
+ $this->add_vp_required_filters();
2110
+ }
2111
+
2112
+ function add_vp_required_filters() {
2113
+ // Log ins
2114
+ if ( $this->get_option( 'login_lockdown' ) ) {
2115
+ add_action( 'login_form', array( $this, 'add_js_token' ) );
2116
+ add_filter( 'authenticate', array( $this, 'authenticate' ), 999 );
2117
+ }
2118
+
2119
  // Report back to VaultPress
2120
  add_action( 'shutdown', array( $this, 'do_pings' ) );
2121
 
vp-scanner.php CHANGED
@@ -3,82 +3,72 @@
3
  class VP_FileScan {
4
  var $path;
5
  var $last_dir = null;
6
- var $offset;
 
7
 
8
- function VP_FileScan($path) {
9
- if ( file_exists( $path ) )
10
  $this->last_dir = $this->path = @realpath( $path );
 
 
 
11
  }
12
 
13
- function get_files($limit = false) {
14
- static $dirs;
15
- if ( null == $dirs ) {
16
- $dirs = VP_FileScan::scan_dirs( $this->path );
17
- if ( is_dir( $this->path ) )
18
- array_unshift( $dirs, $this->path );
 
 
19
  }
20
-
21
- if ( empty( $this->last_dir ) )
22
- return array ();
23
- elseif ( is_file( $this->last_dir ) )
24
- return array ( $this->last_dir );
25
-
26
- $result = array ();
27
- $count = 0;
28
- $size = count( $dirs );
29
- $offset = $this->offset;
30
-
31
- for ( $i = array_search( $this->last_dir, $dirs ); false !== $i && $i < $size; $i++ ) {
32
- $path = $dirs[$i];
33
- $n = ( false !== $limit ) ? ( $limit - $count ) : false;
34
- $files = VP_FileScan::scan_files( $path, $n, $offset );
35
- $count += count( $files );
36
- $result = array_merge( $result, $files );
37
-
38
- if ( $limit && $count >= $limit ) {
39
- $this->offset += count( $files );
40
- $this->last_dir = $path;
41
- break;
42
- }
43
- $offset = 0;
44
- }
45
- if ( $i == $size )
46
- $this->last_dir = false;
47
- return $result;
48
  }
49
 
50
- static function scan_dirs($path) {
51
- if ( !is_dir( $path ) )
52
- return array ();
53
-
54
- $sub_dirs = VP_FileScan::scan_files( $path, false, 0, true );
55
- foreach ( $sub_dirs as $sub_dir )
56
- $sub_dirs = array_merge( $sub_dirs, VP_FileScan::scan_dirs( $sub_dir ) );
57
- return $sub_dirs;
58
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- static function scan_files($path, $limit = false, $offset = 0, $scan_dir = false) {
61
- $entries = array ();
62
- $ignore = $count = 0;
63
- if ( $handle = @opendir( $path ) ) {
64
- while ( false !== ( $entry = readdir( $handle ) ) ) {
65
- if ( $entry != "." && $entry != ".." ) {
66
- $entry = realpath( $path . DIRECTORY_SEPARATOR . $entry );
67
- if ( $scan_dir ) {
68
- if ( is_dir( $entry ) )
69
- $entries[] = $entry;
70
- } elseif ( is_file( $entry ) && vp_is_interesting_file( $entry ) ) {
71
- if ( $offset && ++$ignore <= $offset )
72
- continue;
73
- if ( false !== $limit && ++$count > $limit )
74
- break;
75
- $entries[] = realpath( $entry );
76
- }
77
  }
78
  }
79
  closedir( $handle );
80
  }
81
- return $entries;
82
  }
83
  }
84
 
@@ -110,7 +100,7 @@ function vp_is_interesting_file($file) {
110
  function vp_scan_file($file, $tmp_file = null) {
111
  $real_file = vp_get_real_file_path( $file, $tmp_file );
112
  $file_size = file_exists( $real_file ) ? @filesize( $real_file ) : 0;
113
- if ( !$file_size || $file_size > apply_filters( 'scan_max_file_size', 3 * 1024 * 1024 ) ) // don't scan empty or files larger than 3MB.
114
  return false;
115
 
116
  $file_content = null;
@@ -121,35 +111,43 @@ function vp_scan_file($file, $tmp_file = null) {
121
  if ( !vp_is_interesting_file( $file ) ) // only scan relevant files.
122
  return false;
123
 
 
 
 
124
  $found = array ();
125
  foreach ( $GLOBALS['vp_signatures'] as $signature ) {
 
 
126
  // if there is no filename_regex, we assume it's the same of vp_is_interesting_file().
127
  if ( empty( $signature->filename_regex ) || preg_match( '#' . addcslashes( $signature->filename_regex, '#' ) . '#i', $file ) ) {
128
- if ( null === $file_content )
129
- $file_content = file_get_contents( $real_file );
130
 
131
  $is_vulnerable = true;
132
- reset( $signature->patterns );
133
  $matches = array ();
134
- while ( $is_vulnerable && list( , $pattern ) = each( $signature->patterns ) ) {
135
- if ( !preg_match( '#' . addcslashes( $pattern, '#' ) . '#im', $file_content, $match ) ) {
136
- $is_vulnerable = false;
137
- break;
 
 
 
 
138
  }
139
- $matches[] = $match;
 
140
  }
 
141
  // Additional checking needed?
142
- $is_vulnerable = apply_filters_ref_array( 'is_infected_by_' . $signature->name, array ( $is_vulnerable, $file, $real_file, &$file_content, &$matches ) );
 
143
  if ( $is_vulnerable ) {
144
- $found[$signature->id] = $matches;
145
  if ( isset( $signature->severity ) && $signature->severity > 8 ) // don't continue scanning
146
  break;
147
  }
148
  }
149
  }
150
 
151
- if ( empty( $found ) ) // only apply the filter when no signature is matched
152
- return apply_filters_ref_array( 'after_scan_file', array ( false, $file, $real_file, &$file_content ) );
153
-
154
- return $found;
155
  }
3
  class VP_FileScan {
4
  var $path;
5
  var $last_dir = null;
6
+ var $offset = 0;
7
+ var $ignore_symlinks = false;
8
 
9
+ function VP_FileScan( $path, $ignore_symlinks = false ) {
10
+ if ( is_dir( $path ) )
11
  $this->last_dir = $this->path = @realpath( $path );
12
+ else
13
+ $this->last_dir = $this->path = dirname( @realpath( $path ) );
14
+ $this->ignore_symlinks = $ignore_symlinks;
15
  }
16
 
17
+ function get_files( $limit = 100 ) {
18
+ $files = array();
19
+ if ( is_dir( $this->last_dir ) ) {
20
+ $return = $this->_scan_files( $this->path, $files, $this->offset, $limit, $this->last_dir );
21
+ $this->offset = $return[0];
22
+ $this->last_dir = $return[1];
23
+ if ( count( $files ) < $limit )
24
+ $this->last_dir = false;
25
  }
26
+ return $files;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
 
29
+ function _scan_files( $path, &$files, $offset, $limit, &$last_dir ) {
30
+ $_offset = 0;
31
+ if ( is_readable( $path ) && $handle = opendir( $path ) ) {
32
+ while( false !== ( $entry = readdir( $handle ) ) ) {
33
+ if ( '.' == $entry || '..' == $entry )
34
+ continue;
35
+
36
+ $_offset++;
37
+ $full_entry = $path . DIRECTORY_SEPARATOR . $entry;
38
+ $next_item = ltrim( str_replace( $path, '', $last_dir ), DIRECTORY_SEPARATOR );
39
+ $next = preg_split( '#(?<!\\\\)' . preg_quote( DIRECTORY_SEPARATOR, '#' ) . '#', $next_item, 2 );
40
+
41
+ // Skip if the next item is not found.
42
+ if ( !empty( $next[0] ) && $next[0] != $entry )
43
+ continue;
44
+ if ( rtrim( $last_dir, DIRECTORY_SEPARATOR ) == rtrim( $path, DIRECTORY_SEPARATOR ) && $_offset < $offset )
45
+ continue;
46
+ if ( $this->ignore_symlinks && is_link( $full_entry ) )
47
+ continue;
48
+
49
+ if ( rtrim( $last_dir, DIRECTORY_SEPARATOR ) == rtrim( $path, DIRECTORY_SEPARATOR ) ) {
50
+ // Reset last_dir and offset when we reached the previous last_dir value.
51
+ $last_dir = '';
52
+ $offset = 0;
53
+ }
54
 
55
+ if ( is_file( $full_entry ) ) {
56
+ if ( !vp_is_interesting_file( $full_entry ) )
57
+ continue;
58
+ $_return_offset = $_offset;
59
+ $_return_dir = dirname( $full_entry );
60
+ $files[] = $full_entry;
61
+ } elseif ( is_dir( $full_entry ) ) {
62
+ list( $_return_offset, $_return_dir ) = $this->_scan_files( $full_entry, $files, $offset, $limit, $last_dir );
63
+ }
64
+ if ( count( $files ) >= $limit ) {
65
+ closedir( $handle );
66
+ return array( $_return_offset, $_return_dir );
 
 
 
 
 
67
  }
68
  }
69
  closedir( $handle );
70
  }
71
+ return array( $_offset, $path );
72
  }
73
  }
74
 
100
  function vp_scan_file($file, $tmp_file = null) {
101
  $real_file = vp_get_real_file_path( $file, $tmp_file );
102
  $file_size = file_exists( $real_file ) ? @filesize( $real_file ) : 0;
103
+ if ( !is_readable( $real_file ) || !$file_size || $file_size > apply_filters( 'scan_max_file_size', 3 * 1024 * 1024 ) ) // don't scan empty or files larger than 3MB.
104
  return false;
105
 
106
  $file_content = null;
111
  if ( !vp_is_interesting_file( $file ) ) // only scan relevant files.
112
  return false;
113
 
114
+ if ( !isset( $GLOBALS['vp_signatures'] ) )
115
+ $GLOBALS['vp_signatures'] = array();
116
+
117
  $found = array ();
118
  foreach ( $GLOBALS['vp_signatures'] as $signature ) {
119
+ if ( !is_object( $signature ) || !isset( $signature->patterns ) )
120
+ continue;
121
  // if there is no filename_regex, we assume it's the same of vp_is_interesting_file().
122
  if ( empty( $signature->filename_regex ) || preg_match( '#' . addcslashes( $signature->filename_regex, '#' ) . '#i', $file ) ) {
123
+ if ( null === $file_content || !is_array( $file_content ) )
124
+ $file_content = file( $real_file );
125
 
126
  $is_vulnerable = true;
 
127
  $matches = array ();
128
+ if ( is_array( $file_content ) && ( $signature->patterns ) && is_array( $signature->patterns ) ) {
129
+ reset( $signature->patterns );
130
+ while ( $is_vulnerable && list( , $pattern ) = each( $signature->patterns ) ) {
131
+ if ( ! $match = preg_grep( '#' . addcslashes( $pattern, '#' ) . '#im', $file_content ) ) {
132
+ $is_vulnerable = false;
133
+ break;
134
+ }
135
+ $matches += $match;
136
  }
137
+ } else {
138
+ $is_vulnerable = false;
139
  }
140
+ $debug_data = array( 'matches' => $matches );
141
  // Additional checking needed?
142
+ if ( method_exists( $signature, 'get_detailed_scanner' ) && $scanner = $signature->get_detailed_scanner() )
143
+ $is_vulnerable = $scanner->scan( $is_vulnerable, $file, $real_file, $file_content, $debug_data );
144
  if ( $is_vulnerable ) {
145
+ $found[$signature->id] = $debug_data;
146
  if ( isset( $signature->severity ) && $signature->severity > 8 ) // don't continue scanning
147
  break;
148
  }
149
  }
150
  }
151
 
152
+ return apply_filters_ref_array( 'post_scan_file', array ( $found, $file, $real_file, &$file_content ) );
 
 
 
153
  }