#!/usr/bin/python2
# This file is part of SSHconf.
#
#    SSHconf 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 3 of the License, or
#    (at your option) any later version.
#
#    SSHconf 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 SSHconf.  If not, see <http://www.gnu.org/licenses/>.
#
# Author: Frank "ancho" Becker 
#
# Copyright 2010 Frank Becker <ancho@gmx.net>
from sshconf.ssh.paramiko.config import SSHConfig
'''
Created on 31.12.2009

@author: frank
'''

import os
import logging
from options import *

class SSHConf(object):
    '''
    This class should read and write host configuration for SSH.
    See man ssh_config
    '''
    
    def __init__(self):
        '''
        Opens a filedescriptor for ssh_config file
        '''
        self._configfile    = os.path.expanduser("~/.ssh/config")
        self._basename      = os.path.dirname(self._configfile)
        self._hostmanager   = HostManager()
        self._log           = logging.getLogger("SSHConf");
        self._ssh_config    = SSHConfig()
    
    def read_config(self):
        host = None
        
        try:
            configfd    = open( self._configfile, "r")
        except IOError:
            #create file
            configfd   = open(self._configfile, "w")
            configfd.close()
            #open it for reading now
            configfd    = open( self._configfile, "r")
        
        self._ssh_config.parse(configfd)
        
        for options in self._ssh_config._config[1:]:
            self._log.debug(options)
            host = Host(options)
            self._hostmanager.add_host(host)
        
        configfd.close()
    
    def write_config(self, file=None):
        
        if( file ):
            conffd = open( file, 'w' )
        else:
            conffd = open( self._configfile, 'w' )
            
        for host in self._hostmanager.get_host_list():
            conffd.write( str(host) + "\n" )
        
        conffd.close()
        
    
    
    def get_host_manager(self):
        return self._hostmanager
    
    def get_configfile(self):
        return self._configfile

    def get_basename(self):
        return self._basename
    
class HostManager():
    
    def __init__(self):
        self._hostliste = set()
        self._log       = logging.getLogger("HostManager")
        
    def __iter__(self):
        return self._hostliste
        
    def get_host_list(self):
        self.__update_host_ids()
        return sorted(self._hostliste, self.__compare_hosts)

    
    def __update_host_ids(self):
        id=1
        for host in sorted(self._hostliste, self.__compare_hosts):
            host.set_id(id)
            id = id + 1 
    
    def get_host_names(self):
        hostnames = []
        for host in self.get_host_list():
            hostnames.append( host.get_option(oHost) )
        hostnames.sort()
        return hostnames

    def print_hosts(self):
        for host in self._hostliste:
            self._log.debug( host )
            

    def get_host_by_name(self, name):
        for host in self.get_host_list():
            if host.get_option(oHost) == name:
                return host
        
        return None
    
    def get_host_by_index(self, index):
        return self.get_host_list()[index]
    
    def add_host(self, host):
        self._log.info("add host: %s " % host.get_option(oHost) )
        id = self.__get_new_id()
        host.set_id(id)
        self._log.debug( str(host) )
        self._hostliste.add( host )
        self.__update_host_ids()
        return host.get_id()
    
    def new_host(self):
        options = { option[ oHost ]: 'NewHost' }
        host = Host( options )
        self.add_host(host)
        return host
        
    def del_host(self, host):
        self._log.debug("del host: " + host.get_option(oHost) )
        self._log.debug( str(host) )
        self._hostliste.remove( host )
        
    def del_host_by_name(self, name):
        host = self.get_host_by_name(name)
        if host != None: self.del_host(host)

    def del_host_by_Index(self, index):
        host = self.get_host_by_index(index)
        if host != None: self.del_host(host)
          
    def __get_new_id(self):
        return len(self._hostliste) + 1
    
    def __compare_hosts(self, a, b):
        if( a.get_option(oHost) < b.get_option(oHost) ):
            return -1
        else:
            return 1
    
class Host():
    
    
    def __init__(self, options):
        '''
        Define a Datastructure for SSH host configuration
        '''
        self._id        = None
        self._options   = options
        self._log       = logging.getLogger("Host")
        self._log.debug(options)
        self._localfwdmanager  = ForwardManager()
        self._remotefwdmanager = ForwardManager()
        
        if self._options.has_key( option[ oLocalForward ] ):
            self._add_forwards( oLocalForward )
        
        if self._options.has_key( option[ oRemoteForward ] ):
            self._add_forwards( oRemoteForward )
    
    def get_options(self):
        return self._options
    
    def _add_forwards(self, forwardtyp):
        
        forwards = self._options[ option[ forwardtyp ] ]
        
        for forward in forwards:
            proplist = forward.split(" ")
            local_part  = proplist[0].split(":")
            remote_part = proplist[1].split(":")
                    
            rhost        = remote_part[0]
            rhost_port   = remote_part[1] 
                     
            if len( local_part ) == 1 :
                port = local_part[0]
                if( forwardtyp == oLocalForward ):        
                    lfwd = LocalForward( port, rhost, rhost_port )
                else:
                    lfwd = RemoteForward( port, rhost, rhost_port )
                    
            elif len( local_part ) == 2:
                port = local_part[1]
                bind_address = local_part[0]
                
                if( forwardtyp == oLocalForward ):
                    lfwd = LocalForward( port, rhost, rhost_port, bind_address )
                else:
                    lfwd = RemoteForward( port, rhost, rhost_port, bind_address )
            
            if( forwardtyp == oLocalForward ):        
                self.get_local_forward_manager().add_forward( lfwd )
            else:
                self.get_remote_forward_manager().add_forward( lfwd )
    
    def get_option(self, opt):
        ret = ""
        
        if self._options.has_key( option[opt] ):
            ret = self._options[ option[opt] ]
        
        if ret == "": 
            ret = self._get_defaultvalue(opt)
        
        return ret
    
    def set_option(self, opt, value):
        self._options[ option[ opt ] ] = value
    
    def _get_defaultvalue(self, opt):
        ret = ""
        
        if default_value.has_key( opt ):
            ret = default_value[ opt ]
        
        return ret
    
    def get_id(self):
        return self._id
    
    def set_id(self, id):
        self._id = id
        

    def __str__(self):
        retstring = "#" + str(self._id) + ". " + str( self.get_option(oHost) ) + "\n" 

        retstring += oHost + " " + str( self.get_option(oHost) ) + "\n"
        
        for opt in option.keys():
            if opt not in [oHost,oLocalForward,oRemoteForward]:
                if ( self.get_option( opt )   != self._get_defaultvalue( opt ) ):
                    retstring += opt + ' ' + str( self.get_option(opt) )  + '\n'
            else:
                pass

        for lfwd in self._localfwdmanager.get_forward_list():
            retstring += str( lfwd ) + '\n'

        for rfwd in self._remotefwdmanager.get_forward_list():
            retstring += str( rfwd ) + '\n'                   

        return retstring
        
    
    def get_local_forward_manager(self):
        return self._localfwdmanager
    
    def get_remote_forward_manager(self):
        return self._remotefwdmanager
    
    def __eq__(self, host):
        if( host == None ): return False
        if( self.get_option(oHost) == host.get_option(oHost) ):
            return True
        else:
            return False
        
    def __ne__(self, host):
        
        if( host == None ): return True
        
        if( self.get_option(oHost) != host.get_option(oHost) ):
            return True
        else:
            return False
    
    def __hash__(self):
        return hash( self.get_option(oHost) )
    
class ForwardManager():
    
    def __init__(self):
        self._fwdList = []
        self._log = logging.getLogger( "ForwardManager" )
        
    def __iter__(self):
        return self._fwdList
        
    def add_forward(self, localforward):
        self._fwdList.append( localforward )
        
    def del_forward(self, localforward):
        self._fwdList.remove( localforward )
        
    def get_forward_list(self):
        return self._fwdList
    
    def get_forward_by_index(self, index):
        return self._fwdList[index]
    
    def print_forwards(self):
        for lfwd in self._fwdList:
            self._log.debug( lfwd.toString() )


class Forward():
    def __init__(self, port, host, host_port,  bind_address="localhost" ):
        self._bind_address  = bind_address
        self._port          = port
        self._host          = host
        self._host_port     = host_port

    def get_bind_address(self):
        return self._bind_address
    
    def get_port(self):
        return self._port
    
    def get_host(self):
        return self._host
    
    def get_host_port(self):
        return self._host_port
    
    def set_bind_address(self, bind_address):
        self._bind_address = bind_address
    
    def set_port(self, port):
        self._port = port
    
    def set_host(self, host):
        self._host = host
    
    def set_host_port(self, host_port):
        self._host_port = host_port
        

class LocalForward(Forward):
    
    
    def __str__(self):
        ret = oLocalForward + " " + self._bind_address + ":" + str( self._port ) + " " + str( self._host ) + ":" + str( self._host_port )
        return ret 

class RemoteForward(Forward):
    
    
    def __str__(self):
        ret = oRemoteForward + " " + self._bind_address + ":" + str( self._port ) + " " + str( self._host ) + ":" + str( self._host_port )
        return ret 

