Changeset 122

Show
Ignore:
Timestamp:
06/06/05 04:05:36 (3 years ago)
Author:
athomas
Message:
  • Migrated to CLY (no longer linked with svn:externals).
  • Started adding hooks into grammar.
  • Passing engine to Firewall objects rather than resolver.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • fwc/trunk

    • Property svn:externals deleted
  • fwc/trunk/Firewall.py

    r121 r122  
    1111        class InvalidRule(Error): pass 
    1212 
    13         def __init__(self, name, resolver): 
     13        def __init__(self, name, engine): 
    1414                self.__rules = [] 
    1515                self.name = name 
    16                 self.resolver = resolver 
     16                self.engine = engine 
    1717 
    1818        def resolve_rule(self, rule): 
  • fwc/trunk/fwc

    r121 r122  
    11#!/usr/bin/python 
    22 
    3 from CLI.CLI import * 
    43import types 
    54import re 
     
    76import readline 
    87import textwrap 
     8from CLY.Parser import Parser, Result 
     9from CLY.Interactive import interact 
     10from CLY.util import * 
    911from Rule import Rule 
    1012from Object import Object 
    1113from Firewall import Firewall 
    1214from Resolver import Resolver 
    13 from util import * 
     15from Engine import Engine 
    1416from fnmatch import fnmatch 
    1517 
    16 CONF_DIR = "/etc/fwcrc" 
    17  
    18 # Globally useful stuff 
    19   
    20 resolver = Resolver() 
    21 firewalls = {} 
    22 firewall = None 
    23 firewall = Firewall("localhost", resolver) 
    24  
    25 def check_port_protocol(context): 
    26         return 'protocol' in context and context['protocol'] in [ 'tcp', 'udp' ] 
    27  
    28 def have_firewall(context): 
    29         """ Return true if the current firewall is have_firewall. """ 
    30         return firewall 
    31  
    32 def have_modifiable_firewall(context): 
    33         return firewall 
    34  
    35 # Help extractors 
    36   
    37 def help_object(context, type): 
    38         help = { '<%s>' % type : '%s object.' % type.title() } 
    39         for o in resolver.get_objects(type): 
    40                 if o.description: 
    41                         help[o.name] = '%s (%s)' % (o.description, o.value) 
    42                 else: 
    43                         help[o.name] = '%s object (%s)' % (type.title(), o.value) 
    44         return help 
    45  
    46 def help_network(context): 
    47         help = { '<network>' : 'Network address.'} 
    48         for o in resolver.get_objects('network'): 
    49                 if o.description: 
    50                         help[o.name] = "%s (%s)" % (o.description, o.value) 
    51                 else: 
    52                         help[o.name] = o.value 
    53         return help 
    54                  
    55 def help_port(context): 
    56         help = { '<port>' : 'Port.'} 
    57         for o in resolver.get_objects('port'): 
    58                 text = o.description or "Port object %s" % o.name 
    59                 help[o.name] = text + " (%s)" % o.value 
    60         return help 
    61  
    62 def help_firewall(context): 
    63         help = {} 
    64         for f in firewalls: 
    65                 help[f] = "Firewall %s" % firewalls[f].name 
    66         return help 
    67  
    68 # Command validators 
    69   
    70 def NETWORK(context, str): 
    71         return re.match(Object.NETWORK_PATTERN, str) \ 
    72                 or str in help_object(context, 'network') 
    73  
    74 def PORT(context, str): 
    75         return re.match(Object.PORT_PATTERN, str) \ 
    76                 or str in help_object(context, 'port') 
    77  
    78 def PORT_RANGE(context, str): 
    79         range = str.split('-') 
    80         return PORT(context, range[0]) and (len(range) < 2 or len(range) > 1 and PORT(context, range[1])) 
    81  
    82 def RULE(context, str): 
    83         try: 
    84                 return int(str) >= 0 and int(str) < len(firewall.get_rules()) 
    85         except: 
    86                 return False 
    87  
    88 def FIREWALL(context, str): 
    89         return str in help_firewall(context) 
    90  
    91 # Commands 
    92   
    93 def insert_rule(context, action, source = [], sport = [], destination = [], dport = [], protocol = None, description = None, where = 'bottom', index = None, state = 'new', log = None, reject_type = None, reject_subtype = None): 
    94         if log: 
    95                 if log == 'log': 
    96                         log = True 
    97                 else: 
    98                         log = log[1] 
    99         if index != None: index = int(index) 
    100         firewall.add(Rule(action, tolist(source), tolist(sport), tolist(destination), tolist(dport), protocol, description, state, log, reject_type, reject_subtype), where, index) 
    101  
    102 def remove_rule(context, rules): 
    103         firewall.remove(map(int, rules)) 
    104  
    105 def move_rule(context, old, where = 'top', index = None): 
    106         if index and old == index: return 
    107         try: 
    108                 old = int(old) 
    109                 new = firewall.add(firewall.get_rule(old), where, index and int(index) or None) 
    110                 if new < old: old += 1 
    111                 firewall.remove(old) 
    112         except TypeError, IndexError: 
    113                 error("Invalid move") 
    114                 raise 
    115  
    116 def list_ruleset(context): 
    117         for ruleno, rule in enumerate(firewall.get_rules()): 
    118                 cmd = "^B%s^B" % rule.action 
    119                 if rule.state != 'new': 
    120                         cmd += " state ^B" + rule.state + "^B" 
    121                 if rule.protocol: 
    122                         try: 
    123                                 cmd += ' protocol ^B%s^B' % int(rule.protocol) 
    124                         except: 
    125                                 cmd += " ^B" + rule.protocol + "^B" 
    126                 if rule.source or rule.sport: 
    127                         cmd += " from" 
    128                         if rule.source: 
    129                                 cmd += " ^B" + ' '.join(rule.source) + "^B" 
    130                         if rule.sport: 
    131                                 cmd += " port ^B" + ' '.join(rule.sport) + "^B" 
    132                 if rule.destination or rule.dport: 
    133                         cmd += " to" 
    134                         if rule.destination: 
    135                                 cmd += " ^B" + ' '.join(rule.destination) + "^B" 
    136                         if rule.dport: 
    137                                 cmd += " port ^B" + ' '.join(rule.dport) + "^B" 
    138                 if rule.reject_type: 
    139                         cmd += " with ^B%s^B" % rule.reject_type 
    140                         if rule.reject_subtype: 
    141                                 cmd += " ^B%s^B" % rule.reject_subtype 
    142                 if rule.log: 
    143                         cmd += " log" 
    144                         if type(rule.log) is str: 
    145                                 cmd += " message ^B'%s'^B" % rule.log 
    146                 if rule.description: 
    147                         cmd += " description ^B'%s'^B" % rule.description 
    148                 cprint("^B%3i:^B %s" % (ruleno, cmd)) 
    149  
    150 def create_object(context, type, name, value, description = None): 
    151         try: 
    152                 resolver.add_object(Object(type, name, value, description)) 
    153         except Resolver.Error, e: 
    154                 error(e) 
    155  
    156  
    157 def list_objects(context, type = 'all', filter = '*'): 
    158         if type == 'all': 
    159                 type = Resolver.get_object_types() 
    160         filter = filter.lower() 
    161         if '*' not in filter and '?' not in filter: 
    162                 filter = '*%s*' % filter 
    163         for t in tolist(type): 
    164                 objects = [x for x in resolver.get_objects(t) if fnmatch((x.name + (x.description and x.description or '')).lower(), filter) or x.description and fnmatch(x.description, filter)] 
    165                 for o in sorted(objects, lambda a, b: cmp(a.name, b.name)): 
    166                         text = "%s ^B%s^B ^B^6%s^N" % (o.type, o.name, ' '.join(map(str, tolist(o.value)))) 
    167                         if o.description: text += " description ^2'%s'^N" % o.description 
    168                         cprint(text) 
    169  
    170 def quit(context = None): 
    171         info("Firewall console exit.") 
    172         sys.exit(0) 
    173  
    174 # Grammar content 
    175   
    176 placement_rules = { 
    177         'replace' : { 
    178                 GROUP : 10, 
    179                 UNLESS_VAR : [ 'old', 'where' ], 
    180                 VAR : 'where', 
    181                 HELP : 'Replace an existing rule.', 
    182                 RULE : { 
    183                         HELP : ('<rule>', 'Rule to replace.'), 
    184                         VAR : 'index', 
    185                         JUMP : RETURN, 
    186                 }, 
    187         }, 
    188         'top|bottom' : { 
    189                 GROUP : 10, 
    190                 UNLESS_VAR : 'where', 
    191                 VAR : 'where', 
    192                 HELP : { 
    193                         'top' : 'Insert rule at top of ruleset.', 
    194                         'bottom' : 'Insert rule at bottom of ruleset (default).', 
    195                 }, 
    196                 JUMP : RETURN, 
    197         }, 
    198         'before|after' : { 
    199                 GROUP : 10, 
    200                 UNLESS_VAR : 'where', 
    201                 VAR : 'where', 
    202                 HELP : { 
    203                         'before' : 'Insert rule before another.', 
    204                         'after' : 'Insert rule after another.', 
    205                 }, 
    206                 RULE : { 
    207                         HELP : ('<rule>', 'Rule to insert relative to.'), 
    208                         VAR : 'index', 
    209                         JUMP : RETURN, 
    210                 }, 
    211         }, 
    212 
    213  
    214 protocol_rules = { 
    215         lambda ctx, token: resolver.have_object(Object.PROTOCOL, token) : { 
    216                 GROUP : 30, 
    217                 UNLESS_VAR : 'protocol', 
    218                 VAR : 'protocol', 
    219                 HELP : lambda x: [(x.name, x.description or 'Protocol %s' % x.value) for x in resolver.get_objects('protocol')], 
    220                 JUMP : RETURN, 
    221         }, 
    222         'protocol' : { 
    223                 GROUP : 30, 
    224                 HELP : 'Arbitrary IP protocol number.', 
    225                 UNLESS_VAR : 'protocol', 
    226                 '\d+' : { 
    227                         HELP : ('<protocol>', 'IP protocol number.'), 
    228                         JUMP : RETURN, 
    229                         VAR : 'protocol', 
    230                 }, 
    231         }, 
    232 
    233  
    234 # Grammar 
    235 cli = CLI({ 
    236         'nat' : { 
    237                 MERGE : [ placement_rules, protocol_rules ], 
    238                 GROUP : 10, 
    239                 HELP : 'NAT matching packets.', 
    240                 VAR : 'action', 
    241                 'to' : { 
    242                         HELP : 'Specify destination to NAT to.', 
    243                         Object.NETWORK_PATTERN : { 
    244                                 VAR : 'nat_ip', 
    245                                 HELP : ('<ip>', 'NAT to IP address.'), 
    246                                 'if' : { 
    247                                         HELP : 'NAT on the following conditions.', 
    248                                         JUMP : 'commands', 
    249                                 }, 
    250                         }, 
    251                 }, 
    252         }, 
    253         'accept|drop|reject' : { 
    254                 HOOK : 'match', 
    255                 GROUP : 10, 
    256                 IF : have_modifiable_firewall, 
    257                 GLOBAL_LABEL : 'commands', 
    258                 MERGE : [ placement_rules, protocol_rules ], 
    259                 VAR : 'action', 
    260                 ACTION : { 
    261                         HELP : 'Add rule to firewall.', 
    262                         # Only if a source or destination have actually been specified 
    263                         #IF : lambda ctx: 'source' in ctx or 'destination' in ctx, 
    264                         ACTION : insert_rule, 
    265                 }, 
    266                 HELP : { 
    267                         'accept' : 'Add rule accepting matching packets.', 
    268                         'drop' : 'Add rule dropping matching packets.', 
    269                         'reject' : 'Add rule rejecting matching packets.', 
    270                 }, 
    271                 'with' : { 
    272                         IF : lambda ctx: 'reject_type' not in ctx and ctx['action'] == 'reject', 
    273                         HELP : 'Reject with the specified packet type.', 
    274                         'tcp' : { 
    275                                 VAR : 'reject_type', 
    276                                 HELP : 'Reject with tcp... packet.', 
    277                                 'reset' : { 
    278                                         VAR : 'reject_subtype', 
    279                                         IF : lambda ctx: 'protocol' in ctx and ctx['protocol'] == 'tcp', 
    280                                         HELP : 'Reject with TCP reset.', 
    281                                         JUMP : 'commands', 
    282                                 }, 
    283                         }, 
    284                         'network|host|protocol|admin' : { 
    285                                 VAR : 'reject_type', 
    286                                 HELP : { 
    287                                         'network' : 'Reject with icmp-network... packet.', 
    288                                         'host' : 'Reject with icmp-host... packet.', 
    289                                         'protocol' : 'Reject with icmp-protocol... packet.', 
    290                                         'admin' : 'Reject with icmp-admin... packet.', 
    291                                 }, 
    292                                 'unreachable' : { 
    293                                         HELP : lambda ctx: { 'unreachable' : 'Reject with icmp-%s-unreachable.' % ctx['reject_type'] }, 
    294                                         IF : lambda ctx: ctx['reject_type'] != 'admin', 
    295                                         VAR : 'reject_subtype', 
    296                                         JUMP : 'commands', 
    297                                 }, 
    298                                 'prohibited' : { 
    299                                         HELP : lambda ctx: { 'prohibited' : 'Reject with icmp-%s-prohibited.' % ctx['reject_type'] }, 
    300                                         IF : lambda ctx: ctx['reject_type'] in ('network', 'host', 'admin'), 
    301                                         VAR : 'reject_subtype', 
    302                                         JUMP : 'commands', 
    303                                 }, 
    304                         }, 
    305  
    306                 }, 
    307                 'from' : { 
    308                         GROUP : 20, 
    309                         HELP : 'Match source network or port.', 
    310                         IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'sport' in ctx) or not 'source' in ctx, 
    311                         NETWORK : { 
    312                                 VAR : 'source', 
    313                                 LABEL : 'source', 
    314                                 JUMP : 'commands', 
    315                                 JUMP_TO : 'source', 
    316                                 GROUP : 20, 
    317                                 'port' : { 
    318                                         GROUP : 20, 
    319                                         RANGE : 1, 
    320                                         HELP : 'Match source port.', 
    321                                         PORT : { 
    322                                                 LABEL : 'sport', 
    323                                                 VAR : 'sport', 
    324                                                 JUMP : 'commands', 
    325                                                 JUMP_TO : 'sport', 
    326                                                 HELP : lambda ctx: help_object(ctx, 'port'), 
    327                                         }, 
    328                                         IF : check_port_protocol, 
    329                                 }, 
    330                                 HELP : help_network, 
    331                         }, 
    332                         'port' : { 
    333                                 HELP : 'Match source port.', 
    334                                 GROUP : 20, 
    335                                 PORT : { 
    336                                         HELP : help_port, 
    337                                         LABEL : 'sports', 
    338                                         VAR : 'sport', 
    339                                         JUMP : 'commands', 
    340                                         # This allows syntax like: accept from port 22 23 25 to anywhere 
    341                                         JUMP_TO : 'sports', 
    342                                 }, 
    343                                 IF : check_port_protocol, 
    344                         }, 
    345                 }, 
    346                 'description' : { 
    347                         '.+' : { 
    348                                 VAR : 'description', 
    349                                 JUMP : 'commands', 
    350                                 HELP : ('<description>', 'Rule description.'), 
    351                         }, 
    352                         RANGE : 1, 
    353                         HELP : 'Set rule description.' 
    354                 }, 
    355                 'to' : { 
    356                         GROUP : 20, 
    357                         IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'dport' in ctx) or not 'destination' in ctx, 
    358                         UNLESS_VAR : [ 'dport', 'destination' ], 
    359                         NETWORK : { 
    360                                 GROUP : 20, 
    361                                 LABEL : 'destination', 
    362                                 VAR : 'destination', 
    363                                 JUMP : 'commands', 
    364                                 JUMP_TO : 'destination', 
    365                                 'port' : { 
    366                                         GROUP : 20, 
    367                                         PORT : { 
    368                                                 LABEL : 'dport', 
    369                                                 VAR : 'dport', 
    370                                                 JUMP : 'commands', 
    371                                                 JUMP_TO : 'dport', 
    372                                                 HELP : help_port, 
    373                                         }, 
    374                                         IF : check_port_protocol, 
    375                                         HELP : 'Match destination port.', 
    376                                 }, 
    377                                 HELP : help_network, 
    378                         }, 
    379                         'port' : { 
    380                                 GROUP : 20, 
    381                                 PORT : { 
    382                                         LABEL : 'dports', 
    383                                         VAR : 'dport', 
    384                                         JUMP : 'commands', 
    385                                         JUMP_TO : 'dports', 
    386                                         HELP : help_port, 
    387                                 }, 
    388                                 IF : check_port_protocol, 
    389                                 HELP : 'Match destination port.', 
    390                         }, 
    391                         HELP : 'Match destination network or port.', 
    392                 }, 
    393                 'log' : { 
    394                         VAR : 'log', 
    395                         UNLESS_VAR : 'log', 
    396                         HELP : "Log a message when this rule matches.", 
    397                         'message' : { 
    398                                 HELP : 'Log with custom message.', 
    399                                 '.+' : { 
    400                                         HELP : ('<message>', 'Custom log message.'), 
    401                                         JUMP : 'commands', 
    402                                         VAR : 'log', 
    403                                 }, 
    404                         }, 
    405                         JUMP : 'commands', 
    406                 }, 
    407         }, 
    408         'quit|exit' : { 
    409                 HELP : [ 'quit', 'Exit.' ], 
    410                 ACTION : { 
    411                         HELP : 'Exit.', 
    412                         ACTION : quit, 
    413                 }, 
    414         }, 
    415         'object' : { 
    416                 HELP : 'Ruleset object manipulation.', 
    417                 'import' : { 
    418                         LABEL : 'import', 
    419                         'all' : { 
    420                                 RANGE : 1, 
    421                                 GROUP : 10, 
    422                                 HELP : 'Also import aliases.', 
    423                                 VAR : 'aliases', 
    424                                 JUMP : 'import', 
    425                         }, 
    426                         's|'.join(Resolver.get_object_types()) + 's' : { 
    427                                 GROUP : 20, 
    428                                 VAR : 'import_types', 
    429                                 HELP : lambda ctx: [(x + 's', 'Import system %s objects' % x) for x in Resolver.get_object_types()], 
    430                                 ACTION : lambda ctx, import_types, **args: resolver.populate_defaults(tolist(import_types), with_aliases = 'aliases' in ctx), 
    431                         }, 
    432                         HELP : 'Import system objects.', 
    433                         ACTION : lambda ctx, **args: resolver.populate_defaults(with_aliases = 'aliases' in ctx), 
    434                 }, 
    435                 'create' : { 
    436                         HELP : 'Create a ruleset object.', 
    437                         '|'.join(Resolver.get_object_types()) : { 
    438                                 VAR : 'type', 
    439                                 HELP : lambda ctx: [(x, 'Create %s object' % x) for x in Resolver.get_object_types()], 
    440                                 Object.NAME_PATTERN : { 
    441                                         VAR : 'name', 
    442                                         HELP : { '<name>' : 'Name of object to create.' }, 
    443                                         LABEL : 'create', 
    444                                         'description' : { 
    445                                                 UNLESS_VAR : 'description', 
    446                                                 ORDER : 10, 
    447                                                 GROUP : 20, 
    448                                                 '.+' : { 
    449                                                         VAR : 'description', 
    450                                                         HELP : { '<description>' : 'Description of object.' }, 
    451                                                         JUMP : 'create', 
    452                                                         ACTION : { 
    453                                                                 IF_VAR : 'value', 
    454                                                                 ACTION : create_object, 
    455                                                         }, 
    456                                                 }, 
    457                                                 HELP : 'Optional description of object.' 
    458                                         }, 
    459                                         lambda ctx, token: re.match(Object.TYPE_PATTERNS[ctx['type']], token) or token in help_object(ctx, ctx['type']) : { 
    460                                                 ORDER : 20, 
    461                                                 GROUP : (10, 'Add ...'), 
    462                                                 VAR : 'value', 
    463                                                 HELP : lambda ctx: [(k, v) for k, v in help_network(ctx).iteritems() if 'value' in ctx and k not in tolist(ctx['value']) or 'value' not in ctx], 
    464                                                 JUMP : 'create', 
    465                                                 ACTION : create_object, 
    466                                         }, 
    467                                 }, 
    468                         }, 
    469                 }, 
    470                 'delete' : { 
    471                         HELP : 'Delete a ruleset object.', 
    472                         '|'.join(Resolver.get_object_types()) : { 
    473                                 VAR : 'type', 
    474                                 HELP : lambda ctx: [(x + 's', 'Remove %s objects' % x) for x in Resolver.get_object_types()], 
    475                         }, 
    476                 }, 
    477                 'list' : { 
    478                         HELP : 'List objects.', 
    479                         ACTION : { 
    480                                 HELP : 'List objects.', 
    481                                 ACTION : list_objects, 
    482                         }, 
    483                         's|'.join(Resolver.get_object_types()) + 's|all' : { 
    484                                 ORDER : 10, 
    485                                 VAR : 'type', 
    486                                 HELP : lambda ctx: [(x + 's', 'List %s objects' % x) for x in Resolver.get_object_types()] + [ ('all', 'All object types.') ], 
    487                                 ACTION : list_objects, 
    488                                 '.+' : { 
    489                                         VAR : 'filter', 
    490                                         HELP : ('<glob>', 'Filter results using the given glob.'), 
    491                                         ACTION : list_objects, 
    492                                 }, 
    493                         }, 
    494                         '.+' : { 
    495                                 ORDER : 20, 
    496                                 VAR : 'filter', 
    497                                 HELP : ('<glob>', 'Filter results using the given glob.'), 
    498                                 ACTION : list_objects, 
    499                         }, 
    500                 } 
    501         }, 
    502         'delete' : { 
    503                 GROUP : 20, 
    504                 IF : have_modifiable_firewall, 
    505                 HELP : 'Remove rule(s) from the ruleset.', 
    506                 LABEL : 'rule', 
    507                 RULE : { 
    508                         VAR : 'rules', 
    509                         HELP : ('<rule>', 'Rule to remove.'), 
    510                         JUMP : 'rule', 
    511                         ACTION : remove_rule, 
    512                 }, 
    513         }, 
    514         'list' : { 
    515                 GROUP : 20, 
    516                 IF : have_firewall, 
    517                 HELP : 'List ruleset.', 
    518                 ACTION : list_ruleset, 
    519         }, 
    520         'move' : { 
    521                 GROUP : 20, 
    522                 IF : have_modifiable_firewall, 
    523                 HELP : 'Move rule.', 
    524                 RULE : { 
    525                         LABEL : 'move', 
    526                         VAR : 'old', 
    527                         HELP : ('<rule>', 'Rule to move.'), 
    528                         MERGE : placement_rules, 
    529                         ACTION : { 
    530                                 HELP : 'Move rule.', 
    531                                 ACTION : move_rule 
    532                         }, 
    533                 }, 
    534         }, 
    535         'firewall' : { 
    536                 HELP : "Commands for firewall management.", 
    537                 'acquire' : { 
    538                         HELP : 'Acquire a firewall for management.', 
    539                         '\w+' : { 
    540                         }, 
    541                 }, 
    542                 'list' : { 
    543                         HELP : "List available firewalls.", 
    544                 }, 
    545                 FIREWALL : { 
    546                         HELP : help_firewall, 
    547                 }, 
    548         }, 
    549 
    550 
    551  
    552 #from IPTables import IPTables 
    553 #engine = IPTables(cli) 
    554  
    555 # Command line interaction 
    556   
    557  
    558 cli_inject_text = '' 
    559 bind_key = hasattr(readline, 'bind_key') and readline.bind_key or None 
    560 force_redisplay = hasattr(readline, 'force_redisplay') and readline.force_redisplay or None 
    561 set_input_hook = hasattr(readline, 'set_input_hook') and readline.set_input_hook or None 
    562 cursor = hasattr(readline, 'cursor') and readline.cursor or None 
    563  
    564 def cli_completion(text, state): 
    565         line = readline.get_line_buffer()[0:readline.get_begidx()] 
    566         ctx = None 
    567         try: 
    568                 result = cli.parse(line) 
    569         except CLI.Error: 
    570                 pass 
    571         try: 
    572                 help = result.candidates() 
    573                 out = [] 
    574                 for h in help: 
    575                         if h[0] != '<' and h.startswith(text): 
    576                                 out.append(h + ' ') 
    577         except e: 
    578                 print e 
    579         return out[state] 
    580  
    581 def cli_injector(): 
    582         try: 
    583                 global cli_inject_text 
    584                 readline.insert_text(cli_inject_text) 
    585                 cli_inject_text = '' 
    586         except Exception, e: 
    587                 pass 
    588  
    589 def generate_help(line, need_linefeed = False): 
    590         result = cli.parse(line) 
    591         # We are not at the end of the line 
    592         if result.token: 
    593                 readline.insert_text("?") 
    594                 return 
    595         if cursor: 
    596                 if result.context.parsed_tokens and result.context.parsed_tokens[-1].start() >= cursor(): 
    597                         return 
    598         if need_linefeed: 
    599                 print 
    600         format_help(result.help()) 
    601         if force_redisplay: 
    602                 force_redisplay() 
    603  
    604 def cli_help(key, count): 
    605         generate_help(readline.get_line_buffer(), True) 
    606  
    607 def format_help(help): 
    608         length = 0 
    609         for group in help: 
    610                 for h in help[group]: 
    611                         if len(h[0]) > length: 
    612                                 length = len(h[0]) 
    613         header, footer = None, None 
    614         if HELP_HEADER in help: 
    615                 header = help[HELP_HEADER] 
    616                 del(help[HELP_HEADER]) 
    617         if HELP_FOOTER in help: 
    618                 footer = help[HELP_FOOTER] 
    619                 del(help[HELP_FOOTER]) 
    620         if header: 
    621                 cprint(header) 
    622                 print 
    623         count = 0 
    624         last_order = None 
    625         for order, group in sorted(help, lambda a, b: cmp(a[0], b[0])): 
    626                 if last_order != None and order != last_order: 
    627                         print 
    628                 last_order = order 
    629                 if group: 
    630                         cprint("  ^6%s^N" % group) 
    631                 elif count: 
    632                         print_next = 1 
    633                 for h in help[(order, group)]: 
    634                         cprint("    ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) 
    635                 count += 1 
    636         if footer: 
    637                 print 
    638                 cprint(footer) 
    639  
    640 readline.set_completer(cli_completion) 
    641 readline.set_startup_hook(cli_injector) 
    642 # Use custom readline extensions not in 2.4 
    643 if bind_key and set_input_hook: 
    644         bind_key(ord('?'), cli_help) 
    645         #set_input_hook(timeout) 
    646  
     18engine = Engine() 
    64719cprint(""" 
    64820Welcome to the ^BFireWall Console^B (^BFWC^B) 
     
    65224 
    65325while True: 
    654         command = '' 
    655         try: 
    656                 command = raw_input("fwc> ") 
    657         except KeyboardInterrupt: 
    658                 print 
    659         except EOFError: 
    660                 print 
    661                 quit() 
     26        result = interact(engine._parser, engine._prompt) 
    66227 
    663         command = re.sub(r'^\s+|\s+$', '', command) 
    664         if command == '': continue 
    665  
    666         # Looking for help? 
    667         if command[-1] == '?': 
    668                 command = re.sub(r'\?\s*$', '', command) 
    669                 generate_help(command) 
    670                 cli_inject_text = command 
     28        if result.state == Result.NOP: 
     29                continue 
     30        elif result.state == Result.ENDOFINPUT: 
     31                engine.quit() 
     32        elif result.state == Result.OK: 
     33                try: 
     34                        result() 
     35                except Firewall.Error, e: 
     36                        error(e) 
    67137        else: 
    672                 result = None 
    673 # TODO Make tree syntax/formative errors into CLI.Error  
    674 #               try: 
    675                 result = cli.parse(command) 
    676 #               except CLI.Error, e: 
    677 #                       result = e 
    678  
    679                 if result.state == Result.OK: 
    680                         try: 
    681                                 result() 
    682                         except Firewall.Error, e: 
    683                                 error(e) 
     38                if not result.token: 
     39                        error("%s\n    Candidates are: %s" % (result.message, ', '.join(result.candidates()))) 
    68440                else: 
    685                         if not result.token: 
    686                                 error("%s\n    Candidates are: %s" % (result.message, ', '.join(result.candidates()))) 
    687                         else: 
    688                                 error("%s (near '%s')\n    Candidates are: %s" % (result.message, result.token.group(), ', '.join(result.candidates()))) 
     41                        error("%s (near '%s')\n    Candidates are: %s" % (result.message, result.token.group(), ', '.join(result.candidates())))