Monday, November 29, 2010

Getting over problems

Courtesy: Pravs World

If you can't get through the mountain, Go around it. If you can't go around it, Go over it.

If you can't go over it, sit down and ask yourself, if getting to the other side is all that important? If it is, set about digging a tunnel.

For every problem, there are many solutions. Whatever the problems in life are, you have to find ways to get over it.

Saturday, November 13, 2010

MyApp: Spb Wallet to KeePass convertor

Spb Wallet is one of the best wallet applications that I've used across various mobile platforms. However, it isn't available on all mobile platforms and it isn't free either. Once I moved on to Android, I hit a road-block because Spb Wallet isn't available for Android (yet). I'm sure you would understand how painful it is to not have a wallet app, once you are used to.

KeePass is the alternate. KeePass is an open-source software for password management. To be fair, KeePass isn't as great as Spb Wallet, but does its job. Being open-source, it is available on almost all platforms including desktops.

The pain here is the conversion. I have tonnes of data on Spb Wallet that manually entering them on KeePass is a no-go. Unfortunately, and mostly intentionally, Spb Wallet doesn't export to any well known format for import into KeePass. There weren't any handy tools to convert either. Thankfully Spb Wallet had a text export and KeePass had many import options. But it isn't directly compatible, because Spb Wallet doesn't have any proper structure to the exported TXT file and the grammar is quite ambiguous. KeePass has a well defined XML structure for import (found it by doing a sample XML export from KeePass). I wrote this python script to convert the Spb Wallet TXT export file into the XML format that KeePass can understand. In reality, Spb Wallet has more "specific" fields than KeePass, so there isn't always a direct mapping. Any non-mappable field (Account Number for example) will be appended in the notes section of KeePass so no information is lost.

This script is a simple parser that understands and converts the Spb Wallet TXT export file. It maintains the internal state of parsing and learns dynamically about what the current token is -- some times even looking ahead into the file to resolve ambiguities.

This script is probably not so robust on errors. But this did the job for my Wallet export which is reasonably big.

If you find any bug on this script that you want me to fix, report here. I *MAY* fix it for you.

Here is the source:
#
# Code to Convert Spb Wallet TXT export file to KeePass XML import file.
#
# Original Author: Gerald Naveen A (http://geraldnaveen.blogspot.com)
#
# License:
# You are free to modify/distribute/use for commercial/non-commercial/
# personal applications. Any modification to this code needn't be published. 
# However, any publication of this code or the derivative of this code, should 
# include the original author and this license text.
#
import sys

def mywrite(f, str):
f.write("{0}\n".format(str));
def main():
print "\nSpb Wallet to KeePass Convertor v 1.0 by Gerald Naveen\n";
print "Report bugs at http://geraldnaveen.blogspot.com/2010/11/myapp-spb-wallet-to-keepass-convertor.html\n";
if len(sys.argv) < 3:
 print "Usage: spb_wallet_to_keepass.py <spb_txt_export_file> <keepass_xml_import_file>";
 print "\nWhere,\nspb_txt_export_file: path to the TXT file exported from Spb Wallet.";
 print "keepass_txt_import_file: path to the output XML file, that shall be imported into KeePass.";
 return;
try:
 ifile = open (sys.argv[1], 'r');
except:
 print "Could not open input file", sys.argv[1];
 return;

try:
 ofile = open (sys.argv[2], 'w');
except:
 print "Could not open output file", sys.argv[2];
 return;

FOLDER_NAME_TOKEN=1;
ENTRY_NAME_TOKEN=FOLDER_NAME_TOKEN+1;
BEFORE_NOTES_TOKEN=ENTRY_NAME_TOKEN+1;
NOTES_TOKEN=BEFORE_NOTES_TOKEN+1;
INVALID_VALUE='invalid';

next_token=ENTRY_NAME_TOKEN;
folder_name = INVALID_VALUE;
entry_name = INVALID_VALUE;
user_name = INVALID_VALUE;
password = INVALID_VALUE;
url = INVALID_VALUE;
notes = INVALID_VALUE;
valid_entry = False;

mywrite(ofile, '<?xml version="1.0" encoding="utf-8" standalone="yes"?>');
mywrite(ofile, '<pwlist>');
try:
 for line in ifile:
  line = line.strip('\r\n');
  if len(line) == 0:
   # empty line
   if valid_entry == False:
    # entry name found after folder name
    folder_name = entry_name;
    entry_name = INVALID_VALUE;
   else:
    # found the last line of the entry..dump
    mywrite(ofile, '<pwentry>');
    if folder_name != INVALID_VALUE:
     mywrite(ofile, '<group>{0}</group>'.format(folder_name));
    mywrite(ofile, '<title>{0}</title>'.format(entry_name));    
    if user_name != INVALID_VALUE:
     mywrite(ofile, '<username>{0}</username>'.format(user_name));
    if password != INVALID_VALUE:
     mywrite(ofile, '<password>{0}</password>'.format(password));
    if url != INVALID_VALUE:
     mywrite(ofile, '<url>{0}</url>'.format(url));
    if notes != INVALID_VALUE:
     notes=notes.replace('\n', '&#xD;&#xA;');
     mywrite(ofile, '<notes>{0}</notes>'.format(notes));
    mywrite(ofile, '</pwentry>');
    user_name = INVALID_VALUE;
    password = INVALID_VALUE;
    url = INVALID_VALUE;
    notes = INVALID_VALUE;
   valid_entry = False;
   next_token=ENTRY_NAME_TOKEN;
  else:
   if next_token == ENTRY_NAME_TOKEN:
    entry_name = line;
    next_token = BEFORE_NOTES_TOKEN;
   else:
    valid_entry = True;
    if next_token == BEFORE_NOTES_TOKEN:
     if line.startswith('User Name:'):
      user_name = line[len('User Name:'):].strip(' ');
     elif line.startswith('Password:'):
      password = line[len('Password:'):].strip(' ');
     elif line.startswith('Web Site:'):
      url = line[len('Web Site:'):].strip(' ');
     elif line.startswith('Notes:'):
      if notes == INVALID_VALUE:
       notes = line[len('Notes:'):].strip(' ');
      else:
       notes += '\n' + line[len('Notes:'):].strip(' ');
      next_token = NOTES_TOKEN;
     else:
      # any unknown params should go as notes.
      if notes == INVALID_VALUE:
       notes = line;
      else:
       notes += '\n' + line;
    elif next_token == NOTES_TOKEN:
     # any thing from the notes section.
     notes += '\n' + line;
except:
  print "Unknown error occured while processing the input file.";
mywrite(ofile, '</pwlist>');    
ifile.close();
ofile.close();
print "Success. Now import {0} in KeePass as KeePass XML".format(sys.argv[2]);
if __name__ == "__main__":
   main()
Download spb_wallet_to_keepass.py

Update [26-Nov-2010]:

If the script ran successfully, but the output XML file didn't work on import, it could most likely be a CRLF issue. Try this in such cases:

Lets assume your file is test.txt.

1. Open test.txt in Notepad
2. "Save as" another file, say test2.txt (Note: select Utf8 as Encoding).
3. Use test2.txt as input