2011-10-31

Kasio - Open & Save

Continue from the last post, we are going to write the event handler for the Open and Save buttons this time.  We will also make use of the filecrypto module we wrote in an earlier post.

First we will have to add a few more imports:

import os
import sys
import wx
import filecrypto

Click the Open button to select a file and enter the password to decrypt the file into the text area.

def onOpen(self, event):
    try:
        # ask for file
        dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*",
            wx.OPEN|wx.CHANGE_DIR)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetPath()
        else:
            return
        # ask for password
        dlg = wx.TextEntryDialog(self, "Password?", "Attention", "",
            style=wx.OK|wx.CANCEL|wx.TE_PASSWORD)
        if dlg.ShowModal() == wx.ID_OK:
            self.password = dlg.GetValue()
        else:
            return
        # create file cipher
        fc = filecrypto.FileCrypto()
        # read & decrypt file
        plaintext = fc.load(self.filename, self.password)
        # convert 8-bit string in utf-8 encoding to unicode string
        plaintext = plaintext.decode('utf-8')
        # populate text control
        self.textbox.SetValue(plaintext)
    except:
        self.filename = ''
        self.password = ''
        self.textbox.SetValue('')
        errmsg = str(sys.exc_info()[1])
        dlg = wx.MessageDialog(None, errmsg, 'Attention', wx.OK | wx.ICON_EXCLAMATION)
        dlg.ShowModal()
        print(errmsg)


Click the Save button to save the content in the text area to a file.  If you are saving a new file, you will have to enter a password to encrypt the content.  If you are saving an existing file, the content will be encrypted using the password you entered to open the file in the first place.

def onSave(self, event):
    try:
        # if no filename, ask filename and password
        if len(self.filename) == 0:
            # ask for filename
            dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), "", "*",
                wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
            if dlg.ShowModal() == wx.ID_OK:
                self.filename = dlg.GetPath()
            else:
                return
            # ask for password
            dlg = wx.TextEntryDialog(self, "Password?", "Attention", "",
                style=wx.OK|wx.CANCEL|wx.TE_PASSWORD)
            if dlg.ShowModal() == wx.ID_OK:
                self.password = dlg.GetValue()
            else:
                return
        # create file cipher
        fc = filecrypto.FileCrypto()
        # get the text
        plaintext = self.textbox.GetValue()
        # convert unicode string to 8-bit string in utf-8 encoding
        plaintext = plaintext.encode('utf-8')
        # encrypt and write to file
        fc.save(self.filename, self.password, plaintext)
    except:
        errmsg = str(sys.exc_info()[1])
        dlg = wx.MessageDialog(None, errmsg, 'Attention', wx.OK | wx.ICON_EXCLAMATION)
        dlg.ShowModal()
        print(errmsg)


Seems that wxPython likes unicode while the filecrypto module likes 8-bit string.  Hence you can see there are encode() and decode() calls in the onSave() and onLoad() event handlers to keep them both happy.

Let's implement the Find button in the next post.





2011-10-25

File Encryption GUI

Continue from my last post, I will build a GUI front end for the file encryption utility in wxPython.

The GUI is very simple, just a text editing area and four buttons: Open, Save, Find, Exit.



The source code for the GUI is listed below.  We use a vertical BoxSizer to separate the text area and the button area, and a horizontal BoxSizer for the four buttons.  We align the buttons to the right, and create dummy button click event handlers to be implemented in the next post.  Lastly, center the window on screen and set default to the Open button.

import wx

class MainFrame(wx.Frame):
    def __init__(self, parent=None, id=(-1), title='kasio', size=(800,600)):
        wx.Frame.__init__(self, parent, id, title, size=size)
        self.panel = wx.Panel(self, wx.ID_ANY)

        textbox = wx.TextCtrl(self.panel, -1, style=wx.TE_MULTILINE)
        font = wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL)
        textbox.SetFont(font)
        btnOpen = wx.Button(self.panel, -1, 'Open')
        btnSave = wx.Button(self.panel, -1, 'Save')
        btnFind = wx.Button(self.panel, -1, 'Find')
        btnExit = wx.Button(self.panel, -1, 'Exit')

        self.Bind(wx.EVT_BUTTON, self.onOpen, btnOpen)
        self.Bind(wx.EVT_BUTTON, self.onSave, btnSave)
        self.Bind(wx.EVT_BUTTON, self.onFind, btnFind)
        self.Bind(wx.EVT_BUTTON, self.onExit, btnExit)

        topSizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)

        btnSizer.Add(btnOpen, 0, wx.ALL, 1)
        btnSizer.Add(btnSave, 0, wx.ALL, 1)
        btnSizer.Add(btnFind, 0, wx.ALL, 1)
        btnSizer.Add(btnExit, 0, wx.ALL, 1)

        topSizer.Add(textbox, 1, wx.ALL|wx.EXPAND, 1)
        topSizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 3)
        topSizer.Add(btnSizer, 0, wx.ALL|wx.ALIGN_RIGHT, 3)
      
        self.panel.SetSizer(topSizer)
        self.Centre() # center the frame in desktop
        btnOpen.SetDefault()

        self.filename = ''
        self.password = ''
        self.textbox = textbox

    def onOpen(self, event):
        pass

    def onSave(self, event):
        pass

    def onFind(self, event):
        pass

    def onExit(self, event):
        self.Close()

class MainApp(wx.App):
    def OnInit(self):
        frame = MainFrame()
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

def main():
    app = MainApp()
    app.MainLoop()

if __name__=='__main__':
    main()

We will hook up the filecrypto module to the GUI and implement some of the buttons logic in the next post.

2011-10-17

File Encryption Utility

I would like to write a small utility in Python to manage some private and confidential information in text format, and save it to a file with encryption.

First I will need a utility class to:
1.  encrypt some plain text and save it to a disk file
2.  open the encrypted file and decrypt it into plaintext

With the help of the Python Cryptography Toolkit, I can do that easily:

# filecrypto.py

import os

from Crypto.Hash import SHA256 as HASH
from Crypto.Cipher import Blowfish as CIPHER

class FileCrypto(object):

    def _create_cipher(self, password):
        h = HASH.new()
        h.update(password)
        key = h.digest()
        h.update(key)
        iv = h.digest()[:8]
        return CIPHER.new(key, CIPHER.MODE_CFB, iv)

    def load(self, filename, password):
        'decrypt a file into a string of plain text'
        if not os.path.isfile(filename):
            return ""
        with open(filename, 'rb') as f:
            ciphertext = f.read()
        cipher = self._create_cipher(password)
        return cipher.decrypt(ciphertext)

    def save(self, filename, password, plaintext):
        'encrypt a string of plain text into a file'
        cipher = self._create_cipher(password)
        ciphertext = cipher.encrypt(plaintext)
        with open(filename, 'wb') as f:
            f.write(ciphertext)


I pick Blowfish as the encryption algorithm because it allows variable key size.  CFB (Cipher FeedBack) mode is used to that block size padding is not necessary.

For basic unit testing, I add the following to the module:

def unittest():
    import random
    import string

    # test 1 - encrypt 1024 printable chars
    filename = "".join( [random.choice(string.hexdigits) for i in range(8)] )
    password = "".join( [random.choice(string.printable) for i in range(256)] )
    message = "".join( [random.choice(string.printable) for i in range(1024)] )
    if os.path.isfile(filename):
        os.remove(filename)
    fc = FileCrypto()
    assert "" == fc.load(filename, password)
    fc.save(filename, password, message)
    assert message == fc.load(filename, password)
    fc = None
    if os.path.isfile(filename):
        os.remove(filename)

    # test 2 - encrypt byte values 0 to 255
    filename = "".join( [random.choice(string.hexdigits) for i in range(8)] )
    password = "".join( [random.choice(string.printable) for i in range(256)] )
    message = "".join( [chr(x) for x in range(256)] )
    if os.path.isfile(filename):
        os.remove(filename)
    fc = FileCrypto()
    assert "" == fc.load(filename, password)
    fc.save(filename, password, message)
    assert message == fc.load(filename, password)
    fc = None
    if os.path.isfile(filename):
        os.remove(filename)

if __name__ == '__main__':
    unittest()

In the next posting, I will build a GUI using this module to encrypt/decrypt a file.

2011-10-11

FAB call list

Here is a short SL4A Python script to pop up a list of names.  Clicking a name will call the number directly.

I use this script to keep track of the FAB numbers registered with my mobile carrier that I can call free of charge.  The best thing is that I can edit those numbers directly on my phone!


# fabcall.py

fab = [
     'My Friend #1: 111-111-1111',
     'My Friend #2: 222-222-222',
     'My Friend #3: 333-333-3333',
]

droid = android.Android()

if __name__ == '__main__':
    title = 'FAB'
    droid.dialogCreateAlert(title)
    droid.dialogSetItems([x.split(':')[0].strip() for x in fab])
    droid.dialogShow()
    result = droid.dialogGetResponse().result
    if result.has_key('item'):
        index = result['item']
        name, tel = fab[index].split(':')
        droid.makeToast('Dialing ' + name.strip())
        droid.phoneCallNumber(tel.strip())


2011-10-02

Deleting lines in Vim

QUESTION

In Vim, turn the following:

function add(x, y)
    return x + y
endfunc

function subtract(x, y)
    return x - y
endfunc

function multiple(x, y)
    return x * y
endfunc

into:

function add(x, y)
function subtract(x, y)
function multiple(x, y)

ANSWER

:g!/function/d

EXPLANATION

: - go into command mode
g - search the entire file
! - negate the following search pattern
/function/ - look for the word "function"
d - delete the line containing the selection