2011-11-04 18:00:37 +01:00
#!/usr/bin/env python
#
# Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2012-08-23 17:05:07 -07:00
import re
2013-03-02 16:29:14 +01:00
import pkgutil
2013-02-27 18:11:45 +01:00
import sys , os , time , json
2012-08-25 14:11:50 -07:00
import optparse
2013-02-18 23:29:19 +01:00
import platform
2013-03-02 10:22:45 +01:00
from decimal import Decimal
2013-08-22 12:39:41 +02:00
import traceback
2012-08-23 17:05:07 -07:00
2012-05-14 14:09:50 +02:00
try :
import ecdsa
2012-08-19 15:10:17 -07:00
except ImportError :
sys . exit ( " Error: python-ecdsa does not seem to be installed. Try ' sudo pip install ecdsa ' " )
2012-05-14 14:09:50 +02:00
try :
import aes
2012-08-19 15:10:17 -07:00
except ImportError :
sys . exit ( " Error: AES does not seem to be installed. Try ' sudo pip install slowaes ' " )
2012-05-14 14:09:50 +02:00
2012-08-30 00:03:38 +02:00
2013-03-15 13:00:59 +01:00
is_local = os . path . dirname ( os . path . realpath ( __file__ ) ) == os . getcwd ( )
2013-03-12 13:48:16 +01:00
is_android = ' ANDROID_DATA ' in os . environ
2013-03-15 13:00:59 +01:00
import __builtin__
__builtin__ . use_local_modules = is_local or is_android
2013-03-02 10:22:45 +01:00
# load local module as electrum
2013-03-15 13:00:59 +01:00
if __builtin__ . use_local_modules :
2013-03-02 10:22:45 +01:00
import imp
2013-03-15 10:49:08 +01:00
imp . load_module ( ' electrum ' , * imp . find_module ( ' lib ' ) )
imp . load_module ( ' electrum_gui ' , * imp . find_module ( ' gui ' ) )
2012-02-03 08:02:12 +01:00
2013-03-02 10:22:45 +01:00
from electrum import *
2012-06-06 19:26:05 +02:00
2012-10-12 01:50:54 +02:00
# get password routine
def prompt_password ( prompt , confirm = True ) :
import getpass
if sys . stdin . isatty ( ) :
password = getpass . getpass ( prompt )
if password and confirm :
password2 = getpass . getpass ( " Confirm: " )
if password != password2 :
sys . exit ( " Error: Passwords do not match. " )
else :
password = raw_input ( prompt )
if not password :
password = None
return password
2012-11-23 14:31:25 +01:00
def arg_parser ( ) :
2013-04-22 12:19:13 -07:00
usage = " % prog [options] command "
2013-09-18 20:08:41 +02:00
parser = optparse . OptionParser ( prog = usage , add_help_option = False )
parser . add_option ( " -h " , " --help " , action = " callback " , callback = print_help_cb , help = " show this help text " )
2013-09-19 23:13:37 +02:00
parser . add_option ( " -g " , " --gui " , dest = " gui " , help = " User interface: qt, lite, gtk, text or stdio " )
2011-11-29 11:23:49 +01:00
parser . add_option ( " -w " , " --wallet " , dest = " wallet_path " , help = " wallet path (default: electrum.dat) " )
2012-05-13 10:19:28 +02:00
parser . add_option ( " -o " , " --offline " , action = " store_true " , dest = " offline " , default = False , help = " remain offline " )
2011-11-14 20:35:54 +01:00
parser . add_option ( " -a " , " --all " , action = " store_true " , dest = " show_all " , default = False , help = " show all addresses " )
2013-03-01 11:33:51 +01:00
parser . add_option ( " -b " , " --balance " , action = " store_true " , dest = " show_balance " , default = False , help = " show the balance of listed addresses " )
2013-03-01 11:21:10 +01:00
parser . add_option ( " -l " , " --labels " , action = " store_true " , dest = " show_labels " , default = False , help = " show the labels of listed addresses " )
2013-03-23 09:23:57 +01:00
parser . add_option ( " -f " , " --fee " , dest = " tx_fee " , default = None , help = " set tx fee " )
2012-10-11 20:10:12 +02:00
parser . add_option ( " -F " , " --fromaddr " , dest = " from_addr " , default = None , help = " set source address for payto/mktx. if it isn ' t in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It ' s not saved in the wallet. " )
2012-02-08 09:36:19 +01:00
parser . add_option ( " -c " , " --changeaddr " , dest = " change_addr " , default = None , help = " set the change address for payto/mktx. default is a spare address, or the source address if it ' s not in the wallet " )
2013-09-22 20:37:37 +02:00
parser . add_option ( " -s " , " --server " , dest = " server " , default = None , help = " set server host:port:protocol, where protocol is either t (tcp), h (http), s (tcp+ssl), or g (https) " )
2012-10-01 18:14:50 +02:00
parser . add_option ( " -p " , " --proxy " , dest = " proxy " , default = None , help = " set proxy [type:]host[:port], where type is socks4,socks5 or http " )
2012-11-04 12:27:01 +01:00
parser . add_option ( " -v " , " --verbose " , action = " store_true " , dest = " verbose " , default = False , help = " show debugging information " )
2012-12-17 15:08:34 +01:00
parser . add_option ( " -P " , " --portable " , action = " store_true " , dest = " portable " , default = False , help = " portable wallet " )
2013-01-02 16:03:54 +01:00
parser . add_option ( " -L " , " --lang " , dest = " language " , default = None , help = " defaut language used in GUI " )
2013-01-07 16:03:03 +00:00
parser . add_option ( " -u " , " --usb " , dest = " bitkey " , action = " store_true " , help = " Turn on support for hardware wallets (EXPERIMENTAL) " )
2013-09-12 16:08:17 +02:00
parser . add_option ( " -G " , " --gap " , dest = " gap_limit " , default = None , help = " gap limit " )
2013-09-18 23:34:01 +02:00
parser . add_option ( " -W " , " --password " , dest = " password " , default = None , help = " set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc) " )
2012-11-23 14:31:25 +01:00
return parser
2012-11-18 11:34:52 +01:00
2013-09-18 20:08:41 +02:00
def print_help ( parser ) :
parser . print_help ( )
print_msg ( " Type ' electrum help <command> ' to see the help for a specific command " )
print_msg ( " Type ' electrum --help ' to see the list of options " )
run_command ( ' help ' )
exit ( 1 )
def print_help_cb ( self , opt , value , parser ) :
print_help ( parser )
2013-09-18 20:22:30 +02:00
def run_command ( cmd , password = None , args = [ ] ) :
2013-09-18 20:08:41 +02:00
cmd_runner = Commands ( wallet , network )
func = eval ( ' cmd_runner. ' + cmd )
cmd_runner . password = password
try :
result = func ( * args [ 1 : ] )
except BaseException , e :
import traceback
traceback . print_exc ( file = sys . stdout )
sys . exit ( 1 )
if type ( result ) == str :
util . print_msg ( result )
elif result is not None :
util . print_json ( result )
2011-11-04 18:00:37 +01:00
2012-11-18 11:34:52 +01:00
if __name__ == ' __main__ ' :
2012-11-23 14:31:25 +01:00
parser = arg_parser ( )
options , args = parser . parse_args ( )
2013-04-11 22:06:55 +02:00
if options . portable and options . wallet_path is None :
2013-04-20 18:21:51 +02:00
options . wallet_path = os . path . join ( os . path . dirname ( os . path . realpath ( __file__ ) ) , ' electrum.dat ' )
2012-11-04 12:27:01 +01:00
set_verbosity ( options . verbose )
2012-10-29 16:22:53 +01:00
2012-10-11 20:10:12 +02:00
# config is an object passed to the various constructors (wallet, interface, gui)
2013-03-12 13:48:16 +01:00
if is_android :
2013-03-12 14:12:27 +01:00
config_options = { ' wallet_path ' : " /sdcard/electrum.dat " , ' portable ' : True , ' verbose ' : True , ' gui ' : ' android ' , ' auto_cycle ' : True }
2012-11-18 11:34:52 +01:00
else :
2012-11-19 14:05:42 +01:00
config_options = eval ( str ( options ) )
for k , v in config_options . items ( ) :
if v is None : config_options . pop ( k )
2012-11-18 11:34:52 +01:00
2013-02-18 23:29:19 +01:00
# Wallet migration on Electrum 1.7
# Todo: In time we could remove this again
if platform . system ( ) == " Windows " :
util . check_windows_wallet_migration ( )
2012-11-19 14:05:42 +01:00
config = SimpleConfig ( config_options )
2012-03-12 17:55:33 +01:00
2012-02-14 12:45:39 +01:00
if len ( args ) == 0 :
url = None
cmd = ' gui '
elif len ( args ) == 1 and re . match ( ' ^bitcoin: ' , args [ 0 ] ) :
url = args [ 0 ]
cmd = ' gui '
else :
cmd = args [ 0 ]
2012-08-22 11:36:04 -07:00
2012-02-11 13:14:12 +01:00
2013-03-02 18:03:29 +01:00
if cmd == ' gui ' :
gui_name = config . get ( ' gui ' , ' classic ' )
2013-09-24 10:06:03 +02:00
if gui_name in [ ' lite ' , ' classic ' ] : gui_name = ' qt '
2013-03-02 18:03:29 +01:00
try :
2013-09-24 10:06:03 +02:00
gui = __import__ ( ' electrum_gui. ' + gui_name , fromlist = [ ' electrum_gui ' ] )
2013-03-02 18:03:29 +01:00
except ImportError :
2013-08-22 12:39:41 +02:00
traceback . print_exc ( file = sys . stdout )
sys . exit ( )
#sys.exit("Error: Unknown GUI: " + gui_name )
2012-11-20 15:30:46 +01:00
2013-08-30 10:11:10 +02:00
# network interface
2013-09-08 17:23:01 +02:00
network = Network ( config )
network . start ( )
2013-08-30 10:11:10 +02:00
2013-09-08 17:23:01 +02:00
gui = gui . ElectrumGui ( config , network )
2012-02-14 12:45:39 +01:00
gui . main ( url )
2013-08-30 10:11:10 +02:00
2013-09-08 17:23:01 +02:00
network . stop ( )
2012-12-05 10:24:30 +01:00
# we use daemon threads, their termination is enforced.
# this sleep command gives them time to terminate cleanly.
time . sleep ( 0.1 )
2011-11-10 09:34:27 +01:00
sys . exit ( 0 )
2011-11-04 18:00:37 +01:00
2013-08-22 12:39:41 +02:00
2013-10-03 12:39:42 +02:00
if cmd not in known_commands :
cmd = ' help '
cmd = known_commands [ cmd ]
2013-08-22 12:39:41 +02:00
# instanciate wallet for command-line
2013-09-02 15:05:33 +02:00
storage = WalletStorage ( config )
2013-08-22 12:39:41 +02:00
2013-10-03 12:39:42 +02:00
if cmd . requires_wallet :
wallet = Wallet ( storage )
else :
wallet = None
2012-01-15 18:45:30 +01:00
2013-10-03 12:39:42 +02:00
if cmd . name not in [ ' create ' , ' restore ' ] and cmd . requires_wallet and not storage . file_exists :
2012-11-23 18:48:56 +01:00
print_msg ( " Error: Wallet file not found. " )
print_msg ( " Type ' electrum create ' to create a new wallet, or provide a path to a wallet with the -w option " )
2011-11-16 18:26:06 +03:00
sys . exit ( 0 )
2013-10-03 12:39:42 +02:00
if cmd . name in [ ' create ' , ' restore ' ] :
if storage . file_exists :
2012-08-19 17:16:35 -07:00
sys . exit ( " Error: Remove the existing wallet first! " )
2013-09-18 23:34:01 +02:00
if options . password != None :
password = options . password
else :
password = prompt_password ( " Password (hit return if you do not wish to encrypt your wallet): " )
# if config.server is set, the user either passed the server on command line
# or chose it previously already. if he didn't pass a server on the command line,
# we just pick up a random one.
if not config . get ( ' server ' ) :
config . set_key ( ' server ' , pick_random_server ( ) )
fee = options . tx_fee if options . tx_fee else raw_input ( " fee (default: %s ): " % ( str ( Decimal ( wallet . fee ) / 100000000 ) ) )
gap = options . gap_limit if options . gap_limit else raw_input ( " gap limit (default 5): " )
if fee : wallet . set_fee ( float ( fee ) * 100000000 )
if gap : wallet . change_gap_limit ( int ( gap ) )
2012-02-21 14:36:45 +01:00
2013-10-03 12:39:42 +02:00
if cmd . name == ' restore ' :
2012-02-21 14:36:45 +01:00
seed = raw_input ( " seed: " )
try :
seed . decode ( ' hex ' )
except :
2012-07-07 09:24:52 -07:00
print_error ( " Warning: Not hex, trying decode. " )
2012-11-04 19:40:17 +01:00
seed = mnemonic_decode ( seed . split ( ' ' ) )
2012-02-21 14:36:45 +01:00
if not seed :
2012-08-19 17:17:47 -07:00
sys . exit ( " Error: No seed " )
2012-02-21 14:36:45 +01:00
2013-09-11 17:05:50 +02:00
wallet . init_seed ( str ( seed ) )
wallet . save_seed ( )
2013-09-16 06:14:23 +02:00
network = Network ( config )
network . start ( )
wallet . start_threads ( network )
2013-09-28 13:45:49 +02:00
print_msg ( " Recovering wallet... " )
wallet . restore ( lambda x : x )
2013-09-11 17:05:50 +02:00
if wallet . is_found ( ) :
print_msg ( " Recovery successful " )
2012-09-20 16:46:11 +07:00
else :
2013-09-11 17:05:50 +02:00
print_msg ( " Warning: Found no history for this wallet " )
2011-11-06 11:13:58 +01:00
else :
2013-02-03 15:08:26 +01:00
wallet . init_seed ( None )
2013-04-14 19:32:25 +02:00
wallet . save_seed ( )
2013-09-11 17:05:50 +02:00
wallet . create_accounts ( )
wallet . synchronize ( )
2012-11-23 18:48:56 +01:00
print_msg ( " Your wallet generation seed is: " + wallet . seed )
print_msg ( " Please keep it in a safe place; if you lose it, you will not be able to restore your wallet. " )
print_msg ( " Equivalently, your wallet seed can be stored and recovered with the following mnemonic code: " )
print_msg ( " \" " + ' ' . join ( mnemonic_encode ( wallet . seed ) ) + " \" " )
2013-09-18 23:34:01 +02:00
print_msg ( " Wallet saved in ' %s ' " % wallet . storage . path )
2012-05-17 08:49:30 +02:00
if password :
wallet . update_password ( wallet . seed , None , password )
2011-11-04 18:00:37 +01:00
2013-02-28 17:21:30 +01:00
# terminate
sys . exit ( 0 )
2011-11-12 00:07:41 +01:00
2013-02-27 09:04:22 +01:00
2012-10-02 13:15:10 +02:00
# important warning
2013-10-03 12:39:42 +02:00
if cmd . name in [ ' dumpprivkey ' , ' dumpprivkeys ' ] :
2012-11-23 18:48:56 +01:00
print_msg ( " WARNING: ALL your private keys are secret. " )
print_msg ( " Exposing a single private key can compromise your entire wallet! " )
print_msg ( " In particular, DO NOT use ' redeem private key ' services proposed by third parties. " )
2012-10-02 13:15:10 +02:00
2011-11-14 20:35:54 +01:00
# commands needing password
2013-10-03 12:39:42 +02:00
if cmd . requires_password :
2013-02-26 13:56:48 +01:00
if wallet . use_encryption :
2013-01-02 13:39:50 +01:00
password = prompt_password ( ' Password: ' , False )
if not password :
print_msg ( " Error: Password required " )
exit ( 1 )
# check password
try :
2013-01-06 09:41:06 +01:00
seed = wallet . decode_seed ( password )
2013-01-02 13:39:50 +01:00
except :
print_msg ( " Error: This password does not decode this wallet. " )
exit ( 1 )
else :
password = None
2013-01-06 15:57:01 +01:00
seed = wallet . seed
2013-02-26 13:56:48 +01:00
else :
password = None
2011-12-20 14:22:31 +01:00
2013-03-01 14:08:51 +01:00
# add missing arguments, do type conversions
2013-10-03 12:39:42 +02:00
if cmd . name == ' importprivkey ' :
2012-08-01 21:52:52 +02:00
# See if they specificed a key on the cmd line, if not prompt
2013-02-26 13:56:48 +01:00
if len ( args ) == 1 :
args [ 1 ] = prompt_password ( ' Enter PrivateKey (will not echo): ' , False )
2013-10-03 12:39:42 +02:00
elif cmd . name == ' signrawtransaction ' :
2013-02-27 18:11:45 +01:00
args = [ cmd , args [ 1 ] , json . loads ( args [ 2 ] ) if len ( args ) > 2 else [ ] , json . loads ( args [ 3 ] ) if len ( args ) > 3 else [ ] ]
2011-12-20 12:37:48 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' createmultisig ' :
2013-02-27 18:11:45 +01:00
args = [ cmd , int ( args [ 1 ] ) , json . loads ( args [ 2 ] ) ]
2013-02-26 13:56:48 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' createrawtransaction ' :
2013-02-27 18:11:45 +01:00
args = [ cmd , json . loads ( args [ 1 ] ) , json . loads ( args [ 2 ] ) ]
2013-02-26 16:03:04 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' listaddresses ' :
2013-03-01 11:21:10 +01:00
args = [ cmd , options . show_all , options . show_balance , options . show_labels ]
2013-02-26 13:56:48 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name in [ ' payto ' , ' mktx ' ] :
2013-04-05 16:00:34 +02:00
domain = [ options . from_addr ] if options . from_addr else None
args = [ ' mktx ' , args [ 1 ] , Decimal ( args [ 2 ] ) , Decimal ( options . tx_fee ) if options . tx_fee else None , options . change_addr , domain ]
2013-04-08 23:36:26 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name in [ ' paytomany ' , ' mksendmanytx ' ] :
2013-04-08 23:36:26 +01:00
domain = [ options . from_addr ] if options . from_addr else None
outputs = [ ]
for i in range ( 1 , len ( args ) , 2 ) :
if len ( args ) < i + 2 :
print_msg ( " Error: Mismatched arguments. " )
exit ( 1 )
outputs . append ( ( args [ i ] , Decimal ( args [ i + 1 ] ) ) )
args = [ ' mksendmanytx ' , outputs , Decimal ( options . tx_fee ) if options . tx_fee else None , options . change_addr , domain ]
2013-02-26 13:56:48 +01:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' help ' :
2013-03-04 17:36:49 +01:00
if len ( args ) < 2 :
2013-09-18 20:08:41 +02:00
print_help ( parser )
2013-02-26 13:56:48 +01:00
2013-03-01 13:33:11 +01:00
# check the number of arguments
2013-10-03 12:39:42 +02:00
if len ( args ) - 1 < cmd . min_args :
2013-03-01 13:33:11 +01:00
print_msg ( " Not enough arguments " )
print_msg ( " Syntax: " , syntax )
sys . exit ( 1 )
2013-10-03 12:39:42 +02:00
if cmd . max_args > = 0 and len ( args ) - 1 > cmd . max_args :
2013-03-01 13:33:11 +01:00
print_msg ( " too many arguments " , args )
print_msg ( " Syntax: " , syntax )
sys . exit ( 1 )
2013-10-03 12:39:42 +02:00
if cmd . max_args < 0 :
if len ( args ) > cmd . min_args + 1 :
message = ' ' . join ( args [ cmd . min_args : ] )
2013-03-01 13:33:11 +01:00
print_msg ( " Warning: Final argument was reconstructed from several arguments: " , repr ( message ) )
2013-10-03 12:39:42 +02:00
args = args [ 0 : cmd . min_args ] + [ message ]
2013-03-01 13:33:11 +01:00
# open session
2013-10-03 12:39:42 +02:00
if cmd . requires_network and not options . offline :
2013-09-09 13:33:25 +02:00
network = Network ( config )
network . register_callback ( ' connected ' , lambda : sys . stderr . write ( " Connected to " + network . interface . connection_msg + " \n " ) )
if not network . start ( wait = True ) :
2013-03-13 15:26:29 +01:00
print_msg ( " Not connected, aborting. " )
sys . exit ( 1 )
2013-09-09 13:33:25 +02:00
2013-10-03 12:39:42 +02:00
if wallet :
wallet . start_threads ( network )
wallet . update ( )
else :
network = None
2013-09-01 15:26:52 +02:00
2013-02-26 13:56:48 +01:00
# run the command
2011-11-16 18:12:13 +03:00
2013-10-03 12:39:42 +02:00
if cmd . name == ' deseed ' :
2012-05-13 01:32:28 +02:00
if not wallet . seed :
2012-11-27 23:32:39 +01:00
print_msg ( " Error: This wallet has no seed " )
2012-05-13 00:43:22 +02:00
else :
2013-02-25 10:49:31 +01:00
ns = wallet . config . path + ' .seedless '
print_msg ( " Warning: you are going to create a seedless wallet ' \n It will be saved in ' %s ' " % ns )
2012-05-13 01:32:28 +02:00
if raw_input ( " Are you sure you want to continue? (y/n) " ) in [ ' y ' , ' Y ' , ' yes ' ] :
2013-02-25 10:49:31 +01:00
wallet . config . path = ns
2012-05-13 01:32:28 +02:00
wallet . seed = ' '
2013-09-01 23:25:28 +02:00
wallet . storage . put ( ' seed ' , ' ' , True )
2013-02-27 18:01:58 +01:00
wallet . use_encryption = False
2013-09-01 23:25:28 +02:00
wallet . storage . put ( ' use_encryption ' , wallet . use_encryption , True )
2012-05-17 18:10:36 +02:00
for k in wallet . imported_keys . keys ( ) : wallet . imported_keys [ k ] = ' '
2013-05-02 10:54:48 +02:00
wallet . config . set_key ( ' imported_keys ' , wallet . imported_keys , True )
2012-11-23 18:48:56 +01:00
print_msg ( " Done. " )
2012-05-13 01:32:28 +02:00
else :
2012-11-27 23:32:39 +01:00
print_msg ( " Action canceled. " )
2012-05-13 01:32:28 +02:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' getconfig ' :
2012-10-26 17:35:35 +02:00
key = args [ 1 ]
2013-09-01 23:25:28 +02:00
print_msg ( config . get ( key ) )
2012-10-26 17:35:35 +02:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' setconfig ' :
2012-10-20 10:23:34 +02:00
key , value = args [ 1 : 3 ]
2012-10-28 09:19:07 +01:00
if key not in [ ' seed ' , ' seed_version ' , ' master_public_key ' , ' use_encryption ' ] :
2013-09-01 23:25:28 +02:00
config . set_key ( key , value , True )
2012-11-23 18:48:56 +01:00
print_msg ( True )
2012-10-20 10:23:34 +02:00
else :
2012-11-23 18:48:56 +01:00
print_msg ( False )
2012-10-20 10:23:34 +02:00
2013-10-03 12:39:42 +02:00
elif cmd . name == ' password ' :
2012-07-06 21:45:57 -07:00
new_password = prompt_password ( ' New password: ' )
wallet . update_password ( seed , password , new_password )
2011-11-04 18:00:37 +01:00
2013-02-26 13:56:48 +01:00
else :
2013-10-03 12:39:42 +02:00
run_command ( cmd . name , password , args )
2013-02-20 13:10:32 +01:00
2013-10-03 12:39:42 +02:00
if network :
if wallet :
wallet . stop_threads ( )
2013-09-09 13:33:25 +02:00
network . stop ( )
2013-02-25 22:21:07 +01:00
time . sleep ( 0.1 )
sys . exit ( 0 )