#!/usr/bin/env python
# SQL Store Upgrade Script
# for version 1.x to 2.0 of the OpenID library.
# Doesn't depend on the openid library, so you can run this python
# script to update databases for ruby or PHP as well.
#
# Testers note:
#
#   A SQLite3 db with the 1.2 schema exists in
#   openid/test/data/openid-1.2-consumer-sqlitestore.db if you want something
#   to try upgrading.
#
#   TODO:
#    * test data for mysql and postgresql.
#    * automated tests.

import os
import getpass
import sys
from optparse import OptionParser


def askForPassword():
    return getpass.getpass("DB Password: ")

def askForConfirmation(dbname,tablename):
    print """The table %s from the database %s will be dropped, and
    an empty table with the new nonce table schema will replace it."""%(
    tablename, dbname)
    return raw_input("Continue? ").lower().strip().startswith('y')

def doSQLiteUpgrade(db_conn, nonce_table_name='oid_nonces'):
    cur = db_conn.cursor()
    cur.execute('DROP TABLE %s'%nonce_table_name)
    sql = """
    CREATE TABLE %s (
        server_url VARCHAR,
        timestamp INTEGER,
        salt CHAR(40),
        UNIQUE(server_url, timestamp, salt)
    );
    """%nonce_table_name
    cur.execute(sql)
    cur.close()

def doMySQLUpgrade(db_conn, nonce_table_name='oid_nonces'):
    cur = db_conn.cursor()
    cur.execute('DROP TABLE %s'%nonce_table_name)
    sql = """
    CREATE TABLE %s (
        server_url BLOB,
        timestamp INTEGER,
        salt CHAR(40),
        PRIMARY KEY (server_url(255), timestamp, salt)
    )
    TYPE=InnoDB;
    """%nonce_table_name
    cur.execute(sql)
    cur.close()

def doPostgreSQLUpgrade(db_conn, nonce_table_name='oid_nonces'):
    cur = db_conn.cursor()
    cur.execute('DROP TABLE %s'%nonce_table_name)
    sql = """
    CREATE TABLE %s (
        server_url VARCHAR(2047),
        timestamp INTEGER,
        salt CHAR(40),
        PRIMARY KEY (server_url, timestamp, salt)
    );
    """%nonce_table_name
    cur.execute(sql)
    cur.close()
    db_conn.commit()

def main(argv=None):
    parser = OptionParser()
    parser.add_option("-u", "--user", dest="username",
                      default=os.environ.get('USER'),
                      help="User name to use to connect to the DB.  "
                      "Defaults to USER environment variable.")
    parser.add_option('-t', '--table', dest='tablename', default='oid_nonces',
                      help='The name of the nonce table to drop and recreate. '
                      ' Defaults to "oid_nonces", the default table name for '
                      'the openid stores.')
    parser.add_option('--mysql', dest='mysql_db_name',
                      help='Upgrade a table from this MySQL database.  '
                      'Requires username for database.')
    parser.add_option('--pg', '--postgresql', dest='postgres_db_name',
                      help='Upgrade a table from this PostgreSQL database.  '
                      'Requires username for database.')
    parser.add_option('--sqlite', dest='sqlite_db_name',
                      help='Upgrade a table from this SQLite database file.')
    parser.add_option('--host', dest='db_host',
                      default='localhost',
                      help='Host on which to find MySQL or PostgreSQL DB.')
    (options, args) = parser.parse_args(argv)

    db_conn = None

    if options.sqlite_db_name:
        try:
            from pysqlite2 import dbapi2 as sqlite
        except ImportError:
            print "You must have pysqlite2 installed in your PYTHONPATH."
            return 1
        try:
            db_conn = sqlite.connect(options.sqlite_db_name)
        except Exception, e:
            print "Could not connect to SQLite database:", str(e)
            return 1

        if askForConfirmation(options.sqlite_db_name, options.tablename):
            doSQLiteUpgrade(db_conn, nonce_table_name=options.tablename)

    if options.postgres_db_name:
        if not options.username:
            print "A username is required to open a PostgreSQL Database."
            return 1
        password = askForPassword()
        try:
            import psycopg
        except ImportError:
            print "You need psycopg installed to update a postgres DB."
            return 1

        try:
            db_conn = psycopg.connect(database = options.postgres_db_name,
                                      user = options.username,
                                      host = options.db_host,
                                      password = password)
        except Exception, e:
            print "Could not connect to PostgreSQL database:", str(e)
            return 1

        if askForConfirmation(options.postgres_db_name, options.tablename):
            doPostgreSQLUpgrade(db_conn, nonce_table_name=options.tablename)

    if options.mysql_db_name:
        if not options.username:
            print "A username is required to open a MySQL Database."
            return 1
        password = askForPassword()
        try:
            import MySQLdb
        except ImportError:
            print "You must have MySQLdb installed to update a MySQL DB."
            return 1

        try:
            db_conn = MySQLdb.connect(options.db_host, options.username,
                                      password, options.mysql_db_name)
        except Exception, e:
            print "Could not connect to MySQL database:", str(e)
            return 1

        if askForConfirmation(options.mysql_db_name, options.tablename):
            doMySQLUpgrade(db_conn, nonce_table_name=options.tablename)

    if db_conn:
        db_conn.close()
    else:
        parser.print_help()

    return 0


if __name__ == '__main__':
    retval = main()
    sys.exit(retval)
