#!/usr/bin/env python
# @package      hubzero-mw2-dispatch
# @file         dispatch
# @author       Pascal Meunier <pmeunier@purdue.edu>
# @copyright    Copyright (c) 2016-2017 HUBzero Foundation, LLC.
# @license      http://opensource.org/licenses/MIT MIT
#
# Copyright (c) 2016-2017 HUBzero Foundation, LLC.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# HUBzero is a registered trademark of HUBzero Foundation, LLC.
#

"""
This script is an SSH forced command that validates incoming commands
and calls the appropriate scripts.  It prevents arbitrary command injection.  
It is to be used in the .ssh/authorized_keys file of the user receiving the commands.
Deployment: execution hosts (calling /usr/bin/maxwell_service and virtual SSH commands) 
and file servers (calling /usr/bin/maxwell_fs)

We want to use the same SSH key pair to contact exechosts and file servers from the web server.
"""

import sys
import os
import glob
from hubzero.mw.constants import SERVICE_LOG, HOST_K, VIRTUALSSH_K, EXEC_CONFIG_FILE, FILE_CONFIG_FILE

HOST_CONF = {}
try:
  # read all available configuration files: execution host configuration or the file server configuration
  if os.path.isfile(EXEC_CONFIG_FILE):
    execfile(EXEC_CONFIG_FILE)
  if os.path.isfile(FILE_CONFIG_FILE):
    execfile(FILE_CONFIG_FILE)
except EnvironmentError:
  print "Unable to read configuration file, exiting."
  print "The configuration file '%s' or '%s' needs to exist" % (EXEC_CONFIG_FILE, FILE_CONFIG_FILE)
  sys.exit(1)

HOST_MERGED = HOST_K
HOST_MERGED.update(HOST_CONF)

CLIENT_PATHS = ['/usr/bin/virtualssh_client', '/usr/bin/vssh_exec_proxy']

def log(msg):
  l = open(SERVICE_LOG, "a")
  l.write("dispatch: " + sys.argv[0] + ": " + msg + "\n")
  l.close()

def err(msg):
  open(SERVICE_LOG,"a").write("dispatch: ERROR: " + sys.argv[0] + ": " + msg + "\n")
  #sys.stderr.write(sys.argv[0] + ": " + msg + "\n")
  #syslog.syslog(syslog.LOG_ERR, sys.argv[0] + ": " + msg)
  sys.exit(1)

if not os.environ.has_key("SSH_ORIGINAL_COMMAND"):
  err("invoked without SSH command")

command = os.environ["SSH_ORIGINAL_COMMAND"]
args = command.split()
log("invoking: %s" % command)

#
# Middleware execution host service
#
if args[0] == HOST_MERGED["SERVICE_PATH"]:
  os.execve(args[0], args, os.environ)
  err("failed execve() for execution host service")

#
# Middleware fileserver
#
if args[0] == HOST_MERGED["FS_PATH"]:
  os.execve(args[0], args, os.environ)
  err("failed execve() for file server")


#
# Logfile transfer
#
if len(args) > 1 and args[0] in ("scp", VIRTUALSSH_K["SCP_PATH"]) and args[1] == "-f":
  # -f:  "from"
  newargs=[args[0], args[1]]
  for x in range(2, len(args)):
    s = args[x]
    s = s.replace("\\","")
    #log("globbing %s" % s)
    newargs += glob.glob(s)
  os.execve(VIRTUALSSH_K["SCP_PATH"], newargs, os.environ)
  err("failed execve()")

#
# Transfer in the notify key.
#
if len(args) > 1 and args[0] in ("scp", VIRTUALSSH_K["SCP_PATH"]) and args[1] == "-t":
  if len(args) > 3:
    if args[2] == "--" and args[3] == HOST_MERGED["NOTIFY_KEY"]:
      os.execve(VIRTUALSSH_K["SCP_PATH"], args, os.environ)
      err("failed execve()")
    else:
      err("wrong destination path")
  else:
    if args[2] == HOST_MERGED["NOTIFY_KEY"]:
      # -t:  "to"
      os.execve(VIRTUALSSH_K["SCP_PATH"], args, os.environ)
      err("failed execve()")
    else:
      err("wrong destination path")

#
# Virtual SSH
# Consider using separate VSSH key pair instead
if args[0] in CLIENT_PATHS:
  os.execve(args[0], args, os.environ)
  err("failed execve()")

#
# Only for testing.
#
if args[0] == "/root/test":
  print str.join(' ', args)
  sys.exit(0)

#
# Error...
#
err("Unrecognized command: " + str.join(' ', args))

