Sanjeev Sabhlok's notes on technology, hardware, gardening

Category: Wordpress

Clean up wp_postmeta table in the WordPress database. It hogs a lot of SQL memory.

postmeta seems to collect on its own. I had >200k postmeta records. Ridiculous.

Unfortunately, with very little idea of what these metas do, I had to adopt a conservative approach and not delete the whole table.

STOPGAP SOLUTION

  1. Go to myphyphadmin > relevant database > wp_postmeta table
  2. In the ‘meta_key’ row, click the icon with ‘lists’ (i.e. browse distinct values upon mouseover)
  3. Sort by rows (descending)
  4. This gives you the names of meta_keys that are hogging your space

SQL query:

SELECT COUNT( * ) AS `Rows` , `meta_key`
FROM `wp_sys1_postmeta`
GROUP BY `meta_key`
ORDER BY `Rows` DESC
LIMIT 30 , 30

Now identify meta_keys that are linked with defunct plugins and get rid of them by going back to the table view and running SQL query of the type:

DELETE FROM wp_postmeta WHERE meta_key = ‘_yoast_wpseo_linkdex’;

And so on.

Once you’ve deleted a lot of obviously useless metas, then select rows with overhead and optimize.

DELETE MULTIPLE ROWS

Use this:

DELETE FROM wp_postmeta WHERE `meta_key` LIKE ‘%blogger%’;

 

Continue Reading

Dreamhost’s MySQL VPS and VPS – some notes

[RELATED POST]

NOTE TO SELF: I DON’T NEED MYSQL VPS. I’VE TESTED THIS TWICE ALREADY AND THE PROBLEMS NEVER GO AWAY THROUGH THE UPGRADE.

In August 2017:

I was having massive surge in errors in the error.log and got SICK of things and decided to try an upgrade to MySQL VPS. Turned out that NOT A SINGLE ERROR GOT REDUCED.

This proved (once again) that the database was NOT causing problems. It was the use of CPU and RAM in the server.

The solution to that is different: a) clear out plugins, etc. and b) if that doesn’t work then upgrade to VPS.

I used this website – found it extremely useful.

Earlier, in 2015:

Switched back to shared hosting since memory issues were more chronic on VPS mySQL

WHAT ABOUT PAYMENT?
My question to Dreamhost:
I had paid a 3-year subscription for shared server ($284.14 till September 2019). If I decide to switch my entire account to VPS will the balance amount be automatically applied towards my VPS subscription?

Their response:

If you were to only purchase the web VPS service and not Mysql VPS as well. The shared plan you have would need to stay active to manage the shared database services. [In other words I’d be paying $7.89+ $15 per month (= $22.89 minimum) per month].

VPS and MySql VPS are independent services and purchased separately. Accordingly, if you were to purchase VPS and MySql VPS. The shared hosting plan could be closed since it won’t be used. The remaining unused payment would be applied on the VPS and MySql VPS services. [In other words I’d be paying $30 per month minimum].

SO FAR I’VE NOT HAD TO UPGRADE SINCE ALL PROBLEMS HAVE BEEN AT MY END (PLUGINS, ETC). Theoretically, Apache can serve up to 1M requests easily. ALL problems are therefore on my server side (basically, BAD PLUGINS).

BACKGROUND NOTES

FROM MY PREVIOUS CORREPSONDENCE WITH DREAMHOST, THIS IS WHAT THEY SAID

[My username] went over the allowed memory limit on the server. When an FTP user uses up excessive memory we have a script in place to stop those processes. Procwatch (Process Watcher) is a daemon that runs constantly on shared servers to monitor the usage of RAM/CPU and execution time so that no single user can use an  inappropriately high percentage of the shared resources and impact the overall health of the server or the server’s ability to serve all users’ pages. This is done to preserve quality of service and not adversely affect the stability of the shared environment.

[They then provided me with an] excerpt from the log kept by our Process Watcher, the daemon that is killing your troublesome php5.cgi processes:

[THEIR SUGGESTIONS]

Now, it’s important to get these errors fixed to keep your sites running with optimum speed, as well as keep our servers happy. Unfortunately it can often be a bit of a trial-and-error procedure, but I will provide as much information as I can to aide you in this process.

Firstly, it should be noted that any sites running under your user can contribute to the problems you are encountering. For example, if site A is  running processes that are taking up 85% of your individual limit, and site B tries to run a concurrent process that takes up 20% of the memory  cap, site B will have it’s process killed since it is the one that took you above your limits, even though site A’s script is the obvious memory  hog… For this reason, it’s important to keep all of your user’s sites optimized and running smoothly.

There are quite a few things that can contribute to high memory consumption on any given site, but I will mention the most common causes…

Plugins – Especially third-party plugins can often be poorly written and run up the memory consumption. I often recommend disabling all non-critical plugins and see if the problem gets better, then enabling them one-by-one until you are able to identify one that causes problems. Generally, if any plugins are not in use, remove them and reinstall them at a later time if they are needed. If there are any plugins that are currently in use, you will want to check for any recent updates available for the plugins and proceed with updating plugins to the latest stable version.”

Database related overhead, or an unnecessary amount of database queries – If you are trying to query your database for thousands of results at a time, this can also cause issues… Try to keep all database sizes and queries to a minimum by deleting irrelevant or old information from your database. You might also try optimizing tables in your databases by visiting your database through PHPMyAdmin. Simply use the “Check tables having overhead” link located directly underneath your tables and then “With Selected:”, choose “Optimize Table”. This might reduce overhead in your database, which is basically a lot of empty and redundant space that can slow your queries down.

It is also possible that a spike in traffic, or a heavy run by a site indexer (such as the GoogleBot), or even abusive hits by a given IP address, could cause this on a short term basis, so you might want to inspect your access logs for such activity (you can often manage this problem by implementing a robots.txt or by blocking abusive IP addresses with .htaccess).

The following articles on improving WordPress performance may help you with more optimization tasks to perform:

These links may help as well for further information/troubleshooting: https://help.dreamhost.com/hc/en-us/articles/216349808-Common-reasons-for-poor-website-performance

https://help.dreamhost.com/hc/en-us/articles/215036388-How-do-I-optimize-my-database

https://help.dreamhost.com/hc/en-us/articles/215836488-What-about-my-slow-site-can-I-check-

https://help.dreamhost.com/hc/en-us/sections/203242637-Optimization

You might as well want to try looking over the suggestions offered at our wiki pages on this matter:

http://wiki.dreamhost.com/Poor_performance

http://wiki.dreamhost.com/Optimize_database

http://wiki.dreamhost.com/WordPress_Optimization

http://wiki.dreamhost.com/Database_Maintenance

http://wiki.dreamhost.com/Slow_site_troubleshooting

http://wiki.dreamhost.com/Finding_Causes_of_Heavy_Usage

Implementing the above suggestions more often than not will fix the ProcWatch errors, but if you are still finding that your sites encounter the  same problems, you might want to consider moving to a VPS (http://wiki.dreamhost.com/DreamHost_PS) or dedicated server where you can reserve  sufficient RAM for your own processes to run!

FURTHER BACKGROUND

I agree with this commentator:

@Ipstenu-DH Yeap I can’t complain about the Shared Hosting, it worked really well. With some minor exceptions here and there… over all I can say DH’s Shared Hosting is quite good.
The site gets only 3000-5000 UV (unique visitors) per day on average. On Shared Hosting it worked well even with 30K per day. Actually I remember 2 years ago (2011) it even held up perfectly a record 75,000 in a single day + 70,000 the second day UV. It worked like a charm.

I barely get 1500+ visitors on all my sites, combined, so shared hosting should be perfectly fine.

KEY DIFFERENCE BETWEEN DREAMHOST AND OTHER VPSs

On Dreamhost, mysql databases are on a separate server (a typical shared MySQL allows up to 300 connections at one time – which should be fine in most cases).

Even with VPS (called “file VPS”) your mysql databases will still be on shared hosting (unless you install MySQL server on your VPS and move your databases to it). Shared MySQL databases are included for free with web (file) VPSes.

Question: Can I run my own mysql server on the same VPS server as my websites?

No. You can continue to use shared MySQL servers if you have VPS OR you can move your MySQL databases to a separate MySQL VPS.  [BOTH cost separately]

Dreamhost doesn’t support running a database server on your web VPS. You can install MySQL yourself using an admin user, but it won’t be available through the Panel.

VPS MYSQL

“Add a VPS MySQL Server” on dreamhost

DreamHost offers “MySQL VPS” that costs $15 a month (minimum). Source

http://wiki.dreamhost.com/DreamHost_PS_MySQL#Adding_MySQL_VPS

MySQL VPS – These databases are hosted on a MySQL service isolated from other customers. RAM is guaranteed for the customer. MySQL VPS is optimized with database query caching. About three quarters of VPS MySQL Dreamhost customers run at 300 MB.

[DreamHost PS MySQL uses Linux-VServer to offer an isolated database server.This is different from the standard shared MySQL server which an account uses by default, as the shared MySQL server shares other users databases and resources. With the PS MySQL, your databases and resources are protected from other users in the same was a web private server is.] VPS MySQL service starts at $15/month, which includes 300MB RAM and unlimited disk space. The most expensive plan is $200/month, which includes 4000MB RAM. When you activate VPS MySQL for the first time, you receive a one week trial. During the trial period, the amount of RAM is set at 2300MB RAM. When the free trial ends, the RAM adjusts to your site’s needs.  [Source]

My initial conclusion: Given the technical complexity and challenge of installing MySQL on one’s own VPS, it is not necessary to get a separate VPS. managed SQL VPS is good enough.

RUNNING MYSQL ON THE VPS SERVER ON DREAMHOST

If you get VPS you should create your own SQL server on the VPS, else you’ll continue to pay for shared SQL.

Source (for info below)

You can run your own MySQL server on DreamHost’s VPS.

  1. Create an admin user for your VPS that has sudo abilities, and log into your VPS with that through ssh.
  2. Tweak apt so you can install the mysql-server package. Part of installing packages through apt involves temporarily storing files in /tmp and then running them from there. Unfortunately, the /tmp directory is mounted on DreamHost’s VPS servers with the noexec option, which means that you can’t run files that are present in that directory. That basically prevents you from installing the mysql-server package until you tweak apt to temporarily stage files in /var/tmp instead. Do this by: Creating a file called apt.conf in the /etc/apt/directory, and edit it so the contents are the following:

APT:: ExtractTemplates::TempDir “/var/tmp”;

  1. Then, install the mysql-server package:
  2. sudo apt-get install mysql-server;

(When it asks to set a root password, make sure and set one.)

  1. Now, edit the file /etc/mysql/my.cnf and set the following options:

bind-address=psXXXXX.dreamhostps.com

(Replace psXXXXX with the name of your dreamhost VPS.)

  1. Restart your mysql service:

sudo service mysql restart

At this point, you should be able to log in to your new mysql server:

mysql -u root -p

and then perform what SQL functions you need to.

Install phpmyadmin using the tutorial here: http://wiki.phpmyadmin.net/pma/Quick_Install

First you’ll want to create a user (that isn’t your root user) to log into phpmyadmin:

mysql> CREATE USER ‘newusr’@’%’ IDENTIFIED BY ‘your_password’;

mysql> GRANT ALL PRIVILEGES ON *.* TO ‘newusr’@’%’ WITH GRANT OPTION;

At this point, you can sync your old databases to your new mysql server using the built-in sync tool that’s in DreamHost’s installations of phpmyadmin.  Then, just edit the wp-config.php file in the folder of your WordPress installations, and change the line that says the following to your DreamHost VPS:

define(‘DB_HOST’, ‘psXXXXXX.dreamhostps.com’);

Continue Reading

Tim Thumb exploit found on my wordpress installation (identified by Anti-Malware from GOTMLS.NET)

Anti Malware scanner has identified a Tim Thumb exploit. Sounds really bad. I'm going to delete the files.

THE CODE OF THE DELETED FILE TIMTHUMB.PHP

<?php
/*
    TimThumb script created by Tim McDaniels and Darren Hoyt with tweaks by Ben Gillbanks
    http://code.google.com/p/timthumb/

    MIT License: http://www.opensource.org/licenses/mit-license.php

    Paramters
    ———
    w: width
    h: height
    zc: zoom crop (0 or 1)
    q: quality (default is 75 and max is 100)
    
    HTML example: <img src="/scripts/timthumb.php?src=/images/whatever.jpg&w=150&h=200&zc=1" alt="" />
*/

/*
$sizeLimits = array(
    "100×100",
    "150×150",
);

error_reporting(E_ALL);
ini_set("display_errors", 1); 
*/

// check to see if GD function exist
if(!function_exists('imagecreatetruecolor')) {
    displayError('GD Library Error: imagecreatetruecolor does not exist – please contact your webhost and ask them to install the GD library');
}

define ('CACHE_SIZE', 250);        // number of files to store before clearing cache
define ('CACHE_CLEAR', 5);        // maximum number of files to delete on each cache clear
define ('VERSION', '1.12');        // version number (to force a cache refresh

if (function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) {
    $imageFilters = array(
        "1" => array(IMG_FILTER_NEGATE, 0),
        "2" => array(IMG_FILTER_GRAYSCALE, 0),
        "3" => array(IMG_FILTER_BRIGHTNESS, 1),
        "4" => array(IMG_FILTER_CONTRAST, 1),
        "5" => array(IMG_FILTER_COLORIZE, 4),
        "6" => array(IMG_FILTER_EDGEDETECT, 0),
        "7" => array(IMG_FILTER_EMBOSS, 0),
        "8" => array(IMG_FILTER_GAUSSIAN_BLUR, 0),
        "9" => array(IMG_FILTER_SELECTIVE_BLUR, 0),
        "10" => array(IMG_FILTER_MEAN_REMOVAL, 0),
        "11" => array(IMG_FILTER_SMOOTH, 0),
    );
}

// sort out image source
$src = get_request("src", "");
if($src == '' || strlen($src) <= 3) {
    displayError ('no image specified');
}

// clean params before use
$src = cleanSource($src);
// last modified time (for caching)
$lastModified = filemtime($src);

// get properties
$new_width         = preg_replace("/[^0-9]+/", "", get_request("w", 0));
$new_height     = preg_replace("/[^0-9]+/", "", get_request("h", 0));
$zoom_crop         = preg_replace("/[^0-9]+/", "", get_request("zc", 1));
$quality         = preg_replace("/[^0-9]+/", "", get_request("q", 80));
$filters        = get_request("f", "");

if ($new_width == 0 && $new_height == 0) {
    $new_width = 100;
    $new_height = 100;
}

// set path to cache directory (default is ./cache)
// this can be changed to a different location
$cache_dir = './cache';

// get mime type of src
$mime_type = mime_type($src);

// check to see if this image is in the cache already
check_cache ($cache_dir, $mime_type);

// if not in cache then clear some space and generate a new file
cleanCache();

ini_set('memory_limit', "50M");

// make sure that the src is gif/jpg/png
if(!valid_src_mime_type($mime_type)) {
    displayError("Invalid src mime type: " .$mime_type);
}

if(strlen($src) && file_exists($src)) {

    // open the existing image
    $image = open_image($mime_type, $src);
    if($image === false) {
        displayError('Unable to open image : ' . $src);
    }

    // Get original width and height
    $width = imagesx($image);
    $height = imagesy($image);
    
    // generate new w/h if not provided
    if( $new_width && !$new_height ) {
        
        $new_height = $height * ( $new_width / $width );
        
    } elseif($new_height && !$new_width) {
        
        $new_width = $width * ( $new_height / $height );
        
    } elseif(!$new_width && !$new_height) {
        
        $new_width = $width;
        $new_height = $height;
        
    }
    
    // create a new true color image
    $canvas = imagecreatetruecolor( $new_width, $new_height );
    imagealphablending($canvas, false);
    // Create a new transparent color for image
    $color = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
    // Completely fill the background of the new image with allocated color.
    imagefill($canvas, 0, 0, $color);
    // Restore transparency blending
    imagesavealpha($canvas, true);

    if( $zoom_crop ) {

        $src_x = $src_y = 0;
        $src_w = $width;
        $src_h = $height;

        $cmp_x = $width  / $new_width;
        $cmp_y = $height / $new_height;

        // calculate x or y coordinate and width or height of source

        if ( $cmp_x > $cmp_y ) {

            $src_w = round( ( $width / $cmp_x * $cmp_y ) );
            $src_x = round( ( $width – ( $width / $cmp_x * $cmp_y ) ) / 2 );

        } elseif ( $cmp_y > $cmp_x ) {

            $src_h = round( ( $height / $cmp_y * $cmp_x ) );
            $src_y = round( ( $height – ( $height / $cmp_y * $cmp_x ) ) / 2 );

        }
        
        imagecopyresampled( $canvas, $image, 0, 0, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h );

    } else {

        // copy and resize part of an image with resampling
        imagecopyresampled( $canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height );

    }
    
    if ($filters != '' && function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) {
        // apply filters to image
        $filterList = explode("|", $filters);
        foreach($filterList as $fl) {
            $filterSettings = explode(",", $fl);
            if(isset($imageFilters[$filterSettings[0]])) {
            
                for($i = 0; $i < 4; $i ++) {
                    if(!isset($filterSettings[$i])) {
                        $filterSettings[$i] = null;
                    }
                }
                
                switch($imageFilters[$filterSettings[0]][1]) {
                
                    case 1:
                    
                        imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
                        break;
                    
                    case 2:
                    
                        imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
                        break;
                    
                    case 3:
                    
                        imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
                        break;
                    
                    default:
                    
                        imagefilter($canvas, $imageFilters[$filterSettings[0]][0]);
                        break;
                        
                }
            }
        }
    }
    
    // output image to browser based on mime type
    show_image($mime_type, $canvas, $cache_dir);
    
    // remove image from memory
    imagedestroy($canvas);
    
} else {

    if(strlen($src)) {
        displayError("image " . $src . " not found");
    } else {
        displayError("no source specified");
    }
    
}

/**
 * 
 */
function show_image($mime_type, $image_resized, $cache_dir) {

    global $quality;

    // check to see if we can write to the cache directory
    $is_writable = 0;
    $cache_file_name = $cache_dir . '/' . get_cache_file();

    if (touch($cache_file_name)) {
        
        // give 666 permissions so that the developer 
        // can overwrite web server user
        chmod ($cache_file_name, 0666);
        $is_writable = 1;
        
    } else {
        
        $cache_file_name = NULL;
        header ('Content-type: ' . $mime_type);
        
    }

    switch ($mime_type) {
    
        case 'image/jpeg':
            imagejpeg($image_resized, $cache_file_name, $quality);
            break;
        
        default :
            $quality = floor ($quality * 0.09);
            imagepng($image_resized, $cache_file_name, $quality);
            
    }
    
    if ($is_writable) {
        show_cache_file ($cache_dir, $mime_type);
    }

    imagedestroy ($image_resized);
    
    displayError ("error showing image");

}

/**
 * 
 */
function get_request( $property, $default = 0 ) {
    
    if( isset($_REQUEST[$property]) ) {
    
        return $_REQUEST[$property];
        
    } else {
    
        return $default;
        
    }
    
}

/**
 * 
 */
function open_image($mime_type, $src) {

    $mime_type = strtolower($mime_type);
    
    if (stristr ($mime_type, 'gif')) {
    
        $image = imagecreatefromgif($src);
        
    } elseif (stristr($mime_type, 'jpeg')) {
    
        @ini_set ('gd.jpeg_ignore_warning', 1);
        $image = imagecreatefromjpeg($src);
        
    } elseif (stristr ($mime_type, 'png')) {
    
        $image = imagecreatefrompng($src);
        
    }
    
    return $image;

}

/**
 * clean out old files from the cache
 * you can change the number of files to store and to delete per loop in the defines at the top of the code
 */
function cleanCache() {

    $files = glob("cache/*", GLOB_BRACE);
    
    if (count($files) > 0) {
    
        $yesterday = time() – (24 * 60 * 60);
        
        usort($files, 'filemtime_compare');
        $i = 0;
        
        if (count($files) > CACHE_SIZE) {
            
            foreach ($files as $file) {
                
                $i ++;
                
                if ($i >= CACHE_CLEAR) {
                    return;
                }
                
                if (@filemtime($file) > $yesterday) {
                    return;
                }
                
                if (file_exists($file)) {
                    unlink($file);
                }
                
            }
            
        }
        
    }

}


/**
 * compare the file time of two files
 */
function filemtime_compare($a, $b) {

    return filemtime($a) – filemtime($b);
    
}


/**
 * determine the file mime type
 */
function mime_type($file) {

    if (stristr(PHP_OS, 'WIN')) { 
        $os = 'WIN';
    } else { 
        $os = PHP_OS;
    }

    $mime_type = '';

    if (function_exists('mime_content_type')) {
        $mime_type = mime_content_type($file);
    }
    
    // use PECL fileinfo to determine mime type
    if (!valid_src_mime_type($mime_type)) {
        if (function_exists('finfo_open')) {
            $finfo = @finfo_open(FILEINFO_MIME);
            if ($finfo != '') {
                $mime_type = finfo_file($finfo, $file);
                finfo_close($finfo);
            }
        }
    }

    // try to determine mime type by using unix file command
    // this should not be executed on windows
    if (!valid_src_mime_type($mime_type) && $os != "WIN") {
        if (preg_match("/FREEBSD|LINUX/", $os)) {
            $mime_type = trim(@shell_exec('file -bi ' . escapeshellarg($file)));
        }
    }

    // use file's extension to determine mime type
    if (!valid_src_mime_type($mime_type)) {

        // set defaults
        $mime_type = 'image/png';
        // file details
        $fileDetails = pathinfo($file);
        $ext = strtolower($fileDetails[“extension”]);
        // mime types
        $types = array(
             'jpg'  => 'image/jpeg',
             'jpeg' => 'image/jpeg',
             'png'  => 'image/png',
             'gif'  => 'image/gif'
         );
        
        if (strlen($ext) && strlen($types[$ext])) {
            $mime_type = $types[$ext];
        }
        
    }
    
    return $mime_type;

}


/**
 * 
 */
function valid_src_mime_type($mime_type) {

    if (preg_match("/jpg|jpeg|gif|png/i", $mime_type)) {
        return true;
    }
    
    return false;

}


/**
 * 
 */
function check_cache ($cache_dir, $mime_type) {

    // make sure cache dir exists
    if (!file_exists($cache_dir)) {
        // give 777 permissions so that developer can overwrite
        // files created by web server user
        mkdir($cache_dir);
        chmod($cache_dir, 0777);
    }

    show_cache_file ($cache_dir, $mime_type);

}


/**
 * 
 */
function show_cache_file ($cache_dir, $mime_type) {

    $cache_file = $cache_dir . '/' . get_cache_file();

    if (file_exists($cache_file)) {
        
        $gmdate_mod = gmdate("D, d M Y H:i:s", filemtime($cache_file));
        
        if(! strstr($gmdate_mod, "GMT")) {
            $gmdate_mod .= " GMT";
        }
        
        if (isset($_SERVER[“HTTP_IF_MODIFIED_SINCE”])) {
        
            // check for updates
            $if_modified_since = preg_replace ("/;.*$/", "", $_SERVER[“HTTP_IF_MODIFIED_SINCE”]);
            
            if ($if_modified_since == $gmdate_mod) {
                header("HTTP/1.1 304 Not Modified");
                die();
            }

        }
        
        $fileSize = filesize ($cache_file);
        
        // send headers then display image
        header ('Content-Type: ' . $mime_type);
        header ('Accept-Ranges: bytes');
        header ('Last-Modified: ' . $gmdate_mod);
        header ('Content-Length: ' . $fileSize);
        header ('Cache-Control: max-age=9999, must-revalidate');
        header ('Expires: ' . $gmdate_mod);
        
        readfile ($cache_file);
        
        die();

    }
    
}


/**
 * 
 */
function get_cache_file() {

    global $lastModified;
    static $cache_file;
    
    if (!$cache_file) {
        $cachename = $_SERVER[‘QUERY_STRING’] . VERSION . $lastModified;
        $cache_file = md5($cachename) . '.png';
    }
    
    return $cache_file;

}


/**
 * check to if the url is valid or not
 */
function valid_extension ($ext) {

    if (preg_match("/jpg|jpeg|png|gif/i", $ext)) {
        return TRUE;
    } else {
        return FALSE;
    }
    
}


/**
 *
 */
function checkExternal ($src) {

    $allowedSites = array(
        'flickr.com',
        'picasa.com',
        'blogger.com',
        'wordpress.com',
        'img.youtube.com',
    );

    if (ereg('http://', $src) == true) {
    
        $url_info = parse_url ($src);
        
        $isAllowedSite = false;
        foreach ($allowedSites as $site) {
            if (ereg($site, $url_info[‘host’]) == true) {
                $isAllowedSite = true;
            }
        }
        
        if ($isAllowedSite) {
        
            $fileDetails = pathinfo($src);
            $ext = strtolower($fileDetails[‘extension’]);
            
            $filename = md5($src);
            $local_filepath = 'temp/' . $filename . '.' . $ext;
            
            if (!file_exists($local_filepath)) {
                
                if (function_exists('curl_init')) {
                
                    $fh = fopen($local_filepath, 'w');
                    $ch = curl_init($src);
                    
                    curl_setopt($ch, CURLOPT_URL, $src);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
                    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
                    curl_setopt($ch, CURLOPT_HEADER, 0);
                    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0');
                    curl_setopt($ch, CURLOPT_FILE, $fh);
                    
                    if (curl_exec($ch) === FALSE) {
                        if (file_exists($local_filepath)) {
                            unlink($local_filepath);
                        }
                        displayError('error reading file ' . $src . ' from remote host: ' . curl_error($ch));
                    }
                    
                    curl_close($ch);
                    fclose($fh);

                } else {
            
                    if (!$img = file_get_contents($src)) {
                        displayError('remote file for ' . $src . ' can not be accessed. It is likely that the file permissions are restricted');
                    }
                    
                    if (file_put_contents($local_filepath, $img) == FALSE) {
                        displayError('error writing temporary file');
                    }
                    
                }
                
                if (!file_exists($local_filepath)) {
                    displayError('local file for ' . $src . ' can not be created');
                }
                
            }
            
            $src = $local_filepath;
            
        } else {
        
            displayError('remote host "' . $url_info[‘host’] . '" not allowed');
            
        }
        
    }
    
    return $src;
    
}


/**
 * tidy up the image source url
 */
function cleanSource($src) {

    $host = str_replace('www.', '', $_SERVER[‘HTTP_HOST’]);
    $regex = "/^((ht|f)tp(s|):\/\/)(www\.|)" . $host . "/i";
    
    $src = preg_replace ($regex, '', $src);
    $src = htmlentities ($src);
    $src = checkExternal ($src);
    
    // remove slash from start of string
    if (strpos($src, '/') === 0) {
        $src = substr ($src, -(strlen($src) – 1));
    }

    // don't allow users the ability to use '../' 
    // in order to gain access to files below document root
    $src = preg_replace("/\.\.+\//", "", $src);
    
    // get path to image on file system
    $src = get_document_root($src) . '/' . $src;

    return $src;

}


/**
 * 
 */
function get_document_root ($src) {

    // check for unix servers
    if(file_exists($_SERVER[‘DOCUMENT_ROOT’] . '/' . $src)) {
        return $_SERVER[‘DOCUMENT_ROOT’];
    }

    // check from script filename (to get all directories to timthumb location)
    $parts = array_diff(explode('/', $_SERVER[‘SCRIPT_FILENAME’]), explode(‘/’, $_SERVER[‘DOCUMENT_ROOT’]));
    $path = $_SERVER[‘DOCUMENT_ROOT’];
    foreach ($parts as $part) {
        $path .= '/' . $part;
        if (file_exists($path . '/' . $src)) {
            return $path;
        }
    }    
    
    // the relative paths below are useful if timthumb is moved outside of document root
    // specifically if installed in wordpress themes like mimbo pro:
    // /wp-content/themes/mimbopro/scripts/timthumb.php
    $paths = array(
        ".",
        "..",
        "../..",
        "../../..",
        "../../../..",
        "../../../../.."
    );
    
    foreach ($paths as $path) {
        if(file_exists($path . '/' . $src)) {
            return $path;
        }
    }
    
    // special check for microsoft servers
    if (!isset($_SERVER[‘DOCUMENT_ROOT’])) {
        $path = str_replace("/", "\\", $_SERVER[‘ORIG_PATH_INFO’]);
        $path = str_replace($path, "", $_SERVER[‘SCRIPT_FILENAME’]);
        
        if (file_exists($path . '/' . $src)) {
            return $path;
        }
    }    
    
    displayError('file not found ' . $src);

}


/**
 * generic error message
 */
function displayError($errorString = '') {

    header('HTTP/1.1 400 Bad Request');
    die($errorString);
    
}
?>

Continue Reading