sockspy version 2.4 imported from cvs
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Nov 2009 23:39:53 +0000 (23:39 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 26 Nov 2009 23:39:53 +0000 (23:39 +0000)
Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
bin/sockspy.tcl

index 3a97fa0258a748f7629ba70ad37507601653df12..852c41224885929cf851c19dddc465c02338e2e2 100644 (file)
@@ -8,6 +8,7 @@ exec wish $0 ${1+"$@"}
 # version 1.0 - december 10, 1998
 # version 2.0 - January, 2002 by Keith Vetter
 # KPV Nov 01, 2002 - added proxy mode
+# KPV Dec 21, 2002 - added extract window
 # 
 # spy on conversation between a tcp client and server
 #
@@ -23,220 +24,273 @@ exec wish $0 ${1+"$@"}
 #        http://localhost:8080/index.html
 #        (or set your browser's proxy to use 8080 on the sockspy machine)
 
-catch {package require uri}                                            ;# Error handled below
+catch {package require uri}                     ;# Error handled below
 
 array set state {
-       version 2.4
-       bbar 1
-       ascii 1
-       auto 1
-       msg ""
-       fixed {}
-       fixedbold {}
-       playback ""
-       gui 0
-       listen ""
-       title "not connected"
-       proxy 0
-       fname ""
+    version 2.4
+    bbar 1
+    ascii 1
+    autoscroll 1
+    autowrap 1
+    capture 1
+    msg ""
+    fixed {}
+    fixedbold {}
+    playback ""
+    gui 0
+    listen ""
+    title "not connected"
+    proxy 0
+    fname ""
 }
+array set colors {client green server cyan meta red meta2 yellow}
 array set SP {proxyPort 8080 clntPort 8080 servHost "" servPort 80}
-#set filters(client) {^(GET |POST |HEAD )}
-#set filters(server) {^(HTTP/|Location: )}
+set extract(client) {^(GET |POST |HEAD )}
+set extract(server) {^(HTTP/|Location: |Content-)}
+#set extract(meta) {.}
+set extract(meta2) {.}
 ##+##########################################################################
 # 
-# createMain
-# 
-# Creates the display
+# createMain -- Creates the display
 # 
 proc createMain {} {
-       global state tcl_platform
-       
+    global state colors tcl_platform
+    
     if {! $state(gui)} return
-       
-       set state(fixed) [font create -family courier -size 10]
-       set state(fixedbold) [font create -family courier -size 10 -weight bold]
-       if {"$tcl_platform(platform)" == "windows"} {
-               doFont -1
-       }
-       wm title . "SockSpy"
-       wm resizable .  1 1
-       wm protocol . WM_DELETE_WINDOW Shutdown         ;# So we shut down cleanly
-
-       #
-       # Set up the menus
-       #
-       menu .m -tearoff 0
-       . configure -menu .m
-       .m add cascade -menu .m.file -label "File" -underline 0
-       .m add cascade -menu .m.view -label "View" -underline 0
-       .m add cascade -menu .m.help -label "Help" -underline 0
-       
-       menu .m.file -tearoff 0
-       .m.file add command -label "Save" -underline 0 -command saveOutput
-       .m.file add command -label "Reconnect" -underline 0 -command GetSetup
-       .m.file add separator
-       .m.file add command -label "Exit" -underline 1 -command Shutdown
-       
-       menu .m.view -tearoff 0
-       .m.view add command -label " Clear" -underline 1 -command clearOutput
-       .m.view add separator
-       .m.view add checkbutton -label " ButtonBar" -variable state(bbar) \
-                       -underline 1 -command ButtonBar
-       .m.view add separator
-       .m.view add command -label " + Font" -command [list doFont 1]
-       .m.view add command -label " - Font" -command [list doFont -1]
-       .m.view add separator
-       .m.view add radiobutton -label " Hex" -underline 1 \
-                       -variable state(ascii) -value 0 -command redraw
-       .m.view add radiobutton -label " ASCII" -underline 1 \
-                       -variable state(ascii) -value 1 -command redraw
-       .m.view add separator
-       .m.view add checkbutton -label " Autoscroll" -underline 5 \
-                       -variable state(auto)
-
-       menu .m.help -tearoff 0
-       .m.help add command -label Help -underline 1 -command Help
-       .m.help add separator
-       .m.help add command -label About -command About
-       #
-       # Title and status window
-       #
-       pack [frame .bbar] -side top -fill x
-       frame .cmd -relief sunken -bd 2
-       radiobutton .cmd.hex   -text Hex   -variable state(ascii) \
-                       -value 0 -command redraw
-       radiobutton .cmd.ascii -text ASCII -variable state(ascii) \
-                       -value 1 -command redraw
-       checkbutton .cmd.auto  -text Autoscroll -variable state(auto)
-       button .cmd.clear -text Clear -command clearOutput
-       button .cmd.incr  -text "+ Font" -command [list doFont 1]
-       button .cmd.decr  -text "- Font" -command [list doFont -1]
-       button .cmd.save  -text Save -command saveOutput
-       button .cmd.kill  -text Exit -command Shutdown
-       pack .cmd.kill .cmd.save .cmd.clear .cmd.decr .cmd.incr .cmd.auto \
-                       .cmd.ascii .cmd.hex -side right -padx 3 -pady 3
-       pack .cmd -side top -fill x -pady 5 -in .bbar
-       
-       frame .top
-       pack .top -side top -fill x -pady 2 -expand 0
-       #button .clear -text Clear -command clearOutput
-       #pack .clear -in .top -side right -padx 3
-
-       label .title -relief ridge -textvariable state(title)
-       .title config -font "[.title cget -font] bold"
-       pack .title -in .top -side left -fill both -expand 1
-       
-       label .stat -textvariable state(msg) -relief ridge -anchor w
-       pack .stat -side bottom -fill x 
-
-       #
-       # Now for the output area of the display
-       #
-       text .out -width 80 -height 50 -font $state(fixed) \
-                       -yscrollcommand ".scroll set" -bg white -setgrid 1
-       .out tag configure server -background cyan -borderwidth 2 -relief raised \
-               -lmargin1 5 -lmargin2 5
-       .out tag configure client -background green -borderwidth 2 -relief raised \
-               -lmargin1 5 -lmargin2 5
-       .out tag configure client2 -font $state(fixedbold)
-       .out tag configure meta   -background red    -borderwidth 2 -relief raised \
-               -lmargin1 5 -lmargin2 5
-       .out tag configure meta2  -background yellow -borderwidth 2 -relief raised \
-               -lmargin1 5 -lmargin2 5
-       scrollbar .scroll -orient vertical -command {.out yview}
-       pack .scroll -side right -fill y
-       pack .out -side left -fill both -expand 1
-       bind .out <Control-l> clearOutput
-       bind all <Alt-c> {console show}
-       focus .out
-       wm geometry . +10+10
+    
+    set state(fixed) [font create -family courier -size 10]
+    set state(fixedbold) [font create -family courier -size 10 -weight bold]
+    if {"$tcl_platform(platform)" == "windows"} {
+        doFont -1
+    }
+    wm title . "SockSpy -- $state(title)"
+    wm resizable .  1 1
+    wm protocol . WM_DELETE_WINDOW Shutdown     ;# So we shut down cleanly
+
+    #
+    # Set up the menus
+    #
+    menu .m -tearoff 0
+    . configure -menu .m
+    .m add cascade -menu .m.file -label "File" -underline 0
+    .m add cascade -menu .m.view -label "View" -underline 0
+    .m add cascade -menu .m.help -label "Help" -underline 0
+    
+    menu .m.file -tearoff 0
+    .m.file add command -label "Save" -underline 0 -command saveOutput
+    .m.file add command -label "Reconnect" -underline 0 -command GetSetup
+    .m.file add separator
+    .m.file add command -label "Exit" -underline 1 -command Shutdown
+    
+    menu .m.view -tearoff 0
+    .m.view add command -label " Clear" -underline 1 -command clearOutput
+    .m.view add separator
+    .m.view add checkbutton -label " ButtonBar" -variable state(bbar) \
+            -underline 1 -command ButtonBar
+    .m.view add checkbutton -label " Extract Window" -variable state(extract) \
+            -underline 1 -command ToggleExtract
+    .m.view add separator
+    .m.view add command -label " + Font" -command [list doFont 1]
+    .m.view add command -label " - Font" -command [list doFont -1]
+    .m.view add separator
+    .m.view add radiobutton -label " Hex" -underline 1 \
+            -variable state(ascii) -value 0 -command redraw
+    .m.view add radiobutton -label " ASCII" -underline 1 \
+            -variable state(ascii) -value 1 -command redraw
+    .m.view add separator
+    .m.view add checkbutton -label " Autoscroll" -underline 5 \
+            -variable state(autoscroll)
+    .m.view add checkbutton -label " Autowrap" -underline 5 \
+            -variable state(autowrap) -command ToggleWrap
+    .m.view add checkbutton -label " Capture" -underline 5 \
+            -variable state(capture) -command ToggleCapture
+
+    menu .m.help -tearoff 0
+    .m.help add command -label Help -underline 1 -command Help
+    .m.help add separator
+    .m.help add command -label About -command About
+    #
+    # Title and status window
+    #
+    frame .bbar
+    frame .cmd -relief sunken -bd 2
+    radiobutton .cmd.hex -text Hex -variable state(ascii) \
+            -value 0 -command redraw
+    radiobutton .cmd.ascii -text ASCII -variable state(ascii) \
+            -value 1 -command redraw
+    checkbutton .cmd.autos -text Autoscroll -variable state(autoscroll)
+    checkbutton .cmd.autow -text Autowrap -variable state(autowrap) \
+         -command ToggleWrap
+    checkbutton .cmd.capture -text Capture -variable state(capture) \
+         -command ToggleCapture
+    button .cmd.clear -text Clear -command clearOutput
+    #button .cmd.incr -text "+ Font" -command [list doFont 1]
+    #button .cmd.decr -text "- Font" -command [list doFont -1]
+    button .cmd.save -text Save -command saveOutput
+    button .cmd.kill -text Exit -command Shutdown
+    pack .cmd -side top -fill x -pady 5 -in .bbar
+    pack .cmd.kill .cmd.save .cmd.clear .cmd.autow .cmd.autos .cmd.capture \
+        -side right -padx 3 -pady 3
+    #label .title -relief ridge -textvariable state(title)
+    #.title config -font "[.title cget -font] bold"
+    label .stat -textvariable state(msg) -relief ridge -anchor w
+
+    #
+    # Now for the output area of the display
+    #
+    scrollbar .yscroll -orient vertical -command {.out yview}
+    scrollbar .xscroll -orient horizontal -command {.out xview}
+    text .out -width 80 -height 50 -font $state(fixed) -bg white -setgrid 1 \
+            -yscrollcommand ".yscroll set" -xscrollcommand ".xscroll set"
+    foreach t [array names colors] {
+        .out tag configure $t -background $colors($t) -borderwidth 2 \
+            -relief raised -lmargin1 5 -lmargin2 5
+    }
+    .out tag raise sel                          ;# Selection is most prominent
+
+    grid .bbar -       -row 0 -sticky ew
+    grid .out .yscroll -row 1 -sticky news
+    grid .xscroll      -row 2 -sticky ew
+    grid .stat -       -row 3 -sticky ew
+    grid rowconfigure    . 1 -weight 1
+    grid columnconfigure . 0 -weight 1
+
+    bind .out <Control-l> clearOutput
+    bind all <Alt-c> {console show}
+    focus .out
+    wm geometry . +10+10
 }
 ##+##########################################################################
 # 
-# doFont
+# createExtract -- Creates the extract toplevel window.
+# 
+proc createExtract {} {
+    global state colors
+    
+    if {[winfo exists .extract]} {
+        wm deiconify .extract
+        return
+    }
+    set top ".extract"
+    toplevel $top
+    wm title $top "SockSpy Extract"
+    wm protocol $top WM_DELETE_WINDOW [list ToggleExtract -1]
+    if {[regexp {(\+[0-9]+)(\+[0-9]+)$} [wm geom .] => wx wy]} {
+        wm geom $top "+[expr {$wx+35+[winfo width .]}]+[expr {$wy+15}]"
+    }
+
+    frame $top.top -bd 2 -relief ridge
+    label $top.top.c  -text "Client Filter" -anchor e
+    entry $top.top.ce -textvariable extract(client) -bg $colors(client)
+    label $top.top.s  -text "Server Filter" -anchor e
+    entry $top.top.se -textvariable extract(server) -bg $colors(server)
+    label $top.top.m  -text "Metadata Filter" -anchor e
+    entry $top.top.me -textvariable extract(meta2) -bg $colors(meta2)
+    
+    text $top.out -width 80 -height 20 -font $state(fixed) -bg beige \
+        -setgrid 1 -wrap none -yscrollcommand [list $top.yscroll set] \
+        -xscrollcommand [list $top.xscroll set]
+    foreach t [array names colors] {
+        $top.out tag configure $t -background $colors($t) -borderwidth 2 \
+            -relief raised -lmargin1 5 -lmargin2 5
+    }
+    $top.out tag raise sel                      ;# Selection is most prominent
+
+    scrollbar $top.yscroll -orient vertical -command [list $top.out yview]
+    scrollbar $top.xscroll -orient horizontal -command [list $top.out xview]
+    grid $top.top - -row 0     -sticky ew -ipady 10
+    grid $top.out $top.yscroll -sticky news
+    grid $top.xscroll          -sticky ew
+
+    grid rowconfigure    $top 1 -weight 1
+    grid columnconfigure $top 0 -weight 1
+
+    grid $top.top.c $top.top.ce -row 0 -sticky ew
+    grid $top.top.s $top.top.se        -sticky ew
+    grid $top.top.m $top.top.me        -sticky ew
+    grid columnconfigure $top.top 1 -weight 1
+    grid columnconfigure $top.top 2 -minsize 10
+}
+##+##########################################################################
 # 
-# Changes the size of the font used for the display text
+# doFont -- Changes the size of the font used for the display text
 # 
 proc doFont {delta} {
-       global state
-       set size [font configure $state(fixed) -size]
+    global state
+    set size [font configure $state(fixed) -size]
 
-       incr size $delta
-       font configure $state(fixed) -size $size
-       font configure $state(fixedbold) -size $size
+    incr size $delta
+    font configure $state(fixed) -size $size
+    font configure $state(fixedbold) -size $size
 }
 ##+##########################################################################
 # 
-# clearOutput
-# 
-# Erases the content of the output window
+# clearOutput -- Erases the content of the output window
 # 
 proc clearOutput {} {
-       global state
-       if {$state(gui)} {
-               .out delete 0.0 end
-       }
-       set state(playback) ""
+    global state
+    if {$state(gui)} {
+        .out delete 0.0 end
+        catch {.extract.out delete 0.0 end}
+    }
+    set state(playback) ""
 }
 ##+##########################################################################
 # 
-# redraw
-# 
-# Redraws the contents of the output window. It does this by replaying
-# the input stream.
+# redraw -- Redraws the contents of the output window.
+#
+# It does this by replaying the input stream.
 # 
 proc redraw {} {
-       global state 
+    global state 
 
-       set save $state(auto)                                           ;# Disable autoscrolling
-       set state(auto) 0
+    set save_as $state(autoscroll)              ;# Disable autoscrolling
+    set state(autoscroll) 0
 
-       set p $state(playback)                                          ;# Save what gets displayed
-       clearOutput                                                                     ;# Erase current screen
-       foreach {who data} $p {                                         ;# Replay the input stream
-               insertData $who $data
-       }
-       set state(auto) $save
+    set p $state(playback)                      ;# Save what gets displayed
+    clearOutput                                 ;# Erase current screen
+    foreach {who data} $p {                     ;# Replay the input stream
+        insertData $who $data 1
+    }
+    set state(autoscroll) $save_as
 }
 ##+##########################################################################
 # 
-# saveOutput
+# saveOutput -- Saves the content of the output window. 
 # 
-# Saves the content of the output window. It uses the playback stream as
-# its data source.
+# It uses the playback stream as its data source.
 # 
 proc saveOutput {} {
-       global state but
-
-       set but -1
-       after 1 {set but [tk_dialog .what "SockSpy Save" "Save which window?" \
-                       questhead 2 server client both cancel]}
-       after 1 tk_dialogFIX
-       vwait but
-
-       if {$but == -1 || $but == 3} {
-               return
-       }
-       set file [tk_getSaveFile -parent . -initialfile $state(fname)]
-       if {$file == ""} return
-
-       set state(fname) $file
-       if {[catch {open $file w} fd]} {
-               tk_messageBox -message "file $file cannot be opened" -icon error \
-                               -type ok
-               return
-       }
-       fconfigure $fd -translation binary
-       foreach {who data} $state(playback) {
-               if {$who == "meta" || $who == "meta2"} continue
-               if {$but == 2 || ($but == 0 && $who == "server") || \
-                               ($but == 1 && $who == "client")} {
-                       puts $fd $data
-               }
-       }
-       close $fd
-       bell
+    global state but
+
+    set but -1
+    after 1 {set but [tk_dialog .what "SockSpy Save" "Save which window?" \
+            questhead 2 server client both cancel]}
+    after 1 tk_dialogFIX
+    vwait but
+
+    if {$but == -1 || $but == 3} {
+        return
+    }
+    set file [tk_getSaveFile -parent . -initialfile $state(fname)]
+    if {$file == ""} return
+
+    set state(fname) $file
+    if {[catch {open $file w} fd]} {
+        tk_messageBox -message "file $file cannot be opened" -icon error \
+                -type ok
+        return
+    }
+    fconfigure $fd -translation binary
+    foreach {who data} $state(playback) {
+        if {$who == "meta" || $who == "meta2"} continue
+        if {$but == 2 || ($but == 0 && $who == "server") || \
+                ($but == 1 && $who == "client")} {
+            puts $fd $data
+        }
+    }
+    close $fd
+    bell
 }
 ##+##########################################################################
 # 
@@ -247,71 +301,67 @@ proc saveOutput {} {
 # future versions hence the catch.
 # 
 proc tk_dialogFIX {} {
-       for {set i 0} {$i < 5} {incr i} {                       ;# Don't do anything...
-               if {[winfo exists .what]} break                 ;# ...until window is mapped
-               after 200
-       }
-       catch {grid configure .what.button0 -pady 10}
+    if {[winfo exists .what] == 0} {            ;# Don't do anything...
+        after 200 tk_dialogFix                  ;# ...until window is mapped
+    } else {
+        catch {grid configure .what.button0 -pady 10}
+    }
 }
 ##+##########################################################################
 # 
-# printable
-# 
-# Replaces all unprintable characters into dots.
+# printable -- Replaces all unprintable characters into dots.
 # 
 proc printable {s {spaces 0}} {
-       regsub -all {[^\x09\x20-\x7e]} $s "." n
-       if {$spaces} {
-               regsub -all { } $n "_" n
-       }
-       return $n;
+    regsub -all {[^\x09\x20-\x7e]} $s "." n
+    if {$spaces} {
+        regsub -all { } $n "_" n
+    }
+    return $n;
 }
 ##+##########################################################################
 # 
-# insertData
-# 
-# Inserts data into the output window. WHO tells us if it is from
-# the client, server or meta.
-# 
-proc insertData {who data} {
-       global state
-       array set prefix {meta = meta2 = client > server <}
-
-       set data [DoFilter $who $data]
-       if {$data == ""} return
-       lappend state(playback) $who $data                      ;# Save for redraw and saving
-
-       if {$state(ascii) || [regexp {^meta2?$} $who] } {
-               regsub -all \r $data "" data
-               foreach line [split $data \n] {
-                       set line [printable $line]
-                       set tag $who
-                       if {$tag == "client" && [regexp -nocase {^get |post } $line]} {
-                               lappend tag client2
-                       }
-                       if {$state(gui)} {
-                               .out insert end "$line\n" $tag
-                       } else {
-                               puts "$prefix($who)$line"
-                       }
-               }
-       } else {                                                                        ;# Hex output
-               while {[string length $data]} {
-                       set line [string range $data 0 15]
-                       set data [string range $data [string length $line] end]
-                       binary scan $line H* hex
-                       regsub -all {([0-9a-f][0-9a-f])} $hex {\1 } hex
-                       set line [format "%-48.48s  %-16.16s\n" $hex [printable $line 1]] 
-                       if {$state(gui)} {
-                               .out insert end $line $who
-                       } else {
-                               puts "$prefix(who)$line"
-                       }
-               }
-       }
-       if {$state(auto) && $state(gui)} {
-               .out see end
-       }
+# insertData -- Inserts data into the output window. WHO tells us if it is
+# from the client, server or meta.
+# 
+proc insertData {who data {force 0}} {
+    global state
+    array set prefix {meta = meta2 = client > server <}
+
+    DoExtract $who $data                        ;# Display any extracted data
+    if {! $force && ! $state(capture)} return   ;# No display w/o capture on
+    lappend state(playback) $who $data          ;# Save for redraw and saving
+    
+    if {$state(ascii) || [regexp {^meta2?$} $who] } {
+        regsub -all \r $data "" data
+        foreach line [split $data \n] {
+            set line [printable $line]
+            set tag $who
+            if {$tag == "client" && [regexp -nocase {^get |^post } $line]} {
+                lappend tag client2
+            }
+            if {$state(gui)} {
+                .out insert end "$line\n" $tag
+            } else {
+                puts "$prefix($who)$line"
+            }
+        }
+    } else {                                    ;# Hex output
+        while {[string length $data]} {
+            set line [string range $data 0 15]
+            set data [string range $data [string length $line] end]
+            binary scan $line H* hex
+            regsub -all {([0-9a-f][0-9a-f])} $hex {\1 } hex
+            set line [format "%-48.48s  %-16.16s\n" $hex [printable $line 1]] 
+            if {$state(gui)} {
+                .out insert end $line $who
+            } else {
+                puts "$prefix(who)$line"
+            }
+        }
+    }
+    if {$state(autoscroll) && $state(gui)} {
+        .out see end
+    }
 }
 ##+##########################################################################
 # 
@@ -320,108 +370,114 @@ proc insertData {who data} {
 # Puts up an informational message both in the output window and
 # in the status window.
 # 
-proc INFO {msg {who meta}} {
-       global state
-       set state(msg) $msg
-       insertData $who $msg
+proc INFO {msg {who meta} {display 0}} {
+    global state
+    set state(msg) $msg
+    insertData $who $msg $display
 }
 proc ERROR {emsg} {
-       if {$::state(gui)} {
-               tk_messageBox -title "SockSpy Error" -message $emsg -icon error
-       } else {
-               puts $emsg
-       }
-       exit
+    if {$::state(gui)} {
+        tk_messageBox -title "SockSpy Error" -message $emsg -icon error
+    } else {
+        puts $emsg
+    }
 }
 ##+##########################################################################
 # 
-# sockReadable
-# 
-# Called when there is data available on the fromSocket
+# sockReadable -- Called when there is data available on the fromSocket
 # 
 proc sockReadable {fromSock toSock who} {
-       global state
-       set data [read $fromSock]
-       if {[string length $data] == 0} {
-               close $fromSock
-               catch { close $toSock }
-               insertData meta "----- closed connection -----"
-               INFO "waiting for new connection..." 
-               return
-       }
-       if {$toSock == ""} {                                            ;# Not connected yet
-               ProxyConnect $fromSock $data                    ;# Do proxy forwarding
-       } else {
-               catch { puts -nonewline $toSock $data } ;# Forward if we have a socket
-       }
-       insertData $who $data
-       update
+    global state
+    set data [read $fromSock]
+    if {[string length $data] == 0} {
+        close $fromSock
+        catch { close $toSock }
+        insertData meta "----- closed connection -----"
+        INFO "waiting for new connection..." 
+        return
+    }
+    if {$toSock == ""} {                        ;# Not connected yet
+        ProxyConnect $fromSock $data            ;# Do proxy forwarding
+    } else {
+        catch { puts -nonewline $toSock $data } ;# Forward if we have a socket
+    }
+    insertData $who $data
+    update
 }
+##+##########################################################################
+# 
+# ProxyConnect
+# 
+# Called from a new socket connection when we have to determing who
+# to forward to.
+# 
 proc ProxyConnect {fromSock data} {
-       set line1 [lindex [split $data \r] 0]
-       set bad [regexp -nocase {(http:[^ ]+)} $line1 => uri]
-       if {$bad == 0} {
-               INFO "ERROR: cannot extract URI from '$line1'"
-               close $fromSock
-               insertData meta "----- closed connection -----"
-               insertData meta "waiting for new connection..." 
-       }
-       set state(uri) $uri                                             ;# For debugging
-       array set URI [::uri::split $uri]
-       if {$URI(port) == ""} { set URI(port) 80 }
-       set bad [catch {set sockServ [socket $URI(host) $URI(port)]} reason]
-       if {$bad} {
-               set msg "cannot connect to $URI(host):$URI(port) => $reason"
-               INFO $msg
-               close $fromSock
-               insertData meta "----- closed connection -----"
-               insertData meta "waiting for new connection..." 
-               tk_messageBox -icon error -type ok -message $msg
-               break
-       }
-       INFO "fowarding to $URI(host):$URI(port)" meta2
-       fileevent $fromSock readable \
-               [list sockReadable $fromSock $sockServ client]
-       fconfigure $sockServ -blocking 0 -buffering none -translation binary
-       fileevent $sockServ readable \
-               [list sockReadable $sockServ $fromSock server]
-       puts -nonewline $sockServ $data
+    set line1 [lindex [split $data \r] 0]
+    set bad [regexp -nocase {(http:[^ ]+)} $line1 => uri]
+    if {$bad == 0} {
+        INFO "ERROR: cannot extract URI from '$line1'"
+        close $fromSock
+        insertData meta "----- closed connection -----"
+        insertData meta "waiting for new connection..." 
+    }
+    set state(uri) $uri                     ;# For debugging
+    array set URI [::uri::split $uri]
+    if {$URI(port) == ""} { set URI(port) 80 }
+    set bad [catch {set sockServ [socket $URI(host) $URI(port)]} reason]
+    if {$bad} {
+        set msg "cannot connect to $URI(host):$URI(port) => $reason"
+        INFO $msg
+        close $fromSock
+        ERROR $msg
+        insertData meta "----- closed connection -----"
+        insertData meta "waiting for new connection..." 
+        return
+    }
+    INFO "fowarding to $URI(host):$URI(port)" meta2
+    fileevent $fromSock readable \
+        [list sockReadable $fromSock $sockServ client]
+    fconfigure $sockServ -blocking 0 -buffering none -translation binary
+    fileevent $sockServ readable \
+        [list sockReadable $sockServ $fromSock server]
+    puts -nonewline $sockServ $data
 }
 ##+##########################################################################
 # 
-# clntConnect
-# 
-# Called when we get a new client connection
+# clntConnect -- Called when we get a new client connection
 # 
 proc clntConnect {sockClnt ip port} {
-       global state SP
-
-       set state(sockClnt) $sockClnt
-       set state(meta) ""
-       
-       INFO "connect from [fconfigure $sockClnt -sockname] $port" meta2
-       if {$state(proxy) || $SP(servHost) == {} || $SP(servHost) == "none"} {
-               set sockServ ""
-       } else {
-               set n [catch {set sockServ [socket $SP(servHost) $SP(servPort)]} reason]
-               if {$n} {
-                       INFO "cannot connect: $reason"
-                       ERROR "cannot connect to $servHost $servPort: $reason"
-               }
-               INFO "connecting to $SP(servHost):$SP(servPort)" meta2
-       }
-
-       ;# Configure connection to the client
-       fconfigure $sockClnt -blocking 0 -buffering none -translation binary
-       fileevent $sockClnt readable \
-                       [list sockReadable $sockClnt $sockServ client]
-
-       ;# Configure connection to the server
-       if {[string length $sockServ]} {
-               fconfigure $sockServ -blocking 0 -buffering none -translation binary
-               fileevent $sockServ readable \
-                               [list sockReadable $sockServ $sockClnt server]
-       }
+    global state SP
+
+    set state(sockClnt) $sockClnt
+    set state(meta) ""
+    
+    INFO "connect from [fconfigure $sockClnt -sockname] $port" meta2
+    if {$state(proxy) || $SP(servHost) == {} || $SP(servHost) == "none"} {
+        set sockServ ""
+    } else {
+        set n [catch {set sockServ [socket $SP(servHost) $SP(servPort)]} reason]
+        if {$n} {
+            INFO "cannot connect: $reason"
+            close $sockClnt
+            ERROR "cannot connect to $SP(servHost) $SP(servPort): $reason"
+            insertData meta "----- closed connection -----"
+            insertData meta "waiting for new connection..." 
+            
+        }
+        INFO "connecting to $SP(servHost):$SP(servPort)" meta2
+    }
+
+    ;# Configure connection to the client
+    fconfigure $sockClnt -blocking 0 -buffering none -translation binary
+    fileevent $sockClnt readable \
+            [list sockReadable $sockClnt $sockServ client]
+
+    ;# Configure connection to the server
+    if {[string length $sockServ]} {
+        fconfigure $sockServ -blocking 0 -buffering none -translation binary
+        fileevent $sockServ readable \
+                [list sockReadable $sockServ $sockClnt server]
+    }
 }
 ##+##########################################################################
 # 
@@ -431,236 +487,275 @@ proc clntConnect {sockClnt ip port} {
 # it is already open.
 # 
 proc DoListen {} {
-       global state SP
-
-       catch {close $state(sockClnt)}                          ;# Only the last open connection
-       
-       ;# Close old listener if it exists
-       if {$state(listen) != ""} {
-               set n [catch {close $state(listen)} emsg]
-               if {$n} { INFO "socket close error: $emsg"}
-               set state(listen) ""
-               update                                                                  ;# Need else socket below fails
-       }
-
-       # Listen on clntPort or proxyPort for incoming connections
-       set port $SP(clntPort)
-       if {$state(proxy)} {set port $SP(proxyPort)}
-       set n [catch {set state(listen) [socket -server clntConnect $port]} emsg]
-       
-       if {$n} {
-               INFO "socket open error: $emsg"
-               set state(title) "not connected"
-               return 0
-       } else {
-               if {$state(proxy)} {
-                       set state(title) "proxy localhost:$SP(proxyPort)"
-               } else {
-                       set state(title) "localhost:$SP(clntPort) <--> "
-                       append state(title) "$SP(servHost):$SP(servPort)"
-               }
-               INFO $state(title)
-               INFO "waiting for new connection..."
-       }
-       return 1
+    global state SP
+
+    set rval 1
+    catch {close $state(sockClnt)}              ;# Only the last open connection
+    
+    ;# Close old listener if it exists
+    if {$state(listen) != ""} {
+        set n [catch {close $state(listen)} emsg]
+        if {$n} { INFO "socket close error: $emsg"}
+        set state(listen) ""
+        update                                  ;# Need else socket below fails
+    }
+
+    # Listen on clntPort or proxyPort for incoming connections
+    set port $SP(clntPort)
+    if {$state(proxy)} {set port $SP(proxyPort)}
+    set n [catch {set state(listen) [socket -server clntConnect $port]} emsg]
+    
+    if {$n} {
+        INFO "socket open error: $emsg"
+        set state(title) "not connected"
+        set rval 0
+    } else {
+        if {$state(proxy)} {
+            set state(title) "proxy localhost:$SP(proxyPort)"
+        } else {
+            set state(title) "localhost:$SP(clntPort) <--> "
+            append state(title) "$SP(servHost):$SP(servPort)"
+        }
+        INFO $state(title)
+        INFO "waiting for new connection..."
+    }
+    wm title . "SockSpy -- $state(title)"
+    return $rval
 }
 ##+##########################################################################
 # 
-# GetSetup
-# 
-# Prompts the user for client port, server host and server port
+# GetSetup -- Prompts the user for client port, server host and server port
 # 
 proc GetSetup {} {
-       global state SP ok
-       array set save [array get SP]
-       set ok 0                                                                        ;# Assume cancelling
-
-       ;# Put in some default values
-       if {![string length $SP(proxyPort)]} {set SP(proxyPort) 8080}
-       if {![string length $SP(clntPort)]}  {set SP(clntPort) 8080}
-       if {![string length $SP(servPort)]}  {set SP(servPort) 80}
-       
-       if {! $state(gui)} {
-               catch {close $state(listen)}
-
-               set d "no" ; if {$state(proxy)} { set d yes }
-               set p [Prompt "Proxy mode" $d]
-               if {[regexp -nocase {^y$|^yes$} $p]} {
-                       set state(proxy) 1
-                       set SP(proxyPort) [Prompt "proxy port" $SP(proxyPort)]
-               } else {
-                       set state(proxy) 0
-                       set SP(clntPort) [Prompt "Client port" $SP(clntPort)]
-                       set SP(servHost) [Prompt "Server host" $SP(servHost)]
-                       set SP(servPort) [Prompt "Server port" $SP(servPort)]
-               }
-               DoListen
-               return
-       } 
-
-       catch {destroy .dlg}
-       toplevel .dlg
-       wm title .dlg "Sockspy Setup"
-       wm geom .dlg +176+176
-       #wm transient .dlg .
-       
-       label .dlg.top -bd 2 -relief raised
-       set msg    "You can configure SockSpy to either forward data\n"
-       append msg "a fixed server and port or to use the HTTP Proxy\n"
-       append msg "protocol to dynamically determine the server and\n"
-       append msg "port to forward data to."
-
-       #labelframe .dlg.fforward -text "Fixed Server Forwarding"
-       #labelframe .dlg.fproxy -text "HTTP Proxy"
-       frame .dlg.fforward
-       frame .dlg.fproxy
-
-       label .dlg.msg -text $msg -justify left
-       radiobutton .dlg.forward -text "Use fixed server forwarding" \
-               -variable state(proxy)  -value 0 -anchor w -command GetSetup2
-       label .dlg.fl1 -text "Client Port:" -anchor e
-       entry .dlg.fe1 -textvariable SP(clntPort)
-
-       label .dlg.fl2 -text "Server Host:" -anchor e
-       entry .dlg.fe2 -textvariable SP(servHost)
-       label .dlg.fl3 -text "Server Port:" -anchor e
-       entry .dlg.fe3 -textvariable SP(servPort)
-       
-       radiobutton .dlg.proxy -text "Use HTTP Proxying" \
-               -variable state(proxy)  -value 1 -anchor w -command GetSetup2
-       label .dlg.pl1 -text "Proxy Port:" -anchor e
-       entry .dlg.pe1 -textvariable SP(proxyPort)
-       button .dlg.ok -text OK -width 10 -command [list ValidForm 1]
-       button .dlg.cancel -text Cancel -width 10 -command [list destroy .dlg]
-       
-       grid .dlg.top -row 0 -column 0 -columnspan 3 -sticky ew -padx 10 -pady 10
-       grid columnconfigure .dlg 0 -weight 1
-       grid x .dlg.ok .dlg.cancel -padx 10
-       grid configure .dlg.ok -padx 0
-       grid rowconfigure .dlg 2 -minsize 8
-       
-       pack .dlg.msg -in .dlg.top -side top -fill x -padx 10 -pady 5
-       pack .dlg.fforward .dlg.fproxy -in .dlg.top -side top -fill x \
-               -padx 10 -pady 10
-       
-       grid .dlg.proxy - - -in .dlg.fproxy -sticky w
-       grid x .dlg.pl1 .dlg.pe1 -in .dlg.fproxy -sticky ew
-       grid columnconfigure .dlg.fproxy 0 -minsize .2i
-       grid columnconfigure .dlg.fproxy 2 -weight 1
-       grid columnconfigure .dlg.fproxy 3 -minsize 10
-       grid rowconfigure .dlg.fproxy 2 -minsize 10
-
-       grid .dlg.forward - - -in .dlg.fforward -sticky w
-       grid x .dlg.fl1 .dlg.fe1 -in .dlg.fforward -sticky ew
-       grid x .dlg.fl2 .dlg.fe2 -in .dlg.fforward -sticky ew
-       grid x .dlg.fl3 .dlg.fe3 -in .dlg.fforward -sticky ew
-       grid columnconfigure .dlg.fforward 0 -minsize .2i
-       grid columnconfigure .dlg.fforward 2 -weight 1
-       grid columnconfigure .dlg.fforward 3 -minsize 10
-       grid rowconfigure .dlg.fforward 4 -minsize 10
-       raise .dlg
-
-       bind .dlg.forward <Return>  [bind all <Key-Tab>]
-       bind .dlg.proxy <Return>  [bind all <Key-Tab>]
-       bind .dlg.fe1 <Return> [bind all <Key-Tab>]
-       bind .dlg.fe2 <Return> [bind all <Key-Tab>]
-       bind .dlg.fe3 <Return> [list .dlg.ok invoke]
-       bind .dlg.pe1 <Return> [list .dlg.ok invoke]
-
-       GetSetup2
-       .dlg.pe1 icursor end
-       .dlg.fe2 icursor end
-       if {$state(proxy)} { focus -force .dlg.pe1 } { focus -force .dlg.fe2 }
-
+    global state SP ok
+    array set save [array get SP]
+    set ok 0                                    ;# Assume cancelling
+
+    ;# Put in some default values
+    if {![string length $SP(proxyPort)]} {set SP(proxyPort) 8080}
+    if {![string length $SP(clntPort)]}  {set SP(clntPort) 8080}
+    if {![string length $SP(servPort)]}  {set SP(servPort) 80}
+    
+    if {! $state(gui)} {
+        catch {close $state(listen)}
+
+        set d "no" ; if {$state(proxy)} { set d yes }
+        set p [Prompt "Proxy mode" $d]
+        if {[regexp -nocase {^y$|^yes$} $p]} {
+            set state(proxy) 1
+            set SP(proxyPort) [Prompt "proxy port" $SP(proxyPort)]
+        } else {
+            set state(proxy) 0
+            set SP(clntPort) [Prompt "Client port" $SP(clntPort)]
+            set SP(servHost) [Prompt "Server host" $SP(servHost)]
+            set SP(servPort) [Prompt "Server port" $SP(servPort)]
+        }
+        DoListen
+        return
+    } 
+
+    destroy .dlg
+    toplevel .dlg
+    wm title .dlg "Sockspy Setup"
+    wm geom .dlg +176+176
+    #wm transient .dlg .
+    
+    label .dlg.top -bd 2 -relief raised
+    set msg    "You can configure SockSpy to either forward data\n"
+    append msg "a fixed server and port or to use the HTTP Proxy\n"
+    append msg "protocol to dynamically determine the server and\n"
+    append msg "port to forward data to."
+
+    #labelframe .dlg.fforward -text "Fixed Server Forwarding"
+    #labelframe .dlg.fproxy -text "HTTP Proxy"
+    frame .dlg.fforward
+    frame .dlg.fproxy
+
+    label .dlg.msg -text $msg -justify left
+    radiobutton .dlg.forward -text "Use fixed server forwarding" \
+        -variable state(proxy)  -value 0 -anchor w -command GetSetup2
+    label .dlg.fl1 -text "Client Port:" -anchor e
+    entry .dlg.fe1 -textvariable SP(clntPort)
+
+    label .dlg.fl2 -text "Server Host:" -anchor e
+    entry .dlg.fe2 -textvariable SP(servHost)
+    label .dlg.fl3 -text "Server Port:" -anchor e
+    entry .dlg.fe3 -textvariable SP(servPort)
+    
+    radiobutton .dlg.proxy -text "Use HTTP Proxying" \
+        -variable state(proxy)  -value 1 -anchor w -command GetSetup2
+    label .dlg.pl1 -text "Proxy Port:" -anchor e
+    entry .dlg.pe1 -textvariable SP(proxyPort)
+    button .dlg.ok -text OK -width 10 -command [list ValidForm 1]
+    button .dlg.cancel -text Cancel -width 10 -command [list destroy .dlg]
+    
+    grid .dlg.top -row 0 -column 0 -columnspan 3 -sticky ew -padx 10 -pady 10
+    grid columnconfigure .dlg 0 -weight 1
+    grid x .dlg.ok .dlg.cancel -padx 10
+    grid configure .dlg.ok -padx 0
+    grid rowconfigure .dlg 2 -minsize 8
+    
+    pack .dlg.msg -in .dlg.top -side top -fill x -padx 10 -pady 5
+    pack .dlg.fforward .dlg.fproxy -in .dlg.top -side top -fill x \
+        -padx 10 -pady 10
+    
+    grid .dlg.proxy - - -in .dlg.fproxy -sticky w
+    grid x .dlg.pl1 .dlg.pe1 -in .dlg.fproxy -sticky ew
+    grid columnconfigure .dlg.fproxy 0 -minsize .2i
+    grid columnconfigure .dlg.fproxy 2 -weight 1
+    grid columnconfigure .dlg.fproxy 3 -minsize 10
+    grid rowconfigure .dlg.fproxy 2 -minsize 10
+
+    grid .dlg.forward - - -in .dlg.fforward -sticky w
+    grid x .dlg.fl1 .dlg.fe1 -in .dlg.fforward -sticky ew
+    grid x .dlg.fl2 .dlg.fe2 -in .dlg.fforward -sticky ew
+    grid x .dlg.fl3 .dlg.fe3 -in .dlg.fforward -sticky ew
+    grid columnconfigure .dlg.fforward 0 -minsize .2i
+    grid columnconfigure .dlg.fforward 2 -weight 1
+    grid columnconfigure .dlg.fforward 3 -minsize 10
+    grid rowconfigure .dlg.fforward 4 -minsize 10
     raise .dlg
-       tkwait window .dlg
-       wm deiconify .  
 
-       if {$ok} {
-               DoListen
-       } else {
-               array set SP [array get save]
-       }
+    bind .dlg.forward <Return>  [bind all <Key-Tab>]
+    bind .dlg.proxy <Return>  [bind all <Key-Tab>]
+    bind .dlg.fe1 <Return> [bind all <Key-Tab>]
+    bind .dlg.fe2 <Return> [bind all <Key-Tab>]
+    bind .dlg.fe3 <Return> [list .dlg.ok invoke]
+    bind .dlg.pe1 <Return> [list .dlg.ok invoke]
+
+    GetSetup2
+    .dlg.pe1 icursor end
+    .dlg.fe2 icursor end
+    if {$state(proxy)} { focus -force .dlg.pe1 } { focus -force .dlg.fe2 }
+
+    raise .dlg
+    tkwait window .dlg
+    wm deiconify .  
+
+    if {$ok} {
+        DoListen
+    } else {
+        array set SP [array get save]
+    }
 }
 proc GetSetup2 {} {
-       global state
-       array set s {1 normal 0 disabled}
-       if {! $state(proxy)} { array set s {0 normal 1 disabled} }
-       
-       .dlg.pl1 config -state $s(1)
-       .dlg.pe1 config -state $s(1)
-       foreach w {1 2 3} {
-               .dlg.fl$w config -state $s(0)
-               .dlg.fe$w config -state $s(0)
-       }
+    global state
+    array set s {1 normal 0 disabled}
+    if {! $state(proxy)} { array set s {0 normal 1 disabled} }
+    
+    .dlg.pl1 config -state $s(1)
+    .dlg.pe1 config -state $s(1)
+    foreach w {1 2 3} {
+        .dlg.fl$w config -state $s(0)
+        .dlg.fe$w config -state $s(0)
+    }
 }
 proc ValidForm {kill} {
-       global state SP ok
-       set ok 0
-       if {$state(proxy)} {
-               if {$SP(proxyPort) != ""} {set ok 1}
-       } elseif {$SP(clntPort) !="" && $SP(servHost) !="" && $SP(servPort) !=""} {
-               set ok 1
-       }
-       if {$ok && $kill} {destroy .dlg}
-       return $ok
+    global state SP ok
+    set ok 0
+    if {$state(proxy)} {
+        if {$SP(proxyPort) != ""} {set ok 1}
+    } elseif {$SP(clntPort) !="" && $SP(servHost) !="" && $SP(servPort) !=""} {
+        set ok 1
+    }
+    if {$ok && $kill} {destroy .dlg}
+    return $ok
 }
 ##+##########################################################################
 # 
-# Prompt
-# 
-# Non-gui way to get input from the user.
+# Prompt -- Non-gui way to get input from the user.
 # 
 proc Prompt {prompt {default ""}} {
-       if {$default != ""} {
-               puts -nonewline "$prompt ($default): "
-       } else {
-               puts -nonewline "$prompt: "
-       }
-       flush stdout
-       set n [gets stdin line]
-
-       if {$n == 0 && $default != ""} {
-               set line $default
-       }
-       return $line
+    if {$default != ""} {
+        puts -nonewline "$prompt ($default): "
+    } else {
+        puts -nonewline "$prompt: "
+    }
+    flush stdout
+    set n [gets stdin line]
+
+    if {$n == 0 && $default != ""} {
+        set line $default
+    }
+    return $line
 }
 ##+##########################################################################
 # 
-# Shutdown
-# 
-# Closes the listen port before exiting
+# Shutdown -- Closes the listen port before exiting
 # 
 proc Shutdown {} {
-       global state
+    global state
 
-       catch {close $state(listen)}
-       exit
+    catch {close $state(listen)}
+    exit
 }
 ##+##########################################################################
 # 
-# ButtonBar
-# 
-# Toggles the visibility of the button bar
+# ButtonBar -- Toggles the visibility of the button bar
 # 
 proc ButtonBar {} {
-       global state
-       
-       if {$state(bbar)} {                                                     ;# Need to add button bar
-               pack .cmd -side top -fill x -pady 5 -in .bbar
-       } else {
-               pack forget .cmd
-               .bbar config -height 1                                  ;# Need this to give remove gap
-       }
+    global state
+
+    if {$state(bbar)} {                         ;# Need to add button bar
+        pack .cmd -side top -fill x -pady 5 -in .bbar
+    } else {
+        pack forget .cmd
+        .bbar config -height 1                  ;# Need this to give remove gap
+    }
 }
+##+##########################################################################
+# 
+# ToggleExtract -- Toggles the visibility of the extract window
+# 
+proc ToggleExtract {{how 0}} {
+    global state
+
+    if {$how == -1} {                           ;# Hard kill
+        destroy .extract
+        set state(extract) 0
+        return
+    }
+    if {$state(extract)} {
+        createExtract
+    } else {
+        catch {wm withdraw .extract}
+    }
+}
+##+##########################################################################
+# 
+# ToggleWrap -- turns on or off wrap in the text window
+# 
+proc ToggleWrap {} {
+    global state
+    array set x {0 none 1 char}
+    .out configure -wrap $x($state(autowrap))
+}
+##+##########################################################################
+# 
+# ToggleCapture -- puts up a help message
+# 
+proc ToggleCapture {} {
+    global state
+    if {$state(capture)} {
+        INFO "Data capture display enabled" meta
+        .out config -bg white
+    } else {
+        INFO "Data capture display disabled" meta 1
+        .out config -bg grey88
+    }
+}
+##+##########################################################################
+# 
+# Help -- a simple help system
+# 
 proc Help {} {
-    catch {destroy .help}
+    destroy .help
     toplevel .help
     wm title .help "SockSpy Help"
     wm geom .help "+[expr {[winfo x .] + 50}]+[expr {[winfo y .] + 50}]"
 
     text .help.t -relief raised -wrap word -width 70 -height 25 \
-       -padx 10 -pady 10 -cursor {} -yscrollcommand {.help.sb set}
+    -padx 10 -pady 10 -cursor {} -yscrollcommand {.help.sb set}
     scrollbar .help.sb -orient vertical -command {.help.t yview}
     button .help.dismiss -text Dismiss -command {destroy .help}
     pack .help.dismiss -side bottom -pady 10
@@ -673,102 +768,124 @@ proc Help {} {
     .help.t tag configure title2 -justify center -font "Times 12 bold"
     .help.t tag configure header -font $bold
     .help.t tag configure n -lmargin1 15 -lmargin2 15
-       .help.t tag configure fixed -font $fixed -lmargin1 25 -lmargin2 55
+    .help.t tag configure fixed -font $fixed -lmargin1 25 -lmargin2 55
 
     .help.t insert end "SockSpy\n" title
     .help.t insert end "Authors: Tom Poindexter and Keith Vetter\n\n" title2
 
-       set m "SockSpy lets you watch the conversation of a tcp client and server. "
-       append m "SockSpy acts much like a gateway: it waits for a tcp connection, "
-       append m "then connects to the real server. Data from the client is passed "
-       append m "onto the server, and data from the server is passed onto the "
-       append m "client.\n\n"
-
-       append m "Along the way, the data streams are also displayed in text "
-       append m "widget with data sent from the client displayed in green, data "
-       append m "from the server in blue and connection metadata in red. The data "
-       append m "can be displayed as printable ASCII strings, or as a hex dump "
-       append m "format of both hex and printable characters.\n\n"
-       .help.t insert end "What is SockSpy?\n" header $m n
-
-       set m "Why might you want to use SockSpy? Debugging tcp client/server "
-       append m "programs, examining protocols and diagnosing network problems "
-       append m "are top candidates. Perhaps you just want to figure out how "
-       append m "somethings work. I've used it to bypass firewalls, to rediscover "
-       append m "my lost smtp password, to access a news server on a remote "
-       append m "network, etc.\n\nIt's not a replacement for heavy duty tools "
-       append m "such as 'tcpdump' and other passive packet sniffers. On the "
-       append m "other hand, SockSpy doesn't require any special priviledges to "
-       append m "run (unless of course, you try to listen on a Unix reserved tcp "
-       append m "port less than 1024.)\n\n"
-       .help.t insert end "Why Use SockSpy?\n" header $m n
-
-       set m "Just double click on SockSpy to start it. You will be prompted for "
-       append m "various connection parameters described below.\n\n"
-       append m "Alternatively, you can specify the connection parameters on the "
-       append m "command line. This is also how you can run SockSpy in text mode "
-       append m "without a GUI.\n\n"
-       append m "To start SockSpy from the command line:\n"
-       .help.t insert end "How to Use SockSpy\n" header $m n
-       
-       set m "$ sockspy <listen-port> <server-host> <server-port>\n  or\n"
-       append m "$ sockspy -proxy <proxy-port>\n\n"
-       .help.t insert end $m fixed
-
-       set m "To start SockSpy in text mode without a GUI:\n"
-       .help.t insert end $m n
-       set m "$ tclsh sockspy <listen-port> <server-host> <server-port>\n  or\n"
-       append m "$ tclsh sockspy -proxy <proxy-port>\n\n"
-       .help.t insert end $m fixed
-
-       set m    "<listen-port>: the tcp port on which to listen. Clients should "
-       append m "connect to this port.\n"
-       append m "<server-host>:  the host where the real server runs.\n"
-       append m "<server-port>:  the tcp port on which the real server listens.\n"
-       append m "<proxy-port>:  the tcp port on which to listen in proxy-mode.\n\n"
-       .help.t insert end $m n
-
-       set m "In proxy mode SockSpy works like a simple HTTP proxy server. "
-       append m "Instead of forwarding to a fixed server and port, it follows the "
-       append m "HTTP proxy protocol and reads the server information from the "
-       append m "first line of HTTP request.\n\n"
-       append m "You can turn on proxy mode by selecting it in the SockSpy Setup "
-       append m "dialog, or by specifying -proxy on the command line.\n\n"
-       .help.t insert end "Proxy Mode\n" header $m n
-
-       set m "To spy on HTTP connections to a server, type:\n"
-       .help.t insert end "Example\n" header $m n
-       .help.t insert end "  sockspy 8080 www.some.com 80\n" fixed
-       .help.t insert end "and point your browser to\n" n
-       .help.t insert end "  http://localhost:8080/index.html\n\n" fixed
-       .help.t insert end "Alternatively, you could configure your browser to " n
-       .help.t insert end "use localhost and port 8000 as its proxy, and then " n
-       .help.t insert end "type:\n" n
-       .help.t insert end "  sockspy -proxy 8000\n" fixed
-       .help.t insert end "and user your browser normally.\n" n
-       
+    set m "SockSpy lets you watch the conversation of a tcp client and server. "
+    append m "SockSpy acts much like a gateway: it waits for a tcp connection, "
+    append m "then connects to the real server. Data from the client is passed "
+    append m "onto the server, and data from the server is passed onto the "
+    append m "client.\n\n"
+
+    append m "Along the way, the data streams are also displayed in text "
+    append m "widget with data sent from the client displayed in green, data "
+    append m "from the server in blue and connection metadata in red. The data "
+    append m "can be displayed as printable ASCII strings, or as a hex dump "
+    append m "format of both hex and printable characters.\n\n"
+    .help.t insert end "What is SockSpy?\n" header $m n
+
+    set m "Why might you want to use SockSpy? Debugging tcp client/server "
+    append m "programs, examining protocols and diagnosing network problems "
+    append m "are top candidates. Perhaps you just want to figure out how "
+    append m "somethings work. I've used it to bypass firewalls, to rediscover "
+    append m "my lost smtp password, to access a news server on a remote "
+    append m "network, etc.\n\nIt's not a replacement for heavy duty tools "
+    append m "such as 'tcpdump' and other passive packet sniffers. On the "
+    append m "other hand, SockSpy doesn't require any special priviledges to "
+    append m "run (unless of course, you try to listen on a Unix reserved tcp "
+    append m "port less than 1024.)\n\n"
+    .help.t insert end "Why Use SockSpy?\n" header $m n
+
+    set m "Just double click on SockSpy to start it. You will be prompted for "
+    append m "various connection parameters described below.\n\n"
+    append m "Alternatively, you can specify the connection parameters on the "
+    append m "command line. This is also how you can run SockSpy in text mode "
+    append m "without a GUI.\n\n"
+    append m "To start SockSpy from the command line:\n"
+    .help.t insert end "How to Use SockSpy\n" header $m n
+    
+    set m "$ sockspy <listen-port> <server-host> <server-port>\n  or\n"
+    append m "$ sockspy -proxy <proxy-port>\n\n"
+    .help.t insert end $m fixed
+
+    set m "To start SockSpy in text mode without a GUI:\n"
+    .help.t insert end $m n
+    set m "$ tclsh sockspy <listen-port> <server-host> <server-port>\n  or\n"
+    append m "$ tclsh sockspy -proxy <proxy-port>\n\n"
+    .help.t insert end $m fixed
+
+    set m    "<listen-port>: the tcp port on which to listen. Clients should "
+    append m "connect to this port.\n"
+    append m "<server-host>:  the host where the real server runs.\n"
+    append m "<server-port>:  the tcp port on which the real server listens.\n"
+    append m "<proxy-port>:  the tcp port on which to listen in proxy-mode.\n\n"
+    .help.t insert end $m n
+
+    set m "In proxy mode SockSpy works like a simple HTTP proxy server. "
+    append m "Instead of forwarding to a fixed server and port, it follows the "
+    append m "HTTP proxy protocol and reads the server information from the "
+    append m "first line of HTTP request.\n\n"
+    append m "You can turn on proxy mode by selecting it in the SockSpy Setup "
+    append m "dialog, or by specifying -proxy on the command line.\n\n"
+    .help.t insert end "Proxy Mode\n" header $m n
+
+    set m "The extract window lets you extract out specific parts of the "
+    append m "data stream. As data arrives from the client, server or as "
+    append m "metadata it gets matched against the appropriate regular "
+    append m "expression filter. If it matches, then the data gets displayed "
+    append m "in the extract window. (Malformed regular expression are "
+    append m "silently ignored.)\n\n"
+    .help.t insert end "Extract Window\n" header $m n
+
+    set m "To spy on HTTP connections to a server, type:\n"
+    .help.t insert end "Example\n" header $m n
+    .help.t insert end "  sockspy 8080 www.some.com 80\n" fixed
+    .help.t insert end "and point your browser to\n" n
+    .help.t insert end "  http://localhost:8080/index.html\n\n" fixed
+    .help.t insert end "Alternatively, you could configure your browser to " n
+    .help.t insert end "use localhost and port 8000 as its proxy, and then " n
+    .help.t insert end "type:\n" n
+    .help.t insert end "  sockspy -proxy 8000\n" fixed
+    .help.t insert end "and user your browser normally.\n" n
+    
     .help.t config -state disabled
 }
-
+##+##########################################################################
+# 
+# About -- simple about box
+# 
 proc About {} {
-       set m "SockSpy  version $::state(version)\n"
-       append m "by Tom Poindexter and Keith Vetter\n"
-       append m "Copyright 1998-[clock format [clock seconds] -format %Y]\n"
-       append m "A program to eavesdrop on a tcp client server conversation."
-       tk_messageBox -icon info -title "About SockSpy" -message $m -parent .
+    set m "SockSpy  version $::state(version)\n"
+    append m "by Tom Poindexter and Keith Vetter\n"
+    append m "Copyright 1998-[clock format [clock seconds] -format %Y]\n\n"
+    append m "A program to eavesdrop on a tcp client server conversation."
+    tk_messageBox -icon info -title "About SockSpy" -message $m -parent .
 }
+##+##########################################################################
+# 
+# DoExtract -- Displays any data matching the RE in the extract window
+# 
+proc DoExtract {who data} {
+    global state extract
 
-proc DoFilter {who data} {
-       global filters
-
-       if {! [info exists filters($who)]} { return $data }
-       set ndata ""
-       foreach line [split $data \n] {
-               if {[regexp $filters($who) $line]} {
-                       lappend ndata $line
-               }
-       }
-       return [join $ndata "\n"]
+    if {! $state(gui)} return
+    if {! [info exists extract($who)]} return
+    if {! [winfo exists .extract]} return
+    
+    regsub -all \r $data "" data
+    foreach line [split $data \n] {
+        if {$extract($who) == ""} continue
+        catch {
+            if {[regexp -nocase $extract($who) $line]} {
+                .extract.out insert end "$line\n" $who
+            }
+        }
+    }
+    if {$state(autoscroll)} {
+        .extract.out see end
+    }
 }
 ################################################################
 ################################################################
@@ -776,34 +893,40 @@ proc DoFilter {who data} {
 
 set state(gui) [info exists tk_version]
 if {[catch {package present uri}]} {
-       ERROR "ERROR: SockSpy requires the uri package from tcllib."
+    ERROR "ERROR: SockSpy requires the uri package from tcllib."
+    exit 0
 }
-       
+    
 if {$state(gui)} { wm withdraw . }
 createMain
 
+if {[lindex $argv 0] == "-go"} {
+    set argv [list 8080 localhost 80]
+    set argc 3
+}
+
 if {[lindex $argv 0] == "-proxy"} {
-       set state(proxy) 1
-       if {$argc == 2} {
-               set SP(proxyPort) [lindex $argv 1]
-               DoListen
-       } else {
-               GetSetup
-       }
+    set state(proxy) 1
+    if {$argc == 2} {
+        set SP(proxyPort) [lindex $argv 1]
+        DoListen
+    } else {
+        GetSetup
+    }
 } else {
-       set state(proxy) 0
-       if {$argc >= 1} { set SP(clntPort) [lindex $argv 0] }
-       if {$argc >= 2} { set SP(servHost) [lindex $argv 1] }
-       if {$argc >= 3} { set SP(servPort) [lindex $argv 2] }
-       if {$argc >= 3} {
-               DoListen
-       } else {
-               GetSetup
-       }
+    set state(proxy) 0
+    if {$argc >= 1} { set SP(clntPort) [lindex $argv 0] }
+    if {$argc >= 2} { set SP(servHost) [lindex $argv 1] }
+    if {$argc >= 3} { set SP(servPort) [lindex $argv 2] }
+    if {$argc >= 3} {
+        DoListen
+    } else {
+        GetSetup
+    }
 }
 
 if {! $state(gui)} {
-       vwait forever                                                           ;# tclsh needs this
+    vwait forever                               ;# tclsh needs this
 } else {
-       wm deiconify .
+    wm deiconify .
 }