#!/usr/bin/python
# requires python-argparse

import argparse, os

parser = argparse.ArgumentParser(
 description='Converts a directory tree with bookmark files to Netscape ' + \
             'style bookmarks html on stdout.',
 epilog='Remember to always verify output. ' + \
        'When parsing desktop files, the filenames are used rather than ' + \
        'the Name fields.')
parser.add_argument('-i', '--indir', default=os.curdir,
 help='Set input directory; will be searched for bookmark files; ' + \
      'default is the current directory')
parser.add_argument('-t', '--type', choices=['ie', 'desktop'], default='ie',
 help='Set type of bookmarks to search for; "ie" for simple IE-style ' + \
      'bookmarks (ending with .url), or "desktop" for freedesktop link ' + \
      'files (ending with .desktop); "ie" is the default')
parser.add_argument('-H', '--hidden', action='store_true',
 help='Include hidden files and directories (those beginning with ".") ' + \
      'when searching (default is to ignore)')
args = parser.parse_args()

if os.path.isdir(args.indir):
 root = os.path.normpath(args.indir)
else:
 raise Exception('Invalid directory')

import sys, fnmatch, string, ConfigParser, time, cgi

if args.type == 'ie':
 searchPattern = '*.url'
elif args.type == 'desktop':
 searchPattern = '*.desktop'

if args.hidden:
 searchPattern = '[!.]' + searchPattern

class NonLinkDesktopFile(Exception):
 pass

class InvalidBookmarkFile(Exception):
 pass

date = str(round(time.time())).rstrip('0').rstrip('.')
countMatchedFiles = 0
countNonLink = 0
countValidBookmarks = 0
countWrittenBookmarks = 0

def readBookmark(path, file):
 global countNonLink
 global countValidBookmarks
 fullPath = os.path.join(path, file)
 bookmark = ConfigParser.ConfigParser()
 try:
  bookmark.read(fullPath)
  if args.type == 'ie':
   url = bookmark.get('InternetShortcut', 'URL')
  elif args.type == 'desktop':
   if bookmark.get('Desktop Entry', 'Type') != 'Link':
    countNonLink = countNonLink + 1
    raise NonLinkDesktopFile
   url = bookmark.get('Desktop Entry', 'URL')
  if url == '':
   raise InvalidBookmarkFile
 except (InvalidBookmarkFile, ConfigParser.Error):
  sys.stderr.write('Invalid bookmark file: ' + fullPath + '\n')
  raise InvalidBookmarkFile
 else:
  countValidBookmarks = countValidBookmarks + 1
  return (path, os.path.splitext(file)[0], cgi.escape(url, quote=True))

def returnItems(pattern=searchPattern, root=root):
 global countMatchedFiles
 for dirpath, dirnames, filenames in os.walk(root, topdown=True):
  if args.hidden == False:
   for dir in fnmatch.filter(dirnames, '[.]*'):
    dirnames.remove(dir)
  dirnames.sort(key=str.lower)
  bookmarks = []
  for file in sorted(fnmatch.filter(filenames, pattern)):
   countMatchedFiles = countMatchedFiles + 1
   try:
    yield readBookmark(dirpath, file)
   except (InvalidBookmarkFile, NonLinkDesktopFile):
    pass

def writeOut(root=root):
 global countWrittenBookmarks
 sys.stdout.write('<!DOCTYPE NETSCAPE-Bookmark-file-1>\n' + \
                  '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; ' + \
                  'charset=UTF-8">\n<TITLE>Bookmarks</TITLE>\n' + \
                  '<H1>Bookmarks</H1>\n<DL>\n')
 currentPathL = string.split(os.path.normpath(root).lstrip('/'), \
                os.path.sep)[:-1]
 for path, name, url in returnItems():
  bookmarkPathL = string.split(path.lstrip('/'), os.path.sep)
  if currentPathL != bookmarkPathL:
   for i in range(len(bookmarkPathL)):
    while len(currentPathL) > len(bookmarkPathL) or \
          (len(currentPathL) > i and currentPathL[i] != bookmarkPathL[i]):
     #print 'folder-stop:', currentPathL[-1]
     sys.stdout.write('</DL><p>\n')
     currentPathL.pop()
    while len(currentPathL) < (i + 1) or currentPathL[i] != bookmarkPathL[i]:
     #print 'folder-start:', bookmarkPathL[i]
     sys.stdout.write('<DT><H3 FOLDED ADD_DATE="' + date + '">' + \
                      bookmarkPathL[i] + '</H3>\n<DL><p>\n')
     currentPathL.append(bookmarkPathL[i])
  sys.stdout.write('<DT><A HREF="' + url + '" ADD_DATE="' + date + \
                   '" LAST_VISIT="0" LAST_MODIFIED="0">' + name + '</A>\n')
  countWrittenBookmarks = countWrittenBookmarks + 1
 sys.stdout.write('</DL>\n')

try:
 writeOut()
except IOError, e:
 if e.errno != 32:
  raise
 else:
  quit()

if args.type == 'desktop':
 desktopNonLinkNote = 'Desktop files without Link type: ' + str(countNonLink) + '\n'
else:
 desktopNonLinkNote = ''

sys.stderr.write(args.type + ' files found: ' + \
                 str(countMatchedFiles) + '\n' + desktopNonLinkNote \
                 + 'Valid bookmarks: ' + str(countValidBookmarks) + \
                 '\nBookmarks written out: ' + str(countWrittenBookmarks) + \
                 '\n')
