<?php



/**

 * W3 Total Cache CDN Plugin

 */

if (!defined('W3TC')) {

    die();

}



w3_require_once(W3TC_INC_DIR . '/functions/file.php');

w3_require_once(W3TC_INC_DIR . '/functions/http.php');

w3_require_once(W3TC_LIB_W3_DIR . '/Plugin.php');



/**

 * Class W3_Plugin_CdnAdmin

 */

class W3_Plugin_CdnAdmin extends W3_Plugin {

    /**

     * Instantiates worker with common functionality on demand

     *

     * @return W3_Plugin_CdnCommon

     */

    function _get_common() {

        return w3_instance('W3_Plugin_CdnCommon');

    }



    /**

     * Activation action

     */

    function activate() {

        w3_require_once(W3TC_INC_DIR . '/functions/activation.php');

        global $wpdb;



        $this->schedule();

        $this->schedule_upload();



        if ($this->_config->get_boolean('cdn.enabled') && !w3_is_cdn_mirror($this->_config->get_string('cdn.engine')) && !$this->table_create(true)) {

            $error = sprintf('Unable to create table <strong>%s%s</strong>: %s', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE, $wpdb->last_error);



            w3_activate_error($error);

        }

        if ($this->_config->get_boolean('cdn.enabled')) {

            if (w3_can_modify_rules(w3_get_browsercache_rules_cache_path())) {

                try {

                    $this->write_rules();

                } catch (Exception $e)

                {}

            }

        }

    }



    /**

     * Deactivation action

     */

    function deactivate() {

        $errors = array('errors' => array(), 'errors_short_form' => array(), 'ftp_form' => null);

        $results = array();



        $this->table_delete();

        $this->unschedule_upload();

        $this->unschedule();

        if (w3_can_modify_rules(w3_get_browsercache_rules_cache_path())) {

            $results[] = $this->remove_rules_with_message();

        }

        if ($results) {

            foreach ($results as $result) {

                if ($result['errors']) {

                    $errors['errors'] = array_merge($errors['errors'], $result['errors']);

                    $errors['errors_short_form'] = array_merge($errors['errors_short_form'], $result['errors_short_form']);

                    if (!isset($errors['ftp_form']))

                        $errors['ftp_form'] = $result['ftp_form'];

                }

            }

        }

        return $errors;



    }



    /**

     * Called from admin interface after configuration is changed

     */

    function after_config_change() {

        $this->schedule();

        $this->schedule_upload();

    }



    /**

     * Schedules cron events

     */

    function schedule() {

        if ($this->_config->get_boolean('cdn.enabled') && !w3_is_cdn_mirror($this->_config->get_string('cdn.engine'))) {

            if (!wp_next_scheduled('w3_cdn_cron_queue_process')) {

                wp_schedule_event(current_time('timestamp'), 'w3_cdn_cron_queue_process', 'w3_cdn_cron_queue_process');

            }

        } else {

            $this->unschedule();

        }

    }



    /**

     * Schedule upload event

     */

    function schedule_upload() {

        if ($this->_config->get_boolean('cdn.enabled') && $this->_config->get_boolean('cdn.autoupload.enabled') && !w3_is_cdn_mirror($this->_config->get_string('cdn.engine'))) {

            if (!wp_next_scheduled('w3_cdn_cron_upload')) {

                wp_schedule_event(current_time('timestamp'), 'w3_cdn_cron_upload', 'w3_cdn_cron_upload');

            }

        } else {

            $this->unschedule_upload();

        }

    }



    /**

     * Unschedules cron events

     */

    function unschedule() {

        if (wp_next_scheduled('w3_cdn_cron_queue_process')) {

            wp_clear_scheduled_hook('w3_cdn_cron_queue_process');

        }

    }



    /**

     * Unschedule upload event

     */

    function unschedule_upload() {

        if (wp_next_scheduled('w3_cdn_cron_upload')) {

            wp_clear_scheduled_hook('w3_cdn_cron_upload');

        }

    }



    /**

     * Create queue table

     *

     * @param bool $drop

     * @return int

     */

    function table_create($drop = false) {

        global $wpdb;



        if ($drop) {

            $sql = sprintf('DROP TABLE IF EXISTS `%s%s`', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);



            $wpdb->query($sql);

        }



        $sql = sprintf("CREATE TABLE IF NOT EXISTS `%s%s` (

            `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

            `local_path` varchar(500) NOT NULL DEFAULT '',

            `remote_path` varchar(500) NOT NULL DEFAULT '',

            `command` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1 - Upload, 2 - Delete, 3 - Purge',

            `last_error` varchar(150) NOT NULL DEFAULT '',

            `date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',

            PRIMARY KEY (`id`),

            UNIQUE KEY `path` (`local_path`, `remote_path`),

            KEY `date` (`date`)

        ) /*!40100 CHARACTER SET latin1 */", $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);



        $wpdb->query($sql);



        return $wpdb->result;

    }



    /**

     * Delete queue table

     *

     * @return int

     */

    function table_delete() {

        global $wpdb;



        $sql = sprintf('DROP TABLE IF EXISTS `%s%s`', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);



        return $wpdb->query($sql);

    }



    /**

     * Purge attachment

     *

     * Upload _wp_attached_file, _wp_attachment_metadata, _wp_attachment_backup_sizes

     *

     * @param integer $attachment_id

     * @param array $results

     * @return boolean

     */

    function purge_attachment($attachment_id, &$results) {

        $files = $this->_get_common()->get_attachment_files($attachment_id);



        return $this->_get_common()->purge($files, false, $results);

    }



    /**

     * Updates file date in the queue

     *

     * @param integer $queue_id

     * @param string $last_error

     * @return integer

     */

    function queue_update($queue_id, $last_error) {

        global $wpdb;



        $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);



        return $wpdb->query($sql);

    }



    /**

     * Removes from queue

     *

     * @param integer $queue_id

     * @return integer

     */

    function queue_delete($queue_id) {

        global $wpdb;



        $sql = sprintf('DELETE FROM %s WHERE id = %d', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE, $queue_id);



        return $wpdb->query($sql);

    }



    /**

     * Empties queue

     *

     * @param integer $command

     * @return integer

     */

    function queue_empty($command) {

        global $wpdb;



        $sql = sprintf('DELETE FROM %s WHERE command = %d', $wpdb->prefix . W3TC_CDN_TABLE_QUEUE, $command);



        return $wpdb->query($sql);

    }



    /**

     * Returns queue

     *

     * @param integer $limit

     * @return array

     */

    function queue_get($limit = null) {

        global $wpdb;



        $sql = sprintf('SELECT * FROM %s%s ORDER BY date', $wpdb->prefix, W3TC_CDN_TABLE_QUEUE);



        if ($limit) {

            $sql .= sprintf(' LIMIT %d', $limit);

        }



        $results = $wpdb->get_results($sql);

        $queue = array();



        if ($results) {

            foreach ((array) $results as $result) {

                $queue[$result->command][] = $result;

            }

        }



        return $queue;

    }



    /**

     * Process queue

     *

     * @param integer $limit

     */

    function queue_process($limit) {

        $items = 0;

        

        $commands = $this->queue_get($limit);

        $force_rewrite = $this->_config->get_boolean('cdn.force.rewrite');



        if (count($commands)) {

            $cdn = $this->_get_common()->get_cdn();



            foreach ($commands as $command => $queue) {

                $files = array();

                $results = array();

                $map = array();



                foreach ($queue as $result) {

                    $files[] = $this->_get_common()->build_file_descriptor($result->local_path, $result->remote_path);

                    $map[$result->local_path] = $result->id;

                    $items++;

                }



                switch ($command) {

                    case W3TC_CDN_COMMAND_UPLOAD:

                        $dispatcher = w3_instance('W3_Dispatcher');

                        foreach ($files as $file) {

                            $local_file_name = $file['local_path'];

                            $remote_file_name = $file['remote_path'];

                            if (!file_exists($local_file_name)) {

                                $dispatcher->create_file_for_cdn($local_file_name);

                            }

                        }



                        $cdn->upload($files, $results, $force_rewrite);

                        

                        foreach ($results as $result) {

                            if ($result['result'] == W3TC_CDN_RESULT_OK) {

                                $dispatcher->on_cdn_file_upload($result['local_path']);

                            }

                        }

                        break;



                    case W3TC_CDN_COMMAND_DELETE:

                        $cdn->delete($files, $results);

                        break;



                    case W3TC_CDN_COMMAND_PURGE:

                        $cdn->purge($files, $results);

                        break;

                }



                foreach ($results as $result) {

                    if ($result['result'] == W3TC_CDN_RESULT_OK) {

                        $this->queue_delete($map[$result['local_path']]);

                    } else {

                        $this->queue_update($map[$result['local_path']], $result['error']);

                    }

                }

            }

        }

        

        return $items;

    }



    /**

     * Export library to CDN

     *

     * @param integer $limit

     * @param integer $offset

     * @param integer $count

     * @param integer $total

     * @param array $results

     * @return void

     */

    function export_library($limit, $offset, &$count, &$total, &$results) {

        global $wpdb;



        $count = 0;

        $total = 0;



        $upload_info = w3_upload_info();



        if ($upload_info) {

            $sql = sprintf('SELECT

        		pm.meta_value AS file,

                pm2.meta_value AS metadata

            FROM

                %sposts AS p

            LEFT JOIN

                %spostmeta AS pm ON p.ID = pm.post_ID AND pm.meta_key = "_wp_attached_file"

            LEFT JOIN

            	%spostmeta AS pm2 ON p.ID = pm2.post_ID AND pm2.meta_key = "_wp_attachment_metadata"

            WHERE

                p.post_type = "attachment"  AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL)

            GROUP BY

            	p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix);



            if ($limit) {

                $sql .= sprintf(' LIMIT %d', $limit);



                if ($offset) {

                    $sql .= sprintf(' OFFSET %d', $offset);

                }

            }



            $posts = $wpdb->get_results($sql);



            if ($posts) {

                $count = count($posts);

                $total = $this->get_attachments_count();

                $files = array();



                foreach ($posts as $post) {

                    $post_files = array();



                    if ($post->file) {

                        $file = $this->_get_common()->normalize_attachment_file($post->file);



                        $local_file = $upload_info['basedir'] . '/' . $file;

                        $remote_file = ltrim($upload_info['baseurlpath'] . $file, '/');



                        $post_files[] = $this->_get_common()->build_file_descriptor($local_file, $remote_file);

                    }



                    if ($post->metadata) {

                        $metadata = @unserialize($post->metadata);



                        $post_files = array_merge($post_files, $this->_get_common()->get_metadata_files($metadata));

                    }



                    $post_files = apply_filters('w3tc_cdn_add_attachment', $post_files);



                    $files = array_merge($files, $post_files);

                }



                $this->_get_common()->upload($files, false, $results);

            }

        }

    }



    /**

     * Imports library

     *

     * @param integer $limit

     * @param integer $offset

     * @param integer $count

     * @param integer $total

     * @param array $results

     * @return boolean

     */

    function import_library($limit, $offset, &$count, &$total, &$results) {

        global $wpdb;



        $count = 0;

        $total = 0;

        $results = array();



        $upload_info = w3_upload_info();

        $uploads_use_yearmonth_folders = get_option('uploads_use_yearmonth_folders');

        $document_root = w3_get_document_root();



        @set_time_limit($this->_config->get_integer('timelimit.cdn_import'));



        if ($upload_info) {

            /**

             * Search for posts with links or images

             */

            $sql = sprintf('SELECT

        		ID,

        		post_content,

        		post_date

            FROM

                %sposts

            WHERE

                post_status = "publish"

                AND (post_type = "post" OR post_type = "page")

                AND (post_content LIKE "%%src=%%"

                	OR post_content LIKE "%%href=%%")

       		', $wpdb->prefix);



            if ($limit) {

                $sql .= sprintf(' LIMIT %d', $limit);



                if ($offset) {

                    $sql .= sprintf(' OFFSET %d', $offset);

                }

            }



            $posts = $wpdb->get_results($sql);



            if ($posts) {

                $count = count($posts);

                $total = $this->get_import_posts_count();

                $regexp = '~(' . $this->get_regexp_by_mask($this->_config->get_string('cdn.import.files')) . ')$~';

                $import_external = $this->_config->get_boolean('cdn.import.external');



                foreach ($posts as $post) {

                    $matches = null;

                    $replaced = array();

                    $attachments = array();

                    $post_content = $post->post_content;



                    /**

                     * Search for all link and image sources

                     */

                    if (preg_match_all('~(href|src)=[\'"]?([^\'"<>\s]+)[\'"]?~', $post_content, $matches, PREG_SET_ORDER)) {

                        foreach ($matches as $match) {

                            list($search, $attribute, $origin) = $match;



                            /**

                             * Check if $search is already replaced

                             */

                            if (isset($replaced[$search])) {

                                continue;

                            }



                            $error = '';

                            $result = false;



                            $src = w3_normalize_file_minify($origin);

                            $dst = '';



                            /**

                             * Check if file exists in the library

                             */

                            if (stristr($origin, $upload_info['baseurl']) === false) {

                                /**

                                 * Check file extension

                                 */

                                $check_src = $src;



                                if (w3_is_url($check_src)) {

                                    $qpos = strpos($check_src, '?');



                                    if ($qpos !== false) {

                                        $check_src = substr($check_src, 0, $qpos);

                                    }

                                }



                                if (preg_match($regexp, $check_src)) {

                                    /**

                                     * Check for already uploaded attachment

                                     */

                                    if (isset($attachments[$src])) {

                                        list($dst, $dst_url) = $attachments[$src];

                                        $result = true;

                                    } else {

                                        if ($uploads_use_yearmonth_folders) {

                                            $upload_subdir = date('Y/m', strtotime($post->post_date));

                                            $upload_dir = sprintf('%s/%s', $upload_info['basedir'], $upload_subdir);

                                            $upload_url = sprintf('%s/%s', $upload_info['baseurl'], $upload_subdir);

                                        } else {

                                            $upload_subdir = '';

                                            $upload_dir = $upload_info['basedir'];

                                            $upload_url = $upload_info['baseurl'];

                                        }



                                        $src_filename = pathinfo($src, PATHINFO_FILENAME);

                                        $src_extension = pathinfo($src, PATHINFO_EXTENSION);



                                        /**

                                         * Get available filename

                                         */

                                        for ($i = 0; ; $i++) {

                                            $dst = sprintf('%s/%s%s%s', $upload_dir, $src_filename, ($i ? $i : ''), ($src_extension ? '.' . $src_extension : ''));



                                            if (!file_exists($dst)) {

                                                break;

                                            }

                                        }



                                        $dst_basename = basename($dst);

                                        $dst_url = sprintf('%s/%s', $upload_url, $dst_basename);

                                        $dst_path = ltrim(str_replace($document_root, '', w3_path($dst)), '/');



                                        if ($upload_subdir) {

                                            w3_mkdir($upload_subdir, 0777, $upload_info['basedir']);

                                        }



                                        $download_result = false;



                                        /**

                                         * Check if file is remote URL

                                         */

                                        if (w3_is_url($src)) {

                                            /**

                                             * Download file

                                             */

                                            if ($import_external) {

                                                $download_result = w3_download($src, $dst);



                                                if (!$download_result) {

                                                    $error = 'Unable to download file';

                                                }

                                            } else {

                                                $error = 'External file import is disabled';

                                            }

                                        } else {

                                            /**

                                             * Otherwise copy file from local path

                                             */

                                            $src_path = $document_root . '/' . urldecode($src);



                                            if (file_exists($src_path)) {

                                                $download_result = @copy($src_path, $dst);



                                                if (!$download_result) {

                                                    $error = 'Unable to copy file';

                                                }

                                            } else {

                                                $error = 'Source file doesn\'t exists';

                                            }

                                        }



                                        /**

                                         * Check if download or copy was successful

                                         */

                                        if ($download_result) {

                                            w3_require_once(W3TC_INC_DIR . '/functions/mime.php');



                                            $title = $dst_basename;

                                            $guid = ltrim($upload_info['baseurlpath'] . $title, ',');

                                            $mime_type = w3_get_mime_type($dst);



                                            $GLOBALS['wp_rewrite'] = new WP_Rewrite();



                                            /**

                                             * Insert attachment

                                             */

                                            $id = wp_insert_attachment(array(

                                                'post_mime_type' => $mime_type,

                                                'guid' => $guid,

                                                'post_title' => $title,

                                                'post_content' => '',

                                                'post_parent' => $post->ID

                                            ), $dst);



                                            if (!is_wp_error($id)) {

                                                /**

                                                 * Generate attachment metadata and upload to CDN

                                                 */

                                                require_once ABSPATH . 'wp-admin/includes/image.php';

                                                wp_update_attachment_metadata($id, wp_generate_attachment_metadata($id, $dst));



                                                $attachments[$src] = array(

                                                    $dst,

                                                    $dst_url

                                                );



                                                $result = true;

                                            } else {

                                                $error = 'Unable to insert attachment';

                                            }

                                        }

                                    }



                                    /**

                                     * If attachment was successfully created then replace links

                                     */

                                    if ($result) {

                                        $replace = sprintf('%s="%s"', $attribute, $dst_url);



                                        // replace $search with $replace

                                        $post_content = str_replace($search, $replace, $post_content);



                                        $replaced[$search] = $replace;

                                        $error = 'OK';

                                    }

                                } else {

                                    $error = 'File type rejected';

                                }

                            } else



                            {

                                $error = 'File already exists in the media library';

                            }



                            /**

                             * Add new entry to the log file

                             */



                            $results[] = array(

                                'src' => $src,

                                'dst' => $dst_path,

                                'result' => $result,

                                'error' => $error

                            );

                        }

                    }



                    /**

                     * If post content was chenged then update DB

                     */

                    if ($post_content != $post->post_content) {

                        wp_update_post(array(

                            'ID' => $post->ID,

                            'post_content' => $post_content

                        ));

                    }

                }

            }

        }

    }



    /**

     * Rename domain

     *

     * @param array $names

     * @param integer $limit

     * @param integer $offset

     * @param integer $count

     * @param integer $total

     * @param integer $results

     * @return void

     */

    function rename_domain($names, $limit, $offset, &$count, &$total, &$results) {

        global $wpdb;



        @set_time_limit($this->_config->get_integer('timelimit.domain_rename'));



        $count = 0;

        $total = 0;

        $results = array();



        $upload_info = w3_upload_info();



        foreach ($names as $index => $name) {

            $names[$index] = str_ireplace('www.', '', $name);

        }



        if ($upload_info) {

            $sql = sprintf('SELECT

        		ID,

        		post_content,

        		post_date

            FROM

                %sposts

            WHERE

                post_status = "publish"

                AND (post_type = "post" OR post_type = "page")

                AND (post_content LIKE "%%src=%%"

                	OR post_content LIKE "%%href=%%")

       		', $wpdb->prefix);



            if ($limit) {

                $sql .= sprintf(' LIMIT %d', $limit);



                if ($offset) {

                    $sql .= sprintf(' OFFSET %d', $offset);

                }

            }



            $posts = $wpdb->get_results($sql);



            if ($posts) {

                $count = count($posts);

                $total = $this->get_rename_posts_count();

                $names_quoted = array_map('w3_preg_quote', $names);



                foreach ($posts as $post) {

                    $matches = null;

                    $post_content = $post->post_content;

                    $regexp = '~(href|src)=[\'"]?(https?://(www\.)?(' . implode('|', $names_quoted) . ')' . w3_preg_quote($upload_info['baseurlpath']) . '([^\'"<>\s]+))[\'"]~';



                    if (preg_match_all($regexp, $post_content, $matches, PREG_SET_ORDER)) {

                        foreach ($matches as $match) {

                            $old_url = $match[2];

                            $new_url = sprintf('%s/%s', $upload_info['baseurl'], $match[5]);

                            $post_content = str_replace($old_url, $new_url, $post_content);



                            $results[] = array(

                                'old' => $old_url,

                                'new' => $new_url,

                                'result' => true,

                                'error' => 'OK'

                            );

                        }

                    }



                    if ($post_content != $post->post_content) {

                        wp_update_post(array(

                            'ID' => $post->ID,

                            'post_content' => $post_content

                        ));

                    }

                }

            }

        }

    }



    /**

     * Returns attachments count

     *

     * @return integer

     */

    function get_attachments_count() {

        global $wpdb;



        $sql = sprintf('SELECT COUNT(DISTINCT p.ID)

FROM %sposts AS p

LEFT JOIN %spostmeta AS pm ON p.ID = pm.post_ID

AND pm.meta_key =  "_wp_attached_file"

LEFT JOIN %spostmeta AS pm2 ON p.ID = pm2.post_ID

AND pm2.meta_key =  "_wp_attachment_metadata"

WHERE p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL)', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix);



        return $wpdb->get_var($sql);

    }



    /**

     * Returns import posts count

     *

     * @return integer

     */

    function get_import_posts_count() {

        global $wpdb;



        $sql = sprintf('SELECT

        		COUNT(*)

            FROM

                %sposts

            WHERE

                post_status = "publish"

                AND (post_type = "post" OR post_type = "page")

                AND (post_content LIKE "%%src=%%"

                	OR post_content LIKE "%%href=%%")

                ', $wpdb->prefix);



        return $wpdb->get_var($sql);

    }



    /**

     * Returns rename posts count

     *

     * @return integer

     */

    function get_rename_posts_count() {

        return $this->get_import_posts_count();

    }



    /**

     * Returns regexp by mask

     *

     * @param string $mask

     * @return string

     */

    function get_regexp_by_mask($mask) {

        $mask = trim($mask);

        $mask = w3_preg_quote($mask);



        $mask = str_replace(array(

            '\*',

            '\?',

            ';'

        ), array(

            '@ASTERISK@',

            '@QUESTION@',

            '|'

        ), $mask);



        $regexp = str_replace(array(

            '@ASTERISK@',

            '@QUESTION@'

        ), array(

            '[^\\?\\*:\\|"<>]*',

            '[^\\?\\*:\\|"<>]'

        ), $mask);



        return $regexp;

    }



    function update_cnames(&$error) {

        $cdn = $this->_get_common()->get_cdn();

        $cdn->update_cnames($error);

    }



    /**

     * Returns rules

     *

     * @param bool $cdnftp

     * @return string

     */

    function generate_rules($cdnftp = false) {

        switch (true) {

            case w3_is_apache():

            case w3_is_litespeed():

                return $this->generate_rules_apache($cdnftp);



            case w3_is_nginx():

                return $this->generate_rules_nginx($cdnftp);

        }



        return false;

    }



    /**

     * Returns apache rules

     *

     * @param bool $cdnftp

     * @return string

     */

    function generate_rules_apache($cdnftp = false) {

        $rules = '';

        $rules .= W3TC_MARKER_BEGIN_CDN . "\n";

        $w3_dispatcher = w3_instance('W3_Dispatcher');



        if ($w3_dispatcher->should_cdn_generate_canonical($cdnftp)) {

            $mime_types = $this->_get_other_types();

            $extensions = array_keys($mime_types);

            $extensions_lowercase = array_map('strtolower', $extensions);

            $extensions_uppercase = array_map('strtoupper', $extensions);

            $host = ($cdnftp) ? w3_get_home_domain() : '%{HTTP_HOST}';

            $rules .= "<FilesMatch \"\\.(" . implode('|', array_merge($extensions_lowercase, $extensions_uppercase)) . ")$\">\n";

            $rules .= "   <IfModule mod_rewrite.c>\n";

            $rules .= "      RewriteEngine On\n";

            $rules .= "      RewriteCond %{HTTPS} !=on\n";

            $rules .= "      RewriteRule .* - [E=CANONICAL:http://$host%{REQUEST_URI},NE]\n";

            $rules .= "      RewriteCond %{HTTPS} =on\n";

            $rules .= "      RewriteRule .* - [E=CANONICAL:https://$host%{REQUEST_URI},NE]\n";

            $rules .= "   </IfModule>\n";

            $rules .= "   <IfModule mod_headers.c>\n";

            $rules .= '      Header set Link "<%{CANONICAL}e>; rel=\"canonical\""' . "\n";

            $rules .= "   </IfModule>\n";

            $rules .= "</FilesMatch>\n";

        }



        if (!$cdnftp)

            $rules .= "<FilesMatch \"\.(ttf|otf|eot|woff)$\">\n";

        $rules .= "<IfModule mod_headers.c>\n";

        $rules .= "    Header set Access-Control-Allow-Origin \"*\"\n";

        $rules .= "</IfModule>\n";

        if (!$cdnftp)

            $rules .= "</FilesMatch>\n";

        $rules .= W3TC_MARKER_END_CDN . "\n";



        return $rules;

    }



    /**

     * Returns nginx rules

     *

     * @param bool $cdnftp

     * @return string

     */

    function generate_rules_nginx($cdnftp = false) {

        $rules = '';

        $rules .= W3TC_MARKER_BEGIN_CDN . "\n";

        $w3_dispatcher = w3_instance('W3_Dispatcher');

        if ($w3_dispatcher->should_cdn_generate_canonical($cdnftp)) {

            $mime_types = $this->_get_other_types();

            $extensions = array_keys($mime_types);

            $rules .= "location ~ \.(" . implode('|', $extensions) . ")$ {\n";

            $rules .= $this->generate_canonical_nginx($cdnftp);

            $rules .= "}\n";

        }



        if (!$cdnftp)

            $rules .= "location ~ \\.(ttf|otf|eot|woff)$ {\n";

        $rules .= "   add_header Access-Control-Allow-Origin \"*\";\n";

        if (!$cdnftp)

            $rules .= "}\n";

        $rules .= W3TC_MARKER_END_CDN . "\n";



        return $rules;

    }



    /**

     * Returns nginx canonical rule

     * @param $cdnftp

     * @return string

     */

    public function generate_canonical_nginx($cdnftp) {

        $home = ($cdnftp) ? w3_get_home_domain() : '$host';

        return '   add_header Link "<$scheme://' . $home . '$uri>; rel=\"canonical\"";' . "\n";

    }



    /**

     * Writes rules

     *

     * @throws FilesystemCredentialException with S/FTP form if it can't get the required filesystem credentials

     * @throws FileOperationException

     */

    function write_rules() {

        $path = w3_get_browsercache_rules_cache_path();



        if (file_exists($path)) {

            $data = @file_get_contents($path);



            if ($data === false) {

                return false;

            }

        } else {

            $data = '';

        }



        $replace_start = strpos($data, W3TC_MARKER_BEGIN_CDN);

        $replace_end = strpos($data, W3TC_MARKER_END_CDN);



        if ($replace_start !== false && $replace_end !== false && $replace_start < $replace_end) {

            $replace_length = $replace_end - $replace_start + strlen(W3TC_MARKER_END_CDN) + 1;

        } else {

            $replace_start = false;

            $replace_length = 0;



            $search = array(

                W3TC_MARKER_BEGIN_MINIFY_CORE => 0,

                W3TC_MARKER_BEGIN_PGCACHE_CORE => 0,

                W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP => 0,

                W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE => 0,

                W3TC_MARKER_BEGIN_WORDPRESS => 0,

                W3TC_MARKER_END_PGCACHE_CACHE => strlen(W3TC_MARKER_END_PGCACHE_CACHE) + 1,

                W3TC_MARKER_END_MINIFY_CACHE => strlen(W3TC_MARKER_END_MINIFY_CACHE) + 1

            );



            foreach ($search as $string => $length) {

                $replace_start = strpos($data, $string);



                if ($replace_start !== false) {

                    $replace_start += $length;

                    break;

                }

            }

        }



        $rules = $this->generate_rules();



        if ($replace_start !== false) {

            $data = w3_trim_rules(substr_replace($data, $rules, $replace_start, $replace_length));

        } else {

            $data = w3_trim_rules($data . $rules);

        }



        w3_require_once(W3TC_INC_DIR . '/functions/activation.php');

        w3_wp_write_to_file($path, $data);

    }



    /**

     * Check rules

     *

     * @return boolean

     */

    function check_rules() {

        $path = w3_get_browsercache_rules_cache_path();

        $search = $this->generate_rules();



        return (($data = @file_get_contents($path)) && strstr(w3_clean_rules($data), w3_clean_rules($search)) !== false);

    }



    /**

     * Removes cache rules

     *

     * @throws FilesystemCredentialException with S/FTP form if it can't get the required filesystem credentials

     * @throws FileOperationException

     */

    function remove_rules() {

        $path = w3_get_browsercache_rules_cache_path();



        if (file_exists($path)) {

            if (($data = @file_get_contents($path)) !== false) {

                $data = $this->erase_rules($data);



                w3_require_once(W3TC_INC_DIR . '/functions/activation.php');

                w3_wp_write_to_file($path, $data);

            }

        }

    }



    /**

     * Erases cache rules

     *

     * @param string $data

     * @return string

     */

    function erase_rules($data) {

        $data = w3_erase_rules($data, W3TC_MARKER_BEGIN_CDN, W3TC_MARKER_END_CDN);



        return $data;

    }



    /**

     * Returns other mime types

     *

     * @return array

     */

    function _get_other_types() {

        $mime_types = include W3TC_INC_DIR . '/mime/other.php';



        return $mime_types;

    }



    /**

     * Returns required rules for module

     * @return array

     */

    function get_required_rules() {

        $rewrite_rules = array();

        if ($this->_config->get_boolean('cdn.enabled') && $this->_config->get_string('cdn.engine') == 'ftp') {

            $domain = $this->_get_common()->get_cdn()->get_domain();

            $cdn_rules_path = sprintf('ftp://%s/%s', $domain, w3_get_cdn_rules_path());

            $rewrite_rules[] = array('filename' => $cdn_rules_path, 'content' => $this->generate_rules());

        }



        if ($this->_config->get_boolean('cdn.enabled') || $this->_config->get_boolean('cloudflare.enabled')) {

            $browsercache_rules_cache_path = w3_get_browsercache_rules_cache_path();

            $rewrite_rules[] = array('filename' => $browsercache_rules_cache_path, 'content' => $this->generate_rules());

        }

        return $rewrite_rules;

    }



    /**

     * @return array

     */

    function remove_rules_with_message() {

        $ftp_form = null;

        $errors = array();

        $errors_short_form = array();



        if ($this->check_rules()) {

            try {

                $this->remove_rules();

            } catch(Exception $e) {

                $errors[] = sprintf('CDN/CloudFlare added rules that need to be removed. To remove them manually, edit the configuration file (<strong>%s</strong>) and remove all lines between and including <strong>%s</strong> and <strong>%s</strong> markers inclusive.'

                    , w3_get_browsercache_rules_cache_path(),

                    W3TC_MARKER_BEGIN_CDN, W3TC_MARKER_BEGIN_CDN);

                $errors_short_form[] = sprintf('Edit file (<strong>%s</strong>) and remove all lines between and including <strong>%s</strong> and <strong>%s</strong> markers inclusive.'

                    , w3_get_browsercache_rules_cache_path(),

                    W3TC_MARKER_BEGIN_CDN, W3TC_MARKER_BEGIN_CDN);

                if ($e instanceof FilesystemCredentialException)

                    $ftp_form = $e->ftp_form();

            }

        }

        return array('errors' => $errors, 'ftp_form' => $ftp_form, 'errors_short_form' => $errors_short_form);

    }

}

