view contrib/pyhgsh/pyhgsh @ 2425:eee271568e8a default tip

Initial version of pyhgsh
author Josef "Jeff" Sipek <jeffpc@optonline.net>
date Sun, 18 Jun 2006 16:48:38 -0400
parents
children
line wrap: on
line source

#!/usr/bin/python
#
# pyhgsh - restricted login shell for mercurial implemented in Python
#
# Copyright 2006 Josef "Jeff" Sipek <jsipek@cs.sunysb.edu>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License, incorporated herein by reference.
#
# this program is login shell for dedicated mercurial user account. it
# only allows few actions:
#
# 1. run hg in server mode on specific repository.
#
# 2. run interactive shell, allows for repository maintainance
#
# only tested on linux yet. patches for non-linux systems welcome.
#

import os
import sys

name	= "pyhgsh"
version	= "0.5"

prompt	= "> "
repofile= "pyhgsh.repos"

repos	= []

def debugexit(argv, code):
	print argv
	sys.exit(0)

def write(str):
	sys.stdout.write(str)

def valid_repo(r):
	" returns true if r is in list of repos"
	global repos

	for okr in repos:
		if okr == r:
			return True
	
	return False

def cmd_help(args):
	print name + ": The Python Mercurial shell"
	print ""
	print "available commands:"
	print ""
	for c in cmds:
		print " %s\t%s" % (c[0], c[2])

def cmd_verify(args):
	try:
		repo = args[0]
	except IndexError:
		print name + ": you must specify a valid repository"
		return

	if not valid_repo(repo):
		print name + ": '%s' is not a valid repository" % (repo,)
		return

	os.system("hg -R %s verify" % (repo,))

def cmd_repos(args):
	for r in repos:
		print r

def cmd_quit(args):
	sys.exit(0)

def cmd_version(args):
	print name + " (version " + version + ")"
	os.system("hg version | head -1")

def cmd_invalid(cmd):
	print name + ": Invalid command '%s'" % (cmd,)

def interactive():
	"""
	This is an interactive shell handler
	"""
	print ""
	cmd_help([])
	print ""

	while True:
		write(prompt)

		try:
			line = sys.stdin.readline()
			tok = line.strip().split(" ")
		except KeyboardInterrupt:
			# ctrl-c
			print ""
			continue

		if len(line) == 0:
			# ctrl-d ?
			print ""
			cmd_quit([])

		if tok[0] != "":
			# non-empty command
			ok = False
			for c in cmds:
				if tok[0] == c[0]:
					# command found, execute
					c[1](tok[1:])
					ok = True
					break

			if not ok:
				# command not found
				cmd_invalid(tok[0])

def execute(cmd):
	"""
	Request for execution of a command handler
	"""
	
	# this is the potentially exploitable area of the shell therefore we
	# must make sure that everything is checked properly before we
	# execute the command

	tok = cmd.split(" ")

	# it must be a hg call
	if tok[0] != "hg":
		print "Not a hg call"
		return

	# FIXME: hacky
	if cmd.find(";") != -1:
		print "Detected attept to exploit shell"
		return

	# FIXME: make sure the repo is allowed

	os.system(cmd)

def main(argv):
	"""
	main, get things prepared
	"""
	
	argv.pop(0)

	# read in list of allowed repositories
	global repos
	f = open(repofile)
	repos = f.readlines()
	f.close()

	for i in range(len(repos)):
		repos[i] = repos[i].strip()

	if len(argv) == 0:
		# no args
		interactive()

	elif len(argv) == 2:
		if argv[0] == "-c":
			# execute command
			execute(argv[1])

		else:
			# unknown
			debugexit(argv, 1)

	else:
		# unkown
		debugexit(argv, 1)

# commented out commands are not implemented
cmds = (
#	("clone",	cmd_clone,	"clone a repository"),
	("help",	cmd_help,	"this screen help"),
#	("init",	cmd_init,	"init a repository"),
#	("pull",	cmd_pull,	"pull changes from one repository to another"),
#	("push",	cmd_push,	"push changes from one repository to another"),
	("quit",	cmd_quit,	"close this session"),
	("repos",	cmd_repos,	"list repositories available"),
	("verify",	cmd_verify,	"verify a repository"),
	("version",	cmd_version,	"display version"),
)

if __name__ == "__main__":
	main(sys.argv)