e202319ebb 2007-09-24 mjanssen: #!/home/drh/bin/tobe e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: # Simple chat client for Tcl/Tk. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: package require Tk e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: set SERVERHOST fossil-scm.hwaci.com e202319ebb 2007-09-24 mjanssen: # set SERVERHOST 127.0.0.1 e202319ebb 2007-09-24 mjanssen: #set SERVERHOST 64.5.53.192 e202319ebb 2007-09-24 mjanssen: set SERVERPORT 8615 e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Setup the user interface e202319ebb 2007-09-24 mjanssen: wm title . Fossil-Chat e202319ebb 2007-09-24 mjanssen: wm iconname . [wm title .] e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: menu .mb -type menubar aeb2ac781d 2007-09-24 drh: if {$tcl_platform(platform)=="unix" && $tcl_platform(os)!="Darwin"} { e202319ebb 2007-09-24 mjanssen: pack .mb -side top -fill x e202319ebb 2007-09-24 mjanssen: } else { e202319ebb 2007-09-24 mjanssen: . config -menu .mb e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: .mb add cascade -label File -underline 0 -menu .mb.file e202319ebb 2007-09-24 mjanssen: menu .mb.file -tearoff 0 e202319ebb 2007-09-24 mjanssen: .mb.file add command -label Send -command send_message e202319ebb 2007-09-24 mjanssen: .mb.file add command -label {Remove older messages} -command cleanup_record e202319ebb 2007-09-24 mjanssen: .mb.file add separator e202319ebb 2007-09-24 mjanssen: .mb.file add command -label {Exit} -command exit e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: frame .who e202319ebb 2007-09-24 mjanssen: pack .who -side right -anchor n -fill y e202319ebb 2007-09-24 mjanssen: label .who.title -text {Users: } e202319ebb 2007-09-24 mjanssen: pack .who.title -side top -anchor nw e202319ebb 2007-09-24 mjanssen: label .who.list -anchor w -justify left -text {} e202319ebb 2007-09-24 mjanssen: pack .who.list -side top -anchor nw -expand 1 -padx 5 e202319ebb 2007-09-24 mjanssen: label .who.time -text {} -justify right e202319ebb 2007-09-24 mjanssen: proc update_time {} { e202319ebb 2007-09-24 mjanssen: after 1000 update_time e202319ebb 2007-09-24 mjanssen: set now [clock seconds] 5e3f5322e2 2007-09-25 drh: set time [clock format $now -format %H:%M -gmt 1] 5e3f5322e2 2007-09-25 drh: .who.time config -text "UTC: $time" e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: update_time e202319ebb 2007-09-24 mjanssen: pack .who.time -side bottom -anchor sw e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: frame .input e202319ebb 2007-09-24 mjanssen: pack .input -side bottom -fill x e202319ebb 2007-09-24 mjanssen: text .input.t -bd 1 -relief sunken -bg white -fg black -width 60 -height 3 \ e202319ebb 2007-09-24 mjanssen: -wrap word -yscrollcommand [list .input.sb set] -takefocus 1 e202319ebb 2007-09-24 mjanssen: bind .input.t <Key-Return> {send_message; break} e202319ebb 2007-09-24 mjanssen: pack .input.t -side left -fill both -expand 1 e202319ebb 2007-09-24 mjanssen: scrollbar .input.sb -orient vertical -command [list .input.t yview] e202319ebb 2007-09-24 mjanssen: pack .input.sb -side left -fill y e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: frame .msg e202319ebb 2007-09-24 mjanssen: pack .msg -side top -fill both -expand 1 e202319ebb 2007-09-24 mjanssen: text .msg.t -bd 1 -relief sunken -bg white -fg black -width 60 -height 20 \ e202319ebb 2007-09-24 mjanssen: -wrap word -yscrollcommand [list .msg.sb set] -takefocus 0 e202319ebb 2007-09-24 mjanssen: bindtags .msg.t [list .msg.t . all] e202319ebb 2007-09-24 mjanssen: .msg.t tag config error -foreground red e202319ebb 2007-09-24 mjanssen: .msg.t tag config meta -foreground forestgreen e202319ebb 2007-09-24 mjanssen: .msg.t tag config norm -foreground black e202319ebb 2007-09-24 mjanssen: pack .msg.t -side left -fill both -expand 1 e202319ebb 2007-09-24 mjanssen: scrollbar .msg.sb -orient vertical -command [list .msg.t yview] e202319ebb 2007-09-24 mjanssen: pack .msg.sb -side left -fill y e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: update e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Send periodic messages to keep the TCP/IP link up e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc keep_alive {} { e202319ebb 2007-09-24 mjanssen: global TIMER SOCKET e202319ebb 2007-09-24 mjanssen: catch {after cancel $TIMER} e202319ebb 2007-09-24 mjanssen: set TIMER [after 300000 keep_alive] e202319ebb 2007-09-24 mjanssen: catch {puts $SOCKET noop; flush $SOCKET} e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Connect to the server e202319ebb 2007-09-24 mjanssen: proc connect {} { e0232ce1a0 2007-09-24 mjanssen: global SOCKET tcl_platform e202319ebb 2007-09-24 mjanssen: catch {close $SOCKET} e202319ebb 2007-09-24 mjanssen: if {[catch { e202319ebb 2007-09-24 mjanssen: set SOCKET [socket $::SERVERHOST $::SERVERPORT] e202319ebb 2007-09-24 mjanssen: fconfigure $SOCKET -translation binary -blocking 0 e0232ce1a0 2007-09-24 mjanssen: puts $SOCKET [list login $tcl_platform(user) fact,fuzz] e202319ebb 2007-09-24 mjanssen: flush $SOCKET e202319ebb 2007-09-24 mjanssen: fileevent $SOCKET readable handle_input e202319ebb 2007-09-24 mjanssen: keep_alive e202319ebb 2007-09-24 mjanssen: } errmsg]} { e202319ebb 2007-09-24 mjanssen: if {[tk_messageBox -icon error -type yesno -parent . -message \ e202319ebb 2007-09-24 mjanssen: "Unable to connect to server. $errmsg.\n\nTry again?"]=="yes"} { e202319ebb 2007-09-24 mjanssen: after 100 connect e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: connect e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Send the message text contained in the .input.t widget to the server. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc send_message {} { e202319ebb 2007-09-24 mjanssen: set txt [.input.t get 1.0 end] e202319ebb 2007-09-24 mjanssen: .input.t delete 1.0 end e202319ebb 2007-09-24 mjanssen: regsub -all "\[ \t\n\f\r\]+" [string trim $txt] { } txt e202319ebb 2007-09-24 mjanssen: if {$txt==""} return e202319ebb 2007-09-24 mjanssen: global SOCKET 5e3f5322e2 2007-09-25 drh: puts $SOCKET [list message $txt] e202319ebb 2007-09-24 mjanssen: flush $SOCKET e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: .mb add cascade -label "Transfer" -underline 0 -menu .mb.files e202319ebb 2007-09-24 mjanssen: menu .mb.files -tearoff 0 e202319ebb 2007-09-24 mjanssen: .mb.files add command -label "Send file..." -command send_file e202319ebb 2007-09-24 mjanssen: .mb.files add command -label "Delete files" -command delete_files \ e202319ebb 2007-09-24 mjanssen: -state disabled e202319ebb 2007-09-24 mjanssen: .mb.files add separator e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Encode a string (possibly containing binary and \000 characters) into e202319ebb 2007-09-24 mjanssen: # single line of text. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc encode {txt} { e202319ebb 2007-09-24 mjanssen: return [string map [list % %25 + %2b " " + \n %0a \t %09 \000 %00] $txt] e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Undo the work of encode. Convert an encoded string back into its original e202319ebb 2007-09-24 mjanssen: # form. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc decode {txt} { e202319ebb 2007-09-24 mjanssen: return [string map [list %00 \000 %09 \t %0a \n + " " %2b + %25 %] $txt] e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Delete all of the downloaded files we are currently holding. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc delete_files {} { e202319ebb 2007-09-24 mjanssen: global FILES e202319ebb 2007-09-24 mjanssen: .mb.files delete 3 end e202319ebb 2007-09-24 mjanssen: array unset FILES e202319ebb 2007-09-24 mjanssen: .mb.files entryconfigure 1 -state disabled e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Prompt the user to select a file from the disk. Then send that e202319ebb 2007-09-24 mjanssen: # file to all chat participants. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc send_file {} { e202319ebb 2007-09-24 mjanssen: global SOCKET e202319ebb 2007-09-24 mjanssen: set openfile [tk_getOpenFile] e202319ebb 2007-09-24 mjanssen: if {$openfile==""} return e202319ebb 2007-09-24 mjanssen: set f [open $openfile] e202319ebb 2007-09-24 mjanssen: fconfigure $f -translation binary e202319ebb 2007-09-24 mjanssen: set data [read $f] e202319ebb 2007-09-24 mjanssen: close $f 5e3f5322e2 2007-09-25 drh: puts $SOCKET [list file [file tail $openfile] [encode $data]] e202319ebb 2007-09-24 mjanssen: flush $SOCKET e202319ebb 2007-09-24 mjanssen: set time [clock format [clock seconds] -format {%H:%M} -gmt 1] e202319ebb 2007-09-24 mjanssen: .msg.t insert end "\[$time\] sent file [file tail $openfile]\ e202319ebb 2007-09-24 mjanssen: - [string length $data] bytes\n" meta e202319ebb 2007-09-24 mjanssen: .msg.t see end e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Save the named file to the disk. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc save_file {filename} { e202319ebb 2007-09-24 mjanssen: global FILES e202319ebb 2007-09-24 mjanssen: set savefile [tk_getSaveFile -initialfile $filename] e202319ebb 2007-09-24 mjanssen: if {$savefile==""} return e202319ebb 2007-09-24 mjanssen: set f [open $savefile w] e202319ebb 2007-09-24 mjanssen: fconfigure $f -translation binary e202319ebb 2007-09-24 mjanssen: puts -nonewline $f [decode $FILES($filename)] e202319ebb 2007-09-24 mjanssen: close $f e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Handle a "file" message from the chat server. e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc handle_file {from filename data} { e202319ebb 2007-09-24 mjanssen: global FILES e202319ebb 2007-09-24 mjanssen: foreach prior [array names FILES] { e202319ebb 2007-09-24 mjanssen: if {$filename==$prior} break e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: if {![info exists prior] || $filename!=$prior} { e202319ebb 2007-09-24 mjanssen: .mb.files add command -label "Save \"$filename\"" \ e202319ebb 2007-09-24 mjanssen: -command [list save_file $filename] e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: set FILES($filename) $data e202319ebb 2007-09-24 mjanssen: .mb.files entryconfigure 1 -state active e202319ebb 2007-09-24 mjanssen: set time [clock format [clock seconds] -format {%H:%M} -gmt 1] e202319ebb 2007-09-24 mjanssen: .msg.t insert end "\[$time $from\] " meta "File: \"$filename\"\n" norm e202319ebb 2007-09-24 mjanssen: .msg.t see end e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Handle input from the server e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc handle_input {} { e202319ebb 2007-09-24 mjanssen: global SOCKET e202319ebb 2007-09-24 mjanssen: if {[eof $SOCKET]} { e202319ebb 2007-09-24 mjanssen: disconnect e202319ebb 2007-09-24 mjanssen: return e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: set line [gets $SOCKET] e202319ebb 2007-09-24 mjanssen: if {$line==""} return e202319ebb 2007-09-24 mjanssen: set cmd [lindex $line 0] e202319ebb 2007-09-24 mjanssen: if {$cmd=="userlist"} { e202319ebb 2007-09-24 mjanssen: set ulist {} e202319ebb 2007-09-24 mjanssen: foreach u [lrange $line 1 end] { e202319ebb 2007-09-24 mjanssen: append ulist $u\n e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: .who.list config -text [string trim $ulist] 5e3f5322e2 2007-09-25 drh: } elseif {$cmd=="message"} { e202319ebb 2007-09-24 mjanssen: set time [clock format [clock seconds] -format {%H:%M} -gmt 1] e202319ebb 2007-09-24 mjanssen: set from [lindex $line 1] e202319ebb 2007-09-24 mjanssen: .msg.t insert end "\[$time $from\] " meta [lindex $line 2]\n norm e202319ebb 2007-09-24 mjanssen: .msg.t see end e202319ebb 2007-09-24 mjanssen: bell e202319ebb 2007-09-24 mjanssen: wm deiconify . e202319ebb 2007-09-24 mjanssen: update e202319ebb 2007-09-24 mjanssen: raise . e202319ebb 2007-09-24 mjanssen: } elseif {$cmd=="noop"} { e202319ebb 2007-09-24 mjanssen: # do nothing e202319ebb 2007-09-24 mjanssen: } elseif {$cmd=="meta"} { e202319ebb 2007-09-24 mjanssen: set now [clock seconds] e202319ebb 2007-09-24 mjanssen: set time [clock format $now -format {%H:%M} -gmt 1] e202319ebb 2007-09-24 mjanssen: .msg.t insert end "\[$time\] [lindex $line 1]\n" meta e202319ebb 2007-09-24 mjanssen: .msg.t see end 5e3f5322e2 2007-09-25 drh: } elseif {$cmd=="file"} { e202319ebb 2007-09-24 mjanssen: if {[info commands handle_file]=="handle_file"} { e202319ebb 2007-09-24 mjanssen: handle_file [lindex $line 1] [lindex $line 2] [lindex $line 3] e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Handle a broken socket connection e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc disconnect {} { e202319ebb 2007-09-24 mjanssen: global SOCKET e202319ebb 2007-09-24 mjanssen: close $SOCKET e202319ebb 2007-09-24 mjanssen: set q [tk_messageBox -icon error -type yesno -parent . -message \ e202319ebb 2007-09-24 mjanssen: "TCP/IP link lost. Try to reconnet?"] e202319ebb 2007-09-24 mjanssen: if {$q=="yes"} { e202319ebb 2007-09-24 mjanssen: connect e202319ebb 2007-09-24 mjanssen: } else { e202319ebb 2007-09-24 mjanssen: exit e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: } e202319ebb 2007-09-24 mjanssen: e202319ebb 2007-09-24 mjanssen: # Remove all but the most recent 100 message from the message log e202319ebb 2007-09-24 mjanssen: # e202319ebb 2007-09-24 mjanssen: proc cleanup_record {} { e202319ebb 2007-09-24 mjanssen: .msg.t delete 1.0 {end -100 lines} e202319ebb 2007-09-24 mjanssen: }