CPAN tricks

What modules are installed on my system?

# cpan -l

From the cpan prompt, how can I tell whether a module is already installed, and what version?

cpan> i module_name

And how can I tell which modules are out-of-date or have updated versions?

cpan> r

…which gives a result like:

Package namespace installed latest in CPAN file
Archive::Extract       0.70   0.72 BINGOS/Archive-…0.72.tar.gz
Attribute::Handlers    0.94   0.96 SMUELLER/Attribute…0.96.tar.gz
AutoLoader             5.73   5.74 SMUELLER/AutoLoader-5.74.tar.gz
B::Debug               1.18   1.19 RURBAN/B-Debug-1.19.tar.gz
Carp                   1.29 1.3301 ZEFRAM/Carp-1.3301.tar.gz
Clone                  0.36   0.37 GARU/Clone-0.37.tar.gz
Compress::Bzip2        2.16   2.17 RURBAN/Compress-….tar.gz
DB_File               1.827  1.831 PMQS/DB_File-1.831.tar.gz
Data::Dumper          2.145  2.151 SMUELLER/Data…151.tar.gz

Fix: Perlbrew can’t install 5.21.2

If you find that perlbrew is unable to install perl 5.21.0, and the build log shows the following message:

First let's make sure your kit is complete. Checking...
Looks good...

*** WHOA THERE!!! ***

This is an UNSTABLE DEVELOPMENT release.
The version of this perl5 distribution is 21, that is, odd,
(as opposed to even) and that signifies a development release.
If you want a maintenance release, you want an even-numbered version.

Do NOT install this into production use.
Data corruption and crashes are possible.

It is most seriously suggested that you do not continue any further
unless you want to help in developing and debugging Perl.

If you still want to build perl, you can answer 'y' now,
or pass -Dusedevel to Configure.

Do you really want to continue? [n]

Okay, bye.

This is a known and resolved issue and has been patched. The simplest way to make things work is to simply say:

$ perlbrew self-update
$ perlbrew install 5.21.2   
  ## works now.

WebGUI8 pre-release install

Here’s a successful installation of the nascent WebGUI 8 as it stands, as of mid July 2014.

I fired up a barebones Debian 7.6 box. One from Linode would work nicely.

NOTE: If you want to use a later version of Perl than comes bundled with your Linux distribution, you’ll have to install it system-wide. The installer does not yet work with perlbrew.

For now you’ll have to run the installer as root, or with sudo. (The only step that fails is when /etc/nginx/conf.d/webgui8.conf gets created; this should be corrected fairly soon)

To start off, install some basics.

# apt-get install gpm git
### optional, personal preference
# apt-get install emacs23-nox screen htop

Then, we will need a database. The installer currently has difficulty installing mysql/Percona (Percona fails because at least as of Debian 7.6, the module still depends on openssl0.9.8 which can’t be installed as it’s a downgrade from openssl1.0.x that comes with Debian), and I’d prefer MariaDB in any case, so let’s take care of this now:

Following the instructions from MariaDB  use the following commands on Debian:

# apt-key adv --recv-keys --keyserver 0xcbcb082a1bb943db
# echo deb wheezy main > /etc/apt/sources.list.d/mariadb.list
# apt-get update
# apt-get install mariadb-server

Note: Recent versions of Debian support putting *.list files in the sources.list.d/ directory, which is a handy way of avoiding management and manual editing of the sources.list file itself. On Ubuntu, you can use add-apt-repository (instead of creating the mariadb.list file as shown above). UBUNTU ONLY:

# sudo add-apt-repository 'deb wheezy main'

You will also need a library for the PNG Imager. Again, the installer should encompass this, but for now, follow instructions from Imager::File::PNG documentation to prevent any dependency resolution failures later:

# apt-get install libpng12-dev

I downloaded the latest pre-release installer into a temporary directory as follows:

# mkdir /opt/webgui_installer
# cd /opt/webgui_installer
# git clone .

Now, run the installer:

# perl

and follow the prompts. If something goes wrong, you’ll have to follow the recovery procedure below. But if you get this far:

Running upgrade script…. Installation is wrapping up.
/data/ shows how to manually launch WebGUI.

and then:

││Installation complete.  Go to http://    ││
││ and set up the new site. ││
││The admin user is "Admin" with password  ││
││"123qwe".                                ││
││Please hit any reasonable key to exit the││

you should be ready to, at last:

# service nginx restart
# bash /data/

and browse to your (presuming you have defined in /etc/hosts to point to


To retry the installation, before re-running the installer, you must do:

# userdel webgui_user
# mysql -u root -p
mysql> drop database www_example_com;
mysql> exit
# rm -rf /data

NOTE: You can work around many of the abort-on-error conditions if you need to re-run the installer, by pressing “s” to skip individual commands.

WebGUI versus WordPress: An overview

A different, and more detailed albeit slightly dated, feature comparison can be found at CMS-Matrix.

In general, WebGUI tends to bundle the most-used features, whereas WordPress leaves most of them to plugins. WordPress does have a vibrant plugin and theme community, but they are often outmoded and of varying quality.

WordPress, hosted
on typical
Shared or VPS
typical V8
Language PHP Perl
Template System Theme files, written in PHP Template::Toolkit
HTML5 Framework None by default.
Built-in jQuery conflicts with Bootstrap.
YUI among others [wikipedia]
Object Orientation Partly, built-in PHP Moose[1]
Database MySQL MySQL, possible PostgreSQL support
Web Server Apache nginx
Web Server Linkage mod_php Plack [2]plackup, starman and starlet
Multi-Site One WP install can serve multiple sub-sites, but many global variables mean unrelated virtual hosts require entirely separate installs Object-oriented, so theorietically possible to have a single installation serve unlimited unrelated virtual hosts
  1. Particularly Moose attributes, method modifiers and roles.
  2. Plack::Response and Plack::Request objects

Prevent WordPress from asking FTP details

When installing a new plugin, or upgrading an existing theme, plugin, or WordPress itself, you may be asked for FTP details. But, you say, I thought I had this configured to update directly. Here’s how to force WordPress to upgrade without FTP.

In your wp-config.php file, add a line near the top:

define('FS_METHOD', 'direct');

This forces WordPress to write to files directly. Once this is done, you will get error messages that better explain what isn’t working.

For example, you may need group write privileges on the wp-content/upgrade directory:

chmod g+w wp-content/upgrade

If you get this error:

Could not remove the old plugin.

you will also need to ensure there is group write on all the plugins themselves:

chmod -R g+w wp-content/plugins

WebGUI: Kickstarter for a Modern Perl CMS

UPDATE: The Kickstarter project reached its funding goal! Codename for Version 8 will be Allium Cepa. There is a project page on GitHub.

Scott Walters’ 2014 Kickstarter, “Create Perl Competition to the PHP Content Management System” is somewhat open ended as to what this Perl-based CMS would be, but likely it will be based on the GPL’d WebGUI system created by Plain Black Corp. According to Scott’s writeup,

…Version 7 is still being maintained, but 8 was a massive modernization effort that reworked core to use Moose, Plack, Try::Tiny, and cleaned things up.

Thanks to fantastic test suite coverage, the API and core of 8 work well, but the rewritten admin needs attention. Also, WebGUI has traditionally targeted large companies, so the difficult install process was not a major liability. To compete, installation has to be dead simple. The themes are not adequately modern (which is to say they look a little outdated).

I plan to get wG8 out of alpha, move it to a community development model, finish the installer I created for it and OSX support, and work with designers to create a modernized theme.

The installation script is currently Curses based, eliminating the web hole even in the first steps. A Docker install may also be included.


…wG is one of the most impressive and mature things ever created in Perl, and the community often raves to me about how much better they like it than competing systems.

− scrottie in the WebGUI Forums

More from scrottie (July, 2014): Perl Needs a User Friendly CMS []


From the Abstract, “Programming WebGUI: WebGUI 8 as a Web App Framework” by Scott Walters, (October 2011)

the WebGUI content management system.

Building apps on top of CMSes has become trendy in the Python and PHP camps, and for good reason: CMSes provide the primitives to build the most common types of Web applications, they’re easy to interface with, and the idea of a tree of editable objects translates well to interactive Web sites.

WebGUI ships with a pile of ready to use pieces of logic (called “assets”, implementing things such as shopping carts and Wikis) for laypersons to build a site out of, but it’s also a fantastically programmer friendly framework for building more of these assets in.

WebGUI 8 is the upcoming iteration, sporting a new Plack interface, Moose attribute based asset definition, and countless modernizations and improvements. WebGUI handles the tedious work of data persistence, permissions, generating edit forms, pagination, and more.

See Also

Interpreting a WordPress Export XML file

In preparation to writing a tool that can import most of an existing WordPress site into WebGUI (see the Kickstarter to release WebGUI Version 8) the good news is that a WordPress export file can be loaded into Perl quite easily:

use XML::Simple qw(:strict);
my $xs = XML::Simple->new();
my $ref = $xs->XMLin($ARGV[0], ForceArray => 1, KeyAttr => []);

Pass that script the name of your export file, and you can peruse the results with

use Data::Dumper;
print Dumper($ref);

Here’s what you will find:

  • The entirety of the file is in XML format. Happily, this means all the quoting and Unicode in it seems to be loaded correctly by XML::Simple.
  • The top tag describes the file as rss version 2.0
  • Inside that, the entirety of a website is defined in a channel tag. All the remaining tags I describe below are nested exactly one level inside the channel.
  • The first tags give the WordPress site’s title, existing URL base link, and description.
  • The top pubDate is the date that the site was exported.
  • There are language, base_site_url, and base_blog_url tags; I believe base_site is the “WordPress Address” and base_blog is the “Site Address” as described on the General Settings page in the WP Admin.
  • Next will be a series of wp:author tags, each containing the author_id (WordPress reveals much of its schema relationships through its integer id fields), login name (which may differ from the display_name as shown in a post/page),  email address, and first and last names
  • Following the authors is a list of wp:category tags which describe the categories in which WordPress posts are placed. Strangely, pages − although stored exactly the same as posts − cannot normally be given categories in WP. Perhaps the only useful data here is the cat_name field that is used as the display name for the categories. Within each post stored in the XML, a post-to-category relation is given by matching the category_nicename here to the <category domain=”category” nicename=”XXX”> in the post. Note, this exposes how WP’s original category system was extended to be arbitrary “taxonomies” − thus the domain=”category”.
  • Then we encounter a group of wp:tag … each of which has an integer id (WP’s schema relationships showing), a slug (WP’s term for what you put in the to get the index page into that bunch of tags, or whatever; slugs are URL-safe and do not include spaces or  punctuation other than dashes) and a tag_name (which can have spaces or punctuation). Similar to categories, a you will want to match post’s <category domain=”post_tag” nicename=”XXX”> with the slug value from here.  As with categories, for some reason WP by default won’t assign tags to pages, only posts.
  • Following the tags are the <item> entries.  Each of these contains a WordPress post, page, attachment, or custom post: details below.
  • That’s all that’s in the file.

WordPress <item> contents

  • title
  • link − A search-engine unfriendly canonical link in the style of ?p=post_id …probably can be safely ignored
  • pubDate − seems unreliable
  • post_date − actual original date that post was first Published, in the WordPress vernacular. This is in whatever the server’s local timezone was at a slightly random deviation from the actual global time.
  • post_date_gmt − usually zero, unreliable, or flat-out lies. WordPress, failing Codd’s third normal form since 2006!
  • dc:creator − a lame attempt at appearing LDAP compatible, this seems to contain the author’s login name
  • guid − allegedly a global resource identifier, this is complete fiction, and utterly unreliable for any purpose whatsoever. Ignore.
  • description − often blank, but sometimes set through WP’s admin
  • content:encoded − the actual content of the post/page, subject to WordPress’s wpautop function which turns \n\n as stored here, back into somewhat properly nested <p>…</p> tags, with a slew of exceptions and special handling. That mischmasch of logic is executed each time WP displays anything − WordPress, mangling your text each runtime since 2006!
  • post_id − WP’s internal integer relation id for this post/page
  • comment_status − “open” or “closed”. If open, consult Wikipedia under “xss” for an idea of they mayhem which might ensue.
  • ping_status − “open” or “closed”. If open, an invitation to xmlrpc ddos the site for no discernable reason.
  • post_name − often blank if item is a Post, or the item’s “slug” if a Page (or sometimes if a custom post type).  Note that WP enforces that each item have a unique post_name, so you can’t have pages with URL’s /hotels/ma/springfield and /hotels/oh/springfield − you’ll need to use something nasty like /hotels/ma/springfield-ma and /hotels/oh/springfield-oh.  WordPress, mangling your URLs since 2007!
  • status − draft, published, private… other values may be legal. There has been some attempts to create real workflow systems in WP, giving users “capabilities” such as Subscriber, Contributor, Editor, Administrator. Implementing that correctly would imply putting other values here to indicate an item’s position in that workflow. This is ragingly incomplete and subject to custom plugins mucking about.
  • post_parent − Theoretically only for Pages not Posts, this gives the parent’s post_id in the hierarchy of pages.
  • menu_order − WP’s half-hearted attempt to let you dictate the order of pages in the sidebar, the usefulness of this has been massacred by the “Menu” system introduced in WP 3.0 and in other ways that make administering a real hierarchical tree of pages nearly impossible.
  • post_type − post or page, or some other custom post type or taxonomy. Attempting to import a site with anything other than post or page here may be an exercise in frustration or futility.
  • is_sticky − 0 or 1, just when you thought it might be “true” or “false” or “yes” or “no”, or maybe “vrai” or “faux”, 真 or 假… PHP is fun like that. Nonzero if the post is supposed to somehow be sticky, although exactly what that means is left to the Theme Author as an exercise.
  • category − zero or more domain=category, post_tag, or custom taxonomy entries. Cross-referenced by the nicename value which must be unique across all taxonomies (same problem thinking with uniqueness as page slugs and URLs).
  • wp:postmeta − an array of wp:meta_key / wp:meta_value pairs which the user can edit through the WP admin. Sometimes used to fun effects by themes or plugins, these can actually do or be anything at all.

That’s the more-or-less organization of this file. As you can see it delightfully shows that some of WordPress’s foundations still suffer original cracks.

Example code:


use XML::Simple qw(:strict);
binmode(STDOUT, ":utf8");   # enable Unicode output

my $xs = XML::Simple->new();
my $ref = $xs->XMLin($ARGV[0], ForceArray => 1, KeyAttr => []);

use Data::Dumper;

use HTML::TreeBuilder;
sub rectify_html {

    my $munged_text = shift;
    $munged_text =~ s/\n\n/\n<p>/g;

    my @pre_segments;
    my $seg_id=0;
    $munged_text =~ s{<pre\b(.*?)>(.*?)</pre\s*>}{$pre_segments[++$seg_id]=$2; "<pre data-seg=\"$seg_id\"$1></pre>";}gsex;

    my $atree = HTML::TreeBuilder->new();

    # Prepare to store comments. Requires wrapping in <html><body>

    # Replace original text contents for <pre> elements
    foreach my $pre_element ($atree->look_down('_tag', 'pre')) {

    # print Dumper($atree);
    print $atree->as_HTML(undef, ' ', {});

# Here we show just the contents of the posts and pages.

my $posts = $ref->{channel}[0]->{item};

foreach my $post (@{$posts}) {
    if ($post->{'wp:post_type'}[0] =~ /^post|page$/) {
    print "Entry with ID=$post->{'wp:post_id'}[0] of type $post->{'wp:post_type'}[0] contains:\n";
    print $post->{'content:encoded'}[0];
    print "\n-----------------\n";
    print rectify_html($post->{'content:encoded'}[0]);
    print "\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n";
    } else {
    print Dumper($post->{'wp:post_type'});

Setting up Asterisk VOIP on a Linode with Debian

Asterisk is an open-source telephone solution that runs over the internet instead of running through copper lines. It offers a variety of features such as voicemail and conference calling, much like a land line telephone can.

For this guide we will install Asterisk from source rather than from Ubuntu’s repositories. The newer version offers several additional features, including the ability to integrate a Google Voice account as a trunk. We will use FreePBX as a web interface for our Asterisk configuration.

Read the rest of the story on Linode’s site

I used Debian 7.5 instead of Ubuntu. When I got to the section where it said to:

apt-get install linux-virtual

I actually had to follow the instructions here to get the local kernel matching the dahdi_dummy kernel module.

I also had to install the correct kernel source, from the hint here, before the make commands would work:

apt-get install linux-headers-`uname -r`

The php5-suhosin package does not exist in the stable repository, so I had to omit that from the php apt-get line.

By default, Debian has the Apache document root at /var/www; we want to change that to /var/www/html. The VirtualHost directive is in file /etc/apache2/sites-available/default − change the appropriate lines:

        DocumentRoot /var/www/html
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        <Directory /var/www/html/>

To be able to install the Google Voice module, I had to:

# su - asterisk 
$ cd /var/www/html/admin/modules/
$ mkdir _cache


Shell script to open mysql with WordPress parameters

mysql `grep DB_NAME ~/$1wp-config.php | cut -d \' -f 4` -u `grep DB_USER ~/$1wp-config.php | cut -d \' -f 4` --password=`grep DB_PASS ~/$1wp-config.php | cut -d \' -f 4`

Save this as a script, such as ~/wp-mysql and do a chmod a+x on it.

Rather than lots of copying and pasting from wp-config.php, we pull the values directly from the file.  Note that this assumes you have put wp-config.php in your home directory and not in public_html.  If you want to use a different wp-config, pass the subdirectory name as the first argument. For example,

~/wp-mysql domains/

Putting the password on the command line is not much of a security risk, as mysql replaces the actual password in the command line with ‘xxxxxx’ as you can see by doing ps aux while the mysql monitor is running.

"Si datur citrea, sucus faciunt" (When life gives you lemons, make lemonade)