Pidgin is a fantastic piece of software. It’s cross-platform (so it works on more than just Windows) and supports many instant messaging protocols (like AIM, Windows Live Messenger, and Yahoo Messenger). Most importantly, though, Pidgin provides developers an interface for creating plug-ins. Unfortunately for someone like me who generally only works in Microsoft Visual Studio when using C/C++, Pidgin is designed to be built using a Linux-style command line environment. Just downloading and installing everything necessary to be able to build a Pidgin plug-in was nightmarish. It took me many hours over several days to finally get everything configured properly. The first step was to install Cygwin and MiniGW. This part was fairly painless, but part of the configuration suggested adding MiniGW to the PATH variable within the .bashrc file in my home directory. Unfortunately, Cygwin did not make a home directory for me and I couldn’t figure out where the home directory should be, so I just ended up adding to the PATH variable in the bash.bashrc file in the /etc directory. The next step was to download all of the dependencies needed to build Pidgin: GTK+, gettext, Libxml2, Perl 5.10, Tcl 8.4.5, GtkSpell, Enchant, Mozilla NSS, SILC Toolkit, Meanwhile, Bonjour SDK, Cyrus SASL, Intltool, and Pidgin’s Crash Reporting Library. Pidgin suggests putting all of these within a subdirectory of the Pidgin development directory, but such a thought offends my sense of organization. After all, these libraries could be used by software other than Pidgin. To compound my issues, the path to my development folders contains a space (bad idea, I know). It was quite a struggle to figure out how to modify the make file settings so that all of the dependencies could be found. What gave me the most trouble was that all of the paths in the make files were given in a relative fashion using a Unix path format (e.g. $(PIDGIN_TREE_TOP)/../win32-dev), but I was supposed to use a Windows path format to point to my dependencies directory (e.g. C:/My\ Documents/Etc/Libraries). I found this especially weird since Cygwin uses Unix paths (it even mounts Windows file systems to /cygdrive/c, cygdrive/d, and so on). After figuring out to use a Windows-style path, I was finally able to build Pidgin.
Building Pidgin wasn’t the end goal, though. It was just a way of preparing my system to build Pidgin plug-ins. With my system properly configured, building a test plug-in was trivial. Next, I turned to actually designing and implementing the thing. I decided to monitor incoming messages for any message beginning with a forward slash (/). Any such message would be interpreted as a command for the computer to execute. I chose to use the common /command arg1 arg2… format as a nice, simple way to encode commands. I also decided to implement a simple permissions system to determine which users could use which commands. (I wouldn’t want random strangers executing commands on my computer!) Additionally, any instant messaging account currently active in the user’s Pidgin would have permission to run any command. This would save me the trouble of configuring a default super user. After I finished implementing and testing the permissions system, I decided I began to tackle the most useful feature of my plug-in: sending a screenshot of the computer to a cell phone. This feature presented two problems. First, as mentioned before, Pidgin is designed to be cross-platform and grabbing an image of a user’s screen is a very platform-specific operation. Second, while AIM supports SMS, it does not support MMS, so I couldn’t send any images that way.
My solution to the first problem was a decision to implement any significant platform-specific operations into separate executables and have the plug-in call them as child processes. I opened up Google and Visual C++ 2010 Express and set about writing a Win32 application to grab an image of the screen and send it to standard output. If you’ve written a Win32 application before, you’ll know that WinMain does not provide you with an argc/argv equivalent (it provides only the command line as single string). Fortunately, Windows provides some global versions or argv and argc in stdlib.h: __argc, __argv, and __wargv (you can also get __targv if you use tchar.h). I decided that I would output the image in either JPEG or PNG format, depending on which format produced the smaller file size. Typically, this will be PNG because it is better suited for compressing images where coloring is not particularly varied. After some searching, I settled upon using libpng and the IJG JPEG Library. By default, both libraries prefer to just write the data directly to a stream, but since I wanted to compare the resulting file sizes (and because I despise using temp files when they’re not necessary), I had to create an internal buffering class that I called ExpandingBuffer. It’s basically like an std::vector
Once I was satisfied with my screenshot program, it was time to figure out how to send it through e-mail. I toyed with the idea of writing my own implementation of SMTP, but why reinvent the wheel? It would be better to just use a library or program that someone else had already written. While searching, I stumbled across a program called cURL. cURL is a nice program that supports many Internet transfer protocols including SMTP and POP3, the latter of which I intend to use at some later point. I should state, at this point, that Pidgin makes use of GLib, a cross-platform library that implements some common functionality. I was specifically interested in its child process execution functions, the documentation of which I found to be a bit confusing (I’m still not sure if I’m using the functionality properly). At first, I coded my plug-in to execute the screenshot program, which wrote the image to a pipe created by GLib. When that program terminated, I then started cURL, read the data from the screenshot program’s output pipe, formatted the data for e-mailing, and then wrote it to cURL’s input pipe. If you’re familiar with fully buffered streams, you can probably guess what happened: the screenshot program froze. Pipes are a kind of fully buffered stream, which simply means that the entire stream is stored in a buffer somewhere in memory. Once that buffer is full, any writes to that stream will be blocked until enough data is read from the stream to make room for the data that is to be written. This meant that I had to recode my plug-in so that cURL was executed while the screenshot program was still running so I could read the data from the screenshot pipe (and send it to cURL) to make room for more data to be written to the pipe. The documentation for cURL is pretty lackluster. I couldn’t find any documentation on how to tell cURL that I was done sending data to it. I found a post on the Internet somewhere that said I should send the e-mail terminating CRLF.CRLF, but that didn’t work. After a while, I figured out that all I needed to do was close the pipe. It seems obvious now, but at the time, it was a frustrating problem.
Now that I had all of the code in place, it was time to test out my plug-in. I sent a command from my phone to take a screenshot of my computer and send it back to the phone. I could see cURL open up, but it didn’t seem like it did anything. It almost seemed like it crashed. After some investigating, I figured out that Avast’s E-mail Shield was causing cURL to fail, so I disabled that and retried. Again, cURL opened up but this time it appeared to do something. However, my phone failed to receive the screenshot. After some digging, I found that cURL had a command line option to output trace information to a file. That revealed that cURL was unable to connect to the remote SMTP server. I tried to connect to the server myself using HyperTerminal but that also failed. I checked my firewall and modem settings. Neither was set to block cURL or port 25 (the SMTP port). Eventually, I began to suspect that my ISP (AT&T) might be blocking the port to prevent spammers from using it. I did some research online and found that AT&T does indeed block the port but that they can unblock it for you upon request. A blog posting I found detailed how AT&T wanted to charge the author money to unblock port 25 and how it was a pain for him to finally get it unblocked for free, so I had a feeling I was going to have some issues getting the port unblocked as well. I was right. The first few people I talked to at AT&T confirmed that they block port 25 and that I’d have to contact they’re premium technical support and pay them either $15 a month for a year or about $50 one time to have the port unblocked. I’m pretty sure that’s actually illegal, but I’m just a layman (FCC’s Open Internet Rules). I politely informed the AT&T representative that I thought it was wrong of them to charge money to people for AT&T to stop blocking something they shouldn’t be blocking in the first place. I asked if I could be forwarded to someone so that I could make a complaint. After being forwarded around to a few different people at AT&T, none of whom could or would take my complaint, I ended up talking to a technical support person who informed me that he could indeed unblock the port for me at no charge. Finally! It only took two hours on the phone. I once again went and tested my plug-in. Nothing. I checked cURL’s trace file. It appeared as though the e-mail had successfully been sent. To verify, I had my plug-in send the e-mail to a regular e-mail account instead of to my phone. That worked fine, except that my e-mail ended up the spam folder. I was wondering now if my cell phone provider (Verizon) was filtering my e-mails. I contacted someone at Verizon and I was informed that they don’t do spam filtering except by specific addresses/servers. My address and server were clearly not spamming anyone, so it seemed like I had a problem somewhere else. I tidied up my code a bit, converting all sent LFs to CRLFs (as per the SMTP protocol), but that had no impact. I thought that perhaps Verizon was actually filtering my e-mails because cURL was identifying my computer by its local name (rather than by some domain name). As stated before, cURL has very poor documentation. I couldn’t find any information on how to change how cURL identifies my computer to the SMTP server. Fortunately, cURL is open source, so I spent a few hours searching through the source until I figured out how to do it (for anyone with a similar problem, it gets tacked onto the URL like “smtp://somesmtpserver.com/identity”). After some tinkering, I discovered that the Verizon SMTP server seems to discard e-mails if the identified domain name does not match both the e-mail address of the sender and the IP address of your machine. Luckily, I have a domain name that points to my machine, so once I started using that, everything worked! I now had a Pidgin plug-in that could send screenshots to my phone. However, there’s still quite a bit more work to do before the plug-in will be of much use.