Message Notifications using PHP and Pushbullet

I’ve been amusing myself following through an excellent series on self-hosting WordPress by Ashley Rich. Mostly so I can experiment with Nginx, pinch the best ideas and use them myself 🙂

Everything had gone swimmingly on a test VPS running the latest LTS version of Ubuntu, but when I tried to implement some of the ideas on the shared hosting1 where this blog runs I ran into a bit of trouble. I was trying to get WordPress installation checksum verification2 and push messaging via Pushbullet working, but the https connect to the Pushbullet API would always fail. After some experimentation it turned out that the command line version of Curl was ancient3 and it wasn’t going to work. I already knew I was wasting my time asking the hosting company to upgrade the version; I had tried that before with another shell program that was obsolete and they (politely) declined4. So, time to get programming!

I already know that the python version on the hosting is ancient and doesn’t have SSL support at all5, ditto Perl, so I am going to have to use PHP. I won’t have a problem with that as there are versions up to 7.1 on this host and the SSL libraries are properly current too. Command-line PHP isn’t great, but it will work just fine for this application.

Here is the resulting simple program to send a notification using the Pushbullet API. This is called from a Bash script if the WordPress verification checks fail; the resulting warning message turns up on all my registered devices.

<?php
/**
* PHP program to send messages to pushbullet.
* Usage: php message-pushbullet.php creds='...' title='...' body='...' [quiet] [headers]
*/
date_default_timezone_set('UTC');
error_reporting (E_ALL);

$url = "https://api.pushbullet.com/v2/pushes";
$type = "note";

if(php_sapi_name() == 'cli'){
    //convert cli params to GET params (chop off name of program which is 1st)
    parse_str(implode('&', array_slice($argv, 1)), $_GET);
}
//process parameters
$credentials = isset($_GET['creds']) ? trim($_GET['creds']) : "";
$title = isset($_GET['title']) ? trim($_GET['title']) : "";
$body = isset($_GET['body']) ? trim($_GET['body']) : "";
$quiet = isset($_GET['quiet']) ? true : false; //silent mode
$headers = isset($_GET['headers']) ? true : false; //show headers for debugging

if(!$credentials || !$title || !$body){
    echo "Usage: message-pushbullet creds='...' title='...' body='...' [quiet] [headers]\n";
    exit(1);
}

$post_fields = json_encode(compact('type', 'title', 'body'));

$ch = curl_init();

$options = [
    CURLOPT_URL => $url,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_MAXREDIRS => 6,
    CURLOPT_HTTPHEADER => ['Access-Token: ' . $credentials, 
                            'Content-Type: application/json',
                            'Content-Length: ' . strlen($post_fields)],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $post_fields,
    CURLOPT_HEADER => $headers,
];
curl_setopt_array($ch, $options);

$result = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if(!$quiet){
    echo $result;
}
//check ok
if($status == 200){
    exit (0);
}
//failed
exit(1);

/* end */

Command-line parameter handling isn’t great as standard, so I cheat and turn them into GET parameters6. This handily means the program would work if used as a web script too with no further effort.

Oddly the API docs specify the parameters as JSON, but the version Ashley Rich created uses standard POST-style variables set using the CURL -d option and this works too. The Pushbullet API talks about maintaining backwards-compatibility to earlier versions, so I suspect there was a pre-JSON version at one time.

Most of the time was spent sorting out the Curl parameters needed 🙂

 

WordPress Plugin WP-Mail-SMTP Self-signed Certificate Patch

I am using an excellent plugin by Callum Macdonald to send mail from my WordPress blog. This works absolutely fine with the various mail-servers that I have pointed it at, but embarrassingly not with my own home mail-server7.

Now, this isn’t exactly a problem since I don’t need to send mail via my own home mail-server from my blog, but as with all things geeky I got curious why it wouldn’t work. I know the mail-server works fine8 and I can connect and send /receive from all my computers and devices, so I was a bit puzzled. What was especially odd is that it worked from WAMP on my laptop, but not from a test site in a Vagrant virtual machine on the same laptop. So I went Googling and found this. Doh! My WAMP installation is still on PHP 5.59, but the Vagrant instance is on 7.1 – and I still have a self-signed certificate on my mail-server.

So; just because I could10, I created a very simple WordPress plugin to apply a filter to WP Mail SMTP to add the extra parameters for self-signed certificate connections. The code looks like this:

add_filter( 'wp_mail_smtp_custom_options' , function( $phpmailer ){

    $phpmailer->SMTPOptions = array(
        'ssl' => array(
            'verify_peer' => false,
            'verify_peer_name' => false,
            'allow_self_signed' => true
        )
    );
    return $phpmailer;
});

Couldn’t be simpler eh? This can be stuck in functions.php or in a plugin like I did11.

My plugin can be downloaded here: WP-Mail-SMTP Self-signed Certificate Patch Plugin.

Now; just a work of caution – you really shouldn’t do this. You should replace your self-signed certificate with a real one from Let’s Encrypt or a commercial provider. There’s not much excuse for using self-signed certificates now that it is so easy and cheap / free to get a real one (except for testing).

I had even less excuse as I already have a Let’s Encrypt certificate on Apache running on the same physical server as the mail-server12. After making and testing this plugin I actually fixed the certificate problem properly and I can confirm that exactly the same certificates that work for Apache (and Nginx) also work with Postfix. The only remaining issue that I need to sort is to modify the routine that re-starts Apache on certificate renewal to also restart Postfix.

A few minutes work and I’ve got until June to do it 😉