# Copyright (C) 2004-2007 Tresys Technology, LLC
#  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

namespace eval Apol_Cond_Rules {
    variable vals
    variable widgets
}

proc Apol_Cond_Rules::create {tab_name nb} {
    variable vals
    variable widgets

    _initializeVars

    set frame [$nb insert end $tab_name -text "Conditional Expressions"]
    set topf [frame $frame.top]
    set bottomf [frame $frame.bottom]
    pack $topf -expand 0 -fill both -pady 2
    pack $bottomf -expand 1 -fill both -pady 2

    set rules_box [TitleFrame $topf.rules_box -text "Rule Selection"]
    set obox [TitleFrame $topf.obox -text "Search Options"]
    set dbox [TitleFrame $bottomf.dbox -text "Conditional Expressions Display"]
    pack $rules_box -side left -expand 0 -fill both -padx 2
    pack $obox -side left -expand 1 -fill both -padx 2
    pack $dbox -expand 1 -fill both -padx 2

    # Rule selection subframe
    set fm_rules [$rules_box getframe]
    set allow [checkbutton $fm_rules.allow -text "allow" \
                   -onvalue $::QPOL_RULE_ALLOW -offvalue 0 \
                   -variable Apol_Cond_Rules::vals(rs:avrule_allow)]
    set auditallow [checkbutton $fm_rules.auditallow -text "auditallow" \
                        -onvalue $::QPOL_RULE_AUDITALLOW -offvalue 0 \
                        -variable Apol_Cond_Rules::vals(rs:avrule_auditallow)]
    set dontaudit [checkbutton $fm_rules.dontaudit -text "dontaudit" \
                       -onvalue $::QPOL_RULE_DONTAUDIT -offvalue 0 \
                       -variable Apol_Cond_Rules::vals(rs:avrule_dontaudit)]
    set type_transition [checkbutton $fm_rules.type_transition -text "type_trans" \
                             -onvalue $::QPOL_RULE_TYPE_TRANS -offvalue 0 \
                             -variable Apol_Cond_Rules::vals(rs:type_transition)]
    set type_member [checkbutton $fm_rules.type_member -text "type_member" \
                         -onvalue $::QPOL_RULE_TYPE_MEMBER -offvalue 0 \
                         -variable Apol_Cond_Rules::vals(rs:type_member)]
    set type_change [checkbutton $fm_rules.type_change -text "type_change" \
                         -onvalue $::QPOL_RULE_TYPE_CHANGE -offvalue 0 \
                         -variable Apol_Cond_Rules::vals(rs:type_change)]
    grid $allow $type_transition -sticky w -padx 2
    grid $auditallow $type_member -sticky w -padx 2
    grid $dontaudit $type_change -sticky w -padx 2

    # Search options subframes
    set ofm [$obox getframe]
    set bool_frame [frame $ofm.bool]
    pack $bool_frame -side left -padx 4 -pady 2 -anchor nw
    set enable [checkbutton $bool_frame.enable \
                    -variable Apol_Cond_Rules::vals(enable_bool) \
                    -text "Boolean"]
    set widgets(combo_box) [ComboBox $bool_frame.combo_box \
                                -textvariable Apol_Cond_Rules::vals(name) \
                                -helptext "Type or select a boolean variable" \
                                -state disabled -entrybg white -autopost 1]
    set widgets(regexp) [checkbutton $bool_frame.regexp \
                             -text "Search using regular expression" \
                             -state disabled \
                             -variable Apol_Cond_Rules::vals(use_regexp)]
    trace add variable Apol_Cond_Rules::vals(enable_bool) write \
        [list Apol_Cond_Rules::_toggleSearchBools]
    pack $enable -anchor w
    pack $widgets(combo_box) $widgets(regexp) -padx 4 -anchor nw -expand 0 -fill x

    set ok_button [button $ofm.ok -text OK -width 6 \
                       -command Apol_Cond_Rules::_search]
    pack $ok_button -side right -anchor ne -padx 5 -pady 5

    set widgets(results) [Apol_Widget::makeSearchResults [$dbox getframe].results]
    pack $widgets(results) -expand yes -fill both

    return $frame
}

proc Apol_Cond_Rules::open {ppath} {
    variable widgets
    $widgets(combo_box) configure -values [Apol_Cond_Bools::getBooleans]
}

proc Apol_Cond_Rules::close {} {
    variable widgets

    _initializeVars
    $widgets(combo_box) configure -values {}
    Apol_Widget::clearSearchResults $widgets(results)
}

proc Apol_Cond_Rules::getTextWidget {} {
    variable widgets
    return $widgets(results).tb
}

#### private functions below ####

proc Apol_Cond_Rules::_initializeVars {} {
    variable vals
    array set vals [list \
                        rs:avrule_allow $::QPOL_RULE_ALLOW \
                        rs:avrule_auditallow $::QPOL_RULE_AUDITALLOW \
                        rs:avrule_dontaudit $::QPOL_RULE_DONTAUDIT \
                        rs:type_transition $::QPOL_RULE_TYPE_TRANS \
                        rs:type_member $::QPOL_RULE_TYPE_MEMBER \
                        rs:type_change $::QPOL_RULE_TYPE_CHANGE \
                        enable_bool 0 \
                        name {} \
                        use_regexp 0]
}

proc Apol_Cond_Rules::_toggleSearchBools {name1 name2 op} {
    variable vals
    variable widgets
    if {$vals(enable_bool)} {
        $widgets(combo_box) configure -state normal
        $widgets(regexp) configure -state normal
    } else {
        $widgets(combo_box) configure -state disabled
        $widgets(regexp) configure -state disabled
    }
}

proc Apol_Cond_Rules::_search {} {
    variable vals
    variable widgets
    .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state disabled

    Apol_Widget::clearSearchResults $widgets(results)
    if {![ApolTop::is_policy_open]} {
        tk_messageBox -icon error -type ok -title "Error" -message "No current policy file is opened."
        .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
        return
    }

    set avrule_selection 0
    foreach {key value} [array get vals rs:avrule_*] {
        set avrule_selection [expr {$avrule_selection | $value}]
    }
    set terule_selection 0
    foreach {key value} [array get vals rs:type_*] {
        set terule_selection [expr {$terule_selection | $value}]
    }
    if {$avrule_selection == 0 && $terule_selection == 0} {
            tk_messageBox -icon error -type ok -title "Error" -message "At least one rule must be selected."
            .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
            return
    }

    set bool_name {}
    if {$vals(enable_bool)} {
        if {[set bool_name $vals(name)] == {}} {
            tk_messageBox -icon error -type ok -title "Error" -message "No booleean selected."
            .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
            return
        }
    }

    set q [new_apol_cond_query_t]
    $q set_bool $::ApolTop::policy $bool_name
    if {$vals(use_regexp)} {
        $q set_regex $::ApolTop::policy 1
    }

    set v [$q run $::ApolTop::policy]
    $q -acquire
    $q -delete
    set results [cond_vector_to_list $v]
    $v -acquire
    $v -delete

    if {[llength $results] == 0} {
        set text "Search returned no results."
    } else {
        set text "[llength $results] conditional"
        if {[llength $results] != 1} {
            append text s
        }
        append text " match the search criteria.  Expressions are in Reverse Polish Notation.\n\n"
    }
    Apol_Widget::appendSearchResultText $widgets(results) $text
    if {![info exists apol_progress]} {
        Apol_Progress_Dialog::wait "Conditional Expressions" "Rendering conditionals" \
        {
            if {[ApolTop::is_capable "syntactic rules"]} {
                $::ApolTop::qpolicy build_syn_rule_table
            }
            set counter 1
            set num_results [llength $results]
            foreach r [lsort -index 0 $results] {
                apol_tcl_set_info_string $::ApolTop::policy "Rendering $counter of $num_results"
                set text [_renderConditional $r $avrule_selection $terule_selection $counter]
                Apol_Widget::appendSearchResultText $widgets(results) "$text\n\n"
                incr counter
            }
        }
    }
    .mainframe.frame.nb.frules.nb.fApol_Cond_Rules.top.obox.f.ok configure -state normal
}

proc Apol_Cond_Rules::_renderConditional {cond avrules terules cond_number} {
    set cond_expr [apol_cond_expr_render $::ApolTop::policy $cond]
    set i [$cond get_av_true_iter $::ApolTop::qpolicy $avrules]
    set av_true_vector [new_apol_vector_t $i]
    $i -acquire
    $i -delete
    set i [$cond get_av_false_iter $::ApolTop::qpolicy $avrules]
    set av_false_vector [new_apol_vector_t $i]
    $i -acquire
    $i -delete
    set i [$cond get_te_true_iter $::ApolTop::qpolicy $terules]
    set te_true_vector [new_apol_vector_t $i]
    $i -acquire
    $i -delete
    set i [$cond get_te_false_iter $::ApolTop::qpolicy $terules]
    set te_false_vector [new_apol_vector_t $i]
    $i -acquire
    $i -delete

    variable widgets
    set text "conditional expression $cond_number: \[ [join $cond_expr] \]\n"

    Apol_Widget::appendSearchResultText $widgets(results) "$text\nTRUE list:\n"
    if {![ApolTop::is_capable "syntactic rules"]} {
        apol_tcl_avrule_sort $::ApolTop::policy $av_true_vector
        Apol_Widget::appendSearchResultRules $widgets(results) 4 $av_true_vector qpol_avrule_from_void
        apol_tcl_terule_sort $::ApolTop::policy $te_true_vector
        Apol_Widget::appendSearchResultRules $widgets(results) 4 $te_true_vector qpol_terule_from_void
    } else {
        set syn_avrules [apol_avrule_list_to_syn_avrules $::ApolTop::policy $av_true_vector NULL]
        Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_avrules qpol_syn_avrule_from_void
        set syn_terules [apol_terule_list_to_syn_terules $::ApolTop::policy $te_true_vector]
        Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_terules qpol_syn_terule_from_void
        $syn_avrules -acquire
        $syn_avrules -delete
        $syn_terules -acquire
        $syn_terules -delete
    }

    Apol_Widget::appendSearchResultText $widgets(results) "\nFALSE list:\n"
    if {![ApolTop::is_capable "syntactic rules"]} {
        apol_tcl_avrule_sort $::ApolTop::policy $av_false_vector
        Apol_Widget::appendSearchResultRules $widgets(results) 4 $av_false_vector qpol_avrule_from_void
        apol_tcl_terule_sort $::ApolTop::policy $te_false_vector
        Apol_Widget::appendSearchResultRules $widgets(results) 4 $te_false_vector qpol_terule_from_void
    } else {
        set syn_avrules [apol_avrule_list_to_syn_avrules $::ApolTop::policy $av_false_vector NULL]
        Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_avrules qpol_syn_avrule_from_void
        set syn_terules [apol_terule_list_to_syn_terules $::ApolTop::policy $te_false_vector]
        Apol_Widget::appendSearchResultSynRules $widgets(results) 4 $syn_terules qpol_syn_terule_from_void
        $syn_avrules -acquire
        $syn_avrules -delete
        $syn_terules -acquire
        $syn_terules -delete
    }

    $av_true_vector -acquire
    $av_true_vector -delete
    $av_false_vector -acquire
    $av_false_vector -delete
    $te_true_vector -acquire
    $te_true_vector -delete
    $te_false_vector -acquire
    $te_false_vector -delete
}
