#!/usr/bin/python2
# -*- coding: latin-1 -*-
# ----------------------------------------------
# Llamar al servidor con interfaz web service
# ----------------------------------------------
# Inicio de programa python sltsrw.py [-d] [-portnnnn]
# -d: Debug, imprime información de trace
# -portnnnn: Escucha por el puerto nnnn en vez de escuchar por el puerto 8080
#
# El programa necesita que este escuchando el servidor sltsrv por el puerto que
# se indica en salt.ini
# Si sltsrv no está activo, lo inicia a partir de la información contenida
# en el archivo salt.ini que ha de estar en la misma carpeta que sltsrw
import sys
import os
import time
import socket
import threading
import cgi
import cStringIO
import SimpleHTTPServer
from StringIO import StringIO
import Cookie
class ScriptRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  """One instance of this class is created for each HTTP request"""
  def do_GET(self):
    """Begin serving a GET request"""
    # build self.body from the query string
    self.body = {}
    if self.path.find('?')>-1:
      qs = self.path.split('?',1)[1]
      self.body = cgi.parse_qs(qs, keep_blank_values=1)
    self.handle_data()
    
  def do_POST(self):
    """Begin serving a POST request. The request data is readable
    on a file-like object called self.rfile"""
    ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
    length = int(self.headers.getheader('content-length'))
    if ctype == 'multipart/form-data':
      self.body = cgi.parse_multipart(self.rfile, pdict)
    elif ctype == 'application/x-www-form-urlencoded':
      qs = self.rfile.read(length)
      self.body = cgi.parse_qs(qs, keep_blank_values=1)
    else:
      self.body = {}                   # Unknown content-type
    # some browsers send 2 more bytes...
    [ready_to_read,x,y] = select.select([self.connection],[],[],0)
    if ready_to_read:
      self.rfile.read(2)
    self.handle_data()
  def handle_data(self):
    """Process the data received"""
    #Comprobar si es cliente autorizado
    if _ini.Existe("sltsrw","ipsok"):
      ipsok=eval(_ini("sltsrw","ipsok"))
      if ipsok!=[]:
        if ipsok.count(self.client_address[0])==0:
          self.send_error(401,"Cliente no autorizado")
          return
    self.resp_headers = {"Content-type":'text/html'} # default
    self.cookie=Cookie.SimpleCookie()
    if self.headers.has_key('cookie'):
      self.cookie=Cookie.SimpleCookie(self.headers.getheader("cookie"))
    if self.body!={}:
      if dbg:
        print "conectat (web) client",self.client_address
      sck=socketConnect()
      if sck!=None:
        sck.send(self.body['salt4msg'][0])
        datos=sck.recv(128000)
      else:
        self.send_error(503,"No responde el server (sltsrv)")
        return
      ctype='text/plain'
      f=StringIO()
      f.write(datos)
      f.seek(0)
      self.resp_headers['Content-type'] = ctype
      self.resp_headers['Content-length'] = str(len(datos))
      self.done(200,f)
      return
    path = self.get_file() # return a file name or None
    if os.path.isdir(path):
      # list directory
      dir_list = self.list_directory(path)
      self.copyfile(dir_list, self.wfile)
      return
    ext = os.path.splitext(path)[1].lower()
    if len(ext)>1 and hasattr(self,"run_%s" %ext[1:]):
      # if run_some_extension() exists
      exec ("self.run_%s(path)" %ext[1:])
    else:
      # other files
      ctype = self.guess_type(path)
      if ctype.startswith('text/'):
        mode = 'r'
      else:
        mode = 'rb'
      try:
        f = open(path,mode)
        self.resp_headers['Content-type'] = ctype
        self.resp_headers['Content-length'] = str(os.fstat(f.fileno())[6])
        self.done(200,f)
      except IOError:
        self.send_error(404, "File not found")
  def done(self, code, infile):
    """Send response, cookies, response headers 
    and the data read from infile"""
    self.send_response(code)
    for morsel in self.cookie.values():
      self.send_header('Set-Cookie', morsel.output(header='').lstrip())
    for (k,v) in self.resp_headers.items():
      self.send_header(k,v)
    self.end_headers()
    infile.seek(0)
    self.copyfile(infile, self.wfile)
  def get_file(self):
    """Set the Content-type header and return the file open
    for reading, or None"""
    path = self.path
    if path.find('?')>1:
      # remove query string, otherwise the file will not be found
      path = path.split('?',1)[0]
    path = self.translate_path(path)
    if os.path.isdir(path):
      for index in "index.html", "index.htm":
        index = os.path.join(path, index)
        if os.path.exists(index):
          path = index
          break
    return path
  def run_py(self, script):
    """Run a Python script"""
    # redirect standard output so that the "print" statements 
    # in the script will be sent to the web browser
    sys.stdout = cStringIO.StringIO()
    # build the namespace in which the script will be run
    namespace = {'request':self.body, 'headers' : self.headers,
      'resp_headers':self.resp_headers, 'Session':self.Session,
      'HTTP_REDIRECTION':HTTP_REDIRECTION}
    try:
      execfile (script,namespace)
    except HTTP_REDIRECTION,url:
      self.resp_headers['Location'] = url
      self.done(301,cStringIO.StringIO())
    except:
      # print a traceback
      # first reset the output stream
      sys.stdout = cStringIO.StringIO()
      exc_type,exc_value,tb=sys.exc_info()
      msg = exc_value.args[0]
      if tb.tb_next is None:     # errors (detected by the parser)
        line = exc_value.lineno
        text = exc_value.text
      else:                      # exceptions
        line = tb.tb_next.tb_lineno
        text = open(script).readlines()[line-1]
      print '%s in file %s : %s' %(exc_type.__name__,
        os.path.basename(script), cgi.escape(msg))
      print '
Line %s' %line
      print '
%s' %cgi.escape(text) self.resp_headers['Content-length'] = sys.stdout.tell() self.done(200,sys.stdout) def run_tpl(self,script): """Templating system with the string substitution syntax introduced in Python 2.4""" # values must be strings, not lists dic = dict([ (k,v[0]) for k,v in self.body.items() ]) # first check if the string.Template class is available if hasattr(string,"Template"): # Python 2.4 or above try: data = string.Template(open(script).read()).substitute(dic) except: exc_type,exc_value,tb=sys.exc_info() msg = exc_value.args[0] data = '%s in file %s : %s' \ %(exc_type.__name__,os.path.basename(script), cgi.escape(msg)) else: data = "Unable to handle this syntax for " + \ "string substitution. Python version must be 2.4 or above" self.resp_headers['Content-length'] = len(data) self.done(200,cStringIO.StringIO(data)) def Session(self): """Session management If the client has sent a cookie named sessionId, take its value and return the corresponding SessionElement objet, stored in sessionDict Otherwise create a new SessionElement objet and generate a random 8-letters value sent back to the client as the value for a cookie called sessionId""" if self.cookie.has_key("sessionId"): sessionId=self.cookie["sessionId"].value else: sessionId=generateRandom(8) self.cookie["sessionId"]=sessionId try: sessionObject = sessionDict[sessionId] except KeyError: sessionObject = SessionElement() sessionDict[sessionId] = sessionObject return sessionObject # end of class ScriptRequestHandler import SocketServer class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass # end of class ThreadingTCPServer class CfgFile: def __init__(self,file=None): import ConfigParser self.cfg=ConfigParser.ConfigParser() if file==None: miCfg=os.path.dirname(os.path.abspath(__file__)) if os.path.basename(miCfg)[-3:].lower()=='zip': miCfg=os.path.dirname(miCfg) miCfg=os.path.abspath(miCfg+"/salt.cfg") else: miCfg=file self.cfg.read(miCfg) def __call__(self,sec,opt): ret=None if self.cfg.has_option(sec,opt): ret=self.cfg.get(sec,opt) lret=ret.lower() if lret=="true" or lret=="sí" or lret=="si" or lret=="1" or lret=="s": ret=True elif lret=="false" or lret=="no" or lret=="0" or lret=="n": ret=False elif ret.isdigit(): ret=eval(ret) return ret def Existe(self,sec,opt): ret=False if self.cfg.has_option(sec,opt): if self.cfg.get(sec,opt) != "": ret=True return ret # End of Class CfgFile _ini=CfgFile() def socketConnect(): try: sck=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sck.connect(('localhost',20001)) except: print 'Cal iniciar el servidor Salt4 (sltsrv)' sck=None return sck miPath=os.path.dirname(os.path.abspath(__file__)) if os.path.basename(miPath)[-3:].lower()=='zip': miPath=os.path.dirname(miPath) esprod=True port=8080 dbg=False if _ini.Existe("sltsrw","port"): port=_ini("sltsrw","port") for arg in sys.argv: if arg[0:2]=="-d": dbg=True elif arg[0:5]=="-port": port=arg[5:] if esprod: s=SocketServer.ThreadingTCPServer(('',port),ScriptRequestHandler) else: s=SocketServer.TCPServer(('',port),ScriptRequestHandler) if dbg: print "Esperant conexió web",port print "exe,prefix,args,miPath",sys.executable,sys.exec_prefix,sys.argv,miPath s.serve_forever()