#!/usr/bin/env python2
# Gufw 14.04.2 - http://gufw.org
# Copyright (C) 2008-2014 Marcos Alvarez Costales https://launchpad.net/~costales
#
# Gufw 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.
#
# Gufw 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 Gufw; if not, see http://www.gnu.org/licenses for more
# information.

import time, os, commands, ConfigParser


class Backend():
    def get_status(self):
        ufw_status = commands.getstatusoutput('LANGUAGE=C ufw status')
        if 'Status: active' in ufw_status[1]:
            return True
        else:
            return False
    
    def get_policy(self, policy):
        if policy == 'incoming':
            ufw_default_incoming = commands.getstatusoutput('grep DEFAULT_INPUT_POLICY /etc/default/ufw')
            if 'ACCEPT' in ufw_default_incoming[1]:
                return 'allow'
            elif 'DROP' in ufw_default_incoming[1]:
                return 'deny'
            elif 'REJECT' in ufw_default_incoming[1]:
                return 'reject'
        
        elif policy == 'outgoing':
            ufw_default_outgoing = commands.getstatusoutput('grep DEFAULT_OUTPUT_POLICY /etc/default/ufw')
            if 'ACCEPT' in ufw_default_outgoing[1]:
                return 'allow'
            elif 'DROP' in ufw_default_outgoing[1]:
                return 'deny'
            elif 'REJECT' in ufw_default_outgoing[1]:
                return 'reject'
    
    def get_ufw_logging(self):
        ufw_cmd = commands.getstatusoutput('cat /etc/ufw/ufw.conf')
        if 'LOGLEVEL=full' in ufw_cmd[1]:
            return 'full'
        elif 'LOGLEVEL=high' in ufw_cmd[1]:
            return 'high'
        elif 'LOGLEVEL=medium' in ufw_cmd[1]:
            return 'medium'
        elif 'LOGLEVEL=low' in ufw_cmd[1]:
            return 'low'
        else:
            return 'off'
    
    def set_status(self, status):
        if status:
            cmd = 'ufw --force enable'
        else:
            cmd = 'ufw disable'
        
        commands.getstatusoutput(cmd)
    
    def set_policy(self, policy, value):
        if policy == 'incoming':
            if value == 'allow':
                cmd = 'ufw default allow incoming'
            elif value == 'deny':
                cmd = 'ufw default deny incoming'
            elif value == 'reject':
                cmd = 'ufw default reject incoming'
        
        elif policy == 'outgoing':
            if value == 'allow':
                cmd = 'ufw default allow outgoing'
            elif value == 'deny':
                cmd = 'ufw default deny outgoing'
            elif value == 'reject':
                cmd = 'ufw default reject outgoing'
        
        commands.getstatusoutput(cmd)
    
    def set_ufw_logging(self, logging):
        if logging == 'off':
            cmd = 'ufw logging off'
        elif logging == 'low':
            cmd = 'ufw logging low'
        elif logging == 'medium':
            cmd = 'ufw logging medium'
        elif logging == 'high':
            cmd = 'ufw logging high'
        elif logging == 'full':
            cmd = 'ufw logging full'
        
        commands.getstatusoutput(cmd)
    
    def reset_fw(self):
        cmd = 'ufw --force reset'
        commands.getstatusoutput(cmd)
    
    def get_cfg_value(self, attribute):
        cfg = ConfigParser.ConfigParser()
        try:
            if not cfg.read(['/etc/gufw/gufw.cfg']):
                return ''
        except:
            os.remove('/etc/gufw/gufw.cfg')
            return ''
        
        if not cfg.has_option('GufwConfiguration', attribute):
            return ''
            
        return cfg.get('GufwConfiguration', attribute)
    
    def set_cfg_value(self, attr, value):
        cfg_file = '/etc/gufw/gufw.cfg'
        if not os.path.exists(cfg_file):
            f = open(cfg_file, 'w')
            cfg = ConfigParser.ConfigParser()
            cfg.add_section("GufwConfiguration")
            cfg.write(f)  
            f.close()  
            os.chmod(cfg_file, 0700) # Just an admin can read this file
        
        cfg = ConfigParser.ConfigParser()
        cfg.read(cfg_file)
        cfg.set('GufwConfiguration', attr, value)
        f = open(cfg_file, 'w')
        cfg.write(f)  
        f.close()
    
    def set_profile_values(self, profile, status, incoming, outgoing, rules):
        file_name = profile + '.profile'
        file_path = os.path.join('/etc', 'gufw', file_name)
        
        cfg = ConfigParser.ConfigParser()
        
        cfg.add_section("fwBasic")
        if status:
            cfg.set('fwBasic', 'status', 'enabled')
        else:
            cfg.set('fwBasic', 'status', 'disabled')
        cfg.set('fwBasic', 'incoming', incoming)
        cfg.set('fwBasic', 'outgoing', outgoing)
        
        i = 0
        for rule in rules:
            if not rule['command']:
                continue
            section = 'Rule' + str(i)
            cfg.add_section(section)
            cfg.set(section, 'ufw_rule',    rule['ufw_rule'])
            cfg.set(section, 'description', rule['description'])
            cfg.set(section, 'command',     rule['command'])
            cfg.set(section, 'policy',      rule['policy'])
            cfg.set(section, 'direction',   rule['direction']) 
            cfg.set(section, 'protocol',    rule['protocol']) 
            cfg.set(section, 'from_ip',     rule['from_ip']) 
            cfg.set(section, 'from_port',   rule['from_port']) 
            cfg.set(section, 'to_ip',       rule['to_ip']) 
            cfg.set(section, 'to_port',     rule['to_port']) 
            cfg.set(section, 'iface',       rule['iface']) 
            cfg.set(section, 'logging',     rule['logging'] )
            i += 1
            
        f = open(file_path, 'w')
        cfg.write(f)  
        f.close()
        os.chmod(file_path, 0700) # Just an admin can read this file
    
    def get_profile_values(self, profile):
        status = False
        incoming = ''
        outgoing = ''
        rules = []
        
        if profile[:1] == '/':
            file_path = profile
        else:
            file_name = profile + '.profile'
            file_path = os.path.join('/etc', 'gufw', file_name)
        
        cfg = ConfigParser.ConfigParser()
        if not cfg.read([file_path]):
            return (status, incoming, outgoing, rules)
        
        if not cfg.has_section('fwBasic'):
            return (status, incoming, outgoing, rules)
        
        for section in cfg.sections():
            
            if section == 'fwBasic':
                if cfg.get('fwBasic', 'status') == 'enabled':
                    status = True
                else:
                    status = False
                incoming = cfg.get('fwBasic', 'incoming')
                outgoing = cfg.get('fwBasic', 'outgoing')
            
            else:
                rule = {'ufw_rule'   : cfg.get(section, 'ufw_rule'), 
                        'description': cfg.get(section, 'description'),
                        'command'    : cfg.get(section, 'command'),
                        'policy'     : cfg.get(section, 'policy'),
                        'direction'  : cfg.get(section, 'direction'),
                        'protocol'   : cfg.get(section, 'protocol'),
                        'from_ip'    : cfg.get(section, 'from_ip'),
                        'from_port'  : cfg.get(section, 'from_port'),
                        'to_ip'      : cfg.get(section, 'to_ip'),
                        'to_port'    : cfg.get(section, 'to_port'),
                        'iface'      : cfg.get(section, 'iface'),
                        'logging'    : cfg.get(section, 'logging')}
                rules.append(rule)
        
        return (status, incoming, outgoing, rules)
    
    
    def delete_file_profile(self, profile):
        commands.getstatusoutput('rm /etc/gufw/' + profile + '.profile')
    
    def rename_file_profile(self, old, new):
        commands.getstatusoutput('mv /etc/gufw/' + old + '.profile /etc/gufw/' + new + '.profile')
    
    def export_profile(self, profile, file):
        commands.getstatusoutput('cp /etc/gufw/' + profile + '.profile ' + file + ' ; chmod 777 ' + file)
    
    def refresh_log(self):
        f = file('/var/log/gufw.log', 'w')
        f.close()
    
    def add_to_log(self, msg):
        try:
            f = open('/var/log/gufw.log', 'r')
            log = f.readlines()
            f.close()
        except:
            log = ''
        
        new_line = '[' + time.strftime('%x %X') + '] ' + msg
        if log:
            new_line = new_line + '\n'
        
        f = open('/var/log/gufw.log', 'w')
        f.write(new_line)
        f.writelines(log)
        f.close()
        
        return new_line

    def get_log(self): # OK
        cmd = commands.getstatusoutput('cat /var/log/gufw.log')
        if cmd[0]:
            return ''
        
        return cmd[1]
    
    def get_rules(self):
        rules = commands.getstatusoutput('LANGUAGE=C ufw status numbered')
        lines = rules[1].split('\n')
        return_rules = []
        
        for line in lines:
            if line and 'ALLOW' in line or 'DENY' in line or 'LIMIT' in line or 'REJECT' in line:
                rule = line.split('] ')
                return_rules.append(' '.join(rule[1].split()))
        
        return return_rules
    
    def add_rule(self, insert, policy, direction, iface, logging, proto, from_ip, from_port, to_ip, to_port):
        # Insert Number
        if insert:
            insert = ' '.join(['insert', insert])
        # Interface
        if iface:
            iface = ' '.join(['on', iface])
        # Protocol
        proto_aux = ''
        if proto:
            proto_aux = ' '.join(['proto', proto])
        # FROM
        if from_ip:
            from_ip = ' '.join(['from', from_ip])
            if from_port:
                from_port = ' '.join(['port', from_port])
        # TO
        if to_ip:
            to_ip = ' '.join(['to', to_ip])
            if to_port:
                to_port = ' '.join(['port', to_port])
        
        # Simple rule
        # ufw [insert NUM] allow|deny|reject|limit [in|out] [log|log-all] PORT[/protocol]
        # ufw &insert &policy &direction &logging &toPort/&proto
        if to_port and not iface and not from_ip and not from_port and not to_ip:
            rule = 'ufw %s %s %s %s %s' %  (insert, policy, direction, logging, to_port)
            if proto:
                rule = '/'.join([rule, proto])
        # Advanced rule
        # ufw [insert NUM] allow|deny|reject|limit [in|out on INTERFACE] [log|log-all] [proto protocol] [from ADDRESS [port PORT]] [to ADDRESS [port PORT]]
        # ufw &insert &policy &direction &iface &logging &proto &fromIP &fromPort &toIP &toPort
        else:
            rule = 'ufw %s %s %s %s %s %s %s %s %s %s' %  (insert, policy, direction, iface, logging, proto_aux, from_ip, from_port, to_ip, to_port)
        
        rule = ' '.join(rule.split()) # Condense
        cmd = commands.getstatusoutput(rule)
        
        result = []
        result.append(rule)
        result.append(cmd[1])
        
        return result # cmd | ufw result
    
    def delete_rule(self, num):
        delete_rule = 'ufw --force delete &number'.replace('&number', num)
        cmd = commands.getstatusoutput(delete_rule)
        
        result = []
        result.append(delete_rule)
        result.append(cmd[1])
        
        return result # cmd | ufw result
    
    def get_listening_report(self):
        return_report = []
        actual_protocol = 'None'
        ufw_report = commands.getstatusoutput('LANGUAGE=C ufw show listening')
        report_lines = ufw_report[1].replace('\n   [', '%')
        report_lines = report_lines.split('\n')
        
        for descomponent_report in report_lines:
            # Set actual protocol
            if not descomponent_report:
                continue
            if 'tcp6:' in descomponent_report:
                actual_protocol = 'TCP6'
                continue
            if 'tcp:' in descomponent_report:
                actual_protocol = 'TCP'
                continue
            if 'udp6:' in descomponent_report:
                actual_protocol = 'UDP6'
                continue
            if 'udp:' in descomponent_report:
                actual_protocol = 'UDP'
                continue
            
            policy = 'None'
            descomponent_report = descomponent_report.strip()
            descomponent_report = descomponent_report.replace('(', '')
            descomponent_report = descomponent_report.replace(')', '')
            
            if ']' in descomponent_report:
                descomponent_policy = descomponent_report.split(']')
                if 'allow' in descomponent_policy[1]:
                    policy = 'allow'
                elif 'deny' in descomponent_policy[1]:
                    policy = 'deny'
                elif 'reject' in descomponent_policy[1]:
                    policy = 'reject'
                elif 'limit' in descomponent_policy[1]:
                    policy = 'limit'
            
            descomponent_report = descomponent_report.split('%')
            descomponent_fields = descomponent_report[0].split(' ')
            # Order: protocol % port % address % application % policy
            return_report.append('%'.join([actual_protocol, descomponent_fields[0], descomponent_fields[1], descomponent_fields[2], policy]))
        
        return return_report
