util.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2016 Philemon <mmgen-py@yandex.com>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. util.py: Low-level routines imported by other modules for the MMGen suite
  20. """
  21. import sys,os,time,stat,re
  22. from hashlib import sha256
  23. from binascii import hexlify,unhexlify
  24. from string import hexdigits
  25. import mmgen.globalvars as g
  26. pnm = g.proj_name
  27. _red,_grn,_yel,_cya,_reset,_grnbg = \
  28. ['\033[%sm' % c for c in '31;1','32;1','33;1','36;1','0','30;102']
  29. def red(s): return _red+s+_reset
  30. def green(s): return _grn+s+_reset
  31. def grnbg(s): return _grnbg+s+_reset
  32. def yellow(s): return _yel+s+_reset
  33. def cyan(s): return _cya+s+_reset
  34. def nocolor(s): return s
  35. def start_mscolor():
  36. if sys.platform[:3] == 'win':
  37. global red,green,yellow,cyan,nocolor
  38. import os
  39. if 'MMGEN_NOMSCOLOR' in os.environ:
  40. red = green = yellow = cyan = grnbg = nocolor
  41. else:
  42. try:
  43. import colorama
  44. colorama.init(strip=True,convert=True)
  45. except:
  46. red = green = yellow = cyan = grnbg = nocolor
  47. def msg(s): sys.stderr.write(s+'\n')
  48. def msg_r(s): sys.stderr.write(s)
  49. def Msg(s): sys.stdout.write(s + '\n')
  50. def Msg_r(s): sys.stdout.write(s)
  51. def msgred(s): sys.stderr.write(red(s+'\n'))
  52. def mmsg(*args):
  53. for d in args:
  54. sys.stdout.write(repr(d)+'\n')
  55. def mdie(*args):
  56. for d in args:
  57. sys.stdout.write(repr(d)+'\n')
  58. sys.exit()
  59. def die(ev,s):
  60. sys.stderr.write(s+'\n'); sys.exit(ev)
  61. def Die(ev,s):
  62. sys.stdout.write(s+'\n'); sys.exit(ev)
  63. def is_mmgen_wallet_label(s):
  64. if len(s) > g.max_wallet_label_len:
  65. msg('ERROR: wallet label length (%s chars) > maximum allowed (%s chars)' % (len(s),g.max_wallet_label_len))
  66. return False
  67. try: s = s.decode('utf8')
  68. except: pass
  69. for ch in s:
  70. if ch not in g.wallet_label_symbols:
  71. msg('ERROR: wallet label contains illegal symbol (%s)' % ch)
  72. return False
  73. return True
  74. # From 'man dd':
  75. # c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
  76. # GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
  77. def parse_nbytes(nbytes):
  78. import re
  79. m = re.match(r'([0123456789]+)(.*)',nbytes)
  80. smap = ('c',1),('w',2),('b',512),('kB',1000),('K',1024),('MB',1000*1000),\
  81. ('M',1024*1024),('GB',1000*1000*1000),('G',1024*1024*1024)
  82. if m:
  83. if m.group(2):
  84. for k,v in smap:
  85. if k == m.group(2):
  86. return int(m.group(1)) * v
  87. else:
  88. msg("Valid byte specifiers: '%s'" % "' '".join([i[0] for i in smap]))
  89. else:
  90. return int(nbytes)
  91. die(1,"'%s': invalid byte specifier" % nbytes)
  92. from mmgen.opts import opt
  93. def qmsg(s,alt=False):
  94. if opt.quiet:
  95. if alt != False: sys.stderr.write(alt + '\n')
  96. else: sys.stderr.write(s + '\n')
  97. def qmsg_r(s,alt=False):
  98. if opt.quiet:
  99. if alt != False: sys.stderr.write(alt)
  100. else: sys.stderr.write(s)
  101. def vmsg(s):
  102. if opt.verbose: sys.stderr.write(s + '\n')
  103. def vmsg_r(s):
  104. if opt.verbose: sys.stderr.write(s)
  105. def Vmsg(s):
  106. if opt.verbose: sys.stdout.write(s + '\n')
  107. def Vmsg_r(s):
  108. if opt.verbose: sys.stdout.write(s)
  109. def dmsg(s):
  110. if opt.debug: sys.stdout.write(s + '\n')
  111. def suf(arg,suf_type):
  112. t = type(arg)
  113. if t == int:
  114. n = arg
  115. elif t == list or t == tuple or t == set:
  116. n = len(arg)
  117. else:
  118. msg('%s: invalid parameter' % arg)
  119. return ''
  120. if suf_type in ('a','es'): return ('es','')[n == 1]
  121. if suf_type in ('k','s'): return ('s','')[n == 1]
  122. def get_extension(f):
  123. a,b = os.path.splitext(f)
  124. return ('',b[1:])[len(b) > 1]
  125. def remove_extension(f,e):
  126. a,b = os.path.splitext(f)
  127. return (f,a)[len(b)>1 and b[1:]==e]
  128. def make_chksum_N(s,nchars,sep=False):
  129. if nchars%4 or not (4 <= nchars <= 64): return False
  130. s = sha256(sha256(s).digest()).hexdigest().upper()
  131. sep = ('',' ')[bool(sep)]
  132. return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
  133. def make_chksum_8(s,sep=False):
  134. s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
  135. return '{} {}'.format(s[:4],s[4:]) if sep else s
  136. def make_chksum_6(s): return sha256(s).hexdigest()[:6]
  137. def is_chksum_6(s): return len(s) == 6 and is_hexstring_lc(s)
  138. def make_iv_chksum(s): return sha256(s).hexdigest()[:8].upper()
  139. def splitN(s,n,sep=None): # always return an n-element list
  140. ret = s.split(sep,n-1)
  141. return ret + ['' for i in range(n-len(ret))]
  142. def split2(s,sep=None): return splitN(s,2,sep) # always return a 2-element list
  143. def split3(s,sep=None): return splitN(s,3,sep) # always return a 3-element list
  144. def split_into_cols(col_wid,s):
  145. return ' '.join([s[col_wid*i:col_wid*(i+1)]
  146. for i in range(len(s)/col_wid+1)]).rstrip()
  147. def capfirst(s):
  148. return s if len(s) == 0 else s[0].upper() + s[1:]
  149. def make_timestamp():
  150. tv = time.gmtime(time.time())[:6]
  151. return '{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}'.format(*tv)
  152. def make_timestr():
  153. tv = time.gmtime(time.time())[:6]
  154. return '{:04d}/{:02d}/{:02d} {:02d}:{:02d}:{:02d}'.format(*tv)
  155. def secs_to_hms(secs):
  156. return '{:02d}:{:02d}:{:02d}'.format(secs/3600, (secs/60) % 60, secs % 60)
  157. def _is_whatstring(s,chars):
  158. return set(list(s)) <= set(chars)
  159. def is_int(s):
  160. try:
  161. int(s)
  162. return True
  163. except:
  164. return False
  165. def is_hexstring(s):
  166. return _is_whatstring(s.lower(),hexdigits.lower())
  167. def is_hexstring_lc(s):
  168. return _is_whatstring(s,hexdigits.lower())
  169. def is_hexstring_uc(s):
  170. return _is_whatstring(s,hexdigits.upper())
  171. def is_b58string(s):
  172. from mmgen.bitcoin import b58a
  173. return _is_whatstring(s,b58a)
  174. def is_utf8(s):
  175. try: s.decode('utf8')
  176. except: return False
  177. else: return True
  178. def is_ascii(s):
  179. try: s.decode('ascii')
  180. except: return False
  181. else: return True
  182. def match_ext(addr,ext):
  183. return addr.split('.')[-1] == ext
  184. def file_exists(f):
  185. try:
  186. os.stat(f)
  187. return True
  188. except:
  189. return False
  190. def file_is_readable(f):
  191. from stat import S_IREAD
  192. try:
  193. assert os.stat(f).st_mode & S_IREAD
  194. except:
  195. return False
  196. else:
  197. return True
  198. def get_homedir():
  199. if 'HOME' in os.environ: # Linux
  200. return os.environ['HOME']
  201. elif 'HOMEPATH' in os.environ: # Windows:
  202. return os.environ['HOMEPATH']
  203. else:
  204. msg('Neither $HOME nor %HOMEPATH% are set')
  205. die(2,"Don't know where to look for bitcoin data directory")
  206. def get_datadir():
  207. return (r'Application Data\Bitcoin','.bitcoin')['HOME' in os.environ]
  208. def get_from_brain_opt_params():
  209. l,p = opt.from_brain.split(',')
  210. return(int(l),p)
  211. def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
  212. r = (0,1)[bool(len(data) % gw)]
  213. return ''.join(
  214. [
  215. ('' if (line_nums == False or i % cols) else '{:06x}: '.format(i*gw)) +
  216. hexlify(data[i*gw:i*gw+gw]) + ('\n',' ')[bool((i+1) % cols)]
  217. for i in range(len(data)/gw + r)
  218. ]
  219. ).rstrip() + '\n'
  220. def decode_pretty_hexdump(data):
  221. from string import hexdigits
  222. pat = r'^[%s]+:\s+' % hexdigits
  223. lines = [re.sub(pat,'',l) for l in data.splitlines()]
  224. try:
  225. return unhexlify(''.join((''.join(lines).split())))
  226. except:
  227. msg('Data not in hexdump format')
  228. return False
  229. def get_hash_params(hash_preset):
  230. if hash_preset in g.hash_presets:
  231. return g.hash_presets[hash_preset] # N,p,r,buflen
  232. else: # Shouldn't be here
  233. die(3,"%s: invalid 'hash_preset' value" % hash_preset)
  234. def compare_chksums(chk1, desc1, chk2, desc2, hdr='', die_on_fail=False):
  235. if not chk1 == chk2:
  236. m = "%s ERROR: %s checksum (%s) doesn't match %s checksum (%s)"\
  237. % ((hdr+':\n ' if hdr else 'CHECKSUM'),desc2,chk2,desc1,chk1)
  238. if die_on_fail:
  239. die(3,m)
  240. else:
  241. vmsg(m)
  242. return False
  243. vmsg('%s checksum OK (%s)' % (capfirst(desc1),chk1))
  244. return True
  245. def compare_or_die(val1, desc1, val2, desc2, e='Error'):
  246. if cmp(val1,val2):
  247. die(3,"%s: %s (%s) doesn't match %s (%s)"
  248. % (e,desc2,val2,desc1,val1))
  249. dmsg('%s OK (%s)' % (capfirst(desc2),val2))
  250. return True
  251. def open_file_or_exit(filename,mode):
  252. try:
  253. f = open(filename, mode)
  254. except:
  255. op = ('writing','reading')['r' in mode]
  256. die(2,"Unable to open file '%s' for %s" % (filename,op))
  257. return f
  258. def check_file_type_and_access(fname,ftype,blkdev_ok=False):
  259. a = ((os.R_OK,'read'),(os.W_OK,'writ'))
  260. access,m = a[ftype in ('output file','output directory')]
  261. ok_types = [
  262. (stat.S_ISREG,'regular file'),
  263. (stat.S_ISLNK,'symbolic link')
  264. ]
  265. if blkdev_ok: ok_types.append((stat.S_ISBLK,'block device'))
  266. if ftype == 'output directory': ok_types = [(stat.S_ISDIR, 'output directory')]
  267. try: mode = os.stat(fname).st_mode
  268. except:
  269. die(1,"Unable to stat requested %s '%s'" % (ftype,fname))
  270. for t in ok_types:
  271. if t[0](mode): break
  272. else:
  273. die(1,"Requested %s '%s' is not a %s" % (ftype,fname,
  274. ' or '.join([t[1] for t in ok_types])))
  275. if not os.access(fname, access):
  276. die(1,"Requested %s '%s' is not %sable by you" % (ftype,fname,m))
  277. return True
  278. def check_infile(f,blkdev_ok=False):
  279. return check_file_type_and_access(f,'input file',blkdev_ok=blkdev_ok)
  280. def check_outfile(f,blkdev_ok=False):
  281. return check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
  282. def check_outdir(f):
  283. return check_file_type_and_access(f,'output directory')
  284. def make_full_path(outdir,outfile):
  285. return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
  286. def _validate_addr_num(n):
  287. from mmgen.tx import is_mmgen_idx
  288. if is_mmgen_idx(n):
  289. return int(n)
  290. else:
  291. msg("'%s': invalid %s address index" % (n,g.proj_name))
  292. return False
  293. def parse_addr_idxs(arg,sep=','):
  294. ret = []
  295. for i in (arg.split(sep)):
  296. j = i.split('-')
  297. if len(j) == 1:
  298. i = _validate_addr_num(i)
  299. if not i: return False
  300. ret.append(i)
  301. elif len(j) == 2:
  302. beg = _validate_addr_num(j[0])
  303. if not beg: return False
  304. end = _validate_addr_num(j[1])
  305. if not end: return False
  306. if end < beg:
  307. msg("'%s-%s': invalid range (end is less than beginning)" % (beg,end))
  308. return False
  309. ret.extend(range(beg,end+1))
  310. else:
  311. msg("'%s': invalid address range argument" % i)
  312. return False
  313. return sorted(set(ret))
  314. def get_new_passphrase(desc,passchg=False):
  315. w = '{}passphrase for {}'.format(('','new ')[bool(passchg)], desc)
  316. if opt.passwd_file:
  317. pw = ' '.join(get_words_from_file(opt.passwd_file,w))
  318. elif opt.echo_passphrase:
  319. pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
  320. else:
  321. for i in range(g.passwd_max_tries):
  322. pw = ' '.join(get_words_from_user('Enter {}: '.format(w)))
  323. pw2 = ' '.join(get_words_from_user('Repeat passphrase: '))
  324. dmsg('Passphrases: [%s] [%s]' % (pw,pw2))
  325. if pw == pw2:
  326. vmsg('Passphrases match'); break
  327. else: msg('Passphrases do not match. Try again.')
  328. else:
  329. die(2,'User failed to duplicate passphrase in %s attempts' %
  330. g.passwd_max_tries)
  331. if pw == '': qmsg('WARNING: Empty passphrase')
  332. return pw
  333. def confirm_or_exit(message, question, expect='YES'):
  334. m = message.strip()
  335. if m: msg(m)
  336. a = question+' ' if question[0].isupper() else \
  337. 'Are you sure you want to %s?\n' % question
  338. b = "Type uppercase '%s' to confirm: " % expect
  339. if my_raw_input(a+b).strip() != expect:
  340. die(2,'Exiting at user request')
  341. # New function
  342. def write_data_to_file(
  343. outfile,
  344. data,
  345. desc='data',
  346. ask_write=False,
  347. ask_write_prompt='',
  348. ask_write_default_yes=True,
  349. ask_overwrite=True,
  350. ask_tty=True,
  351. no_tty=False,
  352. silent=False,
  353. binary=False
  354. ):
  355. if silent: ask_tty = ask_overwrite = False
  356. if opt.quiet: ask_overwrite = False
  357. if ask_write_default_yes == False or ask_write_prompt:
  358. ask_write = True
  359. if opt.stdout or not sys.stdout.isatty() or outfile in ('','-'):
  360. qmsg('Output to STDOUT requested')
  361. if sys.stdout.isatty():
  362. if no_tty:
  363. die(2,'Printing %s to screen is not allowed' % desc)
  364. if ask_tty and not opt.quiet:
  365. confirm_or_exit('','output %s to screen' % desc)
  366. else:
  367. try: of = os.readlink('/proc/%d/fd/1' % os.getpid()) # Linux
  368. except: of = None # Windows
  369. if of:
  370. if of[:5] == 'pipe:':
  371. if no_tty:
  372. die(2,'Writing %s to pipe is not allowed' % desc)
  373. if ask_tty and not opt.quiet:
  374. confirm_or_exit('','output %s to pipe' % desc)
  375. msg('')
  376. of2,pd = os.path.relpath(of),os.path.pardir
  377. msg("Redirecting output to file '%s'" % (of2,of)[of2[:len(pd)] == pd])
  378. else:
  379. msg('Redirecting output to file')
  380. if binary and sys.platform[:3] == 'win':
  381. import msvcrt
  382. msvcrt.setmode(sys.stdout.fileno(),os.O_BINARY)
  383. sys.stdout.write(data)
  384. else:
  385. if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
  386. if ask_write:
  387. if not ask_write_prompt: ask_write_prompt = 'Save %s?' % desc
  388. if not keypress_confirm(ask_write_prompt,
  389. default_yes=ask_write_default_yes):
  390. die(1,'%s not saved' % capfirst(desc))
  391. hush = False
  392. if file_exists(outfile) and ask_overwrite:
  393. q = "File '%s' already exists\nOverwrite?" % outfile
  394. confirm_or_exit('',q)
  395. msg("Overwriting file '%s'" % outfile)
  396. hush = True
  397. f = open_file_or_exit(outfile,('w','wb')[bool(binary)])
  398. try:
  399. f.write(data)
  400. except:
  401. die(2,"Failed to write %s to file '%s'" % (desc,outfile))
  402. f.close
  403. if not (hush or silent):
  404. msg("%s written to file '%s'" % (capfirst(desc),outfile))
  405. return True
  406. def _check_mmseed_format(words):
  407. valid = False
  408. desc = '%s data' % g.seed_ext
  409. try:
  410. chklen = len(words[0])
  411. except:
  412. return False
  413. if len(words) < 3 or len(words) > 12:
  414. msg('Invalid data length (%s) in %s' % (len(words),desc))
  415. elif not is_hexstring(words[0]):
  416. msg("Invalid format of checksum '%s' in %s"%(words[0], desc))
  417. elif chklen != 6:
  418. msg('Incorrect length of checksum (%s) in %s' % (chklen,desc))
  419. else: valid = True
  420. return valid
  421. def _check_wallet_format(infile, lines):
  422. desc = "wallet file '%s'" % infile
  423. valid = False
  424. chklen = len(lines[0])
  425. if len(lines) != 6:
  426. vmsg('Invalid number of lines (%s) in %s' % (len(lines),desc))
  427. elif chklen != 6:
  428. vmsg('Incorrect length of Master checksum (%s) in %s' % (chklen,desc))
  429. elif not is_hexstring(lines[0]):
  430. vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], desc))
  431. else: valid = True
  432. if valid == False:
  433. die(2,'Invalid %s' % desc)
  434. def _check_chksum_6(chk,val,desc,infile):
  435. comp_chk = make_chksum_6(val)
  436. if chk != comp_chk:
  437. msg("%s checksum incorrect in file '%s'!" % (desc,infile))
  438. die(2,'Checksum: %s. Computed value: %s' % (chk,comp_chk))
  439. dmsg('%s checksum passed: %s' % (capfirst(desc),chk))
  440. def get_words_from_user(prompt):
  441. # split() also strips
  442. words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
  443. dmsg('Sanitized input: [%s]' % ' '.join(words))
  444. return words
  445. def get_words_from_file(infile,desc,silent=False):
  446. if not silent:
  447. qmsg("Getting %s from file '%s'" % (desc,infile))
  448. f = open_file_or_exit(infile, 'r')
  449. # split() also strips
  450. words = f.read().split()
  451. f.close()
  452. dmsg('Sanitized input: [%s]' % ' '.join(words))
  453. return words
  454. def get_words(infile,desc,prompt):
  455. if infile:
  456. return get_words_from_file(infile,desc)
  457. else:
  458. return get_words_from_user(prompt)
  459. def remove_comments(lines):
  460. # re.sub(pattern, repl, string, count=0, flags=0)
  461. ret = []
  462. for i in lines:
  463. i = re.sub('#.*','',i,1)
  464. i = re.sub('\s+$','',i)
  465. if i: ret.append(i)
  466. return ret
  467. def get_lines_from_file(infile,desc='',trim_comments=False):
  468. if desc != '':
  469. qmsg("Getting %s from file '%s'" % (desc,infile))
  470. f = open_file_or_exit(infile,'r')
  471. lines = f.read().splitlines() # DOS-safe
  472. f.close()
  473. return remove_comments(lines) if trim_comments else lines
  474. def get_data_from_user(desc='data',silent=False):
  475. data = my_raw_input('Enter %s: ' % desc, echo=opt.echo_passphrase)
  476. dmsg('User input: [%s]' % data)
  477. return data
  478. def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
  479. if dash and infile == '-': return sys.stdin.read()
  480. if not silent:
  481. qmsg("Getting %s from file '%s'" % (desc,infile))
  482. f = open_file_or_exit(infile,('r','rb')[bool(binary)])
  483. data = f.read()
  484. f.close()
  485. return data
  486. def get_seed_from_seed_data(words):
  487. if not _check_mmseed_format(words):
  488. msg('Invalid %s data' % g.seed_ext)
  489. return False
  490. stored_chk = words[0]
  491. seed_b58 = ''.join(words[1:])
  492. chk = make_chksum_6(seed_b58)
  493. vmsg_r('Validating %s checksum...' % g.seed_ext)
  494. if compare_chksums(chk, 'seed', stored_chk, 'input'):
  495. from mmgen.bitcoin import b58decode_pad
  496. seed = b58decode_pad(seed_b58)
  497. if seed == False:
  498. msg('Invalid b58 number: %s' % val)
  499. return False
  500. msg('Valid seed data for Seed ID %s' % make_chksum_8(seed))
  501. return seed
  502. else:
  503. msg('Invalid checksum for {pnm} seed'.format(pnm=pnm))
  504. return False
  505. passwd_file_used = False
  506. def pwfile_reuse_warning():
  507. global passwd_file_used
  508. if passwd_file_used:
  509. qmsg("Reusing passphrase from file '%s' at user request" % opt.passwd_file)
  510. return True
  511. passwd_file_used = True
  512. return False
  513. def get_mmgen_passphrase(desc,passchg=False):
  514. prompt ='Enter {}passphrase for {}: '.format(('','old ')[bool(passchg)],desc)
  515. if opt.passwd_file:
  516. pwfile_reuse_warning()
  517. return ' '.join(get_words_from_file(opt.passwd_file,'passphrase'))
  518. else:
  519. return ' '.join(get_words_from_user(prompt))
  520. def get_bitcoind_passphrase(prompt):
  521. if opt.passwd_file:
  522. pwfile_reuse_warning()
  523. return get_data_from_file(opt.passwd_file,'passphrase').strip('\r\n')
  524. else:
  525. return my_raw_input(prompt, echo=opt.echo_passphrase)
  526. def check_data_fits_file_at_offset(fname,offset,dlen,action):
  527. # TODO: Check for Windows
  528. if stat.S_ISBLK(os.stat(fname).st_mode):
  529. fd = os.open(fname, os.O_RDONLY)
  530. fsize = os.lseek(fd, 0, os.SEEK_END)
  531. os.close(fd)
  532. else:
  533. fsize = os.stat(fname).st_size
  534. if fsize < offset + dlen:
  535. m = ('Input','Destination')[action == 'write']
  536. die(1,
  537. '%s file has length %s, too short to %s %s bytes of data at offset %s'
  538. % (m,fsize,action,dlen,offset))
  539. def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
  540. p = """Enter hash preset for %s,
  541. or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
  542. while True:
  543. ret = my_raw_input(p)
  544. if ret:
  545. if ret in g.hash_presets.keys(): return ret
  546. else:
  547. msg('Invalid input. Valid choices are %s' %
  548. ', '.join(sorted(g.hash_presets.keys())))
  549. continue
  550. else: return hp
  551. def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
  552. try: import readline
  553. except: use_readline = False # Windows
  554. if use_readline and sys.stdout.isatty():
  555. def st_hook(): readline.insert_text(insert_txt)
  556. readline.set_startup_hook(st_hook)
  557. else:
  558. msg_r(prompt)
  559. prompt = ''
  560. from mmgen.term import kb_hold_protect
  561. kb_hold_protect()
  562. if echo:
  563. reply = raw_input(prompt)
  564. else:
  565. from getpass import getpass
  566. reply = getpass(prompt)
  567. kb_hold_protect()
  568. return reply.strip()
  569. def keypress_confirm(prompt,default_yes=False,verbose=False):
  570. from mmgen.term import get_char
  571. q = ('(y/N)','(Y/n)')[bool(default_yes)]
  572. while True:
  573. reply = get_char('%s %s: ' % (prompt, q)).strip('\n\r')
  574. if not reply:
  575. if default_yes: msg(''); return True
  576. else: msg(''); return False
  577. elif reply in 'yY': msg(''); return True
  578. elif reply in 'nN': msg(''); return False
  579. else:
  580. if verbose: msg('\nInvalid reply')
  581. else: msg_r('\r')
  582. def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
  583. from mmgen.term import get_char
  584. while True:
  585. reply = get_char('%s: ' % prompt).strip('\n\r')
  586. if reply in chars or (enter_ok and not reply):
  587. msg('')
  588. return reply
  589. if verbose: msg('\nInvalid reply')
  590. else: msg_r('\r')
  591. def do_license_msg(immed=False):
  592. if opt.quiet or g.no_license: return
  593. import mmgen.license as gpl
  594. p = "Press 'w' for conditions and warranty info, or 'c' to continue:"
  595. msg(gpl.warning)
  596. prompt = '%s ' % p.strip()
  597. from mmgen.term import get_char,do_pager
  598. while True:
  599. reply = get_char(prompt, immed_chars=('','wc')[bool(immed)])
  600. if reply == 'w':
  601. do_pager(gpl.conditions)
  602. elif reply == 'c':
  603. msg(''); break
  604. else:
  605. msg_r('\r')
  606. msg('')
  607. def get_bitcoind_cfg_options(cfg_keys):
  608. cfg_file = os.path.join(get_homedir(), get_datadir(), 'bitcoin.conf')
  609. cfg = dict([(k,v) for k,v in [split2(line.translate(None,'\t '),'=')
  610. for line in get_lines_from_file(cfg_file)] if k in cfg_keys]) \
  611. if file_is_readable(cfg_file) else {}
  612. for k in set(cfg_keys) - set(cfg.keys()): cfg[k] = ''
  613. return cfg
  614. def get_bitcoind_auth_cookie():
  615. f = os.path.join(get_homedir(), get_datadir(), '.cookie')
  616. if file_is_readable(f):
  617. return get_lines_from_file(f)[0]
  618. else:
  619. return ''
  620. def bitcoin_connection():
  621. host,port,user,passwd = 'localhost',8332,'rpcuser','rpcpassword'
  622. cfg = get_bitcoind_cfg_options((user,passwd))
  623. auth_cookie = get_bitcoind_auth_cookie()
  624. import mmgen.rpc
  625. return mmgen.rpc.BitcoinRPCConnection(
  626. host,port,cfg[user],cfg[passwd],auth_cookie=auth_cookie)