#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file originated as a plugin for EventGhost.
# Copyright (C) 2005-2009 Lars-Peter Voss <bitmonster@eventghost.org>
#
# Modification to work on Raspberry Pi and other Linux devices by Aaron Tinsley
# <atinsley@gmail.com>
#
# To learn how to run this file as a background service on the Raspberry Pi, visit
#
# EventGhost is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation;
#
# EventGhost is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import asynchat
import asyncore
from hashlib import md5
import random
import socket
import threading
import locale
import subprocess
import signal
import sys

def signal_handler(signal, frame):
        log('Exiting gracefully...')
        receiver.__stop__()
        sys.exit()
signal.signal(signal.SIGINT, signal_handler)

SYSTEM_ENCODING = locale.getdefaultlocale()[1]
DEBUG = True
if DEBUG:
    def log(msg):
	    print(msg)
else:
    def log(dummyMesg):
        pass


class ServerHandler(asynchat.async_chat):
    """Telnet engine class. Implements command line user interface."""

    def __init__(self, sock, addr, hex_md5, cookie, plugin, server):
        log("Server Handler inited")
        self.plugin = plugin

        # Call constructor of the parent class
        asynchat.async_chat.__init__(self, sock)

        # Set up input line terminator
        self.set_terminator('\n')

        # Initialize input data buffer
        self.data = ''
        self.state = self.state1
        self.ip = addr[0]
        self.payload = [self.ip]
        self.hex_md5 = hex_md5
        self.cookie = cookie


    def handle_close(self):
        log("Closing connection")
        asynchat.async_chat.handle_close(self)


    def collect_incoming_data(self, data):
        """Put data read from socket to a buffer
        """
        # Collect data in input buffer
        log("<<" + repr(data))
        self.data = self.data + data

    def found_terminator(self):
        """
        This method is called by asynchronous engine when it finds
        command terminator in the input stream
        """
        # Take the complete line
        line = self.data

        # Reset input buffer
        self.data = ''

        #call state handler
        self.state(line)


    def initiate_close(self):
        if self.writable():
            self.push("close\n")
        log("Running initiate_close")
        self.state = self.state1


    def state1(self, line):
        """
        get keyword "quintessence\n" and send cookie
        """
        if line == "quintessence":
            self.state = self.state2
            self.push(self.cookie + "\n")
        else:
            self.initiate_close()


    def state2(self, line):
        """get md5 digest
        """
        line = line.strip()[-32:]
        if line == "":
            pass
        elif line.upper() == self.hex_md5:
            self.push("accept\n")
            self.state = self.state3
        else:
            log("NetworkReceiver md5 error")
            self.initiate_close()


    def state3(self, line):
        line = line.decode(SYSTEM_ENCODING)
        if line == "close":
            self.initiate_close()
        elif line[:8] == "payload ":
            self.payload.append(line[8:])
        else:
            if line == "ButtonReleased":
                log('Line is ' + line)
            else:
                event = line
                log("Event is " + event + " and payload is " + self.payload[1])
                if event == "exec":
					command = "python " + self.payload[1]
					try:
						log("Running command: " + command)
						p = subprocess.call(command, shell=True)
					except:
						log("Command \"" + command + "\" failed!")
                else:
					log("Invalid event")
            self.payload = [self.ip]



class Server(asyncore.dispatcher):

    def __init__ (self, port, password, handler):
        self.handler = handler
        self.cookie = hex(random.randrange(65536))
        self.cookie = self.cookie[len(self.cookie) - 4:]
        self.hex_md5 = md5(self.cookie + ":" + password).hexdigest().upper()

        # Call parent class constructor explicitly
        asyncore.dispatcher.__init__(self)

        # Create socket of requested type
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

        # restart the asyncore loop, so it notices the new socket
        threading.Thread(target=asyncore.loop, name="AsyncoreThread").start()

        # Set it to re-use address
        #self.set_reuse_addr()

        # Bind to all interfaces of this host at specified port
        self.bind(('', port))

        # Start listening for incoming requests
        #self.listen (1024)
        self.listen(5)


    def handle_accept (self):
        """Called by asyncore engine when new connection arrives"""
        # Accept new connection
        log("handle_accept")
        (sock, addr) = self.accept()
        ServerHandler(
            sock,
            addr,
            self.hex_md5,
            self.cookie,
            self.handler,
            self
        )



class NetworkReceiver():

    def __init__(self):
	    log("NetworkReceiver init")	

    def __start__(self, port=1024, password="password"):
        self.port = port
        self.password = password
        try:
            self.server = Server(self.port, self.password, self)
        except socket.error, exc:
            raise self.Exception(exc[1])


    def __stop__(self):
        if self.server:
            self.server.close()
        self.server = None


receiver = NetworkReceiver()
receiver.__start__()
signal.pause()
