#!/usr/bin/env ruby
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# 


# ############################################################################
#  GUI Helper (Server remote boot and shutdown)
#
# remote start (uses wake on lan) and shutdown (uses ssh remote login in shutdown command) of my unix "server" (a thinkpad x31) from
# my client machines. The gui enables my wife to use my server (WAF).
#
# requires (gems):
#         - wxRuby
#         - net-ssh
# tested with:
#         - ubuntu 9.04
#         - winodws xp
#         - ruby 1.8.6
#
# author: 
#         Manfred Regele (Munich)
# hints:
#         how to customize: the server name is in @host, its mac address in "addr" and the shutdown commands, user+pw are in the line 
#         Net::SSH.start( ....
#         This minie program ist *not* a piece of art. It is a quick hack to enable a wife to use WOL and SSH without deeper knowledge and
#          furthermore ist my second ruby script and my first wxRuby usage. But it works. Have care.
# ############################################################################

require 'wx'
require "socket"
require 'net/ssh'
require 'ping'


include Wx



class WakeOnLAN
    
    include Socket::Constants
    
    # Exceptions
    class AddressException < RuntimeError; end
    class SetupException < RuntimeError; end
    
    # Regular Expression constants for hardware and IP addresses
    RE_MAC_ADDR = Regexp.new( '^' + (('[0-9A-Fa-f]' * 2).to_a * 6).join(':') + '$' )
    RE_IP_ADDR = Regexp.new( '^' + ('(([0-9][0-9]?)|([01][0-9][0-9])|(2[0-4][0-9])|(2[0-5][0-5]))'.to_a * 4).join('\.') + '$' )
    
    # Constructor method
    def initialize( mac = nil, ip = nil )
        
        if mac.nil?
            @magic = nil; @ip_addr = nil
        elsif ip.nil?
            self.setup( mac )
        else
            self.setup( mac, ip )
        end
        
    end #initialize()
    
    # Packet set-up method. Keeping this out of the constructor allows a
    # single WakeOnLAN object to be re-used for multiple addresses.
    def setup( mac, ip = '255.255.255.255' )
        
        @magic ||= ("\xff" * 6)
        
        # Validate MAC address and craft the magic packet
        raise AddressException,
            'Invalid MAC address' unless self.valid_mac?( mac )
        mac_addr = mac.split(/:/).collect {|x| x.hex}.pack('CCCCCC')
        @magic[6..-1] = (mac_addr * 16)
        
        # Validate IP address
        raise AddressException,
            'Invalid IP address' unless self.valid_ip?( ip )
        @ip_addr = ip
        
    end #set_up()
    
    def valid_mac?( mac )
        if mac =~ RE_MAC_ADDR then true
        else false
        end
    end #valid_mac?()
    
    def valid_ip?( ip )
        if ip =~ RE_IP_ADDR then true
        else false
        end
    end #valid_ip?()
    
    def send_wake()
        
        raise SetupException,
            'Tried to send packet without setting it up' unless @magic
        
        sock = UDPSocket.new
        sock.setsockopt( SOL_SOCKET, SO_BROADCAST, 1 )
        sock.connect( @ip_addr, Socket.getservbyname( 'discard', 'udp' ) )
        sock.send( @magic, 0 )
		sock.close
        
    end #send_wake()

end #class WakeOnLAN
 




class UpAndDownFrame < Frame

  def  server_state()
     
	 if Ping.pingecho(@host, 10) then
        @my_tbState.set_own_background_colour(Wx::GREEN)
		@my_tbState.change_value("! up !")
	 else
        @my_tbState.set_own_background_colour(Wx::RED)
		@my_tbState.change_value("down !")
	 end	 
	 @my_tbState.append_text(Time.now.strftime(" at %H:%M:%S") )
	 
  end

  def initialize()
		@host = "x31"
  
        super(nil, -1, 'Patrizia und Manfred: Server starten..')
        # First create the controls
        @my_panel 		= Panel.new(self)
        @my_label 		= StaticText.new(@my_panel, -1, 'Hier kann man den Server #{@host} im 2. Stock Starten und Stoppen', DEFAULT_POSITION, DEFAULT_SIZE, ALIGN_CENTER)
        @my_textbox 	= TextCtrl.new(@my_panel, -1, '???')
        @my_tbState 	= TextCtrl.new(@my_panel, -1, ' ')

        @my_startButton = Button.new(@my_panel, -1, 'Server starten..')
        @my_stopButton  = Button.new(@my_panel, -1, 'Server ausschalten..')
		
		
        # Bind controls to functions
        evt_button(@my_startButton.get_id()) { |event| my_startButton_click(event)}
        evt_button(@my_stopButton.get_id()) { |event| my_stopButton_click(event)}
        
		# Now do the layout		
        @my_panel_sizer = BoxSizer.new(VERTICAL)
        @my_panel.set_sizer(@my_panel_sizer)
        @my_panel_sizer.add(@my_label, 0, GROW|ALL, 2)
        @my_panel_sizer.add(@my_textbox, 0, GROW|ALL, 2)
        @my_panel_sizer.add(@my_tbState, 0, GROW|ALL, 2)
        @my_panel_sizer.add(@my_startButton, 0, GROW|ALL, 2)        
        @my_panel_sizer.add(@my_stopButton, 0, GROW|ALL, 3)        
		
		timer = Wx::Timer.new(self) 
		evt_timer(timer.id) { server_state;  } 
		timer.start(3000)		
		
        show()
    end

    def my_startButton_click(event)
	    @my_startButton.enable(false)
        @my_textbox.change_value("starting wol #{@host}")
		
		addr = "00:09:6b:2d:f8:7d";				
        wol = WakeOnLAN.new        
        wol.setup( addr )
        @my_textbox.change_value("Sending wake packet for #{addr} ..")
        wol.send_wake()
		
        @my_textbox.append_text(".. sent.")
		
	    @my_startButton.enable(true)
    end

    def my_stopButton_click(event)
	    @my_stopButton.enable(false)
#		host = "x31"
        @my_textbox.change_value("shutting down #{@host}");
		
		Net::SSH.start( @host , "manfred" , :password => "PASSWORT-FILL-IN-HERE") do |ssh|
			@my_textbox.change_value("ssh  to #{@host}")
			
			#stdout = ""
			#ssh.exec!("ls") do |channel, stream, data|
			#	stdout << data if stream == :stdout
			#end
			
			stdout = ""
			ssh.exec!("echo 'not4me' | sudo -S shutdown -a -h now") do |channel, stream, data|
				stdout << data if stream == :stdout
			end	
			@my_textbox.append_text(stdout)
			
			# shell = ssh.shell.sync
			# shell.send_comand "eoch 'not4me' | sudo -S ls"
			#@my_textbox.append_text(stdout)
			
			@my_textbox.append_text(".. done.")
		end
						
		
	    @my_stopButton.enable(true)
    end	
	
end

class UpAndDownApp < App
  def on_init
    UpAndDownFrame.new
  end
end




UpAndDownApp.new.main_loop()


