tools.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 colorama
  28. #from pwman.util.config import get_pass_conf
  29. from pwman.util.callback import Callback
  30. if sys.version_info.major > 2: # pragma: no cover
  31. raw_input = input
  32. if sys.platform != 'win32':
  33. import termios
  34. import fcntl
  35. import readline
  36. _readline_available = True
  37. else: # pragma: no cover
  38. try:
  39. import readline
  40. _readline_available = True
  41. except ImportError as e:
  42. _readline_available = False
  43. _defaultwidth = 10
  44. class ANSI(object):
  45. """
  46. ANSI Colors
  47. """
  48. Reset = 0
  49. Bold = 1
  50. Underscore = 2
  51. Black = 30
  52. Red = 31
  53. Green = 32
  54. Yellow = 33
  55. Blue = 34
  56. Magenta = 35
  57. Cyan = 36
  58. White = 37
  59. def typeset(text, color, bold=False, underline=False,
  60. has_colorama=True): # pragma: no cover
  61. """
  62. print colored strings using colorama
  63. """
  64. if not has_colorama:
  65. return text
  66. if bold:
  67. text = colorama.Style.BRIGHT + text
  68. if underline and not 'win32' in sys.platform:
  69. text = ANSI.Underscore + text
  70. return color + text + colorama.Style.RESET_ALL
  71. def text_to_clipboards(text): # pragma: no cover
  72. """
  73. copy text to clipboard
  74. credit:
  75. https://pythonadventures.wordpress.com/tag/xclip/
  76. """
  77. # "primary":
  78. try:
  79. xsel_proc = sp.Popen(['xsel', '-pi'], stdin=sp.PIPE)
  80. xsel_proc.communicate(text)
  81. # "clipboard":
  82. xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
  83. xsel_proc.communicate(text)
  84. except OSError as e:
  85. print (e, "\nExecuting xsel failed, is it installed ?\n \
  86. please check your configuration file ... ")
  87. def text_to_mcclipboard(text): # pragma: no cover
  88. """
  89. copy text to mac os x clip board
  90. credit:
  91. https://pythonadventures.wordpress.com/tag/xclip/
  92. """
  93. # "primary":
  94. try:
  95. pbcopy_proc = sp.Popen(['pbcopy'], stdin=sp.PIPE)
  96. pbcopy_proc.communicate(text)
  97. except OSError as e:
  98. print (e, "\nExecuting pbcoy failed...")
  99. def open_url(link, macosx=False): # pragma: no cover
  100. """
  101. launch xdg-open or open in MacOSX with url
  102. """
  103. uopen = "xdg-open"
  104. if macosx:
  105. uopen = "open"
  106. try:
  107. sp.Popen([uopen, link], stdin=sp.PIPE)
  108. except OSError as e:
  109. print("Executing open_url failed with:\n", e)
  110. def get_password(question, config):
  111. # TODO: enable old functionallity, with password generator.
  112. if sys.stdin.isatty(): # pragma: no cover
  113. p = getpass.getpass()
  114. else:
  115. p = sys.stdin.readline().rstrip()
  116. return p
  117. def getpassword(question, argsgiven=None,
  118. width=_defaultwidth, echo=False,
  119. reader=getpass.getpass, numerics=False, leetify=False,
  120. symbols=False, special_signs=False,
  121. length=None, config=None): # pragma: no cover
  122. if argsgiven == 1 or length:
  123. while not length:
  124. try:
  125. default_length = config.get_value(
  126. 'Generator', 'default_pw_length') or '8'
  127. length = getinput(
  128. "Password length (default %s): " % default_length,
  129. default=default_length)
  130. length = int(length)
  131. except ValueError:
  132. print("please enter a proper integer")
  133. #password, dumpme = generator.generate_password(
  134. # length, length, True, symbols=leetify, numerics=numerics,
  135. # special_chars=special_signs)
  136. #print ("New password: %s" % (password))
  137. #return password
  138. # no args given
  139. while True:
  140. a1 = reader(question.ljust(width))
  141. if not a1:
  142. return getpassword(
  143. '', argsgiven=1, width=width, echo=echo, reader=reader,
  144. numerics=numerics, leetify=leetify, symbols=symbols,
  145. special_signs=special_signs, length=length, config=config)
  146. a2 = reader("[Repeat] %s" % (question.ljust(width)))
  147. if a1 == a2:
  148. if leetify:
  149. pass # return generator.leetify(a1)
  150. else:
  151. return a1
  152. else:
  153. print ("Passwords don't match. Try again.")
  154. def gettermsize(): # pragma: no cover
  155. if sys.stdout.isatty():
  156. s = struct.pack("HHHH", 0, 0, 0, 0)
  157. f = sys.stdout.fileno()
  158. x = fcntl.ioctl(f, termios.TIOCGWINSZ, s)
  159. rows, cols, width, height = struct.unpack("HHHH", x)
  160. return rows, cols
  161. else:
  162. return 40, 80
  163. def getinput(question, default="", reader=raw_input,
  164. completer=None, width=_defaultwidth): # pragma: no cover
  165. """
  166. http://stackoverflow.com/questions/2617057/\
  167. supply-inputs-to-python-unittests
  168. """
  169. if reader == raw_input:
  170. if not _readline_available:
  171. val = raw_input(question.ljust(width))
  172. if val:
  173. return val
  174. else:
  175. return default
  176. else:
  177. def defaulter():
  178. """define default behavior startup"""
  179. if _readline_available:
  180. readline.insert_text(default)
  181. readline.set_startup_hook(defaulter)
  182. readline.get_completer()
  183. readline.set_completer(completer)
  184. x = raw_input(question.ljust(width))
  185. readline.set_completer(completer)
  186. readline.set_startup_hook()
  187. if not x:
  188. return default
  189. return x
  190. else:
  191. return reader()
  192. class CMDLoop(object): # pragma: no cover
  193. """
  194. The menu that drives editing of a node
  195. """
  196. def __init__(self, config):
  197. self.items = []
  198. self.config = config
  199. def add(self, item):
  200. if (isinstance(item, CliMenuItem)):
  201. self.items.append(item)
  202. else:
  203. print (item.__class__)
  204. def run(self, new_node=None, reader=raw_input):
  205. while True:
  206. for i, x in enumerate(self.items):
  207. current = x.getter
  208. print ("%s - %s: %s" % (i + 1, x.name, current))
  209. print("X - Finish editing")
  210. option = reader("Enter your choice:")[0]
  211. try:
  212. print ("Selection, ", option)
  213. # substract 1 because array subscripts start at 0
  214. selection = int(option) - 1
  215. # new value is created by calling the editor with the
  216. # previous value as a parameter
  217. # TODO: enable overriding password policy as if new node
  218. # is created.
  219. if selection == 0:
  220. new_node.username = getinput("Username:")
  221. self.items[0].getter = new_node.username
  222. self.items[0].setter = new_node.username
  223. elif selection == 1: # for password
  224. new_node.password = get_password("Password:", self.config)
  225. self.items[1].getter = new_node.password
  226. self.items[1].setter = new_node.password
  227. elif selection == 2:
  228. new_node.url = getinput("Url:")
  229. self.items[2].getter = new_node.url
  230. self.items[2].setter = new_node.url
  231. elif selection == 3: # for notes
  232. # new_node.notes = getinput("Notes:")
  233. new_node.notes = reader("Notes:")
  234. self.items[3].getter = new_node.notes
  235. self.items[3].setter = new_node.notes
  236. elif selection == 4:
  237. taglist = getinput("Tags:")
  238. tagstrings = taglist.split()
  239. tags = [tn for tn in tagstrings]
  240. #tags = ''
  241. new_node.tags = tags
  242. self.items[4].setter = new_node.tags
  243. self.items[4].getter = new_node.tags
  244. except (ValueError, IndexError):
  245. if (option.upper() == 'X'):
  246. break
  247. print("Invalid selection")
  248. class CliMenuItem(object): # pragma: no cover
  249. def __init__(self, name, editor, getter, setter):
  250. self.name = name
  251. self.editor = editor
  252. self.getter = getter
  253. self.setter = setter
  254. class CLICallback(Callback): # pragma: no cover
  255. def getinput(self, question):
  256. return raw_input(question)
  257. def getsecret(self, question):
  258. return getpass.getpass(question + ":")
  259. def getnewsecret(self, question):
  260. return getpass.getpass(question + ":")