#!/Net/local/bin/wish -f

# where to find the TIS report executables for things like netacl-summ.sh

set TISBIN "/usr/local/tis/bin_1.3"

# where to find the fileselect.tcl, searchbox.tcl, and taputils.tcl files

set sbx(lib) /home/u/mccurley/fwtk_watch/sblib

# you should not have to edit anything below this line (HAH!)

###############################################################################
# this is a graphical tool to view logs from the TIS firewall toolkit (or     #
# for that matter, any syslog file).  It was written by Kevin McCurley, by    #
# modifying code obtained from other sources:                                 #
#    Searchbox (copyright below)                                              #
#    fileselect (in separate files, but included for convenience)             #
#    code provided by Stuart Clayman (sclayman@cs.ucl.ac.uk) for feeding the  #
#       output of a "tail -f" into a textbox.                                 #
# In order to run this, you will need to have already installed:              #
#     tcl 7.3                                                                 #
#     tk 3.6                                                                  #
#     addinput-3.6b modifications to tk                                       #
# for convenience, I have distributed the Searchbox and fileselect source     #
# along with this rather than require you to find them from another source.   #
###############################################################################

if [file exists ~/.fwtk_watch] {source ~/.fwtk_watch}


# this part of the code is modified from "Searchbox".  Searchbox was
# Copyright (c) 1993  T.A. Phelps
# 
# Permission to use, copy, modify, and distribute this software and its
# documentation for educational, research and non-profit purposes, 
# without fee, and without a written agreement is hereby granted, 
# provided that the above copyright notice and the following three 
# paragraphs appear in all copies.  
# 
# Permission to incorporate this software into commercial products may 
# be obtained from the Office of Technology Licensing, 2150 Shattuck 
# Avenue, Suite 510, Berkeley, CA  94704. 
# 
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 
# ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 
# THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 
# SUCH DAMAGE.
# 
# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 
# PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 
# CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 
# ENHANCEMENTS, OR MODIFICATIONS.

set sbx(version) 1.0.1

source $sbx(lib)/taputils.tcl
source $sbx(lib)/searchbox.tcl
source $sbx(lib)/fileselect.tcl

if [llength $argv]==0 {set thefile "/var/adm/messages"} \
	{set thefile [lfirst $argv]}

puts "using logfile $thefile"

set sb(scrollbarside) right
set sb(fontList) {
   "-adobe-new century schoolbook-medium-r-normal--*-100-*"
   "-adobe-new century schoolbook-medium-r-normal--*-120-*"
   "-adobe-new century schoolbook-medium-r-normal--*-140-*"
   "-adobe-courier-medium-r-normal--*-80-*"
   "-adobe-courier-medium-r-normal--*-100-*"
   "-adobe-courier-medium-r-normal--*-120-*"
   "fixed"
   "9x15"
}
set sb(wrapList) {none char word}
set sb(printList) {lpr enscript}
set sb(text,font) "fixed"
set sb(geom) 800x500+300+10
set sb(text,wrap) none
set sb(text,fontcolor) black
set sb(text,margin) 5
set sb(regexp,case) 1
set sb(incr,case) 0
set sbx(uid) 0

proc sbGUI {{w .fwtk_watch}} {
   global sb sbx

   if {![winfo exists $w]} {toplevel $w}
   wm minsize $w 100 100
   wm geometry $w $sb(geom)

   frame $w.mb

   label $w.mb.status -text bozo

   button $w.help -text "Help"

   menubutton $w.mb.font -text "Font" -menu $w.mb.font.m
   menu $w.mb.font.m
   foreach i $sb(fontList) {
      $w.mb.font.m add radiobutton -label $i -variable sb(text,font) \
         -command "$w.text configure -font [list $i]"
   }

   menubutton $w.mb.wrap -text "Wrap" -menu $w.mb.wrap.m
   menu $w.mb.wrap.m
   foreach i {none char word} {
      $w.mb.wrap.m add radiobutton -label $i -variable sb(text,wrap) \
         -command "$w.text configure -wrap $i"
   }


   menubutton $w.mb.print -text "Print" -menu $w.mb.print.m
   menu $w.mb.print.m
   foreach i $sb(printList) {
      $w.mb.print.m add command -label $i -command "sbPrint $w $i"
   }

   menubutton $w.mb.mail -text "Mail" -menu $w.mb.mail.m
   menu $w.mb.mail.m
   $w.mb.mail.m add command -label "Mail text" -command "mailit $w"

   menubutton $w.mb.options -text "Options" -menu $w.mb.options.m
   menu $w.mb.options.m
   $w.mb.options.m add checkbutton -label "Incremental Search Case Sensitive" \
      -variable sb(incr,case)
   $w.mb.options.m add checkbutton -label "Regexp Search Case Sensitive" \
      -variable sb(regexp,case)

   menubutton $w.mb.filter -text "Filter" -menu $w.mb.filter.m
   menu $w.mb.filter.m
   $w.mb.filter.m add command -label "Reload file" \
	-command "loadfile"
   $w.mb.filter.m add command -label "Show only su" \
	-command "matchlines $w *su:*"
   $w.mb.filter.m add command -label "Show only AUTH" \
	-command "matchlines $w *AUTH*"
   $w.mb.filter.m add command -label "Show only BADAUTH" \
	-command "matchlines $w *BADAUTH*"
   $w.mb.filter.m add command -label "Show only fwtksyserr" \
	-command "matchlines $w *fwtksyserr*"
   $w.mb.filter.m add command -label "Show only deny" \
	-command "matchlines $w *deny*"
   
   menubutton $w.mb.reports -text "Reports" -menu $w.mb.reports.m
   menu $w.mb.reports.m
   $w.mb.reports.m add command -label "netacl" \
	-command "show_report $w netacl-summ.sh"
   $w.mb.reports.m add command -label "authsrv" \
	-command "show_report $w authsrv-summ.sh"
   $w.mb.reports.m add command -label "login" \
	-command "show_report $w login-summ.sh"
   $w.mb.reports.m add command -label "ftp" \
	-command "show_report $w ftp-summ.sh"
   $w.mb.reports.m add command -label "telnet & rlogin" \
	-command "show_report $w tn-gw-summ.sh"
   $w.mb.reports.m add command -label "http" \
	-command "show_report $w http-summ.sh"
   $w.mb.reports.m add command -label "plug-gw proxy" \
	-command "show_report $w plug-gw-summ.sh"
   $w.mb.reports.m add command -label "email" \
	-command "show_report $w smtp-summ.sh"
   $w.mb.reports.m add command -label "summary of current log (slow)" \
	-command "show_report $w current-report.sh"
   $w.mb.reports.m add command -label "summary of all logs (VERY slow)" \
	-command "show_report $w all-report.sh"

   menubutton $w.mb.file -text "File" -menu $w.mb.file.m
   menu $w.mb.file.m
   $w.mb.file.m add command -label "Select file..." \
	-command changefile
   $w.mb.file.m add command -label "Toggle monitoring file" \
	-command tailfile

   pack $w.mb.file $w.mb.font $w.mb.wrap $w.mb.print $w.mb.mail \
	$w.mb.options $w.mb.filter $w.mb.reports -side left
   pack $w.mb.status -in $w.mb -side left -fill x -expand yes
   tk_menuBar $w.mb $w.mb.font $w.mb.wrap $w.mb.print $w.mb.options


   frame $w.t
   text $w.text -font $sb(text,font) -foreground $sb(text,fontcolor) \
      -relief sunken -borderwidth 2 -padx $sb(text,margin) -pady $sb(text,margin) \
      -yscrollcommand "$w.v set" -exportselection yes -wrap $sb(text,wrap) \
      -height 10 -width 5
   bind $w.text <Meta-KeyPress-q> sbQuit
   $w.text tag configure search \
      -foreground [lindex [$w.text configure -background] 4] \
      -background [lindex [$w.text configure -foreground] 4]
   bind $w.text <Any-KeyPress> "
      searchboxKeyNav \[key_state2mnemon %s\] %K \$sb(incr,case) $w.text $w.v $w.mb.status 0
   "
   scrollbar $w.v -orient vertical -command "$w.text yview"
   pack $w.v -in $w.t -side $sb(scrollbarside) -fill y
   pack $w.text -in $w.t -side $sb(scrollbarside) -fill both -expand yes
   $w.text mark set viewpoint end

   frame $w.s
   button $w.search -text "Search" -command "
      if !\[catch {selection get}\] {set sbx(search,string$w) \[selection get\]}
      searchboxSearch \$sbx(search,string$w) 1 \$sb(regexp,case) search $w.text $w.v $w.mb.status $w.cnt
   "
   button $w.next -text "Next" -command "searchboxNext search $w.text $w.v $w.mb.status"

   entry $w.searchpat -font fixed -relief sunken -textvariable sbx(search,string$w)
   emacsbind $w.searchpat
   bind $w.searchpat <Meta-KeyPress-q> sbQuit
   set sbx(search,oldstring$w) ""
   bind $w.searchpat <KeyPress-Return> "
      selection clear $w.text
      if {\$sbx(search,oldstring$w)!=\$sbx(search,string$w)} {
         set sbx(search,oldstring$w) \$sbx(search,string$w)
         $w.search invoke
      } else {$w.next invoke}"
   label $w.cnt
   button $w.quit -text "Quit" -command sbQuit; # write out new params?

   pack $w.search -in $w.s -side left
   pack $w.next -in $w.s -side left -padx 6
   pack $w.searchpat -in $w.s -side left -fill x -expand yes -ipadx 10 -anchor w
   pack $w.cnt -in $w.s -side left

   pack $w.quit -in $w.search -side left -padx 3
   bind all <Meta-KeyPress-q> "$w.quit invoke"
   pack $w.quit -in $w.s -side left

   pack $w.mb -fill x -pady 4
   pack $w.t -fill both -expand yes
   pack $w.s -fill x -pady 6

   foreach i {t text v} {bind $w.$i <Enter> "focus $w.text"}
   bind $w.s <Enter> "focus $w.searchpat"
   foreach k {KeyPress-Tab Shift-KeyPress-Tab} {
      bind $w.searchpat <$k> "focus $w.text"
      bind $w.text <$k> "focus $w.searchpat"
   }

#   an alternative I tried for monochrome screens
#   $w.text tag configure level0 -borderwidth 2 -relief raised -font 13x22
#   $w.text tag configure level1 -borderwidth 2 -relief raised -font 10x20
#   $w.text tag configure level2 -borderwidth 2 -relief raised -font 9x15
#   $w.text tag configure level3 -borderwidth 2 -relief raised -font 8x13
#   $w.text tag configure level4 -borderwidth 2 -relief raised -font fixed

   $w.text tag configure level0 -borderwidth 2 -relief raised \
	-background Red
   $w.text tag configure level1 -borderwidth 2 -relief raised \
	-background Yellow
   $w.text tag configure level2 -borderwidth 2 -relief raised \
	-background Orange
   $w.text tag configure level3 -borderwidth 2 -relief raised \
	-background HotPink
   $w.text tag configure level4 -borderwidth 2 -relief raised \
	-background LightBlue
   $w.text tag configure level5 -borderwidth 2 -relief raised \
	-background cyan-sky
   $w.text tag configure level6 -borderwidth 2 -relief raised \
	-background Green
   

   bind $w.text <Control-q> {destroy .}
   bind $w.text <Control-c> {destroy .}

   focus $w
   return $w
}

proc sbReadFile {filename} {
   global sb sbx win thefile

   $win.text configure -state normal
   $win.text delete 1.0 end
   if {[catch {set lf [glob $filename]}]} {
     okdialog Error "$filename doesn't exist"
   }
   set filename [lfirst $lf]
   if {![file readable $filename]} {
     okdialog {Error} "file $filename is not readable"
   }
   $win.mb.status configure -text "reading $filename ..."
   update idletasks

   if {$filename=="stdin"} {set fid "stdin"} {
      if {[catch {set fid [open $filename]}]} {
	okdialog {Error} "file $filename cannot be opened"
      }
   }

   set cnt 0
   while {![eof $fid]} {
	gets $fid line
      $win.text insert end "$line\n"
      incr cnt
      tag_line $line $cnt
   }
   close $fid

   switch $cnt {
      0 {set cnttxt "empty"}
      1 {set cnttxt "1 line"}
      default {set cnttxt "$cnt lines"}
   }
   $win.cnt configure -text $cnttxt
   $win.mb.status configure -text "static $filename"
   update idletasks

   set thefile $filename
}

proc sbPrint {w cmd} {
   set fid [open "|$cmd" w]
   scan [$w.text index end] "%d" end
   for {set i 1} {$i<$end} {incr i} {
      puts $fid [$w.text get $i.0 "$i.0 lineend"]
   }
   close $fid
}


proc sbQuit {} {
   destroy .
}

# this stuff is modified from the book by Ousterhout: Tcl and the Tk Toolkit

proc okdialog {title text} {
  global button
  toplevel .dlg -class Dialog
  wm title .dlg $title
  set buttonvar 0
  frame .dlg.top -relief raised -bd 1
  pack .dlg.top -side top -fill both
  frame .dlg.bot -relief raised -bd 1
  pack .dlg.bot -side bottom -fill both
  message .dlg.top.msg -width 3i -text $text
  pack .dlg.top.msg -side right -expand 1 -fill both -padx 3m -pady 3m
  button .dlg.bot.butt -text "OK" -command "set buttonvar 1"
  pack .dlg.bot.butt -side left -expand 1 -padx 3m -pady 3m -ipadx 2m -ipady 1m
  set oldFocus [focus]
  grab set .dlg
  focus .dlg
  tkwait variable buttonvar
  focus $oldFocus
  destroy .dlg
}

# most of the stuff below here is added by KSM ###############################

proc changefile {} {
  global inputstream

  if {$inputstream != "NONE"} {
    tailfile
  }
  fileselect sbReadFile {Select Log File}
}

proc loadfile {} {
  global thefile inputstream

  if {$inputstream != "NONE"} {
    tailfile
  }
  sbReadFile $thefile
}
	
#
# this is a toggle of whether to input from a "tail -f thefile"
#

proc tailfile {} {
  global thefile win inputstream inputpid 

  if {$inputstream == "NONE"} {
    $win.mb.status configure -text "monitoring $thefile"
    $win.text delete 1.0 end
    set inputstream [open "|tail -f $thefile" r]
    set inputpid [pid $inputstream]
    fileevent $inputstream readable [list readline $inputstream]
  } else {
#    catch {exec /bin/kill $inputpid}
    catch {close $inputstream}
#    close $inputstream
    $win.mb.status configure -text "static $thefile"
    set inputstream "NONE"
  }
  update idletasks
}

proc tag_line {line cnt} {
      global win
      if [string match *fwtksyserr* $line] {
	$win.text tag add level0 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *alert:* $line] {
	$win.text tag add level1 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *LOCKED* $line] {
	$win.text tag add level2 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *deny* $line] {
	$win.text tag add level3 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *su:* $line] {
	$win.text tag add level4 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *unable* $line] {
	$win.text tag add level5 $cnt.0 "$cnt.0 lineend"
      } elseif [string match *BADAUTH* $line] {
	$win.text tag add level6 $cnt.0 "$cnt.0 lineend"
      }
}

proc mailit {w} {
   set fid [open "|whoami" r]
   gets $fid me
   close $fid
   sbPrint $w "mail $me"
}


proc show_report {w script} {
	global TISBIN thefile inputstream

	if {$inputstream != "NONE"} {
	   tailfile
	}
	$w.mb.status configure -text "processing $thefile..."
	update idletasks

	set input [open "|$TISBIN/$script $thefile" r+]
	$w.text configure -state normal
	$w.text delete 1.0 end
	while {![eof $input]} {
		$w.text insert end [read $input 1000]
	}
	close $input
	$w.text configure -state disabled
	$w.mb.status configure -text "finished $script report"
	scan [$w.text index end] %d numlines
	$w.cnt configure -text "$numlines lines"
}
	


proc matchlines {w patt} {
	global thefile

        $w.mb.status configure -text "filtering $thefile for string $patt..."
        update idletasks
	$w.text configure -state normal
	scan [$w.text index end] %d numlines
	for {set i $numlines} {$i>0} {incr i -1} {
		if ![string match $patt [$w.text get $i.0 "$i.0 lineend"]] {
			$w.text delete $i.0 "$i.0 lineend + 1 chars"
			incr numlines -1
		}
	}
	switch $numlines {
		0 {set cnttxt "empty"}
		1 {set cnttxt "1 line"}
		default {set cnttxt "$numlines lines"}
	}
	$w.cnt configure -text $cnttxt
        $w.mb.status configure -text "filtered $thefile by string $patt"
        update idletasks

}

proc custom_filter {w} {
	toplevel .dlg
	wm title .dlg "Custom search"
	set good ""
	label .dlg.label -text "Enter search string:"
	entry .dlg.str -relief sunken -borderwidth 4 -textvariable good \
		-font fixed
	bind .dlg.str <KeyPress-Return> ".dlg.ok invoke"
	button .dlg.ok -text OK -command "matchlines $w *$good*"
		destroy .dlg.ok"
	pack .dlg.label .dlg.str .dlg.ok -side left -padx 1m -pady 2m
}

proc authbad_filter {w} {
	matchlines $w *AUTH*
}

proc auth_filter {w} {
	matchlines $w *AUTH*
}

proc su_filter {w} {
	matchlines $w *su:*
}

# code provided by Stuart Clayman (sclayman@cs.ucl.ac.uk)

set textViewCutoff 1000

proc readline {pipe} {
	global win
        if { [gets $pipe line] == -1} {
	  return
        }
        processTextLine $line $win
        update idletasks
}

proc processTextLine {line w} {
                global textViewCutoff

                if { [expr {[$w.text index end] > $textViewCutoff}] } {
                        $w.text delete 1.0 2.0
                }
                $w.text insert end "$line"
                if { $line != "" } {$w.text insert end "\n"}
	        scan [$w.text index end] %d numlines
		incr numlines -2
		tag_line $line $numlines
# move to end if looking at end otherwise stay where we are
		$w.text mark set viewpoint end
		if { [$w.text compare viewpoint == end] } {
                        $w.text yview -pickplace end
		}
}

wm withdraw .
if {$tk_version<3.3} {puts stderr "Tcl 7.0/Tk 3.3 required"; exit 1}
bind Text <B1-Motion> {textb1motion %W @%x,%y}
bind Text <ButtonRelease-1> { global test; set text(txnd) 0 }

set win [sbGUI]
$win.mb.status configure -text "unloaded $thefile"

set inputstream "NONE"
set inputpid "NONE"
