Keeping it Small and Simple

2008.01.28

Intro to hacking on open source: wmymail

Filed under: Open Source — Tags: , , , — Lorenzo E. Danielsson @ 13:34

One of the benefits of open source is that you can modify an existing application to work the way you want it or to fix a bug. Yet, many people don’t do this but rather just accept that their application doesn’t behave “right”. If you look at the Linux community, it used to be very different, before the name “Linux” became a marketing managers wet dream. Linux users were programmers (not in the slimy sense of business types in suit and tie who earn $$$ by writing commercial garbage software, but in the real sense of somebody who writes code to fix things) at the same time and modified tools to suit themselves.

I will use a very simple example to show you that this doesn’t have to be hard at all. I’m going to modify a dockapp called wmymail. Now this example is very trivial, but the principles are the same even with larger programs. Since it is so trivial, there should be no problem following along even if you don’t have much programming experience. You’ll be amazed at just how simple this is.

Some of you will already know that there are dockapps that check your gmail account. But I wanted to use wmymail.

You might not be a dockapp user. Maybe you don’t use a window manager like fluxbox, WindowMaker or blackbox. That’s okay. You can still follow along here, even if you don’t intend to use wmymail. Hopefully you will learn something along the way.

Just to remind you, I am nobody in particular. It’s likely that you haven’t heard of me before. I have spent the last 20 or so years being a really lousy programmer, but keep doing it because I love it. If I am able to do this, then so can you. Now let’s get started.

Background

Last night I just couldn’t resist the temptation to get wmymail working in my fluxbox slit. This is a little mail checker that notifies you when you have new mail. By default it checks you mail spool, but with the help of a command-line option and fetchmail, it can check the status of an IMAP account as well.

Getting and compiling the source

I started off by downloading wmymail-0.3, which is the latest version I could find. I extracted it with tar zxf ~/dl/wmymail-0.3.tar.gz. I compiled, which went find, apart from a few warnings. Compiling is very simple. There is a Makefile in the project directory. To compile you just type ‘make’.

You will need to make sure you have a few things. You need gcc to compile, but that comes by default on Linux. You also need to have libdockapp installed. If you are on a brain-dead distro that separates a package into tiny sub-atomic particles, you will also need libdockapp-dev. Make sure you check the names. On my Debian system, the packages are libdockapp2 and libdockapp-dev. There may be some other dependencies. This is a good time to learn how to read your make/gcc output and use it to figure out what is wrong.

Problems with wmymail

I launched wmymail with the following (in fbrun):

% wmymail -F -i

Before that I had set up ~/.fetchmailrc to connect to my gmail account. The file looks as follows (the names have been changed to protect the innocent).


poll imap.gmail.com
proto IMAP
user 'your.user.name@gmail.com'
pass 'your super secret password'
ssl

In a split second wmymail was up and running. But something was not right. It didn’t appear to check my gmail account at all. Moreover, it pooed temp files into my home directory. The normally go away, as long as wmymail dies naturally, but I cannot stand when application put files into my home directory.

I had two problems, that needed to be fixed.

  1. Write temporary files into a directory designated for them
  2. Figure out why the heck wmymail claims I have 0/0 emails.

Stop polluting my $HOME!

I opened up the file wmymail.c in vim and did a search for “tmp”. The very first hit came on line 285 and looks like this:

char tmpfile[20] = "wmymail.XXXXXX";

As you can see, problem one is solved already. All we have to do is prepend the path to the string. Eh.. you may also want to count characters and size the array up just a little. Your generic, re-usable solution would probably involve checking if the user has set $TMPDIR, checking if the directory exists and all those things. At this stage I just needed it to work for me (always solve one problem at a time), so I simply did:

char tmpfile[40] = "/home/lorenzod/tmp/wmymail.XXXXXX";

See how simple that was. You’ve made your first little modification, and your brain could be left in standby while you did it. That is exactly what I mean. These things are not as difficult as you may think.

Why is it not updating my mail status?

When you launch wmymail with fbrun you won’t be able to see any output, which makes it hard to debug. So kill mymail and start it from rxvt instead. Doing this quickly revealed:

% ./wmymail -F -i 5
wmymail: error when using system() to run fetchmail -c: No child processes

Okay, I had no clue what caused this but now we have some form of a lead. I went back to vim, found the checkfetchmail() function, looking for an error message that looked similar to what I had just got. Lines 305-308 looked interesting

if (system(syscmd) < 0) {
perror("wmymail: error when using system() to run fetchmail -c");
return;
}

The variable syscmd just holds the command-line to be executed. So something is prevent the dockapp from proceeding beyond this point. What could that be? To find out we can do like this: declare an integer called ret in the variable decleration secion of the checkfetchmail() function and add the following:

ret = system(syscmd);
printf("%d\n", ret);
if (ret < 0) {
perror("wmymail: error when using system() to run fetchmail -c");
return;
}

Note that I like to outdent temporary code that I add for testing purposes. If we compile and run again, we get:

% ./wmymail -F -i 5
-1
wmymail: error when using system() to run fetchmail -c: No child processes

So the fetchmail process is returning -1 and wmymail is saying that a return less than 0 indicates an error. The reason for that is available in the system man page (look for the section RETURN VALUE). So we’ve read the section, blindly accepted everything that we read, but we still feel that we want to try something. Looking at the temp file (something like ~/tmp/wmymail.DALuun) we see that fetchmail seems to have run fine, at least if it looks something like this:

% cat ~/tmp/wmymail.DALuun
4243 messages (4236 seen) for some_user@gmail.com at imap.gmail.com.

This lead me comment out the part where we check for the return statement.

ret = system(syscmd);
printf("%d\n", ret);
/*
if (ret < 0) {
perror("wmymail: error when using system() to run fetchmail -c");
return;
}
*/

Compile and run again. Now it should work. So we’ve solved our two problems and haven’t even broken a sweat yet. Now since you are a good program (or at least, aspiring to be one) and not a useless waste product of society like myself, you will add a small comment that you need to look into *why* the commented out code wasn’t working. Take that as an exercise for yourself.

But wait..

If you have a lot of mail you will notice that the largest number of mails that wmymail can handle is 999. This means your total mails will constantly say 999, and if you’re lazy like me, your unread mails will also read 999. 😉

This is simple to fix. We will let wmymail display four-digit numbers. First of all look into the xpm directory. You will find a file called main.xpm. If you cat this file you will see that it would parse as a C file. Yes, XPM (X pixamp) is a graphics format made in heaven. It works well with revision control, diff and all the other every-day tools. XPM files can also be open in Gimp, which is what we will do now.

I suck at Gimp, but was still able to do the following without any difficulty. If you are on the artistic side, you can take the opportunity to improve the look. Below you can see the original image and modified one. (Note: I had to convert this to PNG because stupid WordPress can’t handle the *standard* image format.)

mainorig.png mainnew.png
Original XPM Modified XPM

As you can see I’ve added an image to the end of the left digit box and one at the beginning of the right input box. We still have a small gap between the two boxes as well. Now, we just have to figure out how to code this as well. Actually, before you do that compile and run. That way you will better see why you need to modify the code. In general, you should always make a single atomic change, compile, run and test before moving on. Trust me, it will make you life much simpler.

By looking around a bit we realize that numMessages and numUnread look like interesting variables. More digging leads us to the function updatePixmap() (line 400 +/- a few lines depending on how you formatted the previous edits. Find the block that looks like this:

if (numMessages > 998) {
putnumber(999, outPixmap, numbersPixmap, 40, 49);
} else {
putnumber(numMessages, outPixmap, numbersPixmap, 40, 49);
}

And change 998 to 9998 and 999 to 9999. Do the same in the next block (the one relating to unread mails). If you compile and run you will realize that this doesn’t do much to improve the situation. It rather makes it worse. But don’t abandon all hope quite yet.

In the blocks that we just modified, did you notice how there are calls to a function called putnumber()? That just gives me a gut feeling that we might find something interesting there. That function is the one following the one we are currently positioned in so you can just jump down a little and you will find it.

The very first statement of the function putnumber() gives us much information. Three integers are declared: digit1, digit2 and digit3. I bet adding a digit4 would be a step in the right direction!

But, we are not done quite yet. Right underneath we initialize the digit variables. Modify this to look like this:

digit1 = number / 1000;
digit2 = (number % 1000) / 100;
digit3 = (number % 100) / 10;
digit4 = number % 10;

Before you move on, make sure you understand why this works. Take a few numbers, for instance 6481, 5077, 4000, 231 and 4 and work through them. In each case what values will digit1, digit2, digit3 and digit4 hold?

We’re almost done now. Just one last thing. We need to update the next block of code to look as follows:

if (digit1) XCopyArea(DADisplay, numbers, pixmap, defaultGC,
digit1 * 5, 0, 5, 9, destx, desty);

if (digit2 || digit1) XCopyArea(DADisplay, numbers, pixmap, defaultGC,
digit2 * 5, 0, 5, 9, destx + 6, desty);

if (digit3 || digit2 || digit1)
XCopyArea(DADisplay, numbers, pixmap, defaultGC,
digit3 * 5, 0, 5, 9, destx + 12, desty);

XCopyArea(DADisplay, numbers, pixmap, defaultGC,
digit4 * 5, 0, 5, 9, destx + 18, desty);

Again, make sure you understand why the code looks as it does (make sure you have the man pages for Xlib installed). Notice that we’ve added one XCopyArea for digit4. Each digit takes up 6 pixels, so we have to add a multiplier value to destx for each consecutive digit.

Okay, so we are done, right? Good. Compile and run and… oh!!! Now what is wrong?? Why is the total number of messages miss-aligned? If you think about it for 2 seconds it’s obvious. When we edited the XPM we added a digit to the end of the number of unread messages, but the beginning of the total mails box. In other words we need to shift the position where the total number of mails get printed to the left. We already know that the width of a digit is six pixels, but if we didn’t we could open the file in Gimp again, move the mouse over the left-most part of the first digit and read off the coordinates. Simple!

Go back to the updatePixmap() function. More specifically, find the block that (now) starts if (numMessages > 9998) { and change the fourth parameter in each of the putnumber() calls to read 34 instead of 40. You should make a total of two changes.

Now recompile, run and Vittoria!!! You have done it. Good on you. A triple shot of Vodka is a good idea at this stage, especially if you have a grumpy boss looking over your shoulder.

Wasn’t that just so much fun? From now on, any time the behavior of some software application annoys you, download the source and start playing around with it. Er.. you might not want to start with openoffice.org. There are loads and loads of little applications that you can play around with, just to get your feet wet.

Conclusion

So is that it? Is it really that simple? Well, of course, larger projects are more complex. If you want to start hacking on device drivers you have to have (or build up) a solid understanding of the hardware and the kernel. But, you start off by solving simple things in small projects and gradually work your way up. Many of the people you admire started off doing things like this.

Of course, “professional” programmers don’t work like this. They hate programming, only learned it to earn money. They spend the whole day drinking coffee and having sex in corporate toilets. They earn hundreds of $ a day doing so.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: