tools.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #============================================================================
  2. # This file is part of Pwman3.
  3. #
  4. # Pwman3 is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License, version 2
  6. # as published by the Free Software Foundation;
  7. #
  8. # Pwman3 is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with Pwman3; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. #============================================================================
  17. # Copyright (C) 2013 Oz Nahum <nahumoz@gmail.com>
  18. #============================================================================
  19. """
  20. Define the CLI interface for pwman3 and the helper functions
  21. """
  22. from __future__ import print_function
  23. import subprocess as sp
  24. import getpass
  25. import sys
  26. import struct
  27. import os
  28. import colorama
  29. from pwman.util.config import get_pass_conf
  30. from pwman.util.callback import Callback
  31. if sys.version_info.major > 2: # pragma: no cover
  32. raw_input = input
  33. if sys.platform != 'win32':
  34. import termios
  35. import fcntl
  36. import tty
  37. import readline
  38. _readline_available = True
  39. else: # pragma: no cover
  40. try:
  41. #import pyreadline as readline
  42. import readline
  43. _readline_available = True
  44. except ImportError as e:
  45. _readline_available = False
  46. _defaultwidth = 10
  47. class ANSI(object):
  48. """
  49. ANSI Colors
  50. """
  51. Reset = 0
  52. Bold = 1
  53. Underscore = 2
  54. Black = 30
  55. Red = 31
  56. Green = 32
  57. Yellow = 33
  58. Blue = 34
  59. Magenta = 35
  60. Cyan = 36
  61. White = 37
  62. def typeset(text, color, bold=False, underline=False,
  63. has_colorama=True): # pragma: no cover
  64. """
  65. print colored strings using colorama
  66. """
  67. if not has_colorama:
  68. return text
  69. if bold:
  70. text = colorama.Style.BRIGHT + text
  71. if underline and not 'win32' in sys.platform:
  72. text = ANSI.Underscore + text
  73. return color + text + colorama.Style.RESET_ALL
  74. def select(question, possible): # pragma: no cover
  75. """
  76. select input from user
  77. """
  78. for i in range(0, len(possible)):
  79. print ("%d - %-" + str(_defaultwidth) + "s") % (i + 1, possible[i])
  80. while 1:
  81. uinput = getonechar(question)
  82. if uinput.isdigit() and int(uinput) in range(1, len(possible) + 1):
  83. return possible[int(uinput) - 1]
  84. def text_to_clipboards(text): # pragma: no cover
  85. """
  86. copy text to clipboard
  87. credit:
  88. https://pythonadventures.wordpress.com/tag/xclip/
  89. """
  90. # "primary":
  91. try:
  92. xsel_proc = sp.Popen(['xsel', '-pi'], stdin=sp.PIPE)
  93. xsel_proc.communicate(text)
  94. # "clipboard":
  95. xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
  96. xsel_proc.communicate(text)
  97. except OSError as e:
  98. print (e, "\nExecuting xsel failed, is it installed ?\n \
  99. please check your configuration file ... ")
  100. def text_to_mcclipboard(text): # pragma: no cover
  101. """
  102. copy text to mac os x clip board
  103. credit:
  104. https://pythonadventures.wordpress.com/tag/xclip/
  105. """
  106. # "primary":
  107. try:
  108. pbcopy_proc = sp.Popen(['pbcopy'], stdin=sp.PIPE)
  109. pbcopy_proc.communicate(text)
  110. except OSError as e:
  111. print (e, "\nExecuting pbcoy failed...")
  112. def open_url(link, macosx=False): # pragma: no cover
  113. """
  114. launch xdg-open or open in MacOSX with url
  115. """
  116. uopen = "xdg-open"
  117. if macosx:
  118. uopen = "open"
  119. try:
  120. sp.Popen([uopen, link], stdin=sp.PIPE)
  121. except OSError as e:
  122. print("Executing open_url failed with:\n", e)
  123. def getpassword(question, argsgiven=None,
  124. width=_defaultwidth, echo=False,
  125. reader=getpass.getpass, numerics=False, leetify=False,
  126. symbols=False, special_signs=False,
  127. length=None, config=None): # pragma: no cover
  128. if argsgiven == 1 or length:
  129. while not length:
  130. try:
  131. default_length = config.get_value(
  132. 'Generator', 'default_pw_length') or '8'
  133. length = getinput(
  134. "Password length (default %s): " % default_length,
  135. default=default_length)
  136. length = int(length)
  137. except ValueError:
  138. print("please enter a proper integer")
  139. #password, dumpme = generator.generate_password(
  140. # length, length, True, symbols=leetify, numerics=numerics,
  141. # special_chars=special_signs)
  142. #print ("New password: %s" % (password))
  143. #return password
  144. # no args given
  145. while True:
  146. a1 = reader(question.ljust(width))
  147. if not a1:
  148. return getpassword(
  149. '', argsgiven=1, width=width, echo=echo, reader=reader,
  150. numerics=numerics, leetify=leetify, symbols=symbols,
  151. special_signs=special_signs, length=length, config=config)
  152. a2 = reader("[Repeat] %s" % (question.ljust(width)))
  153. if a1 == a2:
  154. if leetify:
  155. pass # return generator.leetify(a1)
  156. else:
  157. return a1
  158. else:
  159. print ("Passwords don't match. Try again.")
  160. def gettermsize(): # pragma: no cover
  161. if sys.stdout.isatty():
  162. s = struct.pack("HHHH", 0, 0, 0, 0)
  163. f = sys.stdout.fileno()
  164. x = fcntl.ioctl(f, termios.TIOCGWINSZ, s)
  165. rows, cols, width, height = struct.unpack("HHHH", x)
  166. return rows, cols
  167. else:
  168. return 40, 80
  169. def getinput(question, default="", reader=raw_input,
  170. completer=None, width=_defaultwidth): # pragma: no cover
  171. """
  172. http://stackoverflow.com/questions/2617057/\
  173. supply-inputs-to-python-unittests
  174. """
  175. if reader == raw_input:
  176. if not _readline_available:
  177. val = raw_input(question.ljust(width))
  178. if val:
  179. return val
  180. else:
  181. return default
  182. else:
  183. def defaulter():
  184. """define default behavior startup"""
  185. if _readline_available:
  186. readline.insert_text(default)
  187. readline.set_startup_hook(defaulter)
  188. readline.get_completer()
  189. readline.set_completer(completer)
  190. x = raw_input(question.ljust(width))
  191. readline.set_completer(completer)
  192. readline.set_startup_hook()
  193. if not x:
  194. return default
  195. return x
  196. else:
  197. return reader()
  198. class CMDLoop(object): # pragma: no cover
  199. """
  200. The menu that drives editing of a node
  201. """
  202. def __init__(self):
  203. self.items = []
  204. def add(self, item):
  205. if (isinstance(item, CliMenuItem)):
  206. self.items.append(item)
  207. else:
  208. print (item.__class__)
  209. def run(self, new_node=None, reader=raw_input):
  210. while True:
  211. i = 0
  212. for x in self.items:
  213. i = i + 1
  214. try:
  215. current = x.getter
  216. except AttributeError:
  217. current = x
  218. # when printing tags, we have list ...
  219. currentstr = b''
  220. if type(current) == list:
  221. for c in current:
  222. print(c, type(c))
  223. try:
  224. currentstr += b' ' + c
  225. except TypeError:
  226. currentstr += b' ' + c.name
  227. # for the case we are not dealing with
  228. # a list of tags
  229. else:
  230. currentstr = current
  231. print ("%s - %s: %s" % (i, x.name, currentstr))
  232. print("X - Finish editing")
  233. option = reader("Enter your choice:")[0]
  234. try:
  235. print ("Selection, ", option)
  236. # substract 1 because array subscripts start at 0
  237. selection = int(option) - 1
  238. # new value is created by calling the editor with the
  239. # previous value as a parameter
  240. # TODO: enable overriding password policy as if new node
  241. # is created.
  242. if selection == 0:
  243. new_node.username = getinput("Username:")
  244. self.items[0].getter = new_node.username
  245. self.items[0].setter = new_node.username
  246. elif selection == 1: # for password
  247. numerics, leet, s_chars = get_pass_conf()
  248. new_node.password = getpassword(
  249. 'New Password:', numerics=numerics, leetify=leet,
  250. special_signs=s_chars)
  251. self.items[1].getter = new_node.password
  252. self.items[1].setter = new_node.password
  253. elif selection == 2:
  254. new_node.url = getinput("Url:")
  255. self.items[2].getter = new_node.url
  256. self.items[2].setter = new_node.url
  257. elif selection == 3: # for notes
  258. # new_node.notes = getinput("Notes:")
  259. new_node.notes = reader("Notes:")
  260. self.items[3].getter = new_node.notes
  261. self.items[3].setter = new_node.notes
  262. elif selection == 4:
  263. taglist = getinput("Tags:")
  264. tagstrings = taglist.split()
  265. tags = [tn for tn in tagstrings]
  266. #tags = ''
  267. new_node.tags = tags
  268. self.items[4].setter = new_node.tags
  269. self.items[4].getter = new_node.tags
  270. except (ValueError, IndexError):
  271. if (option.upper() == 'X'):
  272. break
  273. print("Invalid selection")
  274. def getonechar(question, width=_defaultwidth): # pragma: no cover
  275. question = "%s " % (question)
  276. print (question.ljust(width),)
  277. try:
  278. sys.stdout.flush()
  279. fd = sys.stdin.fileno()
  280. # tty module exists only if we are on Posix
  281. try:
  282. tty_mode = tty.tcgetattr(fd)
  283. tty.setcbreak(fd)
  284. except NameError:
  285. pass
  286. try:
  287. ch = os.read(fd, 1)
  288. finally:
  289. try:
  290. tty.tcsetattr(fd, tty.TCSAFLUSH, tty_mode)
  291. except NameError:
  292. pass
  293. except AttributeError:
  294. ch = sys.stdin.readline()[0]
  295. print(ch)
  296. return ch
  297. class CliMenuItem(object): # pragma: no cover
  298. def __init__(self, name, editor, getter, setter):
  299. self.name = name
  300. self.editor = editor
  301. self.getter = getter
  302. self.setter = setter
  303. class CLICallback(Callback): # pragma: no cover
  304. def getinput(self, question):
  305. return raw_input(question)
  306. def getsecret(self, question):
  307. return getpass.getpass(question + ":")
  308. def getnewsecret(self, question):
  309. return getpass.getpass(question + ":")