baseui.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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, 2014 Oz Nahum Tiram <nahumoz@gmail.com>
  18. # ============================================================================
  19. from __future__ import print_function
  20. import sys
  21. import os
  22. import getpass
  23. import ast
  24. import csv
  25. import time
  26. from colorama import Fore
  27. from pwman.data.nodes import Node
  28. from pwman.ui import tools
  29. from pwman.util.crypto_engine import CryptoEngine
  30. from .base import HelpUI
  31. if sys.version_info.major > 2:
  32. raw_input = input
  33. class BaseCommands(HelpUI):
  34. @property
  35. def _xsel(self):
  36. if self.hasxsel:
  37. return True
  38. def error(self, exception):
  39. if (isinstance(exception, KeyboardInterrupt)):
  40. print('')
  41. else:
  42. print("Error: {0} ".format(exception))
  43. def do_copy(self, args): # pragma: no cover
  44. """copy item to clipboard"""
  45. if not self._xsel:
  46. return
  47. if not args.isdigit():
  48. print("Copy accepts only IDs ...")
  49. ids = args.split()
  50. if len(ids) > 1:
  51. print("Can copy only 1 password at a time...")
  52. return
  53. nodes = self._db.getnodes(ids)
  54. for node in nodes:
  55. ce = CryptoEngine.get()
  56. password = ce.decrypt(node[2])
  57. tools.text_to_clipboards(password)
  58. print("erasing in 10 sec...")
  59. time.sleep(10) # TODO: this should be configurable!
  60. tools.text_to_clipboards("")
  61. def do_open(self, args): # pragma: no cover
  62. ids = self.get_ids(args)
  63. if not args:
  64. self.help_open()
  65. return
  66. nodes = self._db.getnodes(ids)
  67. for node in nodes:
  68. ce = CryptoEngine.get()
  69. url = ce.decrypt(node[3])
  70. tools.open_url(url)
  71. def do_exit(self, args): # pragma: no cover
  72. """close the text console"""
  73. self._db.close()
  74. return True
  75. def do_cls(self, args): # pragma: no cover
  76. """clear the screen"""
  77. os.system("clear")
  78. def do_edit(self, args):
  79. """edit a node"""
  80. pass
  81. def do_export(self, args):
  82. """export the database to a given format"""
  83. try:
  84. args = ast.literal_eval(args)
  85. except Exception:
  86. args = {}
  87. filename = args.get('filename', 'pwman-export.csv')
  88. delim = args.get('delimiter', ';')
  89. nodeids = self._db.listnodes()
  90. nodes = self._db.getnodes(nodeids)
  91. with open(filename, 'w') as csvfile:
  92. writer = csv.writer(csvfile, delimiter=delim)
  93. writer.writerow(['Username', 'URL', 'Password', 'Notes',
  94. 'Tags'])
  95. for node in nodes:
  96. n = Node.from_encrypted_entries(node[1], node[2], node[3],
  97. node[4],
  98. node[5:])
  99. tags = n.tags
  100. tags = ','.join(t.strip().decode() for t in tags)
  101. r = list(map(bytes.decode, [n.username, n.url, n.password,
  102. n.notes]))
  103. writer.writerow(r + [tags])
  104. print("Successfuly exported database to {}".format(
  105. os.path.join(os.getcwd(), filename)))
  106. def do_forget(self, args):
  107. """
  108. drop saved key forcing the user to re-enter the master
  109. password
  110. """
  111. enc = CryptoEngine.get()
  112. enc.forget()
  113. def do_passwd(self, args):
  114. """change the master password of the database"""
  115. pass
  116. def do_tags(self, args):
  117. """
  118. print all existing tags
  119. """
  120. ce = CryptoEngine.get()
  121. print("Tags:")
  122. tags = self._db.listtags()
  123. for t in tags:
  124. print(ce.decrypt(t).decode())
  125. def _get_tags(self, default=None, reader=raw_input):
  126. """
  127. Read tags from user input.
  128. Tags are simply returned as a list
  129. """
  130. # TODO: add method to read tags from db, so they
  131. # could be used for tab completer
  132. print("Tags: ", end="")
  133. sys.stdout.flush()
  134. taglist = sys.stdin.readline()
  135. tagstrings = taglist.split()
  136. tags = [tn for tn in tagstrings]
  137. return tags
  138. def _prep_term(self):
  139. self.do_cls('')
  140. if sys.platform != 'win32':
  141. rows, cols = tools.gettermsize()
  142. else:
  143. rows, cols = 18, 80 # fix this !
  144. cols -= 8
  145. return rows, cols
  146. def _format_line(self, tag_pad, nid="ID", user="USER", url="URL",
  147. tags="TAGS"):
  148. return ("{ID:<3} {USER:<{us}}{URL:<{ur}}{Tags:<{tg}}"
  149. "".format(ID=nid, USER=user,
  150. URL=url, Tags=tags, us=12,
  151. ur=20, tg=tag_pad - 32))
  152. def _print_node_line(self, node, rows, cols):
  153. tagstring = ','.join([t.decode() for t in node.tags])
  154. fmt = self._format_line(cols - 32, node._id, node.username.decode(),
  155. node.url.decode(),
  156. tagstring)
  157. formatted_entry = tools.typeset(fmt, Fore.YELLOW, False)
  158. print(formatted_entry)
  159. def _get_node_ids(self, args):
  160. filter = None
  161. if args:
  162. filter = args.split()[0]
  163. ce = CryptoEngine.get()
  164. filter = ce.encrypt(filter)
  165. nodeids = self._db.listnodes(filter=filter)
  166. return nodeids
  167. def do_list(self, args):
  168. """list all existing nodes in database"""
  169. rows, cols = self._prep_term()
  170. nodeids = self._get_node_ids(args)
  171. nodes = self._db.getnodes(nodeids)
  172. _nodes_inst = []
  173. # user, pass, url, notes
  174. for node in nodes:
  175. _nodes_inst.append(Node.from_encrypted_entries(
  176. node[1],
  177. node[2],
  178. node[3],
  179. node[4],
  180. node[5:]))
  181. _nodes_inst[-1]._id = node[0]
  182. head = self._format_line(cols-32)
  183. print(tools.typeset(head, Fore.YELLOW, False))
  184. for idx, node in enumerate(_nodes_inst):
  185. self._print_node_line(node, rows, cols)
  186. def _get_input(self, prompt):
  187. print(prompt, end="")
  188. sys.stdout.flush()
  189. return sys.stdin.readline().strip()
  190. def _get_secret(self):
  191. # TODO: enable old functionallity, with password generator.
  192. if sys.stdin.isatty(): # pragma: no cover
  193. p = getpass.getpass()
  194. else:
  195. p = sys.stdin.readline().rstrip()
  196. return p
  197. def _do_new(self, args):
  198. node = {}
  199. node['username'] = self._get_input("Username: ")
  200. node['password'] = self._get_secret()
  201. node['url'] = self._get_input("Url: ")
  202. node['notes'] = self._get_input("Notes: ")
  203. node['tags'] = self._get_tags()
  204. node = Node(clear_text=True, **node)
  205. self._db.add_node(node)
  206. return node
  207. def do_new(self, args): # pragma: no cover
  208. # The cmd module stops if and of do_* return something
  209. # else than None ...
  210. # This is bad for testing, so everything that is do_*
  211. # should call _do_* method which is testable
  212. self._do_new(args)