#!/usr/bin/python
# author: nishiki
# mail: nishiki@yaegashi.fr
# date: 16/06/2013
# info: a simple script who manage your passwords

import csv
import re
import os
import sys
import gnupg
import getpass 
import StringIO
import tempfile
import time
import datetime

FILE_GPG    = '/home/adrien/.password-manager'
KEY         = 'adrien.waksberg@believedigital.com'
FILE_PWD    = '/tmp/.tmp_passwd'
TIMEOUT_PWD = 300


class ManagePasswd:

	ID      = 0
	TYPE    = 1
	SERVER  = 2
	LOGIN   = 3
	PASSWD  = 4
	PORT    = 5
	COMMENT = 6


	def __init__(self, key, file_gpg, file_pwd, timeout_pwd=300):
		self.KEY         = key
		self.FILE_PWD    = file_pwd
		self.FILE_GPG    = file_gpg
		self.TIMEOUT_PWD = timeout_pwd

		if os.path.isfile(self.FILE_GPG):
			self.decrypt()
		else:
			self.data = ''
		
	def __del__(self):
		try: 
			self.file_tmp_name
		except:
			self.file_tmp_name = ''

		if os.path.isfile(self.file_tmp_name):
			os.remove(self.file_tmp_name)
		
	# Decrypt a gpg file
	# @rtrn: true if data is decrypted
	def decrypt(self):
		gpg = gnupg.GPG(verbose=False)

		# Manage the passphrase
		if os.path.isfile(self.FILE_PWD):
			stat_file = os.stat(self.FILE_PWD)
			if stat_file.st_mtime + self.TIMEOUT_PWD < time.time():
				open(self.FILE_PWD, 'w').close()
		else:
			open(self.FILE_PWD, 'w').close()

		file_pwd = open(self.FILE_PWD, 'r+')
		os.chmod(self.FILE_PWD, 0600)
		passwd  = file_pwd.readline()
		if not passwd:
			passwd = getpass.getpass('Password GPG: ')
			file_pwd.write(passwd)

		file_pwd.close()

		file_gpg = open(self.FILE_GPG, 'rb')
		self.data = gpg.decrypt_file(file_gpg, passphrase=passwd)
		file_gpg.close()

		if not self.data.ok:
			print 'Error: Your passphrase is probably wrong!'
			os.remove(self.FILE_PWD)
			return False
		else:
			os.utime(self.FILE_PWD, None)
			return True

	# Encrypt a file
	def encrypt(self):
		if os.path.isfile(self.file_tmp_name):
			self.file_tmp.close()
			os.system('gpg -r ' + self.KEY + ' --yes --output ' + self.FILE_GPG + ' --encrypt ' + self.file_tmp_name)
			return True
		else:
			return False

	# Search in some csv data
	# @args: search -> the string to search
	#        type -> the connection type (ssh, web, other)
	# @rtrn: a list with the resultat of the search
	def search(self, search, type=''):
		result = list()
		regex = re.compile('^.*' + search + '.*$')
		regex_type = re.compile('^' + type + '$')

		file_csv = StringIO.StringIO(self.data)
		reader = csv.reader(file_csv, delimiter=';', quotechar='|')
		for row in reader:
			if regex.match(row[self.SERVER]) or regex.match(row[self.COMMENT]):
				if type == '':
					result.append(row)
				else:
					if regex_type.match(row[self.TYPE]):
						result.append(row)
		file_csv.close()
		
		return result

	# Search an id in some csv data
	# @args: id -> the string to search
	# @rtrn: the resultat of the search or false
	def searchId(self, id):
		result = list()

		file_csv = StringIO.StringIO(self.data)
		reader = csv.reader(file_csv, delimiter=';', quotechar='|')
		for row in reader:
			if row[self.ID] == id:
				file_csv.close()
				return row
		
		file_csv.close()
		return False

	# Connect to ssh and display the password
	# @args: search -> 
	def ssh(self, search):
		result = self.search(search, 'ssh')
		for r in result: 
			server = r[self.SERVER]
			login  = r[self.LOGIN]
			port   = r[self.PORT]
			passwd = r[self.PASSWD]

			if not port:
				port = 22

			if passwd:
				os.system('sshpass -p ' + passwd + ' ssh ' + login + '@' + server + ' -p ' + str(port))
			else:
				os.system('ssh ' + login + '@' + server + ' -p ' + str(port))

		if len(result) == 0:
			print 'No result!'


	# Display the connections informations for a server
	def display(self, search, type=''):
		result = self.search(search, type)
		for r in result: 
			print '# --------------------'
			print '# Id: '       + r[self.ID]
			print '# Server: '   + r[self.SERVER]
			print '# Type: '     + r[self.TYPE]
			print '# Login: '    + r[self.LOGIN]
			print '# Password: ' + r[self.PASSWD]
			print '# Port: '     + r[self.PORT]
			print '# Comment: '  + r[self.COMMENT]

		if len(result) == 0:
			print 'No result!'


	# Display help
	def help(self):
		print '# HELP'
		print '# --------------------'
		print 'Add a new item: -a'
		print 'Update an item: -u ID'
		print 'Remove an item: -r ID'
		print 'Show a item: -d search [type]'
		print 'Connect ssh: -s search'
		

	# Add a new item
	def add(self):
		print '# Add a new password'
		print '# --------------------'
		id      = hex(int(time.time()))
		server  = raw_input('Enter the server name or ip: ')
		type    = raw_input('Enter the type of connection (ssh, web, other): ')
		login   = raw_input('Enter the login connection: ')
		passwd  = raw_input('Enter the the password: ')
		port    = raw_input('Enter the connection port (optinal): ')
		comment = raw_input('Enter a comment (optinal): ')
		
		self.generateTmpFile()
		writer = csv.writer(self.file_tmp, delimiter=';', quotechar='|')
		file_csv = StringIO.StringIO(self.data)
		reader = csv.reader(file_csv, delimiter=';', quotechar='|')
		for row in reader:
			writer.writerow(row)
		file_csv.close()
		writer.writerow([id, type, server, login, passwd, port, comment])
		print 'Item has been added!'

	# Remove an item
	# @args: id -> the unique identifiant
	def remove(self, id):
		self.generateTmpFile()
		writer = csv.writer(self.file_tmp, delimiter=';', quotechar='|')
		file_csv = StringIO.StringIO(self.data)
		reader = csv.reader(file_csv, delimiter=';', quotechar='|')
		for row in reader:
			if row[self.ID] != id:
				writer.writerow(row)
			else:
				print 'The item has been removed!'
		file_csv.close()

	# Update an item
	# @args: id -> the item identifiant
	def update(self, id):
		result = self.searchId(id)
		
		print '# Add a new password'
		print '# --------------------'
		id      = result[self.ID]
		server  = raw_input('Enter the server name or ip [' + result[self.SERVER] + ']: ')
		type    = raw_input('Enter the type of connection [' + result[self.TYPE] + ']: ')
		login   = raw_input('Enter the login connection [' + result[self.LOGIN] + ']: ')
		passwd  = raw_input('Enter the the password: ')
		port    = raw_input('Enter the connection port [' + result[self.PORT] + ']: ')
		comment = raw_input('Enter a comment [' + result[self.COMMENT]+ ']: ')

		if not server:
			server = result[self.SERVER]
		if not login:
			login = result[self.LOGIN]
		if not type:
			type = result[self.TYPE]
		if not passwd:
			passwd = result[self.PASSWD]
		if not port:
			port = result[self.PORT]
		if not comment:
			comment = result[self.COMMENT]
		
		self.generateTmpFile()
		writer = csv.writer(self.file_tmp, delimiter=';', quotechar='|')
		file_csv = StringIO.StringIO(self.data)
		reader = csv.reader(file_csv, delimiter=';', quotechar='|')
		for row in reader:
			if row[self.ID] != id:
				writer.writerow(row)
		writer.writerow([id, type, server, login, passwd, port, comment])
		file_csv.close()
		print 'Item has been updated!'

	# Generate a temporary file
	def generateTmpFile(self):
		try:
			self.file_tmp_name
		except:
			self.file_tmp_name = ''

		if not os.path.isfile(self.file_tmp_name):
			(file_tmp_fd, self.file_tmp_name) = tempfile.mkstemp()
			self.file_tmp = os.fdopen(file_tmp_fd, 'r+')
			return True
		else:
			try:
				self.file_tmp = open(file_tmp_name, 'r+')
				return True
			except:
				return False


################
# BEGIN SCRIPT #
################

num_argv = len(sys.argv)

manage = ManagePasswd(KEY, FILE_GPG, FILE_PWD, TIMEOUT_PWD)

# Display the item's informations
if num_argv >= 3 and sys.argv[1] == '-d':
	if num_argv == 4:
		manage.display(sys.argv[2], sys.argv[3])
	else:
		manage.display(sys.argv[2])

# Remove an item
elif num_argv == 3 and sys.argv[1] == '-r':
	manage.remove(sys.argv[2]) 
	manage.encrypt()

# Update an item
elif num_argv == 3 and sys.argv[1] == '-u':
	manage.update(sys.argv[2]) 
	manage.encrypt()

# Connect to ssh
elif num_argv == 3 and sys.argv[1] == '-s':
	manage.ssh(sys.argv[2])

# Add a new item
elif num_argv == 2 and sys.argv[1] == '-a':
	manage.add()
	manage.encrypt()

# Display help
else:
	manage.help()