June 9, 2011 7

Create Indicator Applet for Ubuntu Unity (with Python!)

We're going to walk through the process of creating a status indicator applet for the latest version of Ubuntu running Unity. In this example we're going to create a basic new mail indicator which works with GMail. This will give you a solid grounding in the basic structure of an applet while providing a real-world (albeit simplistic) example that you can easily extend.

Let's go through the final script piece by piece, first our header:

#!/usr/bin/env python

import sys
import gtk
import appindicator

import imaplib
import re

PING_FREQUENCY = 60

The first line instructs the system to find the current environment's version of python and use it to run this file. The first three imports are probably going to be used in every indicator you use. sys is used so we can offer a "Quit" option, GTK drives the interface, and appindicator provides Unity-specific code for creating indicator applets. The last two includes are required for this indicator's specific functionality, checking GMail. Finally we have one application wide constant which dictates how often we should ping (or check) GMail's servers for new mail, in seconds.

class CheckGMail:
    def __init__(self):
        self.ind = appindicator.Indicator("debian-doc-menu",
                                           "indicator-messages",
                                           appindicator.CATEGORY_APPLICATION_STATUS)
        self.ind.set_status(appindicator.STATUS_ACTIVE)
        self.ind.set_attention_icon("new-messages-red")

        self.menu_setup()
        self.ind.set_menu(self.menu)

Next we create a class called CheckGMail which will hold all of our indicator's functionality. To initialize our class first we create the variable "ind" which holds an Indicator object. According to the API this constructor takes 3 (or 4) arguments, a unique id for our indicator, an icon name, and an indicator category.

We set an initial status for our indicator with ind.set_status and then define the icon to use when our indicator requires attention (in this case when we have new mail waiting). Then we call our custom menu_setup() function (which we haven't defined yet), and then the set_menu() function on the Indicator object which takes a GTK menu as an argument and attaches it to our indicator.

def menu_setup(self):
        self.menu = gtk.Menu()

        self.quit_item = gtk.MenuItem("Quit")
        self.quit_item.connect("activate", self.quit)
        self.quit_item.show()
        self.menu.append(self.quit_item)

Here is the aforementioned menu_setup() function. Here we create a new GTK Menu and then define a single menu item called "quit_item". We define the text label for the menu, and then connect it with an action. We instruct the menu item to launch the quit() function when the item is "activated" (clicked) and then add the item to our menu variable.

def main(self):
        self.check_mail()
        gtk.timeout_add(PING_FREQUENCY * 1000, self.check_mail)
        gtk.main()

The main() function constitutes our primary program loop. When our indicator is first loaded we run this function. First it runs the check_mail() function (which has yet to be defined), and then creates a timeout. This causes our program to wait the specified amount of time and then run the given callback function, which is in this case, check_mail(). Then finally we run our main() function again, putting our program into an infinite loop, since we'd like our applet to continually check mail as long as it's running.

def quit(self, widget):
        sys.exit(0)

Here we define the quit() function which we referenced in menu_setup(). This function makes a call to sys.exit which will cause our application to stop running. At this point the process will stop and our indicator applet will disappear.

def check_mail(self):
        messages, unread = self.gmail_checker('myaddress@gmail.com','mypassword')
        if unread > 0:
            self.ind.set_status(appindicator.STATUS_ATTENTION)
        else:
            self.ind.set_status(appindicator.STATUS_ACTIVE)
        return True

This is the core functionality of this specific indicator applet. We're calling the function gmail_checker() which we've borrowed from Martin Brook for the purposes of this example. We load in the number of total messages and the number of unread messages from the provided GMail account. (I should note, this will work with Google Apps account, you are not required to have a @gmail.com address). If there is more than one unread messages found, we change the status of our indicator to STATUS_ATTENTION, which is this case turns our icon red, notifying you of new messages.

def gmail_checker(self, username, password):
        i = imaplib.IMAP4_SSL('imap.gmail.com')
        try:
            i.login(username, password)
            x, y = i.status('INBOX', '(MESSAGES UNSEEN)')
            messages = int(re.search('MESSAGES\s+(\d+)', y[0]).group(1))
            unseen = int(re.search('UNSEEN\s+(\d+)', y[0]).group(1))
            return (messages, unseen)
        except:
            return False, 0

This is the final function of our indicator applet class, and it is the code that we borrowed from Martin. It creates an SSL encrypted connection to an IMAP server, in this case GMail, and will attempt to determine how many total and unread messages are available.

if __name__ == "__main__":
    indicator = CheckGMail()
    indicator.main()

We come a the close of our file with some basic python initialization. This special block will be run when our file is called directly. It instantiated an object of the CheckGMail class and then runs the main() function, which set's everything off for us.

Here is our entire file:

#!/usr/bin/env python

import sys
import gtk
import appindicator

import imaplib
import re

PING_FREQUENCY = 10 # seconds

class CheckGMail:
    def __init__(self):
        self.ind = appindicator.Indicator("new-gmail-indicator",
                                           "indicator-messages",
                                           appindicator.CATEGORY_APPLICATION_STATUS)
        self.ind.set_status(appindicator.STATUS_ACTIVE)
        self.ind.set_attention_icon("new-messages-red")

        self.menu_setup()
        self.ind.set_menu(self.menu)

    def menu_setup(self):
        self.menu = gtk.Menu()

        self.quit_item = gtk.MenuItem("Quit")
        self.quit_item.connect("activate", self.quit)
        self.quit_item.show()
        self.menu.append(self.quit_item)

    def main(self):
        self.check_mail()
        gtk.timeout_add(PING_FREQUENCY * 1000, self.check_mail)
        gtk.main()

    def quit(self, widget):
        sys.exit(0)

    def check_mail(self):
        messages, unread = self.gmail_checker('myaddress@gmail.com','mypassword')
        if unread > 0:
            self.ind.set_status(appindicator.STATUS_ATTENTION)
        else:
            self.ind.set_status(appindicator.STATUS_ACTIVE)
        return True

    def gmail_checker(self, username, password):
        i = imaplib.IMAP4_SSL('imap.gmail.com')
        try:
            i.login(username, password)
            x, y = i.status('INBOX', '(MESSAGES UNSEEN)')
            messages = int(re.search('MESSAGES\s+(\d+)', y[0]).group(1))
            unseen = int(re.search('UNSEEN\s+(\d+)', y[0]).group(1))
            return (messages, unseen)
        except:
            return False, 0

if __name__ == "__main__":
    indicator = CheckGMail()
    indicator.main()

Now we just need to mark the file as executable and run it to see the results.

chmod +x check_gmail.py
./check_gmail.py

Now if you have a bunch of unread messages in your gmail account it will always be red, so you'll have to extend this example if you want to make the new message checking more intelligent. We've learned how to setup a basic indicator applet with it's own menu and icon, and give it a useful piece of functionality. You could easily replace the check_email() function with almost anything else you could program in python normally. Hope this helps to get you started with creating indicators, if you have any questions or comments, please let me know!

Tags:

June 6, 2011 3

Resize HFS+ Partition in Linux

Let's say we've got a left over Time Machine backup disk from our old, sacrilegious OSX install, and we'd like to keep those backups but also shrink the partition down and recover some usable space. Here is an easy way to shrink an HFS+ partition in linux. Please use common sense and make sure that you have another backup of this data before attempting this.

Note to OSX/Time Machine users: I've had some people end up here looking for a way to resize their Time Machine partition. You can easily follow this article with an Ubuntu Live CD.

First plug in the drive and determine which device it is on. In my case it is /dev/sdb. The first thing we need to do is unmount the partition we're looking to resize. You can figure out which partition that is by running parted, like so:

sudo parted /dev/sdb

You will be greeted with a "(parted)" prompt. Enter "print" to view details of the partitions on the drive. In my case, I want to resize partition 2, so we'll "quit" out of parted and unmount partition 2.

sudo umount /dev/sdb2

Now let's go back into parted and resize our partition. We're going to enter into parted, select the partition we'd like to resize, and then we'll be prompted for a start and end to the partition. In my case, I had about 512Gb of data on my 1TB drive. I also had a small partition before this one, which is why it prompted me to start my partition at 210MB. You should probably select whatever it suggests as the start (by simply not entering anything, the item in brackets is the proposed default), and then selecting an end which is a little larger than your usage. I chose 550GB for my end, just to be safe. Below is an output of the whole process:

Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print                                                            
Model: WD My Passport 071A (scsi)
Disk /dev/sdb: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name                  Flags
 1      20.5kB  210MB   210MB   fat32        EFI System Partition  boot
 2      210MB   1000GB  1000GB  hfsx         Time Machine Backups

(parted) resize                                                          
WARNING: you are attempting to use parted to operate on (resize) a file system.
parted's file system manipulation code is not as robust as what you'll find in
dedicated, file-system-specific packages like e2fsprogs.  We recommend
you use parted only to manipulate partition tables, whenever possible.
Support for performing most operations on most types of file systems
will be removed in an upcoming release.
Partition number? 2                                                      
Start?  [210MB]?                                                          
End?  [1000GB]? 550GB                                                    
(parted) quit

After you specify an end point and hit enter, the program will go to work and may take some time to finish. I have a relatively fast machine and I believe the drive is 7200rpm. It took only a few minutes to complete. A successful resize should bring you back to the (parted) prompt. Then just enter "quit" to exit parted. At this point I opened gparted and used it's visual editor to create a file system on the newly created partition. When you plug in the drive you should now see the old and new partitions and have access to all the files on both. Again, please, be smart, back up your data.

May 21, 2011 0

Secure browsing with an SSH port-forwarding socks proxy

When we're browsing the web from a public place we'd like to ensure all our traffic travels over an encrypted channel. W can accomplish this easily with an SSH based proxy. You're going to need a shell (SSH) account on a remote server. The following process will work on *nix and OSX and probably other systems as well. For windows you'll want to use something like PuTTY/Plink, but beyond that you're on your own.

Create a file in your home directory and name it whatever you like, I'm using tunnel.sh. In that file enter the following:

ssh user@server.com -p 22 -ND 8080

First of all, pay attention to case here, we're using a lower case -p to specify the remote port we're connecting on, and a capital -D to specify the local port to serve the proxy from. If you're connecting to your server via port 22 like the example you can actually leave it off, but I've included it in the example since many sysadmins use non-standard ports for ssh (and so should you, if possible). The -N is optional and signals SSH not to open a shell session, so after you've connected and provided a password, the command will simply sit there as if it were hanging, instead of providing you with a command prompt.

Let's flag our file as executable:

chmod +x tunnel.sh

Now we can run our file which will connect to your server and prompt you for your password. Once you enter your password it will simply sit there. You must keep this window open, so either minimize it or move it to a different desktop if you can. Now we have a SOCKS5 compatible port-forwarding proxy at our disposal. Normally when you make a request over the internet, it travels from your computer's network card to your local router and then to the internet in effectively plaintext. Anyone with the slightest inclination is able to view what sites you're visiting, and obtain your login information if it is not being transmitted in a secure fashion (including email, ftp, and many websites). You may have been following the whole firesheep fiasco.

Now that we have our SSH proxy running, we can tunnel all of our traffic through an SSH encrypted channel which effectively hides all of your internet traffic from curious/malicious users on your local network. In order to do this we have to tell our software to use the local proxy running on port 8080. For firefox I recommend foxyproxy. Most instant messengers allow you to use a proxy for a given account. In either case, you need to make sure that you use "localhost", port 8080, and check the options for SOCKS5 proxy. You can check that it works by checking your IP at http://checkip.dyndns.org before and after enabling the proxy.

Tags:

May 16, 2011 0

Keep track of your dynamic IP with wget, sendEmail, and cron

If you need to access your machine remotely and have a dynamic public IP (like me), you may find this system handy. We're going to write a script which determines your IP and emails it to you every day via a cronjob. For simplicity we're going to use GMail's servers to send the email, so you'll need a GMail account, even if it's just a throwaway for this purpose alone.

First let's install a handy script called sendEmail and an additional library for connecting to SSL-enabled servers via perl.

sudo apt-get install sendEmail libio-socket-ssl-perl

Next we're going to create a script which determines your public IP, and then emails it to you in send_my_ip.sh:

#!/bin/bash
IP=`exec wget -q -O - checkip.dyndns.org`
sendemail -s smtp.gmail.com -t wheretosendyourip@server.com -f youraccount@gmail.com -m $IP -xu youraccount@gmail.com -xp yourgmailpassword -o tls=auto

What a mess! Let's take it line by line. The first line is your standard shebang with the path to bash. On the next line we're calling wget to grab the html of http://checkip.dyndns.org and storing it in a variable called IP. On the next line we're calling sendemail (notice the lower case e in the program call), and specifying some arguments including the email address you want to send the IP to, and the account details of the account you're sending it from. These addresses can be the same, as we're just using a GMail account because they're free and it avoids the hassle of setting up a local email server.

Next let's make this script executable:

chmod +x send_my_ip.sh

At this point you can test your script by running it directly from the command line with

./send_my_ip.sh

Finally, we need to create a cronjob which automatically calls this script every day to make sure we always have the current public IP. To edit your cron file run:

crontab -e

If this is your first time editing your cron file, it will prompt you for your editor of choice. I prefer vim, but if you're not familiar with vim, choose nano. I want to run this script which is in my home directory every day a few hours before my day starts. Enter the following on a new line at the bottom of the file:

0 5 * * * ~/send_my_ip.sh

Save and exit the file and that's it. Cron will run the script at the times you set it. There is a ton of information out there on defining cronjobs, so I'll leave it as homework to setup different email schedules.

Tags:

May 12, 2011 1

Use vim bindings in bash / terminal

Once you really get used to using vim, you start wanting everything to work like it. Fortunately there is an easy way to enable vim bindings in your terminal.

set -o vi

To make this change permanent, you can add it to your .bashrc (or .bash_profile) with:

echo "set -o vi" >> ~/.bashrc

This change will enable insert and normal mode in bash. Opening a new bash terminal window will place you in insert mode as you'd expect, and you can hit ESC or CTRL-[ to go to normal mode for all of that vim goodness you're so hooked on. Enjoy!

Tags:

May 2, 2011 1

Fish-style prompt for bash

In search of a more minimalistic bash prompt, I ran across this great one-liner for creating a fish-esque prompt for bash. This prompt only displays the first letter of each directory above your current one, and the full name of the current directory. For example, if I were in:

~/projects/conjurecon/httpdocs/images

it would display:

~/p/c/h/images$

Give it a try, you may like it. Just add the following lines to ~/.bashrc (or ~/.bash_profile). To get your old prompt back just remove the two lines. In either case, you'll have to reload your terminal to see changes to your prompt. Enjoy!

PROMPT_COMMAND='CurDir=`pwd|sed -e "s!$HOME!~!"|sed -re "s!([^/])[^/]+/!\1/!g"`'
PS1="\$CurDir\$ "

I couldn't find the original author of this nice little one-liner. If someone else can, I'd love to link back to the original.

Update for OSX users: If you're trying to get this working on OSX you need to make one small adjustment. The flag for extended regex in sed for OSX is -E and not -r like above, so you can accomplish the same thing like so:

PROMPT_COMMAND='CurDir=`pwd|sed -e "s!$HOME!~!"|sed -Ee "s!([^/])[^/]+/!\1/!g"`'
PS1="\$CurDir\$ "

Tags:

April 28, 2011 4

Enable auto-complete in python interpreter

Enabling auto-complete in the interactive python interpreter will greatly increase your development speed. Sometimes you can't recall a method's name or you're not sure which method to use in the first place.

First we're going to create a file in your home directory called ".pythonrc" which will hold our configuration details. This file should contain the following:

import rlcompleter, readline
readline.parse_and_bind('tab:complete')

Then we'll set the PYTHONSTARTUP environment variable in our .bashrc (or .bash_profile if you're using that). If the PYTHONSTARTUP variable is set to a readable file, then the contents of that file will be run before anything else when the interactive interpreter is run.

echo "export PYTHONSTARTUP=~/.pythonrc" >> .bashrc

Then either open a new terminal window or reload your .bashrc file:

source ~/.bashrc

You're all set, just run python to enter the interpreter and give it a whirl. For example, run:

import sys

Then type in "sys." and hit tab twice, and you'll see all the methods on the sys object.

Tags:

April 28, 2011 0

Python Environment for Ubuntu: Part 2

If you've been following along since Python Environment for Ubuntu: Part 1, you should now have virtualenv (with virtualenvwrapper) and pip installed, ready to be used.

You'll want to create a virtual environment for each of your projects. For example, if I were creating a environment for conjurecode, I might run:

mkvirtualenv conjurecode

It will run through some preliminary installs (including distribute or setuptools), create some hooks that we will take a look at later, and activate your newly created environment. You'll notice that your prompt has now changed to include the name of your environment in parenthesis. This is to notify you that you are currently working in the listed virtualenv. Any python scripts which you run from inside this virtualenv will only have access to these installed packages. To illustrate this somewhat, let's jump into the interactive python interpreter and look at our python path. Then we'll deactivate our virtualenv to return to our normal environment and look at it again.

To enter the interpreter, simply run, after which you will be greeted by a >> prompt:

python

To view our python path, we'll execute the following:

import sys
sys.path

You should see a lot of references to your home directory (such as /home/[your_username]/.virtualenvs) in your python path at this point. Since we are in our virtualenvironment, python knows to use only packages from the virtualenv path. To exit the python interpreter:

exit()

Next let's deactivate our virtualenv and return to our normal environment, simply by entering the following at the command prompt:

deactivate

You should see that you've returned to your normal prompt, indicating that you are no longer in any virtualenv. If you were to run the interactive python interpreter again at this point, you would see references to /usr/local/lib or something similar indicating that python is now using your system wide packages.

When you first created your virtualenv it was automatically activated for you, but once you've deactivated it, you'll need to manually activate it once you're ready to work on your project again. We do this with the workon command and the name of your project. When using workon, it will helpfully autocomplete your virtualenv project names with TAB.

workon conjurecode

If you need to remove a virtualenv, you can use the command rmvirtualenv and provide the name of the environment you'd like to remove. Make sure that the environment you're trying to remove isn't currently active and then issue the command:

rmvirtualenv conjurecode

You should now know how to create, activate, disable and remove virtual environments and have some idea of the way they control which packages your python scripts have access to. Next I'm going to review some basics of using pip to manage packages for each of your virtualenvs.

Tags:

April 28, 2011 3

Python Environment for Ubuntu: Part 1

We're going to go through a few steps to get a nice development environment for working with Python (and Django) on Ubuntu.

We're going to be using pip to install and manage packages, but first we need easy_install, which comes with the setuptools package. To install setuptools (and easy_install), run the following:

sudo apt-get install python-setuptools

To ensure this has worked, run the following command which should return the version number of easy_install.

easy_install --version

To install pip run:

sudo easy_install pip

Again to verify that pip was installed correctly, check its version.

pip --version

Next we'll see pip to install virtualenv and virtuenvwrapper:

sudo pip install virtualenv
sudo pip install virtualenvwrapper

Next, we're going to set virtualenv to store our virtual environments in our home folder to keep things tidy:

mkdir ~/.virtualenvs
echo "export WORKON_HOME=~/.virtualenvs" >> ~/.bashrc

Setup bash to work with virtualenv (this should be the path to your virtualenvwrapper.sh):

echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc
echo "export PIP_VIRTUALENV_BASE=~/.virtualenvs" >> ~/.bashrc

Finally, one more recommended step. We're creating an alias which will cause mkvirtualenv to not use site packages and to use distribute (instead of setuptools).

echo "alias mkvirtualenv='mkvirtualenv --no-site-packages --distribute'" >> ~/.bashrc

To reload our new bash settings without restarting our shell, run the following (or just open a new terminal window), otherwise you will get 'command not found' errors when running virtualenv:

source ~/.bashrc

That's it! You can now begin creating and using virtual environments in Part 2.

Tags:

April 28, 2011 0

Installing MySQLdb for Python on Ubuntu

In order to have MySQL support for python, you must install the MySQLdb package. This can easily be done with pip, but first we'll need to install mysql_config and python-dev (if you haven't already):

sudo apt-get install python-dev
sudo apt-get install libmysqlclient-dev

Then, if you have pip installed, enter into your virtualenv if you're using one, and install via pip like so:

pip install MySQL-python

Tags:

Dedicated Server Hosting by Hivelocity