Communicating with Pidgin from PHP via D-Bus

published on December 18, 2011.

Earlier this week I got an idea of trying to communicate with Pidgin, a chat client, via the terminal. Sounded like a fun thing to hack on, plus, could be made useful (in my head, at least), for things like logging from a web application directly to IM, or, heck, even creating something like Github’s Hubot, commanding a server or an application just via chat. Surely I wasn’t the first one to come up with this idea and after a bit of a googling found out that Pidgin’s libpurple has a nice API for that, exposed via D-Bus.

I first planned to write some scripts for this in Python or C, but when I finally sat down over the weekend to hack on this, realized there is a PHP D-Bus extension, thanks to Derick Rethans! As I rarely have the opportunity/need to play with more “obscure” PHP extensions, decided to give this one a spin… (Note: apart from that D-Bus is used for processes communicating with each other, I have zero knowledge about it, so I might be wrong with some things down below.)

Installing D-Bus for PHP

As the extension requires the dbus-devel package, first make sure it is installed:

$ yum install dbus-devel

The installation of the extension itself is pretty easy:

$ pecl install dbus-beta

Add the extension=dbus.so line to your php.ini, restart Apache if needed and have a look at the phpinfo();, there should be an entry for D-Bus listed.

Note that there is no documentation for this extension at the moment, but, luckily, the sources include an examples directory full of goodies! After fiddling around with those for an hour or so, got the basics of the extension figured out.

One thing I haven’t figured out completely, is how to run scripts which use the Dbus extension via the browser, as in those cases the scripts are dying with a terrible X11 error message. So, run Dbus scripts from the console and all should be fine.

The a-ha! moment

What I learned by looking at the examples, is that the Dbus class is used for creating a proxy, which can be used to call methods on the service/application we’re interested in. But, what methods are available, what arguments do those methods accept (if any), and what will they return as a result?

This can easily be found out by introspection. Create a proxy to an object which implements the Introspectable interface and call the Introspect method on that proxy:

<?php

$dbus = new Dbus;

$proxy = $dbus->createProxy("im.pidgin.purple.PurpleService",
                            "/im/pidgin/purple/PurpleObject",
                            "org.freedesktop.DBus.Introspectable");

$data = $proxy->Introspect();

file_put_contents('introspect.xml', $data);

As the result of the introspection is returned in an XML and can be quite big, putting it in a file for easier viewing.

By looking at the XML file, it’s pretty easy to figure out what’s going on; method names, method arguments, their names and types, and the returned result:

<method name="PurpleAccountGetUsername">
  <arg name="account" type="i" direction="in"></arg>
  <arg name="RESULT" type="s" direction="out"></arg>
</method>

With all this information at our disposal, it’s easy to write a script which does something useful, like, listing all the connected accounts and the protocols they are using:

<?php

$dbus = new Dbus;

$proxy = $dbus->createProxy("im.pidgin.purple.PurpleService",
                            "/im/pidgin/purple/PurpleObject",
                            "im.pidgin.purple.PurpleInterface");

$accounts = $proxy->PurpleAccountsGetAllActive();

foreach ($accounts->getData() as $account) {
    if ($proxy->PurpleAccountIsConnected($account)) {
        $username = $proxy->PurpleAccountGetUsername($account);
        $protocolId = $proxy->PurpleAccountGetProtocolId($account);
        $protocolName = $proxy->PurpleAccountGetProtocolName($account);
        echo $username . " is connected on the " . $protocolName
                       . " (" . $protocolId . ") protocol.\n";
    }
}

A sample output would be something like: “robertbasic@irc.freenode.net is connected on the IRC (prpl-irc) protocol.”

Next steps

Of course, this is far from a chat bot which can execute commands on a remote server, but at least we have some foundation to build on. In the coming days I’ll try to figure out how to create a loop which can be used to listen to different purple events - when a contact comes online, a chat is sent, or received, etc.

Also, it is quite fun trying to figure out a PHP extension just by looking at examples and the C source itself. One can learn a lot this way.

Happy hackin’!

P.S.: Code samples are up on Github!

Update 2011-12-26: the 2nd post, listening to dbus signals with php, is published!

Upgrading to Fedora 16

published on November 12, 2011.

Decided today to upgrade my laptop to Fedora 16, which was released a few days ago. I first switched to Fedora (with XFCE as the desktop environment) from Ubuntu in August, I think. An excellent decision as it is working really great for me. XFCE is also great, really happy that I made this switch.

Anyway, the upgrade from Fedora 15 to 16 went smoothly (although a bit slow, thanks to my shitty internet connection), using the PreUpgrade script/process. I was a bit sceptical about doing an upgrade and not a cleanstall, but gave it a shot after all (note: every time I tried a dist-upgrade with Ubuntu it failed miserably). PreUpgrade was downloading stuff for a bunch of hours and (about) an hour of installing them, the upgrade was… Done. Fedora 16 just booted up and I was using my laptop just as before.

I did the post upgrade steps from the above linked article, but the yum distro-sync step failed; it was complaining something about a “Transaction Check Error” for a libdvdcss package. I simply disabled the rpm.livna.org software source, re-run the distro-sync, it did it’s thing and then re-enabled the source.

The second thing that “wasn’t working” is that Apache and MySQL were not starting on bootup, so I ran chkconfig for both of ‘em:

$ chkconfig --levels 235 mysqld on
$ chkconfig --levels 235 httpd on

Lastly, grub was upgraded to grub2. It was working fine, just that it was showing the grub menu on startup, which is a bit silly given that I’m running only one OS on this machine. Anyway, added the following lines to /etc/default/grub:

GRUB_DISABLE_RECOVERY=true
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=0 # I actually edited this line, from 5 to 0

and ran:

$ grub2-mkconfig -o /boot/grub2/grub.cfg

I also thought for a while that there was an issue with my wifi, that it’s dropping connection randomly, but it only happened once, so I don’t know what to do with it.

Happy hackin’!

Tags: fedora, grub2, upgrade, xfce.
Categories: Development, Software.

Contributing to Zend Framework 2

published on November 10, 2011.

Today a pretty big news hit the interwebs: as of today, the CLA is not required any more to contribute to Zend Framework 2! This means anyone can issue pull requests and submit patches to the new version. Note that if you want to contribute to Zend Framework 1, you still need a signed CLA. I’ve decided to write a quick post with additional information and links, to make it easier getting started with the contributions!

Back in July I wrote a post on helping out with Zend Framework 2, so consider this post as a second part of that post ;)

The development is on git. The original repo is http://git.zendframework.com/?a=summary&p=zf, while the one on Github is a mirror which gets updated twice a day. Not that it matters much, as everyone forks the Github version and sends pull requests against that.

The issues are kept on a Jira instance: http://framework.zend.com/issues/browse/ZF2. Please make sure you are actually on the Zend Framework 2 project, as we have a separate project for ZF 1. Create an account on Jira and off you go. If I’m not mistaken, it is connected to the ZF wiki, so you can use that too.

All ZF2 related stuff on the wiki is located in it’s own section. It’s full of goodies, so take your time to browse it.

We hold biweekly IRC meetings: every second Wednesday, 17:00 UTC. The next one is going to be on November 23rd. Prior to each meeting we set up an agenda, vote on it, decide who will be moderator and then just discuss whatever is there to discuss. The meetings are held on the #zf2-meeting channel, on Freenode. All agendas and logs from previous meetings can also be found on the wiki.

We have a separate #zftalk.2 IRC channel devoted to discussing all things ZF2!

Subscribe to the mailing lists; you’ll be especially interested in the “Contributors” section; all big and small things are discussed there.

A new “thing” introduced to the ZF ecosystem are the RFCs. We’re using those when a new architecture is discussed or a rewrite/refactor of an existing component is to be done.

New components need to go through a proposal process. The proposal process is rumoured to get an overhaul, but (hopefully!) it won’t be going away.

There is also a blog set up, so be sure to subscribe to the feed.

Great and exciting times are ahead of us and I welcome all new ZF contributors! :)

Happy hackin’!

Benchmarking pages behind a login with ab

published on November 09, 2011.

Tonight I decided to relax a bit and what better way of relaxing is there for a geek then to do some bash scripting?! So for fun and no profit I decided to try and benchmark pages with ab, Apache HTTP server benchmarking tool, which are behind a login. Turns out, it’s pretty easy after reading some man pages ;)

ab's help pages gives a few possible leads. We can POST data with the -p option, which would be great if we would like to benchmark the login process itself. But, we want to test the page after the login. So we’ll need the ab’s -C option, which allows for passing cookies in cookie-name=value pairs.

The login process itself is done with curl as it allows us to POST data to a server and store cookies received from the server in a cookie jar. curl writes the cookies in a Netscape cookie file format, whatever that is. Sample line is:

# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.

example.com	FALSE	/	FALSE	0	PHPSESSID	[RANDOM_SESSION_ID]

From this output we’re interested in the [RANDOM_SESSION_ID] cookie value, as the cookie name is simply PHPSESSID and we can just hard-code it. To get the value, we use some obscure *nix magic: grep and cut. grep to grep the line with the PHPSESSID cookie and cut to cut out the 7th column from that line. Easy!

Now that we have the value of the cookie, we just pass it along with ab and done! We’re benchmarking pages behind a login.

The entire script is:

#!/bin/bash

COOKIE_JAR="/tmp/ab-login-cookie-jar"

echo "Logging in..."

curl -c $COOKIE_JAR -d username=user -d password=h4x0r http://example.com/login

echo "Getting the session id..."
PHPSESSID=$(cat $COOKIE_JAR | grep PHPSESSID | cut -f 7)

echo "The session id is:"
echo $PHPSESSID
echo "=================="

ab -n 10 -c 10 -C PHPSESSID=$PHPSESSID http://example.com/loggedin

The script is also on Github here.

Tip: use ab’s -v option to test for HTTP codes and/or redirects to see if you are really on the page you want to be.

Happy hackin’!

A Zend Framework 2 EventManager use case

published on October 19, 2011.

With Zend Framework 2 beta 1 released yesterday and some free time to spare, I decided to finally try and tackle one of the “scariest” additions to the Zend Framework - the EventManager component. Now, I won’t go into details about this whole event voodoo, Matthew already did that. Twice.

Basically, this allows us to have one piece of code to trigger an event and to have one or more listeners listening to this event. When the event gets triggered, the listeners are called and then we can do something, like caching or logging. Logging or caching. Caching. Logging…

See, that’s my problem. All the event examples stop at logging and caching. Truly there must be some other example for which this event stuff can be used for. (Yes, I know. The whole dispatch process is now event driven or whatnot in ZF2, but I need event examples in my application, not in the framework.) I don’t claim I found a perfect example for the events, but I tried.

The problem

One of the most “repetitive” places in my code I found is the save method in my models. Pass an array of data to it, possibly do something with that data, validate it, persist it, maybe do some more data mangling, return true/false to the caller. Over and over again, but just with enough difference between different models that there is actually no double code to pull out to an abstract class or some such.

Say, for example, we have a Post of some sort. It has a title and a slug created from the title. Standard stuff, nothing fancy. For the Post to be valid, it needs to have both the title and the slug set.

Now, without the EventManager, the save method could have a similar flow:

  • call the save method, passing in the data array
  • check if the data array has a slug set, if not, create one from the title
  • validate the data array, to make sure both title and slug are properly set
  • save the post

As I said, pretty standard stuff, so I’ll assume you can imagine that piece of code in your head (read, I’m lazy to write it). The problem: the save method is stuffed with data preparing and validation code.

Using the EventManager

This is where, I hope, the EventManager can help. Call the save method and, just before the persist call, trigger a “pre-save” event and then persist the data. Attach two listeners to this “pre-save” event; the first will do the data preparation, the second will do the validation. There, done. Now the save method doesn’t have that unneeded code around it and can be pulled out to an abstract class, all that is in the event listeners.

Let’s see some code:

<?php

// This is the Post object

class Post
{
    protected $events = null;

    public function events()
    {
        if ($this->events === null) {
            $this->events = new Zend\EventManager\EventManager(__CLASS__);

            $this->events->attach('save', array('SlugifyPost', 'slugify'), 100);
            $this->events->attach('save', array('ValidatePost', 'validate'), 90);
        }

        return $this->events;
    }

    // this method can be pulled out to an abstract model class
    // and reuse it for all the models that extend it
    public function save($data)
    {
        $this->events()->prepareArgs($data);
        $response = $this->events()->trigger('save', $this, $data);

        echo 'data saved! ' . json_encode($response->last());
    }
}

I just set a “save” event to be triggered and attached two listeners to that event, the slugify and the validate methods. When the save method gets called, the event is triggered and the EventManager calls our listeners. One fairly important point here is the prepareArgs method call, which prepares the event arguments in such way, that when these arguments (the $data array in this case) are modified in one listener, this modification is reflected for the other listeners, too. If you don’t want to modify the arguments in the listeners, this call can be omitted. As for the rest of the code, it’s explained in Matthew’s posts and in the ZF2 docs.

And here’s how the slugify method modifies the data:

<?php

class SlugifyPost
{
    public function slugify($event)
    {
        $data = $event->getParams();

        $event->setParam('slug', strtolower(str_replace(' ', '-', $data['title'])));

        return $data;
    }
}

Calling the save method itself remains as it was before introducing the EventManager, which means I could add this to my models without changing the API and not break anything! Genius! Theoretically, anyway…

<?php

$post = new Post;

$values = array(
    'title' => 'My post'
);

try {
    $post->save($values);
} catch(\InvalidArgumentException $e) {
    echo $e->getMessage();
}

You can find this complete example on Github.

Thoughts?

So, what do you think? Does this approach makes sense to you? Do tell. I kinda like the idea, but time will tell in the end, as always.

Happy hackin’!

Robert Basic

Robert Basic

Software developer making web applications better.

Let's work together!

I would like to help you make your web application better.

Robert Basic © 2008 — 2020
Get the feed