#!/usr/bin/wish -f

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

set TISBIN "/home/ark/bin"

set scrollJump 0
set pscrollJump 0
set escrollJump 0
set alertJump 1
set defconRaise 1
set defconDeicon 1

set textViewCutoff 5000
set tailCutoff 100

array set filters {
	"Show only su"		" su: "
	"Show only AUTH"	"AUTH"
	"Securityalerts"	"securityalert"
	"Filter out squid-gw"	"^(?!.*squid-gw)"
}

array set cfilters {
	"Administrative actions"
		".*su: |.*login: |.*sshd.*LOGIN|.*sshd.*login|.*cron.*system.* RELOAD" 	
	"Authenitcation management"
		".*authsrv"
	"Security alerts"
		".*securityalert"
	"Security warnings"
		".*securitywarning"
	"Rejected connections"
		".*deny"
	"Interactive sessions"
		".*x-gw|.*tn-gw|.*rlogin-gw"
	"Remote execution"
		".*rexec-gw|.*rsh-gw"
	"Line printer"
		".*lp-gw"
	"FTP transfers"
		".*ftp-gw"
	"HTTP transfers"
		".*squid"
	"Realaudio"
		".*ra-gw"
	"News"
		".*nntp-gw"
	"Email"				
		".*smap|.*smapd|.*ssmtp|.*pop3-gw|.*imap-gw"
	"Database"				
		".*ms-sql-gw|.*sybase-gw|.*mysql-gw"
	"Messaging (irc,icq,aol)"				
		".*ms-sql-gw|.*sybase-gw|.*mysql-gw"
	"Netacl"				
		".*netacl"
	"IP filtering and NAT"				
		".*ipmon"
}

array set msglevel {
	"critical"	
		"fwtksyserr|fwtkcfgerr|file system full|signal 11"
	"security"
		"alert:"
	"failure"
		"LOCKED|FAILURE|error.*:|squid.*Detected DEAD Parent"
	"rejected"
		"deny|relay denied|spam ignored|BADAUTH|sshd.*fatal:|su:.*D SU"
	"admin"
		"su: |login: |sshd.*LOGIN|sshd.*login|cron.*system.* RELOAD"
	"warning"
		"unable|icmpinfo: |securitywarning|hqdaemon.*warning"
	"auth"
		"AUTHENTICATE|authentication for.*accepted"
	"notice"
		"squid-gw.*unknown tag|squid-gw.*header field|ipmon.*NAT:MAP|squid.*Detected REVIVED Parent|pop3-gw.*insecure mode|bad request|Bad request"
}

array set msgcolor {
	"critical"	"Red"
	"security"	"Yellow"
	"failure" 	"Orange"
	"rejected" 	"HotPink"
	"admin" 	"LightBlue"
	"warning" 	"cyan"
	"auth" 		"Green"
	"notice" 	"MistyRose2"
}

set priority_colorize {
	"critical" "security" "failure" "rejected" "admin" "auth"
}

array set msgalert {
	"critical" 	1
	"security" 	3
	"failure" 	5
	"rejected"	2
	"admin"		4
	"warning"	5
	"auth"		4
	"notice"	5
}

###############################################################################
# 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)             #
# for convenience, I have distributed the Searchbox and fileselect source     #
# along with this rather than require you to find them from another source.   #
###############################################################################

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

puts "using logfile $thefile"

set delay 2

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

set sbx(lib) /home/ark/lib/tcl
set sbx(version) 1.0.1

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


set sb(scrollbarside) right
set sb(fontList) {
   "-dec-terminal-medium-r-normal--14-140-75-75-c-80-iso8859-1"
   "-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"
   "default"
   "6x13"
   "9x15"
}
set sb(wrapList) {none char word}
set sb(printList) {lpr enscript}
set sb(text,font) "fixed"
set sb(geom) 1015x500
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 sb(filter) ""
set sbx(uid) 0
	
if [file exists ~/.fwtk_watch] {source ~/.fwtk_watch}

set defcon 4
set apos 0

# 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.

proc sbGUI {{w .fwtk_watch}} {
   global sb sbx filters msgcolor msglevel scrollJump pscrollJump pstatic \
	  static defcon defconDeicon defconRaise alertJump escrollJump \
	  cfilters rwinid rsource

   set rwinid 1
   if {![winfo exists $w]} {toplevel $w}
   wm minsize $w 100 100
   wm geometry $w $sb(geom)
   wm title $w "Firewall Monitoring Tool"
   wm iconname $w "fw watch"

   if {![winfo exists .elog]} {toplevel .elog}
   wm withdraw .elog

   frame $w.mb

   label $w.mb.status -text bozo

   button $w.help -text "Help"

   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"
   $w.mb.mail.m add command -label "Mail selection" -command "mailsel"

   checkbutton $w.mb.static -text "Static" -variable static -command "tail" -relief raised

   checkbutton $w.mb.freeze -text "Fix pane" -variable scrollJump -relief raised

   button $w.mb.clear -text "Clear" -command "$w.text delete 1.0 end" -pady 0

   button $w.mb.refresh -text "Refresh" -command "refreshfile" -pady 0

   menubutton $w.mb.options -text "Options" -menu $w.mb.options.m
   menu $w.mb.options.m
   menu $w.mb.options.m.font
   foreach i $sb(fontList) {
      $w.mb.options.m.font add radiobutton -label $i -variable sb(text,font) \
         -command "$w.text configure -font [list $i]"
   }
   $w.mb.options.m add cascade -label "Font.." -menu $w.mb.options.m.font
   menu $w.mb.options.m.wrap
   foreach i {none char word} {
      $w.mb.options.m.wrap add radiobutton -label $i -variable sb(text,wrap) \
         -command "$w.text configure -wrap $i"
   }
   $w.mb.options.m add cascade -label "Wrap.." -menu $w.mb.options.m.wrap
   $w.mb.options.m add separator
   $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)
   $w.mb.options.m add separator
   $w.mb.options.m add checkbutton -label "Deiconify on alert" \
      -variable defconDeicon
   $w.mb.options.m add checkbutton -label "Raise on alert" \
      -variable defconRaise
   $w.mb.options.m add checkbutton -label "Auto-scroll to alert log" \
      -variable alertJump

   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 "refreshfile"
   $w.mb.filter.m add separator

   menu $w.mb.filter.m.cfilters
   set ef ""
   foreach i [array names cfilters] {
	if {$ef != ""} {
		set ef "$ef|\($cfilters($i)\)"
	} else {
		set ef "\($cfilters($i)\)"
	}
   }
   set "cfilters(Unsorted - everything else)" "^\(?!$ef\)"
   foreach i [lsort [array names cfilters]] {
	set active_filter($i) 0
	$w.mb.filter.m.cfilters add checkbutton -label "$i" \
	  	-variable active_filter($i) -command gen_cfilter
   }
   $w.mb.filter.m.cfilters add command -label "Select all" \
	-command "set_cfilters 1; gen_cfilter"
   $w.mb.filter.m.cfilters add command -label "Apply filters to frame"  \
	-command {global win,sb						\
		gen_cfilter; 						\
		matchlines $win $sb(filter);				\
		gen_cfilter}	

   $w.mb.filter.m add cascade -menu $w.mb.filter.m.cfilters \
		-label "Additive filters.." -command gen_cfilter
   menu $w.mb.filter.m.filters
   foreach i [lsort [array names filters]] {
	$w.mb.filter.m.filters add radiobutton -label $i \
		-command "set_cfilters 0; matchlines $w $filters($i)"
   }

   $w.mb.filter.m add cascade -menu $w.mb.filter.m.filters \
		-label "Exclusive filters.."
   $w.mb.filter.m add separator
   $w.mb.filter.m add command -label "Clear filter" \
	-command "set sb(filter) {}; 
		  set_cfilters 0; reload_status"
   $w.mb.filter.m add command -label "Customize filter" \
	-command "custom_filter $w"
   
   menubutton $w.mb.reports -text "Reports" -menu $w.mb.reports.m
   menu $w.mb.reports.m
   menu $w.mb.reports.m.source
   $w.mb.reports.m.source add radiobutton -label "Current frame" \
	-variable rsource -value "frame"
   $w.mb.reports.m.source add radiobutton -label "Current logfile" \
	-variable rsource -value "log"
   $w.mb.reports.m.source add radiobutton -label "Select from filesystem" \
	-variable rsource -value "select"
   $w.mb.reports.m add cascade -label "Source.." -menu $w.mb.reports.m.source
   $w.mb.reports.m add command -label "Filters.."
   $w.mb.reports.m add separator
   $w.mb.reports.m add command -label "Network hourly load (total)" \
	-command "show_report $w chart.pl"
   $w.mb.reports.m add command -label "Summary on current logfile" \
	-command "show_report $w summ_complete.pl"
   $w.mb.reports.m add separator
   $w.mb.reports.m add command -label "Customize summary report"

   menubutton $w.mb.events -text "Events" -menu $w.mb.events.m
   menu $w.mb.events.m
   $w.mb.events.m add command -label "Show event history" -command "event_log"
   $w.mb.events.m add command -label "Clear" -command "elog_clear"

   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 tail

   pack $w.mb.file $w.mb.options $w.mb.print $w.mb.mail \
	$w.mb.filter $w.mb.events $w.mb.reports -side left
   pack $w.mb.refresh -side right
   pack $w.mb.clear -side right
   pack $w.mb.freeze -side right
   pack $w.mb.static -side right
   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) \
      -state disabled
   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.mb.status 0
   "
   bind $w.t <KeyPress> {}
   scrollbar $w.v -orient vertical -command "$w.text yview"
   pack $w.v -in $w.text -side $sb(scrollbarside) -fill y
   pack $w.text -in $w.t -side top -fill both -expand yes
   $w.text mark set viewpoint end

   frame $w.tt

   label $w.tt.status -text "Priority messages"
   button $w.tt.defcon -text "Condition: green (DEFCON 4)" -bg grey85 \
             -command "cclean" -pady 0 -state disabled -relief flat  \
	     -disabledforeground black
   checkbutton $w.tt.freeze -text "Fix pane" -variable pscrollJump -relief raised
   checkbutton $w.tt.static -text "Static" -variable pstatic -relief raised
   button $w.tt.clear -text "Clear" -command "$w.text1 delete 1.0 end" -pady 0
   button $w.tt.refresh -text "Refresh" -command "prefresh $w" -pady 0

   pack $w.tt.status -in $w.tt -side left -fill x 
   pack $w.tt.defcon -in $w.tt -side left -fill x -expand yes
   pack $w.tt.refresh -side right
   pack $w.tt.clear -side right
   pack $w.tt.freeze -side right
   pack $w.tt.static -side right


   frame $w.t1
   text $w.text1 -font $sb(text,font) -foreground $sb(text,fontcolor) \
      -relief sunken -borderwidth 2 -padx $sb(text,margin) -pady $sb(text,margin) \
      -yscrollcommand "$w.v1 set" -wrap $sb(text,wrap) \
      -height 6 
   bind $w.text1 <Meta-KeyPress-q> sbQuit
   bind $w.text1 <Any-KeyPress> "
      searchboxKeyNav \[key_state2mnemon %s\] %K \$sb(incr,case) $w.text1 $w.mb.status 0
   "
   bind $w.text1 <Button-1> {
	set scrollJump 1
	$win.text yview -pickplace 1.0
	puts stderr "[$win.text1 get {@%x,%y linestart} {@%x,%y lineend}]"
 	searchboxSearch [$win.text1 get {@%x,%y linestart} {@%x,%y lineend}] \
			0 1 search $win.text $win.mb.status $win.cnt
	searchboxNext search $win.text $win.mb.status
   }
   scrollbar $w.v1 -orient vertical -command "$w.text1 yview"
   pack $w.v1 -in $w.t1 -side right -fill y
   pack $w.text1 -in $w.t1 -side bottom -fill both -expand yes
   $w.text1 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.mb.status $w.cnt
   "
   button $w.next -text "Next" -command "searchboxNext search $w.text $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.tt -fill both 
   pack $w.t1 -fill both 
   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"
   }

   foreach i [array names msgcolor] {
	$w.text tag configure $i -borderwidth 2 -relief raised \
		-background $msgcolor($i)
	$w.text1 tag configure $i -borderwidth 2 -relief raised \
		-background $msgcolor($i)
   }

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

   frame .elog.tt

   label .elog.tt.status -text " Date/Time       Type     Message" -font $sb(text,font)
   checkbutton .elog.tt.freeze -text "Fix pane" -variable escrollJump -relief raised
   button .elog.tt.clear -text "Clear" -command "elog_clear" -pady 0

   pack .elog.tt.status -in .elog.tt -side left -fill x 
   pack .elog.tt.clear -side right
   pack .elog.tt.freeze -side right

   frame .elog.t
   text .elog.text -font $sb(text,font) -foreground $sb(text,fontcolor) \
      -relief sunken -borderwidth 2 -padx $sb(text,margin) -pady $sb(text,margin) \
      -yscrollcommand ".elog.v set" -wrap $sb(text,wrap) \
      -height 15 -xscrollcommand ".elog.h set"
   .elog.text tag configure 1 -borderwidth 2 -relief raised \
		-background red
   .elog.text tag configure 2 -borderwidth 2 -relief raised \
		-background orange
   .elog.text tag configure 3 -borderwidth 2 -relief raised \
		-background yellow
   .elog.text tag configure auth -borderwidth 2 -relief raised \
		-background green
   .elog.text tag configure admin -borderwidth 2 -relief raised \
		-background lightblue
   .elog.text tag configure clear -borderwidth 2 -relief raised \
		-background blue
   .elog.text tag configure lost -borderwidth 2 -relief raised \
		-background cyan
   bind .elog.text <Button-1> {
	set scrollJump 1
	set pscrollJump 1
	$win.text yview -pickplace 1.0
	$win.text1 yview -pickplace 1.0
 	searchboxSearch [.elog.text get {@%x,%y linestart} {@%x,%y linestart +15 chars}] \
			0 1 search $win.text $win.mb.status $win.cnt
	searchboxNext search $win.text $win.mb.status
 	searchboxSearch [.elog.text get {@%x,%y linestart} {@%x,%y linestart +15 chars}] \
			0 1 search $win.text1 $win.mb.status $win.cnt
	searchboxNext search $win.text1 $win.mb.status
   }

   scrollbar .elog.v -orient vertical -command ".elog.text yview"
   pack .elog.v -in .elog.t -side right -fill y
   scrollbar .elog.h -orient horizontal -command ".elog.text xview"
   pack .elog.text -in .elog.t -side bottom -fill both -expand yes
   .elog.text mark set viewpoint end
   pack .elog.tt -fill both
   pack .elog.t -fill both -expand yes
   pack .elog.h -fill x
   button .elog.c -text "Close" -command "wm withdraw .elog"
   pack .elog.c -fill x

   focus $w
   return $w
}

proc set_cfilters {x} {
   global cfilters active_filter
   foreach i [array names cfilters] {
	set active_filter($i) $x
   }
}

proc gen_cfilter {} {
   global sb cfilters active_filter win thefile
   set sb(filter) ""
   foreach i [array names cfilters] {
	if {$active_filter($i)} {
		if {$sb(filter) != ""} {
			set sb(filter) "$sb(filter)|\($cfilters($i)\)"
		} else {
			set sb(filter) "\($cfilters($i)\)"
		}
	}
   }
   if {$sb(filter) != ""} {
   	$win.mb.status configure -text "filtered $thefile by additive filter set"
   } else {
	reload_status
   }
}

proc sbReadFile {filename} {
   global sb sbx win thefile msglevel

   $win.text configure -state normal
   $win.text delete 1.0 end
   $win.text1 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
        processTextLine $line $win
	incr cnt
	update idletasks
   }
   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 printSel {cmd} {
   set fid [open "|$cmd" w]
   if [catch {selection get}] {
	okdialog Error "Mail not sent - no text selected"
   } else {
	puts $fid [selection get]
   	close $fid
   }
}


proc sbQuit {} {
   global inputstream
   if {$inputstream != "NONE"} {
#	catch {exec /bin/kill $inputpid}
	catch {close $inputstream}
   }
   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"} {
    tail
  }
  fileselect sbReadFile {Select Log File}
}

proc loadfile {} {
  global thefile inputstream

  if {$inputstream != "NONE"} {
    tail
    return
  }
  sbReadFile $thefile
}

proc refreshfile {} {
  global thefile inputstream

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

proc reload_status {} {
  global thefile win inputstream
  if {$inputstream == "NONE"} {
    $win.mb.status configure -text "static $thefile"
  } else {
    $win.mb.status configure -text "monitoring $thefile"
  }
}

proc tail {} {
  global delay win thefile inputstream tailCutoff

  if {$inputstream == "NONE"} {
    $win.text configure -state normal
    $win.text delete 1.0 end
    $win.mb.status configure -text "reading $thefile"
    set cnt 0
    set static 0
    set inputstream [open "|tail -$tailCutoff $thefile" r]
    while {![eof $inputstream]} {
	gets $inputstream line
        processTextLine $line $win
	incr cnt
	update idletasks
    }
    close $inputstream

    set inputstream [open $thefile r]
    fconfigure $inputstream -buffering none
    $win.mb.status configure -text "monitoring $thefile"
    catch {seek $inputstream 0 end}

    while {![eof $inputstream]} {
	gets $inputstream line
        processTextLine $line $win
        update idletasks
    }
    update idletasks
    after $delay [list tailread]
  } else {
    catch {close $inputstream}
    $win.mb.status configure -text "static $thefile"
    set inputstream "NONE"
    set static 1
  }
}

proc tailread {} {
   global delay maxlines win thefile inputstream
   if {$inputstream == "NONE"} {
	return
   }
   gets $inputstream data
   if [string length $data] {
      processTextLine $data $win
   }
   after $delay [list tailread]
}   

proc cclean {} {
	global win defcon apos apos1 scrollJump pscrollJump alertJump \
	       escrollJump

    	$win.tt.defcon configure -text "Condition: green (DEFCON 4)" \
				-background grey85 -state disabled   \
				-relief flat

	.elog.text insert end [exec date "+%b %e %H:%M:%S"]
	.elog.text insert end " CLEARED  Defence condition cleared by operator"
	.elog.text tag add clear "end -1 line +16 chars" "end -1 line +24 chars"
	.elog.text insert end "\n"
	if {!$escrollJump } {
		.elog.text mark set viewpoint end
       		.elog.text yview -pickplace end
	}

	if {$alertJump} {
		set scrollJump 1
		set pscrollJump 1
		$win.text yview -pickplace $apos.0 
		$win.text1 yview -pickplace $apos1.0 
	}
	set defcon 4
	set apos 0
}

proc elog_clear {} {
	.elog.text delete 1.0 end
	.elog.text insert end [exec date "+%b %e %H:%M:%S"]
	.elog.text insert end " CLEARED  Event history removed by operator"
	.elog.text tag add clear "end -1 line +16 chars" "end -1 line +24 chars"
	.elog.text insert end "\n"
}

proc do_defcon {} {
	global win defcon defconRaise defconDeicon

	if {$defconRaise} {raise $win}
	if {![winfo ismapped $win] & $defconDeicon} {
		wm deiconify $win
	}
	switch $defcon {
	
	1	{
    		$win.tt.defcon configure -text "Condition: RED (DEFCON 1)" \
		        -bg red -activebackground red -state normal 	   \
			-relief raised
 		}
	
	2	{
    		$win.tt.defcon configure -text "Condition: ORANGE (DEFCON 2)" \
		       -bg orange -activebackground orange -state normal      \
		       -relief raised
		}

	3	{
    		$win.tt.defcon configure -text "Condition: YELLOW (DEFCON 3)" \
		       -bg yellow -activebackground yellow -state normal      \
		       -relief raised
		}

	default {}
	}
}

proc tag_line {line cnt} {
      global win msglevel msgcolor defcon msgalert apos escrollJump

	foreach i [array names msglevel] {
		if [regexp $msglevel($i) $line] {
			$win.text tag add $i $cnt.0 "$cnt.0 lineend"
			if {$msgalert($i) <5} {
				.elog.text insert end [string range $line 0 15]
				if {$msgalert($i) == 4} {
					if {$i == "admin"} {
						.elog.text insert end "ADMIN    "
						.elog.text tag add admin "end -1 line +16 chars" "end -1 line +24 chars"
					} elseif {$i == "auth"} {
						.elog.text insert end "AUTH     "
						.elog.text tag add auth "end -1 line +16 chars" "end -1 line +24 chars"
					}
				} else {
					.elog.text insert end "DEFCON $msgalert($i) "
					.elog.text tag add $msgalert($i) "end -1 line +16 chars" "end -1 line +24 chars"
				}
				.elog.text insert end [string range $line 16 end]
				.elog.text insert end "\n"
				if {!$escrollJump } {
					.elog.text mark set viewpoint end
       					.elog.text yview -pickplace end
				}
			}
			if {$msgalert($i) < $defcon} {
				set apos $cnt
				set defcon $msgalert($i)
				do_defcon
			}
			break
		}
	}
}

proc tag_pline {line cnt} {
      global win msglevel msgcolor priority_colorize apos1 defcon msgalert

	foreach i $priority_colorize {
		if [regexp $msglevel($i) $line] {
			if {$msgalert($i) == $defcon} {
				set apos1 $cnt
			}
			$win.text1 tag add $i $cnt.0 "$cnt.0 lineend"
			incr cnt -1
			break
		}
	}
}

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

proc mailsel {} {
   set fid [open "|whoami" r]
   gets $fid me
   close $fid
   printSel "mail $me"
}


proc show_report {w script} {
	global TISBIN thefile inputstream rwinid sb

	set crwin_id .report_[incr rwinid]
	toplevel $crwin_id
	frame $crwin_id.t
	label $crwin_id.status -text "Generating report.."
	text $crwin_id.text -font $sb(text,font) 			   \
			    -foreground $sb(text,fontcolor) -relief sunken \
			    -borderwidth 2 -padx $sb(text,margin)	   \
			    -pady $sb(text,margin) 			   \
			    -yscrollcommand "$crwin_id.v set" 		   \
			    -wrap $sb(text,wrap) -height 50		   
#			    -xscrollcommand "$crwin_id.h set"	

	scrollbar $crwin_id.v -orient vertical -command "$crwin_id.text yview"
	pack $crwin_id.v -in $crwin_id.t -side right -fill y
#	scrollbar $crwin_id.h -orient horizontal -command "$crwin_id.text xview"
	button $crwin_id.c -text "Close" -command "destroy $crwin_id"
	pack $crwin_id.status -side top
	pack $crwin_id.text -in $crwin_id.t -side bottom -fill both -expand yes
	pack $crwin_id.t -fill both -expand yes
#	pack $crwin_id.h -fill x
	pack $crwin_id.c -fill x

	$crwin_id.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]} {
		$crwin_id.text insert end [read $input 1000]
	}
	close $input
#	$w.text configure -state disabled
	$crwin_id.status configure -text "finished $script report"
#	scan [$w.text index end] %d numlines
#	$w.cnt configure -text "$numlines lines"
}
	

proc prefresh {w} {
	global sb msglevel
	$w.text1 delete 1.0 end 
	scan [$w.text index end] %d numlines
	for {set i 1} {$i<=$numlines} {incr i} {
		foreach j [array names msglevel] {
			set line [$w.text get $i.0 "$i.0 lineend"]
			if [regexp $msglevel($j) $line] {
       				$w.text1 insert end $line
				$w.text1 insert end "\n"
	        		scan [$w.text1 index end] %d pnumlines
				incr pnumlines -2
				tag_pline $line $pnumlines
				break
			}
		}
	}
}

proc matchlines {w patt} {
	global thefile
	global sb

        $w.mb.status configure -text "filtering $thefile for string $patt..."
        update idletasks
	set sb(filter) $patt
	$w.text configure -state normal
	scan [$w.text index end] %d numlines
	for {set i $numlines} {$i>0} {incr i -1} {
		if ![regexp $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
	if {$patt!=""} {
        	$w.mb.status configure -text "filtered $thefile by string $patt"
	} else {
		reload_status
	}
        update idletasks

}

proc custom_filter {w} {
	global sb
	toplevel .dlg
	wm title .dlg "Custom filter"
	set good ""
	label .dlg.label -text "Custom filter (regexp)"
	entry .dlg.str -relief sunken -borderwidth 4 -textvariable sb(filter) \
		-font fixed
	bind .dlg.str <KeyPress-Return> ".dlg.ok invoke"
	button .dlg.ok -text OK -command "matchlines $w \$sb(filter) ; 
					  destroy .dlg"
	button .dlg.clear -text Clear -command "set sb(filter) {}; set_cfilters 0"
	pack .dlg.str -fill x -expand yes -padx 2m -pady 2m
	pack .dlg.label .dlg.clear .dlg.ok -side left -padx 1m -pady 2m
}

proc event_log {} {
	wm title .elog "Event history"
	wm deiconify .elog
	wm protocol .elog WM_DELETE_WINDOW {wm withdraw .elog}
}

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


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

proc processTextLine {line w} {
                global textViewCutoff sb msglevel scrollJump pscrollJump \
		       pstatic apos escrollJump

# move to end if looking at end otherwise stay where we are
                if { [expr {[$w.text index end] > $textViewCutoff}] } {
			set atime [$w.text get 1.0 1.15]
                        $w.text delete 1.0 2.0
			if {$apos} { 
				incr apos -1
				if {!$apos} { 
					.elog.text insert end [exec date "+%b %e %H:%M:%S"]
					.elog.text insert end " WARNING  Alert position for $atime lost in scrollback"
					.elog.text tag add lost "end -1 line +16 chars" "end -1 line +24 chars"
					.elog.text insert end "\n"
					if {!$escrollJump } {
						.elog.text mark set viewpoint end
                	        		.elog.text yview -pickplace end
					}
				}
			}
                }
		if [regexp $sb(filter) $line] {
                	if { $line != "" } {$w.text insert end "\n"}
            		$w.text insert end "$line"
	        	scan [$w.text index end] %d numlines
			incr numlines -1
			tag_line $line $numlines
		}
		if {!$scrollJump } {
			$w.text mark set viewpoint end
			if { [$w.text compare viewpoint == end] } {
                	        $w.text yview -pickplace end
			}
		}
		if {!$pscrollJump} {
			$w.text1 mark set viewpoint end
			if { [$w.text1 compare viewpoint == end] } {
                	        $w.text1 yview -pickplace end
			}
		}
		if (!$pstatic) { 
			foreach i [array names msglevel] {
				if [regexp $msglevel($i) $line] {
                			if { $line != "" } {$w.text1 insert end "\n"}
            				$w.text1 insert end "$line"
	        			scan [$w.text1 index end] %d numlines
					incr numlines -1
					tag_pline $line $numlines
					break
				}
			}
		}
}

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

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

set inputstream "NONE"
set inputpid "NONE"
tail

