#!/usr/bin/env python

import os, sys, pwd, random, string, shutil
import MySQLdb as db

#from sqlchain.util import *

print """\nThis program will configure bitcoind and sqlChain for your system. 
It can optionally create and initialize the MySQL database. It will ask a series of questions 
and then create the database, user, init and cfg files. Re-running this will overwrite options
including passwords but does not remove old files.\n"""
if os.geteuid() != 0:
    print "You need to run with root/sudo privileges - try again."
    sys.exit(0)
go = raw_input( "Continue [Y]/N:" ) or 'Y'
if go.upper() != 'Y':
    sys.exit(0)

btcdir = raw_input( "Enter the bitcoin data directory [ /var/data/bitcoin ]:" ) or "/var/data/bitcoin"
btcboot = raw_input( "Start bitcoind at system boot [Y]/N:" ) or 'Y'
btcuser = raw_input( "User to run bitcoind and sqlchain daemons (will create if not existing) [btc]:" ) or 'btc'
btcpwd = raw_input( "Bitcoin RPC password [create random]:" ) 

print """\nBitcoin can be run in pruning mode to discard block data after sqlchaind has processed it. 
This requires a bitcoind be built with 'manual pruning' to allow control over pruned blocks. This is currently 
available as PR #7871, which can be pulled and merged for a custom build. If you do not have this mode 
available then do not enable pruning as blocks could be pruned before being processed by sqlchaind.\n"""
btcprune = raw_input( "Run bitcoind in pruning mode Y/[N]:" ) or 'N'

sqlboot = raw_input( "Start sqlchaind at system boot [Y]/N:" ) or 'Y'
apiboot = raw_input( "Start sqlchain-api at system boot [Y]/N:" ) or 'Y'   
cfgdir = raw_input( "Directory for config files [ /etc/bitcoin ]:" ) or "/etc/bitcoin"
sqldir = raw_input( "Directory for sqlchain blob and header data files [/var/data/sqlchain]:") or "/var/data/sqlchain"
wwwdir = raw_input( "Root web directory for sqlchain-api [/var/www]:") or "/var/www"

dbname = raw_input( "\nMySQL database name [bitcoin]:" ) or "bitcoin"
dbuser = raw_input( "MySQL database user (will grant privileges) [btc]:" ) or "btc"
dbpwd = raw_input( "MySQL %s user password [create random]:" % dbuser )
rootpwd = raw_input( "MySQL root password (needed to create database and user):" )
    
if btcpwd == '':
    btcpwd = ''.join(random.sample(string.ascii_letters+string.digits,20))
if dbpwd == '':
    dbpwd = ''.join(random.sample(string.ascii_letters+string.digits,20))

## Create user for running bitcoin and sqlchain daemons
try:
    pwd = pwd.getpwnam(btcuser)
except KeyError:
    print "Creating user:",btcuser
    os.system('useradd -r -s /bin/false '+btcuser)
    pwd = pwd.getpwnam(btcuser)

### Create directories if not already    
if not os.path.exists(cfgdir):
    print "Creating directory:", cfgdir
    os.makedirs(cfgdir)
    os.chown(cfgdir, pwd.pw_uid, pwd.pw_gid)
if not os.path.exists(btcdir):
    print "Creating directory:", btcdir
    os.makedirs(btcdir)
    os.chown(btcdir, pwd.pw_uid, pwd.pw_gid)
if not os.path.exists(sqldir):
    print "Creating directory:", sqldir
    os.makedirs(sqldir)
    os.chown(sqldir, pwd.pw_uid, pwd.pw_gid)
if not os.path.exists(wwwdir):
    print "Creating directory:", wwwdir
    os.makedirs(wwwdir)
    os.chown(wwwdir, pwd.pw_uid, pwd.pw_gid)
    for root, dirs, files in os.walk('www'):  
        dst = root[root.index('/'):] if '/'  in root else '/'
        for d in dirs:  
            os.mkdir(os.path.join(wwwdir+dst, d))
            os.chown(os.path.join(wwwdir+dst, d), pwd.pw_uid, pwd.pw_gid)
        for f in files:
            shutil.copy2(os.path.join(root, f), os.path.join(wwwdir+dst, f))
            os.chown(os.path.join(wwwdir+dst, f), pwd.pw_uid, pwd.pw_gid)
    
### Create empty data files and set ownership
for f in ['/blobs.dat','/hdrs.dat']:
    try:
        os.utime(sqldir+f, None)
    except:
        print "Creating data file:", sqldir+f
        open(sqldir+f, 'a').close()
        os.chown(sqldir+f, pwd.pw_uid, pwd.pw_gid)

### Create MySql database and tables   
print "Creating MySQL database:", dbname 
try:
    sqlsrc = open('docs/sqlchain.sql').read()
    sqlcode = ''
    for k,v in [('--CREATE','CREATE'),('--GRANT','GRANT'),('--FLUSH','FLUSH'),('bitcoin',dbname),('sqlpwd',dbpwd),('btc',dbuser)]:
        sqlsrc = sqlsrc.replace(k, v)
    for line in sqlsrc.splitlines():
        if line != '' and line[:2] != '--':
            sqlcode += line
    
    sql = db.connect(user='root', passwd=rootpwd)
    cur = sql.cursor()
    for stmnt in sqlcode.split(';'):
        if stmnt:
            cur.execute(stmnt)
except (IOError, ImportError):
    print "Cannot open docs/sqlchain.sql script"
    print "Skipping further MySQL DB setup\n"
    pass
except db.Error, e:
    print "MySQL Error: %s" % str(e)
    print "Skipping further MySQL DB setup\n"
    pass

### Create init scripts for system boot
initsrc = """
description "sqlChain - Bitcoin SQL layer and API"

start on runlevel [2345]
stop on starting rc RUNLEVEL=[016]

expect fork
respawn limit 5 120
kill timeout 60

"""
initfile = '/etc/init/bitcoin.conf' + ('.disabled' if btcboot.upper() != 'Y' else '')
print "Creating system init file: %s" % initfile
with open(initfile, 'w') as f:
    s = "exec start-stop-daemon --start --pidfile %s/bitcoind.pid --chuid %s: --exec /usr/local/bin/bitcoind -- -conf=%s/bitcoin.conf\n" % (sqldir,btcuser,cfgdir)
    f.write(initsrc+s)
initfile = '/etc/init/sqlchain.conf' + ('.disabled' if sqlboot.upper() != 'Y' else '')
print "Creating system init file: %s" % initfile 
with open(initfile, 'w') as f:
    s = "exec start-stop-daemon --start --pidfile %s/daemon.pid --exec /usr/local/bin/sqlchaind -- %s/sqlchaind.cfg\n" % (sqldir,cfgdir)
    f.write(initsrc.replace('expect fork','')+s)
initfile = '/etc/init/sqlchain-api.conf' + ('.disabled' if apiboot.upper() != 'Y' else '')
print "Creating system init file: %s" % initfile 
with open(initfile, 'w') as f:
    s = "exec start-stop-daemon --start --pidfile %s/api.pid --exec /usr/local/bin/sqlchain-api -- %s/sqlchain-api.cfg\n" % (sqldir,cfgdir)
    f.write(initsrc.replace('expect fork','')+s)
    
### Create sqlchaind.cfg 
sqlcfg = """
{
  "log": "datapath/daemon.log",
  "pid": "datapath/daemon.pid",
  "path": "datapath",
  "db": "localhost:dbuser:dbpwd:dbname",
  "queue": 8,
  "rpc": "http://btcuser:rpcpwd@localhost:8332",
  "debug": false,
  "user": "btcuser",
  "no-sigs": false
}"""
print "Creating config file:", cfgdir+'/sqlchaind.cfg'
for k,v in [('datapath',sqldir),('dbuser',dbuser),('btcuser',btcuser),('rpcpwd',btcpwd),('dbpwd',dbpwd),('dbname',dbname)]:
    sqlcfg = sqlcfg.replace(k, v)
with open(cfgdir+'/sqlchaind.cfg', 'w') as f:
    f.write(sqlcfg)
os.chown(cfgdir+'/sqlchaind.cfg', pwd.pw_uid, pwd.pw_gid)

### Create sqlchain-api.cfg 
apicfg = """
{
  "www": "wwwpath", 
  "user": "btcuser",
  "db": "localhost:dbuser:dbpwd:dbname", 
  "dbinfo": -1, 
  "dbinfo-ts": "0", 
  "rpc": "http://btcuser:rpcpwd@localhost:8332", 
  "pool": 4, 
  "log": "datapath/api.log", 
  "pid": "datapath/api.pid",
  "path": "datapath",
  "debug": false, 
  "block": 0, 
  "listen": "localhost:8085"
}"""
print "Creating config file:", cfgdir+'/sqlchain-api.cfg'
for k,v in [('datapath',sqldir),('wwwpath',wwwdir),('dbuser',dbuser),('btcuser',btcuser),('rpcpwd',btcpwd),('dbpwd',dbpwd),('dbname',dbname)]:
    apicfg = apicfg.replace(k, v)
with open(cfgdir+'/sqlchain-api.cfg', 'w') as f:
    f.write(apicfg)
os.chown(cfgdir+'/sqlchain-api.cfg', pwd.pw_uid, pwd.pw_gid)
    
### Create or update the bitcoin.conf
btcfg = {}
try:
    conf = open(cfgdir+'/bitcoin.conf').read()
    print "Updating file: %s/bitcoin.conf" % cfgdir
    for line in conf.splitlines():
        k,v = line.split('=')
        btcfg[k] = v
except (IOError, ImportError):
    print "Creating file: %s/bitcoin.conf" % cfgdir
btcfg['server'] = 1
btcfg['daemon'] = 1
btcfg['prune'] = 1 if btcprune.upper() == 'Y' else 0
btcfg['rpcuser'] = btcuser
btcfg['rpcpassword'] = btcpwd
btcfg['datadir'] = btcdir
btcfg['pid'] = sqldir+'/bitcoind.pid'

with open(cfgdir+'/bitcoin.conf', 'w') as f:
    for k in btcfg:
        f.write("%s=%s\n" % (k,btcfg[k]))
os.chown(cfgdir+'/bitcoin.conf', pwd.pw_uid, pwd.pw_gid)
    
    
