PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/blog/wp-content/plugins/w3-total-cache/lib/W3/Plugin/Cdn.php

https://github.com/kennethreitz-archive/wordpress-skeleton
PHP | 1472 lines | 984 code | 203 blank | 285 comment | 119 complexity | c8f0fdf3f42fcca9d4a6278dd916c96c MD5 | raw file
  1. <?php
  2. /**
  3. * W3 Total Cache CDN Plugin
  4. */
  5. require_once W3TC_LIB_W3_DIR . '/Plugin.php';
  6. /**
  7. * Class W3_Plugin_Cdn
  8. */
  9. class W3_Plugin_Cdn extends W3_Plugin
  10. {
  11. /**
  12. * Array of replaced URLs
  13. *
  14. * @var array
  15. */
  16. var $replaced_urls = array();
  17. /**
  18. * CDN reject reason
  19. *
  20. * @var string
  21. */
  22. var $cdn_reject_reason = '';
  23. /**
  24. * Run plugin
  25. */
  26. function run()
  27. {
  28. register_activation_hook(W3TC_FILE, array(
  29. &$this,
  30. 'activate'
  31. ));
  32. register_deactivation_hook(W3TC_FILE, array(
  33. &$this,
  34. 'deactivate'
  35. ));
  36. add_filter('cron_schedules', array(
  37. &$this,
  38. 'cron_schedules'
  39. ));
  40. if ($this->_config->get_boolean('cdn.enabled')) {
  41. if ($this->_config->get_string('cdn.engine') != 'mirror') {
  42. add_action('add_attachment', array(
  43. &$this,
  44. 'add_attachment'
  45. ));
  46. add_action('delete_attachment', array(
  47. &$this,
  48. 'delete_attachment'
  49. ));
  50. add_filter('wp_generate_attachment_metadata', array(
  51. &$this,
  52. 'generate_attachment_metadata'
  53. ));
  54. add_action('w3_cdn_cron_queue_process', array(
  55. &$this,
  56. 'cron_queue_process'
  57. ));
  58. }
  59. if ($this->can_cdn()) {
  60. ob_start(array(
  61. &$this,
  62. 'ob_callback'
  63. ));
  64. }
  65. }
  66. }
  67. /**
  68. * Returns plugin instance
  69. *
  70. * @return W3_Plugin_Cdn
  71. */
  72. function &instance()
  73. {
  74. static $instances = array();
  75. if (!isset($instances[0])) {
  76. $class = __CLASS__;
  77. $instances[0] = & new $class();
  78. }
  79. return $instances[0];
  80. }
  81. /**
  82. * Activation action
  83. */
  84. function activate()
  85. {
  86. global $wpdb;
  87. $upload_info = w3_upload_info();
  88. if (!$upload_info) {
  89. $upload_path = get_option('upload_path');
  90. $upload_path = trim($upload_path);
  91. if (empty($upload_path)) {
  92. echo 'Your store uploads folder is not available. Default WordPress directories will be created: <strong>wp-content/uploads/</strong>.<br />';
  93. $upload_path = WP_CONTENT_DIR . '/uploads';
  94. }
  95. w3_writable_error($upload_path);
  96. }
  97. $sql = sprintf('DROP TABLE IF EXISTS `%s%s`', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);
  98. $wpdb->query($sql);
  99. $sql = sprintf("CREATE TABLE IF NOT EXISTS `%s%s` (
  100. `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  101. `local_path` varchar(150) NOT NULL DEFAULT '',
  102. `remote_path` varchar(150) NOT NULL DEFAULT '',
  103. `command` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 - Upload, 2 - Delete',
  104. `last_error` varchar(150) NOT NULL DEFAULT '',
  105. `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  106. PRIMARY KEY (`id`),
  107. UNIQUE KEY `path` (`local_path`, `remote_path`),
  108. KEY `date` (`date`)
  109. ) /*!40100 CHARACTER SET latin1 */", $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);
  110. $wpdb->query($sql);
  111. if (!$wpdb->result) {
  112. $error = sprintf('Unable to create table <strong>%s%s</strong>: %s', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE, $wpdb->last_error);
  113. w3_activate_error($error);
  114. }
  115. $this->schedule();
  116. }
  117. /**
  118. * Deactivation action
  119. */
  120. function deactivate()
  121. {
  122. global $wpdb;
  123. $this->unschedule();
  124. $sql = sprintf('DROP TABLE IF EXISTS `%s%s`', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);
  125. $wpdb->query($sql);
  126. }
  127. /**
  128. * Schedules cron events
  129. */
  130. function schedule()
  131. {
  132. if ($this->_config->get_boolean('cdn.enabled') && $this->_config->get_string('cdn.engine') != 'mirror') {
  133. if (!wp_next_scheduled('w3_cdn_cron_queue_process')) {
  134. wp_schedule_event(time(), 'every_15_min', 'w3_cdn_cron_queue_process');
  135. }
  136. } else {
  137. $this->unschedule();
  138. }
  139. }
  140. /**
  141. * Unschedules cron events
  142. */
  143. function unschedule()
  144. {
  145. if (wp_next_scheduled('w3_cdn_cron_queue_process')) {
  146. wp_clear_scheduled_hook('w3_cdn_cron_queue_process');
  147. }
  148. }
  149. /**
  150. * Cron queue process event
  151. */
  152. function cron_queue_process()
  153. {
  154. $queue_limit = $this->_config->get_integer('cdn.queue.limit');
  155. $this->queue_process($queue_limit);
  156. }
  157. /**
  158. * On attachment add action
  159. *
  160. * @param integer $attachment_id
  161. */
  162. function add_attachment($attachment_id)
  163. {
  164. $files = $this->get_attachment_files($attachment_id);
  165. $files = apply_filters('w3tc_cdn_add_attachment', $files);
  166. $results = array();
  167. $this->upload($files, true, $results);
  168. }
  169. /**
  170. * Generate attachment metadata filter
  171. *
  172. * @param array $metadata
  173. * @return array
  174. */
  175. function generate_attachment_metadata($metadata)
  176. {
  177. $files = $this->get_metadata_files($metadata);
  178. $files = apply_filters('w3tc_cdn_generate_attachment_metadata', $files);
  179. $results = array();
  180. $this->upload($files, true, $results);
  181. return $metadata;
  182. }
  183. /**
  184. * On attachment delete action
  185. *
  186. * @param integer $attachment_id
  187. */
  188. function delete_attachment($attachment_id)
  189. {
  190. $files = $this->get_attachment_files($attachment_id);
  191. $files = apply_filters('w3tc_cdn_delete_attachment', $files);
  192. $results = array();
  193. $this->delete($files, true, $results);
  194. }
  195. /**
  196. * Cron schedules filter
  197. *
  198. * @paran array $schedules
  199. * @return array
  200. */
  201. function cron_schedules($schedules)
  202. {
  203. return array_merge($schedules, array(
  204. 'every_15_min' => array(
  205. 'interval' => 900,
  206. 'display' => 'Every 15 minutes'
  207. )
  208. ));
  209. }
  210. /**
  211. * Returns attachment files by attachment ID
  212. *
  213. * @param integer $attachment_id
  214. * @return array
  215. */
  216. function get_attachment_files($attachment_id)
  217. {
  218. $files = array();
  219. $upload_info = w3_upload_info();
  220. if ($upload_info) {
  221. $attached_file = get_post_meta($attachment_id, '_wp_attached_file', true);
  222. $attachment_metadata = get_post_meta($attachment_id, '_wp_attachment_metadata', true);
  223. if ($attached_file) {
  224. $file = $this->normalize_attachment_file($attached_file);
  225. $local_file = $upload_info['upload_dir'] . '/' . $file;
  226. $remote_file = $upload_info['upload_url'] . '/' . $file;
  227. $files[$local_file] = $remote_file;
  228. }
  229. if ($attachment_metadata) {
  230. $files = array_merge($files, $this->get_metadata_files($attachment_metadata));
  231. }
  232. }
  233. return $files;
  234. }
  235. /**
  236. * OB Callback
  237. *
  238. * @param string $buffer
  239. * @return string
  240. */
  241. function ob_callback($buffer)
  242. {
  243. if ($buffer != '' && w3_is_xml($buffer)) {
  244. $regexps = array();
  245. $upload_info = w3_upload_info();
  246. $site_url_regexp = w3_get_site_url_regexp();
  247. if ($upload_info) {
  248. $regexps[] = '~(["\'])((' . $site_url_regexp . ')?/?(' . w3_preg_quote($upload_info['upload_url']) . '[^"\'>]+))~';
  249. }
  250. if ($this->_config->get_boolean('cdn.includes.enable')) {
  251. $mask = $this->_config->get_string('cdn.includes.files');
  252. if ($mask != '') {
  253. $regexps[] = '~(["\'])((' . $site_url_regexp . ')?/?(' . w3_preg_quote(WPINC) . '/(' . $this->get_regexp_by_mask($mask) . ')))~';
  254. }
  255. }
  256. if ($this->_config->get_boolean('cdn.theme.enable')) {
  257. $theme_dir = preg_replace('~' . $site_url_regexp . '~i', '', get_stylesheet_directory_uri());
  258. $mask = $this->_config->get_string('cdn.theme.files');
  259. if ($mask != '') {
  260. $regexps[] = '~(["\'])((' . $site_url_regexp . ')?/?(' . w3_preg_quote($theme_dir) . '/(' . $this->get_regexp_by_mask($mask) . ')))~';
  261. }
  262. }
  263. if ($this->_config->get_boolean('cdn.minify.enable')) {
  264. $regexps[] = '~(["\'])((' . $site_url_regexp . ')?/?(' . w3_preg_quote(W3TC_CONTENT_MINIFY_DIR_NAME) . '/[a-z0-9-_]+\.include(-footer)?(-nb)?\.(css|js)))~';
  265. }
  266. if ($this->_config->get_boolean('cdn.custom.enable')) {
  267. $masks = $this->_config->get_array('cdn.custom.files');
  268. if (count($masks)) {
  269. $mask_regexps = array();
  270. foreach ($masks as $mask) {
  271. if ($mask != '') {
  272. $mask = w3_normalize_file($mask);
  273. $mask_regexps[] = $this->get_regexp_by_mask($mask);
  274. }
  275. }
  276. $regexps[] = '~(["\'])((' . $site_url_regexp . ')?/?(' . implode('|', $mask_regexps) . '))~i';
  277. }
  278. }
  279. foreach ($regexps as $regexp) {
  280. $buffer = preg_replace_callback($regexp, array(
  281. &$this,
  282. 'link_replace_callback'
  283. ), $buffer);
  284. }
  285. if ($this->_config->get_boolean('cdn.debug')) {
  286. $buffer .= "\r\n\r\n" . $this->get_debug_info();
  287. }
  288. }
  289. return $buffer;
  290. }
  291. /**
  292. * Returns attachment files by metadata
  293. *
  294. * @param array $metadata
  295. * @return array
  296. */
  297. function get_metadata_files($metadata)
  298. {
  299. $files = array();
  300. $upload_info = w3_upload_info();
  301. if ($upload_info) {
  302. if (isset($metadata['file'])) {
  303. $file = $this->normalize_attachment_file($metadata['file']);
  304. if (isset($metadata['sizes'])) {
  305. $file_dir = dirname($file);
  306. foreach ((array) $metadata['sizes'] as $size) {
  307. if (isset($size['file'])) {
  308. $local_file = $upload_info['upload_dir'] . '/' . $file_dir . '/' . $size['file'];
  309. $remote_file = $upload_info['upload_url'] . '/' . $file_dir . '/' . $size['file'];
  310. $files[$local_file] = $remote_file;
  311. }
  312. }
  313. }
  314. }
  315. }
  316. return $files;
  317. }
  318. /**
  319. * Adds file to queue
  320. *
  321. * @param string $local_path
  322. * @param string $remote_path
  323. * @param integer $command
  324. * @param string $last_error
  325. * @return ingteer
  326. */
  327. function queue_add($local_path, $remote_path, $command, $last_error)
  328. {
  329. global $wpdb;
  330. $table = $wpdb->prefix . W3TC_CDN_TABLE_QUEUE;
  331. $sql = sprintf('SELECT id FROM %s WHERE local_path = "%s" AND remote_path = "%s" AND command != %d', $table, $wpdb->escape($local_path), $wpdb->escape($remote_path), $command);
  332. if (($row = $wpdb->get_row($sql))) {
  333. $sql = sprintf('DELETE FROM %s WHERE id = %d', $table, $row->id);
  334. } else {
  335. $sql = sprintf('REPLACE INTO %s (local_path, remote_path, command, last_error, date) VALUES ("%s", "%s", %d, "%s", NOW())', $table, $wpdb->escape($local_path), $wpdb->escape($remote_path), $command, $wpdb->escape($last_error));
  336. }
  337. return $wpdb->query($sql);
  338. }
  339. /**
  340. * Updates file date in the queue
  341. *
  342. * @param integer $queue_id
  343. * @param string $last_error
  344. * @return integer
  345. */
  346. function queue_update($queue_id, $last_error)
  347. {
  348. global $wpdb;
  349. $sql = sprintf('UPDATE %s SET last_error = "%s", date = NOW() WHERE id = %d', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE, $wpdb->escape($last_error), $queue_id);
  350. return $wpdb->query($sql);
  351. }
  352. /**
  353. * Removes from queue
  354. *
  355. * @param integer $queue_id
  356. * @return integer
  357. */
  358. function queue_delete($queue_id)
  359. {
  360. global $wpdb;
  361. $sql = sprintf('DELETE FROM %s WHERE id = %d', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE, $queue_id);
  362. return $wpdb->query($sql);
  363. }
  364. /**
  365. * Empties queue
  366. *
  367. * @param integer $command
  368. * @return integer
  369. */
  370. function queue_empty($command)
  371. {
  372. global $wpdb;
  373. $sql = sprintf('DELETE FROM %s WHERE command = %d', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE, $command);
  374. return $wpdb->query($sql);
  375. }
  376. /**
  377. * Returns queue
  378. *
  379. * @param integer $limit
  380. * @return array
  381. */
  382. function queue_get($limit = null)
  383. {
  384. global $wpdb;
  385. $sql = sprintf('SELECT * FROM %s%s ORDER BY date', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);
  386. if ($limit) {
  387. $sql .= sprintf(' LIMIT %d', $limit);
  388. }
  389. $results = $wpdb->get_results($sql);
  390. $queue = array();
  391. if ($results) {
  392. foreach ((array) $results as $result) {
  393. $queue[$result->command][] = $result;
  394. }
  395. }
  396. return $queue;
  397. }
  398. /**
  399. * Process queue
  400. *
  401. * @param integer $limit
  402. */
  403. function queue_process($limit)
  404. {
  405. $commands = $this->queue_get($limit);
  406. $force_rewrite = $this->_config->get_boolean('cdn.force.rewrite');
  407. if (count($commands)) {
  408. $cdn = & $this->get_cdn();
  409. foreach ($commands as $command => $queue) {
  410. $files = array();
  411. $results = array();
  412. $map = array();
  413. foreach ($queue as $result) {
  414. $files[$result->local_path] = $result->remote_path;
  415. $map[$result->local_path] = $result->id;
  416. }
  417. switch ($command) {
  418. case W3TC_CDN_COMMAND_UPLOAD:
  419. $cdn->upload($files, $results, $force_rewrite);
  420. break;
  421. case W3TC_CDN_COMMAND_DELETE:
  422. $cdn->delete($files, $results);
  423. break;
  424. }
  425. foreach ($results as $result) {
  426. if ($result['result'] == W3_CDN_RESULT_OK) {
  427. $this->queue_delete($map[$result['local_path']]);
  428. } else {
  429. $this->queue_update($map[$result['local_path']], $result['error']);
  430. }
  431. }
  432. }
  433. }
  434. }
  435. /**
  436. * Uploads files to CDN
  437. *
  438. * @param array $files
  439. * @param boolean $queue_failed
  440. * @param array $results
  441. * @return boolean
  442. */
  443. function upload($files, $queue_failed, &$results)
  444. {
  445. $upload = array();
  446. $force_rewrite = $this->_config->get_boolean('cdn.force.rewrite');
  447. foreach ($files as $local_file => $remote_file) {
  448. $local_path = $this->format_local_path($local_file);
  449. $remote_path = $this->format_remote_path($remote_file);
  450. $upload[$local_path] = $remote_path;
  451. }
  452. $cdn = & $this->get_cdn();
  453. if (!$cdn->upload($upload, $results, $force_rewrite)) {
  454. if ($queue_failed) {
  455. foreach ($results as $result) {
  456. if ($result['result'] != W3_CDN_RESULT_OK) {
  457. $this->queue_add($result['local_path'], $result['remote_path'], W3TC_CDN_COMMAND_UPLOAD, $result['error']);
  458. }
  459. }
  460. }
  461. return false;
  462. }
  463. return true;
  464. }
  465. /**
  466. * Deletes files frrom CDN
  467. *
  468. * @param array $files
  469. * @param boolean $queue_failed
  470. * @param array $results
  471. * @return boolean
  472. */
  473. function delete($files, $queue_failed, &$results)
  474. {
  475. $delete = array();
  476. foreach ($files as $local_file => $remote_file) {
  477. $local_path = $this->format_local_path($local_file);
  478. $remote_path = $this->format_remote_path($remote_file);
  479. $delete[$local_path] = $remote_path;
  480. }
  481. $cdn = & $this->get_cdn();
  482. if (!$cdn->delete($delete, $results)) {
  483. if ($queue_failed) {
  484. foreach ($results as $result) {
  485. if ($result['result'] != W3_CDN_RESULT_OK) {
  486. $this->queue_add($result['local_path'], $result['remote_path'], W3TC_CDN_COMMAND_DELETE, $result['error']);
  487. }
  488. }
  489. }
  490. return false;
  491. }
  492. return true;
  493. }
  494. /**
  495. * Export library to CDN
  496. *
  497. * @param integer $limit
  498. * @param integer $offset
  499. * @param integer $count
  500. * @param integer $total
  501. * @param array $results
  502. * @return boolean
  503. */
  504. function export_library($limit, $offset, &$count, &$total, &$results)
  505. {
  506. global $wpdb;
  507. $count = 0;
  508. $total = 0;
  509. $upload_info = w3_upload_info();
  510. if ($upload_info) {
  511. $sql = sprintf('SELECT
  512. pm.meta_value AS file,
  513. pm2.meta_value AS metadata
  514. FROM
  515. %sposts AS p
  516. LEFT JOIN
  517. %spostmeta AS pm ON p.ID = pm.post_ID AND pm.meta_key = "_wp_attached_file"
  518. LEFT JOIN
  519. %spostmeta AS pm2 ON p.ID = pm2.post_ID AND pm2.meta_key = "_wp_attachment_metadata"
  520. WHERE
  521. p.post_type = "attachment"
  522. GROUP BY
  523. p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix);
  524. if ($limit) {
  525. $sql .= sprintf(' LIMIT %d', $limit);
  526. if ($offset) {
  527. $sql .= sprintf(' OFFSET %d', $offset);
  528. }
  529. }
  530. $posts = $wpdb->get_results($sql);
  531. if ($posts) {
  532. $count = count($posts);
  533. $total = $this->get_attachments_count();
  534. $files = array();
  535. foreach ($posts as $post) {
  536. if (!empty($post->metadata)) {
  537. $metadata = @unserialize($post->metadata);
  538. } else {
  539. $metadata = array();
  540. }
  541. if (isset($metadata['file'])) {
  542. $files = array_merge($files, $this->get_metadata_files($metadata));
  543. } elseif (!empty($post->file)) {
  544. $file = $this->normalize_attachment_file($post->file);
  545. $local_file = $upload_info['upload_dir'] . '/' . $file;
  546. $remote_file = $upload_info['upload_url'] . '/' . $file;
  547. $files[$local_file] = $remote_file;
  548. }
  549. }
  550. return $this->upload($files, false, $results);
  551. }
  552. }
  553. return false;
  554. }
  555. /**
  556. * Imports library
  557. *
  558. * @param integer $limit
  559. * @param integer $offset
  560. * @param integer $count
  561. * @param integer $total
  562. * @param array $results
  563. * @return boolean
  564. */
  565. function import_library($limit, $offset, &$count, &$total, &$results)
  566. {
  567. global $wpdb;
  568. $count = 0;
  569. $total = 0;
  570. $results = array();
  571. $site_url = w3_get_site_url();
  572. $upload_info = w3_upload_info();
  573. if ($upload_info) {
  574. /**
  575. * Search for posts with links or images
  576. */
  577. $sql = sprintf('SELECT
  578. ID,
  579. post_content,
  580. post_date
  581. FROM
  582. %sposts
  583. WHERE
  584. post_status = "publish"
  585. AND (post_type = "post" OR post_type = "page")
  586. AND (post_content LIKE "%%src=%%"
  587. OR post_content LIKE "%%href=%%")
  588. ', $wpdb->prefix);
  589. if ($limit) {
  590. $sql .= sprintf(' LIMIT %d', $limit);
  591. if ($offset) {
  592. $sql .= sprintf(' OFFSET %d', $offset);
  593. }
  594. }
  595. $posts = $wpdb->get_results($sql);
  596. if ($posts) {
  597. $count = count($posts);
  598. $total = $this->get_import_posts_count();
  599. $regexp = '~(' . $this->get_regexp_by_mask($this->_config->get_string('cdn.import.files')) . ')$~';
  600. $import_external = $this->_config->get_boolean('cdn.import.external');
  601. foreach ($posts as $post) {
  602. $matches = null;
  603. $replaced = array();
  604. $attachments = array();
  605. $post_content = $post->post_content;
  606. /**
  607. * Search for all link and image sources
  608. */
  609. if (preg_match_all('~(href|src)=[\'"]?([^\'"<>\s]+)[\'"]?~', $post_content, $matches, PREG_SET_ORDER)) {
  610. foreach ($matches as $match) {
  611. list($search, $attribute, $origin) = $match;
  612. /**
  613. * Check if $search is already replaced
  614. */
  615. if (isset($replaced[$search])) {
  616. continue;
  617. }
  618. $error = '';
  619. $result = false;
  620. $src = w3_normalize_file($origin);
  621. $dst = '';
  622. /**
  623. * Check if file exists in the library
  624. */
  625. if (stristr($origin, $upload_info['baseurl']) === false) {
  626. /**
  627. * Check file extension
  628. */
  629. if (preg_match($regexp, $src)) {
  630. /**
  631. * Check for alredy uploaded attachment
  632. */
  633. if (isset($attachments[$src])) {
  634. list($dst, $dst_url) = $attachments[$src];
  635. $result = true;
  636. } else {
  637. $upload_subdir = date('Y/m', strtotime($post->post_date));
  638. $upload_dir = sprintf('%s/%s', $upload_info['upload_dir'], $upload_subdir);
  639. $upload_url = sprintf('%s/%s', $upload_info['upload_url'], $upload_subdir);
  640. $src_filename = pathinfo($src, PATHINFO_FILENAME);
  641. $src_extension = pathinfo($src, PATHINFO_EXTENSION);
  642. /**
  643. * Get available filename
  644. */
  645. for ($i = 0;; $i++) {
  646. $dst = sprintf('%s/%s%s%s', $upload_dir, $src_filename, ($i ? $i : ''), ($src_extension ? '.' . $src_extension : ''));
  647. $dst_path = ABSPATH . $dst;
  648. if (!file_exists($dst_path)) {
  649. break;
  650. }
  651. }
  652. $dst_basename = basename($dst);
  653. $dst_dirname = dirname($dst);
  654. $dst_url = sprintf('%s%s/%s', $site_url, $upload_url, $dst_basename);
  655. w3_mkdir($dst_dirname, 0755, ABSPATH);
  656. $download_result = false;
  657. /**
  658. * Check if file is remote URL
  659. */
  660. if (w3_is_url($src)) {
  661. /**
  662. * Download file
  663. */
  664. if ($import_external) {
  665. $download_result = w3_download($src, $dst_path);
  666. if (!$download_result) {
  667. $error = 'Unable to download file';
  668. }
  669. } else {
  670. $error = 'External file import is disabled';
  671. }
  672. } else {
  673. /**
  674. * Otherwise copy file from local path
  675. */
  676. $src_path = ABSPATH . $src;
  677. if (file_exists($src_path)) {
  678. $download_result = @copy($src_path, $dst_path);
  679. if (!$download_result) {
  680. $error = 'Unable to copy file';
  681. }
  682. } else {
  683. $error = 'Source file doesn\'t exists';
  684. }
  685. }
  686. /**
  687. * Check if download or copy was successful
  688. */
  689. if ($download_result) {
  690. $title = $dst_basename;
  691. $guid = $upload_info['upload_url'] . '/' . $title;
  692. $mime_type = w3_get_mime_type($dst_basename);
  693. $GLOBALS['wp_rewrite'] = & new WP_Rewrite();
  694. /**
  695. * Insert attachment
  696. */
  697. $id = wp_insert_attachment(array(
  698. 'post_mime_type' => $mime_type,
  699. 'guid' => $guid,
  700. 'post_title' => $title,
  701. 'post_content' => ''
  702. ), $dst_path);
  703. if (!is_wp_error($id)) {
  704. /**
  705. * Generate attachment metadata and upload to CDN
  706. */
  707. require_once ABSPATH . 'wp-admin/includes/image.php';
  708. wp_update_attachment_metadata($id, wp_generate_attachment_metadata($id, $dst_path));
  709. $attachments[$src] = array(
  710. $dst,
  711. $dst_url
  712. );
  713. $result = true;
  714. } else {
  715. $error = 'Unable to insert attachment';
  716. }
  717. }
  718. }
  719. /**
  720. * If attachment was successfully created then replace links
  721. */
  722. if ($result) {
  723. $replace = sprintf('%s="%s"', $attribute, $dst_url);
  724. // replace $search with $replace
  725. $post_content = str_replace($search, $replace, $post_content);
  726. $replaced[$search] = $replace;
  727. $error = 'OK';
  728. }
  729. } else {
  730. $error = 'File type rejected';
  731. }
  732. } else {
  733. $error = 'File already exists in the media library';
  734. }
  735. /**
  736. * Add new entry to the log file
  737. */
  738. $results[] = array(
  739. 'src' => $src,
  740. 'dst' => $dst,
  741. 'result' => $result,
  742. 'error' => $error
  743. );
  744. }
  745. }
  746. /**
  747. * If post content was chenged then update DB
  748. */
  749. if ($post_content != $post->post_content) {
  750. wp_update_post(array(
  751. 'ID' => $post->ID,
  752. 'post_content' => $post_content
  753. ));
  754. }
  755. }
  756. }
  757. }
  758. }
  759. /**
  760. * Rename domain
  761. *
  762. * @param array $names
  763. * @param integer $limit
  764. * @param integer $offset
  765. * @param integer $count
  766. * @param integer $total
  767. * @param integer $results
  768. * @return void
  769. */
  770. function rename_domain($names, $limit, $offset, &$count, &$total, &$results)
  771. {
  772. global $wpdb;
  773. $count = 0;
  774. $total = 0;
  775. $results = array();
  776. $site_url = w3_get_site_url();
  777. $upload_info = w3_upload_info();
  778. foreach ($names as $index => $name) {
  779. $names[$index] = str_ireplace('www.', '', $name);
  780. }
  781. if ($upload_info) {
  782. $sql = sprintf('SELECT
  783. ID,
  784. post_content,
  785. post_date
  786. FROM
  787. %sposts
  788. WHERE
  789. post_status = "publish"
  790. AND (post_type = "post" OR post_type = "page")
  791. AND (post_content LIKE "%%src=%%"
  792. OR post_content LIKE "%%href=%%")
  793. ', $wpdb->prefix);
  794. if ($limit) {
  795. $sql .= sprintf(' LIMIT %d', $limit);
  796. if ($offset) {
  797. $sql .= sprintf(' OFFSET %d', $offset);
  798. }
  799. }
  800. $posts = $wpdb->get_results($sql);
  801. if ($posts) {
  802. $count = count($posts);
  803. $total = $this->get_rename_posts_count();
  804. $names_quoted = array_map('w3_preg_quote', $names);
  805. foreach ($posts as $post) {
  806. $matches = null;
  807. $post_content = $post->post_content;
  808. $regexp = '~(href|src)=[\'"]?(https?://(www\.)?(' . implode('|', $names_quoted) . ')/' . w3_preg_quote($upload_info['upload_url']) . '([^\'"<>\s]+))[\'"]~';
  809. if (preg_match_all($regexp, $post_content, $matches, PREG_SET_ORDER)) {
  810. foreach ($matches as $match) {
  811. $old_url = $match[2];
  812. $new_url = sprintf('%s%s%s', $site_url, $upload_info['upload_url'], $match[5]);
  813. $post_content = str_replace($old_url, $new_url, $post_content);
  814. $results[] = array(
  815. 'old' => $old_url,
  816. 'new' => $new_url,
  817. 'result' => true,
  818. 'error' => 'OK'
  819. );
  820. }
  821. }
  822. if ($post_content != $post->post_content) {
  823. wp_update_post(array(
  824. 'ID' => $post->ID,
  825. 'post_content' => $post_content
  826. ));
  827. }
  828. }
  829. }
  830. }
  831. }
  832. /**
  833. * Returns attachments count
  834. *
  835. * @return integer
  836. */
  837. function get_attachments_count()
  838. {
  839. global $wpdb;
  840. $sql = sprintf('SELECT
  841. COUNT(DISTINCT p.ID)
  842. FROM
  843. %sposts AS p
  844. JOIN
  845. %spostmeta AS pm ON p.ID = pm.post_ID AND (pm.meta_key = "_wp_attached_file" OR pm.meta_key = "_wp_attachment_metadata")
  846. WHERE
  847. p.post_type = "attachment"', $wpdb->prefix, $wpdb->prefix);
  848. return $wpdb->get_var($sql);
  849. }
  850. /**
  851. * Returns import posts count
  852. *
  853. * @return integer
  854. */
  855. function get_import_posts_count()
  856. {
  857. global $wpdb;
  858. $sql = sprintf('SELECT
  859. COUNT(*)
  860. FROM
  861. %sposts
  862. WHERE
  863. post_status = "publish"
  864. AND (post_type = "post" OR post_type = "page")
  865. AND (post_content LIKE "%%src=%%"
  866. OR post_content LIKE "%%href=%%")
  867. ', $wpdb->prefix);
  868. return $wpdb->get_var($sql);
  869. }
  870. /**
  871. * Returns rename posts count
  872. *
  873. * @return integer
  874. */
  875. function get_rename_posts_count()
  876. {
  877. return $this->get_import_posts_count();
  878. }
  879. /**
  880. * Exports includes to CDN
  881. */
  882. function get_files_includes()
  883. {
  884. $files = $this->search_files(ABSPATH . WPINC, WPINC, $this->_config->get_string('cdn.includes.files'));
  885. return $files;
  886. }
  887. /**
  888. * Exports theme to CDN
  889. */
  890. function get_files_theme()
  891. {
  892. $theme_dir = ltrim(str_replace(ABSPATH, '', get_stylesheet_directory()), '/\\');
  893. $files = $this->search_files(get_stylesheet_directory(), $theme_dir, $this->_config->get_string('cdn.theme.files'));
  894. return $files;
  895. }
  896. /**
  897. * Exports min files to CDN
  898. */
  899. function get_files_minify()
  900. {
  901. $files = array();
  902. if (W3TC_PHP5) {
  903. require_once W3TC_LIB_W3_DIR . '/Plugin/Minify.php';
  904. $minify = & W3_Plugin_Minify::instance();
  905. $urls = $minify->get_urls();
  906. foreach ($urls as $url) {
  907. $file = basename($url);
  908. if (w3_download($url, W3TC_CONTENT_MINIFY_DIR . '/' . $file) !== false) {
  909. $files[] = W3TC_CONTENT_MINIFY_DIR_NAME . '/' . $file;
  910. }
  911. }
  912. }
  913. return $files;
  914. }
  915. /**
  916. * Exports custom files to CDN
  917. */
  918. function get_files_custom()
  919. {
  920. $files = array();
  921. $custom_files = $this->_config->get_array('cdn.custom.files');
  922. foreach ($custom_files as $custom_file) {
  923. if ($custom_file != '') {
  924. $custom_file = w3_normalize_file($custom_file);
  925. $dir = trim(dirname($custom_file), '/\\');
  926. if ($dir == '.') {
  927. $dir = '';
  928. }
  929. $mask = basename($custom_file);
  930. $files = array_merge($files, $this->search_files(ABSPATH . $dir, $dir, $mask));
  931. }
  932. }
  933. return $files;
  934. }
  935. /**
  936. * Formats local file path
  937. *
  938. * @param string $file
  939. * @return string
  940. */
  941. function format_local_path($file)
  942. {
  943. return ABSPATH . $file;
  944. }
  945. /**
  946. * Formats remote file path
  947. *
  948. * @param string $file
  949. * @return string
  950. */
  951. function format_remote_path($file)
  952. {
  953. return ltrim(w3_get_blog_path(), '/') . $file;
  954. }
  955. /**
  956. * Link replace callback
  957. *
  958. * @param array $matches
  959. * @return string
  960. */
  961. function link_replace_callback($matches)
  962. {
  963. global $wpdb;
  964. static $queue = null, $reject_files = null;
  965. list($match, $quote, $url, $site_url, $path) = $matches;
  966. $path = ltrim($path, '/');
  967. /**
  968. * Check if URL was already replaced
  969. */
  970. if (isset($this->replaced_urls[$url])) {
  971. return $quote . $this->replaced_urls[$url];
  972. }
  973. /**
  974. * Check URL for rejected files
  975. */
  976. if ($reject_files === null) {
  977. $reject_files = $this->_config->get_array('cdn.reject.files');
  978. }
  979. foreach ($reject_files as $reject_file) {
  980. if ($reject_file != '') {
  981. $reject_file = w3_normalize_file($reject_file);
  982. $reject_file_regexp = '~^' . $this->get_regexp_by_mask($reject_file) . '$~i';
  983. if (preg_match($reject_file_regexp, $path)) {
  984. return $quote . $url;
  985. }
  986. }
  987. }
  988. /**
  989. * Don't replace URL for files that are in the CDN queue
  990. */
  991. if ($queue === null) {
  992. $sql = sprintf('SELECT remote_path FROM %s', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE);
  993. $queue = $wpdb->get_col($sql);
  994. }
  995. if (in_array($path, $queue)) {
  996. return $quote . $url;
  997. }
  998. /**
  999. * Do replacement
  1000. */
  1001. $cdn = & $this->get_cdn();
  1002. $blog_path = w3_get_blog_path();
  1003. $new_path = $blog_path . $path;
  1004. $new_url = $cdn->format_url($new_path);
  1005. if ($new_url) {
  1006. $this->replaced_urls[$url] = $new_url;
  1007. return $quote . $new_url;
  1008. }
  1009. return $quote . $url;
  1010. }
  1011. /**
  1012. * Search files
  1013. *
  1014. * @param string $search_dir
  1015. * @param string $mask
  1016. * @param boolean $recursive
  1017. * @return array
  1018. */
  1019. function search_files($search_dir, $base_dir, $mask = '*.*', $recursive = true)
  1020. {
  1021. static $stack = array();
  1022. $files = array();
  1023. $dir = @opendir($search_dir);
  1024. if ($dir) {
  1025. while (($entry = @readdir($dir))) {
  1026. if ($entry != '.' && $entry != '..') {
  1027. $path = $search_dir . '/' . $entry;
  1028. if (is_dir($path) && $recursive) {
  1029. array_push($stack, $entry);
  1030. $files = array_merge($files, $this->search_files($path, $base_dir, $mask, $recursive));
  1031. array_pop($stack);
  1032. } else {
  1033. $regexp = '~^' . $this->get_regexp_by_mask($mask) . '$~i';
  1034. if (preg_match($regexp, $entry)) {
  1035. $files[] = ($base_dir != '' ? $base_dir . '/' : '') . (($p = implode('/', $stack)) != '' ? $p . '/' : '') . $entry;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. @closedir($dir);
  1041. }
  1042. return $files;
  1043. }
  1044. /**
  1045. * Returns regexp by mask
  1046. *
  1047. * @param string $mask
  1048. * @return string
  1049. */
  1050. function get_regexp_by_mask($mask)
  1051. {
  1052. $mask = trim($mask);
  1053. $mask = w3_preg_quote($mask);
  1054. $mask = str_replace(array(
  1055. '\*',
  1056. '\?',
  1057. ';'
  1058. ), array(
  1059. '@ASTERISK@',
  1060. '@QUESTION@',
  1061. '|'
  1062. ), $mask);
  1063. $regexp = str_replace(array(
  1064. '@ASTERISK@',
  1065. '@QUESTION@'
  1066. ), array(
  1067. '[^\\?\\*:\\|"<>]*',
  1068. '[^\\?\\*:\\|"<>]'
  1069. ), $mask);
  1070. return $regexp;
  1071. }
  1072. /**
  1073. * Normalizes attachment file
  1074. *
  1075. * @param string $file
  1076. * @return string
  1077. */
  1078. function normalize_attachment_file($file)
  1079. {
  1080. $upload_info = w3_upload_info();
  1081. if ($upload_info) {
  1082. $file = ltrim(str_replace($upload_info['basedir'], '', $file), '/\\');
  1083. $matches = null;
  1084. if (preg_match('~(\d{4}/\d{2}/)?[^/]+$~', $file, $matches)) {
  1085. $file = $matches[0];
  1086. }
  1087. }
  1088. return $file;
  1089. }
  1090. /**
  1091. * Returns CDN object
  1092. *
  1093. * @return W3_Cdn_Base
  1094. */
  1095. function &get_cdn()
  1096. {
  1097. static $cdn = array();
  1098. if (!isset($cdn[0])) {
  1099. $engine = $this->_config->get_string('cdn.engine');
  1100. $engine_config = array();
  1101. switch ($engine) {
  1102. case 'mirror':
  1103. $engine_config = array(
  1104. 'domain' => $this->_config->get_string('cdn.mirror.domain')
  1105. );
  1106. break;
  1107. case 'ftp':
  1108. $engine_config = array(
  1109. 'host' => $this->_config->get_string('cdn.ftp.host'),
  1110. 'user' => $this->_config->get_string('cdn.ftp.user'),
  1111. 'pass' => $this->_config->get_string('cdn.ftp.pass'),
  1112. 'path' => $this->_config->get_string('cdn.ftp.path'),
  1113. 'pasv' => $this->_config->get_boolean('cdn.ftp.pasv'),
  1114. 'domain' => $this->_config->get_string('cdn.ftp.domain')
  1115. );
  1116. break;
  1117. case 's3':
  1118. $engine_config = array(
  1119. 'key' => $this->_config->get_string('cdn.s3.key'),
  1120. 'secret' => $this->_config->get_string('cdn.s3.secret'),
  1121. 'bucket' => $this->_config->get_string('cdn.s3.bucket')
  1122. );
  1123. break;
  1124. case 'cf':
  1125. $engine_config = array(
  1126. 'key' => $this->_config->get_string('cdn.cf.key'),
  1127. 'secret' => $this->_config->get_string('cdn.cf.secret'),
  1128. 'bucket' => $this->_config->get_string('cdn.cf.bucket'),
  1129. 'id' => $this->_config->get_string('cdn.cf.id'),
  1130. 'cname' => $this->_config->get_string('cdn.cf.cname')
  1131. );
  1132. break;
  1133. }
  1134. require_once W3TC_LIB_W3_DIR . '/Cdn.php';
  1135. $cdn[0] = & W3_Cdn::instance($engine, $engine_config);
  1136. }
  1137. return $cdn[0];
  1138. }
  1139. /**
  1140. * Returns debug info
  1141. *
  1142. * @return string
  1143. */
  1144. function get_debug_info()
  1145. {
  1146. $debug_info = "<!-- W3 Total Cache: CDN debug info:\r\n";
  1147. $debug_info .= sprintf("%s%s\r\n", str_pad('Engine: ', 20), $this->_config->get_string('cdn.engine'));
  1148. if (count($this->replaced_urls)) {
  1149. $debug_info .= "Replaced URLs:\r\n";
  1150. foreach ($this->replaced_urls as $old_url => $new_url) {
  1151. $debug_info .= sprintf("%s => %s\r\n", $old_url, $new_url);
  1152. }
  1153. }
  1154. $debug_info .= '-->';
  1155. return $debug_info;
  1156. }
  1157. /**
  1158. * Check if we can do CDN logic
  1159. * @return boolean
  1160. */
  1161. function can_cdn()
  1162. {
  1163. /**
  1164. * Skip if CDN is disabled
  1165. */
  1166. if (!$this->_config->get_boolean('cdn.enabled')) {
  1167. $this->cdn_reject_reason = 'CDN is disabled';
  1168. return false;
  1169. }
  1170. /**
  1171. * Skip if admin
  1172. */
  1173. if (defined('WP_ADMIN')) {
  1174. $this->cdn_reject_reason = 'wp-admin';
  1175. return false;
  1176. }
  1177. /**
  1178. * Check User agent
  1179. */
  1180. if (!$this->check_ua()) {
  1181. $this->cdn_reject_reason = 'user agent is rejected';
  1182. return false;
  1183. }
  1184. /**
  1185. * Check request URI
  1186. */
  1187. if (!$this->check_request_uri()) {
  1188. $this->cdn_reject_reason = 'request URI is rejected';
  1189. return false;
  1190. }
  1191. return true;
  1192. }
  1193. /**
  1194. * Checks User Agent
  1195. *
  1196. * @return boolean
  1197. */
  1198. function check_ua()
  1199. {
  1200. foreach ($this->_config->get_array('cdn.reject.ua') as $ua) {
  1201. if (stristr($_SERVER['HTTP_USER_AGENT'], $ua) !== false) {
  1202. return false;
  1203. }
  1204. }
  1205. return true;
  1206. }
  1207. /**
  1208. * Checks request URI
  1209. *
  1210. * @return boolean
  1211. */
  1212. function check_request_uri()
  1213. {
  1214. $auto_reject_uri = array(
  1215. 'wp-login',
  1216. 'wp-register'
  1217. );
  1218. foreach ($auto_reject_uri as $uri) {
  1219. if (strstr($_SERVER['REQUEST_URI'], $uri) !== false) {
  1220. return false;
  1221. }
  1222. }
  1223. foreach ($this->_config->get_array('cdn.reject.uri') as $expr) {
  1224. $expr = trim($expr);
  1225. if ($expr != '' && preg_match('@' . $expr . '@i', $_SERVER['REQUEST_URI'])) {
  1226. return false;
  1227. }
  1228. }
  1229. return true;
  1230. }
  1231. }