diff --git a/ChangeLog b/ChangeLog index 9f91328..7d48f27 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-02-10 Karl Runge + * x11vnc: -input option to fine tune allowed client input, + additions to remote control and gui for this. + 2005-02-09 Karl Runge * x11vnc: -users, fix -solid on gnome and kde. * configure.ac: add pwd.h, wait.h, and utmpx.h checks. diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index cc93296..a08556d 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,8 @@ +2005-02-10 Karl Runge + * Add -input to fine tune client input (keystroke, mouse motion, + and button presses). Allow per-client setting via remote cntl. + * fix bug in get_remote_port, add ip2host for client info. + 2005-02-09 Karl Runge * Add -users switch user mechanism and related utilities. * fix -solid for gnome and kde. diff --git a/x11vnc/README b/x11vnc/README index 392af22..41f1db0 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Wed Feb 9 00:21:28 EST 2005 +x11vnc README file Date: Thu Feb 10 23:33:03 EST 2005 The following information is taken from these URLs: @@ -594,6 +594,8 @@ ls -l ./x11vnc/x11vnc Please feel free to [45]contact me if you have any questions, problems, or comments about x11vnc, etc. + + [PayPal] _________________________________________________________________ x11vnc FAQ: diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc index 875ddd2..3ef212f 100755 --- a/x11vnc/tkx11vnc +++ b/x11vnc/tkx11vnc @@ -196,6 +196,8 @@ Permissions forever timeout: -- + input: + -- =SA alwaysshared =SA nevershared =SA dontdisconnect @@ -279,6 +281,11 @@ Check if x11vnc still responds to \"ping\" remote command. set helptext(update-all) " Query the x11vnc server for the current values of all variables. Populate the values into the gui's database. + +Normally the gui will refresh this info every time it interacts +with the x11vnc server, so one doesn't need to use this action +very often (unless something else is changing the state of the +x11vnc server, or new clients have connected, etc). " set helptext(clear-all) " @@ -321,8 +328,27 @@ Terminate the tkx11vnc gui. Any x11vnc servers will be left running. set helptext(current) " Shows a menu of currently connected VNC clients on the x11vnc server. -Allows you to find more information about them or disconnect them. +Allows you to find more information about them, change their input +permissions, or disconnect them. + You will be prompted to confirm any disconnections. +" + + set helptext(client) " +After selecting a VNC client from the \"Clients -> current\" menu, +you will be presented with a dialog that shows the information +about the VNC client. + +You can chose to disconnect the client by clicking on the +\"Disconnect\" checkbox and pressing \"OK\". There will be a +confirmation dialog to doublecheck. + +Alternatively, you can fine tune the VNC client's input permissions +by selecting any of the Keystrokes, Mouse Motion, or Button Clicks +checkboxes and pressing \"OK\". This is like the \"-input\" option +but on a per-client basis. + +To not change any aspects of the VNC client press \"Skip\". " set helptext(solid_color) " @@ -470,12 +496,18 @@ proc textheight {text} { return $count } +proc set_name {name} { + wm title . "$name" + wm iconname . "$name" +} + proc make_toplevel {w {title ""}} { catch {destroy $w} toplevel $w; bind $w "destroy $w" if {$title != ""} { - wm title $w $title + wm title $w $title + wm iconname $w $title } } @@ -1054,6 +1086,195 @@ proc push_new_value {item name new {query 1}} { } } +proc set_kmb_str {} { + global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb + + set str "" + if {$vl_bk} { + append str "K" + } + if {$vl_bm} { + append str "M" + } + if {$vl_bb} { + append str "B" + } + if {$vr_bk || $vr_bm || $vr_bb} { + append str "," + } + if {$vr_bk} { + append str "K" + } + if {$vr_bm} { + append str "M" + } + if {$vr_bb} { + append str "B" + } + entry_insert $str +} + +proc insert_input_window {} { + global text_area cleanup_window + global ffont menu_var + global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb + + append_text "\nUse these checkboxes to set the input permissions, " + append_text "or type in the \"KMB...\"\n-input string manually. " + append_text "Then press \"OK\" or \"Skip\".\n\n" + set w "$text_area.wk_f" + catch {destroy $w} + frame $w -bd 1 -relief ridge -cursor {top_left_arrow} + set fl $w.fl + frame $fl + set fr $w.fr + frame $fr + label $fl.l -font $ffont -text "Normal clients: " + checkbutton $fl.bk -pady 1 -font $ffont -anchor w -variable vl_bk \ + -pady 1 -command set_kmb_str -text "Keystrokes" + checkbutton $fl.bm -font $ffont -anchor w -variable vl_bm \ + -pady 1 -command set_kmb_str -text "Mouse Motion" + checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \ + -pady 1 -command set_kmb_str -text "Button Clicks" + label $fr.l -pady 1 -font $ffont -text "View-only clients:" + checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \ + -pady 1 -command set_kmb_str -text "Keystrokes" + checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \ + -pady 1 -command set_kmb_str -text "Mouse Motion" + checkbutton $fr.bb -font $ffont -anchor w -variable vr_bb \ + -pady 1 -command set_kmb_str -text "Button Clicks" + + if {[info exists menu_var(input)]} { + set input_str $menu_var(input) + } else { + set input_str "" + } + + if {[regexp {(.*),(.*)} $input_str match normal viewonly]} { + ; + } else { + set normal $input_str + set viewonly "" + } + set vl_bk 0 + set vl_bm 0 + set vl_bb 0 + set vr_bk 0 + set vr_bm 0 + set vr_bb 0 + + if {[regexp -nocase {K} $normal]} { + set vl_bk 1 + } + if {[regexp -nocase {M} $normal]} { + set vl_bm 1 + } + if {[regexp -nocase {B} $normal]} { + set vl_bb 1 + } + if {[regexp -nocase {K} $viewonly]} { + set vr_bk 1 + } + if {[regexp -nocase {M} $viewonly]} { + set vr_bm 1 + } + if {[regexp -nocase {B} $viewonly]} { + set vr_bb 1 + } + + pack $fl.l $fl.bk $fl.bm $fl.bb -side top -fill x + pack $fr.l $fr.bk $fr.bm $fr.bb -side top -fill x + pack $fl $fr -side left + update + update idletasks + $text_area window create end -window $w + $text_area see end + $text_area insert end "\n" +# $text_area insert end "\n\n\n\n\n\n\n\n\n" + + set cleanup_window $w +} + +proc set_ca_str {w} { + global ca_bk ca_bm ca_bb ca_bk ca_di + + if {$ca_di} { + entry_insert "disconnect" + $w.bk configure -state disabled + $w.bm configure -state disabled + $w.bb configure -state disabled + return + } + + $w.bk configure -state normal + $w.bm configure -state normal + $w.bb configure -state normal + + set str "" + if {$ca_bk} { + append str "K" + } + if {$ca_bm} { + append str "M" + } + if {$ca_bb} { + append str "B" + } + entry_insert $str +} + +proc insert_client_action_window {input} { + global text_area cleanup_window + global ffont menu_var + global ca_bk ca_bm ca_bb ca_bk ca_di + + append_text "\nUse these checkboxes to set the input permissions " + append_text "for this client\n-or- whether to disconnect it instead. " + append_text "Then press \"OK\" or \"Skip\".\n\n" + set w "$text_area.ca_f" + catch {destroy $w} + frame $w -bd 1 -relief ridge -cursor {top_left_arrow} + checkbutton $w.di -pady 1 -font $ffont -anchor w -variable ca_di \ + -pady 1 -command "set_ca_str $w" -text "Disconnect " + checkbutton $w.bk -font $ffont -anchor w -variable ca_bk \ + -pady 1 -command "set_ca_str $w" -text "Keystrokes" + checkbutton $w.bm -font $ffont -anchor w -variable ca_bm \ + -pady 1 -command "set_ca_str $w" -text "Mouse Motion" + checkbutton $w.bb -font $ffont -anchor w -variable ca_bb \ + -pady 1 -command "set_ca_str $w" -text "Button Clicks" + + set ca_di 0 + set ca_bk 0 + set ca_bm 0 + set ca_bb 0 + + if {[regexp -nocase {K} $input]} { + set ca_bk 1 + } + if {[regexp -nocase {M} $input]} { + set ca_bm 1 + } + if {[regexp -nocase {B} $input]} { + set ca_bb 1 + } + + pack $w.di $w.bk $w.bm $w.bb -side left + update + update idletasks + $text_area window create end -window $w + $text_area see end + $text_area insert end "\n" + + set cleanup_window $w +} + +proc cleanup_text_window {} { + global cleanup_window + if {[info exists cleanup_window]} { + catch {destroy $cleanup_window} + } +} + # For updating a string variable. Also used for simple OK/Skip dialogs # with entry = 0. proc entry_dialog {item {entry 1}} { @@ -1084,6 +1305,13 @@ proc entry_dialog {item {entry 1}} { entry_disable box } + set clean_text_window 0; + + if {$item == "input"} { + insert_input_window + set clean_text_window 1 + } + update # wait for user reply: @@ -1101,6 +1329,11 @@ proc entry_dialog {item {entry 1}} { entry_delete entry_disable menus_enable + + if {$clean_text_window} { + cleanup_text_window; + } + update if {! $entry} { @@ -1205,7 +1438,8 @@ proc see_if_ok {query item expected} { } elseif {[regexp {:[0-9]\.[0-9]} $expected]} { append_text "\t($msg)\n" return 1 - } elseif {$item == "connect" || $item == "disconnect"} { + } elseif {$item == "connect" || $item == "disconnect" + || $item == "client" || $item == "client_input"} { append_text "\t($msg)\n" return 1 } else { @@ -1259,7 +1493,7 @@ proc clear_all {} { continue } if {[info exists menu_var($item)]} { - if [is_action $item] { + if {[is_action $item]} { set menu_var($item) "" } elseif {[value_is_bool $item]} { set menu_var($item) 0 @@ -1494,7 +1728,7 @@ proc do_action {item} { push_new_value $item $name $new 0 set_connected no - } elseif [opt_match Q $item] { + } elseif {[opt_match Q $item]} { push_new_value $item $name $new 1 } else { push_new_value $item $name $new 0 @@ -1660,7 +1894,7 @@ proc split_query {query} { proc set_x11_display {name} { global x11_display set x11_display "x11vnc X display: $name" - wm title . "tkx11vnc - $name" + set_name "tkx11vnc - $name" } proc set_vnc_display {name} { global vnc_display @@ -1672,7 +1906,7 @@ proc set_vnc_url {name} { } proc no_x11_display {} { set_x11_display "(*none*)" - wm title . "tkx11vnc" + set_name "tkx11vnc" } proc no_vnc_display {} { set_vnc_display "(*none*)" @@ -1713,20 +1947,99 @@ proc fetch_displays {} { } } +proc client_dialog {client} { + set cid "" + set host "" + set ip "" + global menu_var text_area cleanup_window item_bool + + append_text "\nClient info string: $client\n\n" + if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} \ + $client m0 m1 m2 m3 m4 m5 m6]} { + # id:ip:port:hostname:input:loginvo + set cid $m1 + set ip $m2 + set port $m3 + set host $m4 + regsub {\..*$} $host "" host + set input $m5 + set logvo $m6 + append_text "Host: $host, Port: $port, IP: $ip, Id: $cid\n" + append_text " - originally logged in as: " + if {$logvo == "1" } { + append_text "View-Only Client\n" + } else { + append_text "Normal Client\n" + } + append_text " - currently allowed input: " + set sk 0 + set sm 0 + set sb 0 + if {[regexp -nocase {K} $input]} { + append_text "Keystroke" + set sk 1 + } + if {[regexp -nocase {M} $input]} { + if {$sk} { + append_text ", " + } + append_text "Mouse-Motion" + set sm 1 + } + if {[regexp -nocase {B} $input]} { + if {$sk || $sm} { + append_text ", " + } + append_text "Button-Click" + set sb 1 + } + if {! $sk && ! $sm && ! $sb} { + append_text "None" + } + append_text "\n" + } + if {$cid == ""} { + append_text "Invalid client info string: $client\n" + return + } + + regsub -all {_} $input "" input + set menu_var(client) "$input" + set item_bool(client) 0 + + insert_client_action_window $input + set rc [entry_dialog client 1] + + cleanup_text_window + + set val $menu_var(client) + #puts "rc: $rc val: $val" + + if {! $rc} { + return; + } elseif {[regexp -nocase {(disconnect|close)} $val]} { + disconnect_dialog $client + } else { + regsub -all -nocase {[^KMB]} $val "" + set item_bool(client_input) 0 + push_new_value "client_input" "client_input" "$cid:$val" 0 + } +} + proc disconnect_dialog {client} { set cid "" set host "" set msg "\n" append msg "*** Client info string: $client\n" - if {[regexp {^(.*):(.*)/(.*)-(.*)$} $client m0 m1 m2 m3 m4]} { - if {$m4 == "ro"} { - set view "(viewonly)" - } else { - set view "(interactive)" - } - set host $m1 - set cid $m3 - append msg "*** Host: $m1, Port: $m2 Id: $m3 $view\n" + if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} $client m0 m1 m2 m3 m4 m5 m6]} { + set cid $m1 + set ip $m2 + set port $m3 + set host $m4 + regsub {\..*$} $host "" host + set input $m5 + set logvo $m6 + append_text "Host: $host, Port: $port, IP: $ip, Id: $cid\n" } if {$cid == ""} { append_text "Invalid client info string: $client\n" @@ -1734,7 +2047,7 @@ proc disconnect_dialog {client} { } append msg "*** To *DISCONNECT* this client press \"OK\", otherwise press \"Skip\"\n" bell - if [warning_dialog $msg "current"] { + if {[warning_dialog $msg "current"]} { push_new_value "disconnect" "disconnect" $cid 1 } else { append_text "disconnect cancelled.\n" @@ -1756,7 +2069,7 @@ proc update_clients_and_repost {} { continue } set name [$casc entrycget $i -label] - if {[regexp {^#} $name]} { + if {[regexp {^num-clients} $name]} { continue } if {[regexp {^refresh-list} $name]} { @@ -1783,12 +2096,20 @@ proc update_clients_menu {list} { $subm add separator set count 0 foreach client [split $list ","] { - regsub {:[0-9][0-9]*/} $client {/} lab - $subm add command -label "$client" \ - -command "disconnect_dialog $client" + if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} \ + $client m0 m1 m2 m3 m4 m5 m6]} { + # id:ip:port:hostname:input:loginvo + set host $m4 + regsub {\..*$} $host "" host + set clabel "$host $m1" + } else { + regsub {:.*$} $client "" clabel + } + $subm add command -label "$clabel" \ + -command "client_dialog $client" incr count } - $subm entryconfigure 0 -label "#clients: $count" + $subm entryconfigure 0 -label "num-clients: $count" } proc set_widgets {} { @@ -2076,7 +2397,7 @@ proc make_widgets {} { pack $df -side top -fill x # text area - text .text -height 11 -relief ridge -font $ffont + text .text -height 12 -relief ridge -font $ffont set text_area .text pack .text -side top -fill both -expand 1 @@ -2622,7 +2943,7 @@ tweak_both screen_blank sb set_template -wm title . "tkx11vnc" +set_name "tkx11vnc" make_widgets; menu_bindings; diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h index cc60b8b..1604b31 100644 --- a/x11vnc/tkx11vnc.h +++ b/x11vnc/tkx11vnc.h @@ -202,6 +202,8 @@ " forever\n" " timeout:\n" " --\n" +" input:\n" +" --\n" " =SA alwaysshared\n" " =SA nevershared\n" " =SA dontdisconnect\n" @@ -285,6 +287,11 @@ " set helptext(update-all) \"\n" "Query the x11vnc server for the current values of all variables.\n" "Populate the values into the gui's database.\n" +"\n" +"Normally the gui will refresh this info every time it interacts\n" +"with the x11vnc server, so one doesn't need to use this action\n" +"very often (unless something else is changing the state of the\n" +"x11vnc server, or new clients have connected, etc).\n" "\"\n" "\n" " set helptext(clear-all) \"\n" @@ -327,10 +334,29 @@ " set helptext(current) \"\n" "Shows a menu of currently connected VNC clients on the x11vnc server.\n" "\n" -"Allows you to find more information about them or disconnect them.\n" +"Allows you to find more information about them, change their input\n" +"permissions, or disconnect them.\n" +"\n" "You will be prompted to confirm any disconnections.\n" "\"\n" "\n" +" set helptext(client) \"\n" +"After selecting a VNC client from the \\\"Clients -> current\\\" menu,\n" +"you will be presented with a dialog that shows the information\n" +"about the VNC client.\n" +"\n" +"You can chose to disconnect the client by clicking on the \n" +"\\\"Disconnect\\\" checkbox and pressing \\\"OK\\\". There will be a\n" +"confirmation dialog to doublecheck.\n" +"\n" +"Alternatively, you can fine tune the VNC client's input permissions\n" +"by selecting any of the Keystrokes, Mouse Motion, or Button Clicks\n" +"checkboxes and pressing \\\"OK\\\". This is like the \\\"-input\\\" option\n" +"but on a per-client basis.\n" +"\n" +"To not change any aspects of the VNC client press \\\"Skip\\\".\n" +"\"\n" +"\n" " set helptext(solid_color) \"\n" "Set the -solid color value.\n" "\"\n" @@ -476,12 +502,18 @@ " return $count\n" "}\n" "\n" +"proc set_name {name} {\n" +" wm title . \"$name\"\n" +" wm iconname . \"$name\"\n" +"}\n" +"\n" "proc make_toplevel {w {title \"\"}} {\n" " catch {destroy $w}\n" " toplevel $w;\n" " bind $w \"destroy $w\"\n" " if {$title != \"\"} {\n" -" wm title $w $title\n" +" wm title $w $title\n" +" wm iconname $w $title\n" " }\n" "}\n" "\n" @@ -1060,6 +1092,195 @@ " }\n" "}\n" "\n" +"proc set_kmb_str {} {\n" +" global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb \n" +"\n" +" set str \"\"\n" +" if {$vl_bk} {\n" +" append str \"K\"\n" +" }\n" +" if {$vl_bm} {\n" +" append str \"M\"\n" +" }\n" +" if {$vl_bb} {\n" +" append str \"B\"\n" +" }\n" +" if {$vr_bk || $vr_bm || $vr_bb} {\n" +" append str \",\"\n" +" }\n" +" if {$vr_bk} {\n" +" append str \"K\"\n" +" }\n" +" if {$vr_bm} {\n" +" append str \"M\"\n" +" }\n" +" if {$vr_bb} {\n" +" append str \"B\"\n" +" }\n" +" entry_insert $str\n" +"}\n" +"\n" +"proc insert_input_window {} {\n" +" global text_area cleanup_window\n" +" global ffont menu_var\n" +" global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb \n" +"\n" +" append_text \"\\nUse these checkboxes to set the input permissions, \"\n" +" append_text \"or type in the \\\"KMB...\\\"\\n-input string manually. \"\n" +" append_text \"Then press \\\"OK\\\" or \\\"Skip\\\".\\n\\n\"\n" +" set w \"$text_area.wk_f\"\n" +" catch {destroy $w}\n" +" frame $w -bd 1 -relief ridge -cursor {top_left_arrow}\n" +" set fl $w.fl\n" +" frame $fl\n" +" set fr $w.fr\n" +" frame $fr\n" +" label $fl.l -font $ffont -text \"Normal clients: \"\n" +" checkbutton $fl.bk -pady 1 -font $ffont -anchor w -variable vl_bk \\\n" +" -pady 1 -command set_kmb_str -text \"Keystrokes\" \n" +" checkbutton $fl.bm -font $ffont -anchor w -variable vl_bm \\\n" +" -pady 1 -command set_kmb_str -text \"Mouse Motion\" \n" +" checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \\\n" +" -pady 1 -command set_kmb_str -text \"Button Clicks\"\n" +" label $fr.l -pady 1 -font $ffont -text \"View-only clients:\"\n" +" checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \\\n" +" -pady 1 -command set_kmb_str -text \"Keystrokes\" \n" +" checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \\\n" +" -pady 1 -command set_kmb_str -text \"Mouse Motion\" \n" +" checkbutton $fr.bb -font $ffont -anchor w -variable vr_bb \\\n" +" -pady 1 -command set_kmb_str -text \"Button Clicks\"\n" +"\n" +" if {[info exists menu_var(input)]} {\n" +" set input_str $menu_var(input)\n" +" } else {\n" +" set input_str \"\"\n" +" }\n" +"\n" +" if {[regexp {(.*),(.*)} $input_str match normal viewonly]} {\n" +" ;\n" +" } else {\n" +" set normal $input_str\n" +" set viewonly \"\"\n" +" }\n" +" set vl_bk 0\n" +" set vl_bm 0\n" +" set vl_bb 0\n" +" set vr_bk 0\n" +" set vr_bm 0\n" +" set vr_bb 0\n" +"\n" +" if {[regexp -nocase {K} $normal]} {\n" +" set vl_bk 1\n" +" }\n" +" if {[regexp -nocase {M} $normal]} {\n" +" set vl_bm 1\n" +" }\n" +" if {[regexp -nocase {B} $normal]} {\n" +" set vl_bb 1\n" +" }\n" +" if {[regexp -nocase {K} $viewonly]} {\n" +" set vr_bk 1\n" +" }\n" +" if {[regexp -nocase {M} $viewonly]} {\n" +" set vr_bm 1\n" +" }\n" +" if {[regexp -nocase {B} $viewonly]} {\n" +" set vr_bb 1\n" +" }\n" +"\n" +" pack $fl.l $fl.bk $fl.bm $fl.bb -side top -fill x\n" +" pack $fr.l $fr.bk $fr.bm $fr.bb -side top -fill x\n" +" pack $fl $fr -side left\n" +" update\n" +" update idletasks\n" +" $text_area window create end -window $w\n" +" $text_area see end\n" +" $text_area insert end \"\\n\"\n" +"# $text_area insert end \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\"\n" +"\n" +" set cleanup_window $w\n" +"}\n" +"\n" +"proc set_ca_str {w} {\n" +" global ca_bk ca_bm ca_bb ca_bk ca_di\n" +"\n" +" if {$ca_di} {\n" +" entry_insert \"disconnect\"\n" +" $w.bk configure -state disabled\n" +" $w.bm configure -state disabled\n" +" $w.bb configure -state disabled\n" +" return\n" +" }\n" +"\n" +" $w.bk configure -state normal\n" +" $w.bm configure -state normal\n" +" $w.bb configure -state normal\n" +"\n" +" set str \"\"\n" +" if {$ca_bk} {\n" +" append str \"K\"\n" +" }\n" +" if {$ca_bm} {\n" +" append str \"M\"\n" +" }\n" +" if {$ca_bb} {\n" +" append str \"B\"\n" +" }\n" +" entry_insert $str\n" +"}\n" +"\n" +"proc insert_client_action_window {input} {\n" +" global text_area cleanup_window\n" +" global ffont menu_var\n" +" global ca_bk ca_bm ca_bb ca_bk ca_di\n" +"\n" +" append_text \"\\nUse these checkboxes to set the input permissions \"\n" +" append_text \"for this client\\n-or- whether to disconnect it instead. \"\n" +" append_text \"Then press \\\"OK\\\" or \\\"Skip\\\".\\n\\n\"\n" +" set w \"$text_area.ca_f\"\n" +" catch {destroy $w}\n" +" frame $w -bd 1 -relief ridge -cursor {top_left_arrow}\n" +" checkbutton $w.di -pady 1 -font $ffont -anchor w -variable ca_di \\\n" +" -pady 1 -command \"set_ca_str $w\" -text \"Disconnect \" \n" +" checkbutton $w.bk -font $ffont -anchor w -variable ca_bk \\\n" +" -pady 1 -command \"set_ca_str $w\" -text \"Keystrokes\" \n" +" checkbutton $w.bm -font $ffont -anchor w -variable ca_bm \\\n" +" -pady 1 -command \"set_ca_str $w\" -text \"Mouse Motion\" \n" +" checkbutton $w.bb -font $ffont -anchor w -variable ca_bb \\\n" +" -pady 1 -command \"set_ca_str $w\" -text \"Button Clicks\"\n" +"\n" +" set ca_di 0\n" +" set ca_bk 0\n" +" set ca_bm 0\n" +" set ca_bb 0\n" +"\n" +" if {[regexp -nocase {K} $input]} {\n" +" set ca_bk 1\n" +" }\n" +" if {[regexp -nocase {M} $input]} {\n" +" set ca_bm 1\n" +" }\n" +" if {[regexp -nocase {B} $input]} {\n" +" set ca_bb 1\n" +" }\n" +"\n" +" pack $w.di $w.bk $w.bm $w.bb -side left\n" +" update\n" +" update idletasks\n" +" $text_area window create end -window $w\n" +" $text_area see end\n" +" $text_area insert end \"\\n\"\n" +"\n" +" set cleanup_window $w\n" +"}\n" +"\n" +"proc cleanup_text_window {} {\n" +" global cleanup_window\n" +" if {[info exists cleanup_window]} {\n" +" catch {destroy $cleanup_window}\n" +" }\n" +"}\n" +"\n" "# For updating a string variable. Also used for simple OK/Skip dialogs\n" "# with entry = 0.\n" "proc entry_dialog {item {entry 1}} {\n" @@ -1090,6 +1311,13 @@ " entry_disable box\n" " }\n" "\n" +" set clean_text_window 0;\n" +"\n" +" if {$item == \"input\"} {\n" +" insert_input_window\n" +" set clean_text_window 1\n" +" }\n" +"\n" " update\n" "\n" " # wait for user reply:\n" @@ -1107,6 +1335,11 @@ " entry_delete\n" " entry_disable\n" " menus_enable\n" +"\n" +" if {$clean_text_window} {\n" +" cleanup_text_window;\n" +" }\n" +"\n" " update\n" "\n" " if {! $entry} {\n" @@ -1211,7 +1444,8 @@ " } elseif {[regexp {:[0-9]\\.[0-9]} $expected]} {\n" " append_text \"\\t($msg)\\n\"\n" " return 1\n" -" } elseif {$item == \"connect\" || $item == \"disconnect\"} {\n" +" } elseif {$item == \"connect\" || $item == \"disconnect\"\n" +" || $item == \"client\" || $item == \"client_input\"} {\n" " append_text \"\\t($msg)\\n\"\n" " return 1\n" " } else {\n" @@ -1265,7 +1499,7 @@ " continue\n" " }\n" " if {[info exists menu_var($item)]} {\n" -" if [is_action $item] {\n" +" if {[is_action $item]} {\n" " set menu_var($item) \"\"\n" " } elseif {[value_is_bool $item]} {\n" " set menu_var($item) 0\n" @@ -1500,7 +1734,7 @@ " push_new_value $item $name $new 0\n" " set_connected no\n" " \n" -" } elseif [opt_match Q $item] {\n" +" } elseif {[opt_match Q $item]} {\n" " push_new_value $item $name $new 1\n" " } else {\n" " push_new_value $item $name $new 0\n" @@ -1666,7 +1900,7 @@ "proc set_x11_display {name} {\n" " global x11_display\n" " set x11_display \"x11vnc X display: $name\"\n" -" wm title . \"tkx11vnc - $name\"\n" +" set_name \"tkx11vnc - $name\"\n" "}\n" "proc set_vnc_display {name} {\n" " global vnc_display\n" @@ -1678,7 +1912,7 @@ "}\n" "proc no_x11_display {} {\n" " set_x11_display \"(*none*)\"\n" -" wm title . \"tkx11vnc\"\n" +" set_name \"tkx11vnc\"\n" "}\n" "proc no_vnc_display {} {\n" " set_vnc_display \"(*none*)\"\n" @@ -1719,20 +1953,99 @@ " }\n" "}\n" "\n" +"proc client_dialog {client} {\n" +" set cid \"\"\n" +" set host \"\"\n" +" set ip \"\"\n" +" global menu_var text_area cleanup_window item_bool\n" +"\n" +" append_text \"\\nClient info string: $client\\n\\n\"\n" +" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} \\\n" +" $client m0 m1 m2 m3 m4 m5 m6]} {\n" +" # id:ip:port:hostname:input:loginvo\n" +" set cid $m1\n" +" set ip $m2\n" +" set port $m3\n" +" set host $m4\n" +" regsub {\\..*$} $host \"\" host\n" +" set input $m5\n" +" set logvo $m6\n" +" append_text \"Host: $host, Port: $port, IP: $ip, Id: $cid\\n\"\n" +" append_text \" - originally logged in as: \"\n" +" if {$logvo == \"1\" } {\n" +" append_text \"View-Only Client\\n\"\n" +" } else {\n" +" append_text \"Normal Client\\n\"\n" +" }\n" +" append_text \" - currently allowed input: \"\n" +" set sk 0\n" +" set sm 0\n" +" set sb 0\n" +" if {[regexp -nocase {K} $input]} {\n" +" append_text \"Keystroke\"\n" +" set sk 1\n" +" }\n" +" if {[regexp -nocase {M} $input]} {\n" +" if {$sk} {\n" +" append_text \", \"\n" +" }\n" +" append_text \"Mouse-Motion\"\n" +" set sm 1\n" +" }\n" +" if {[regexp -nocase {B} $input]} {\n" +" if {$sk || $sm} {\n" +" append_text \", \"\n" +" }\n" +" append_text \"Button-Click\"\n" +" set sb 1\n" +" }\n" +" if {! $sk && ! $sm && ! $sb} {\n" +" append_text \"None\"\n" +" }\n" +" append_text \"\\n\"\n" +" }\n" +" if {$cid == \"\"} {\n" +" append_text \"Invalid client info string: $client\\n\"\n" +" return\n" +" }\n" +"\n" +" regsub -all {_} $input \"\" input\n" +" set menu_var(client) \"$input\"\n" +" set item_bool(client) 0\n" +"\n" +" insert_client_action_window $input\n" +" set rc [entry_dialog client 1]\n" +"\n" +" cleanup_text_window\n" +"\n" +" set val $menu_var(client)\n" +" #puts \"rc: $rc val: $val\"\n" +"\n" +" if {! $rc} {\n" +" return;\n" +" } elseif {[regexp -nocase {(disconnect|close)} $val]} {\n" +" disconnect_dialog $client\n" +" } else {\n" +" regsub -all -nocase {[^KMB]} $val \"\" \n" +" set item_bool(client_input) 0\n" +" push_new_value \"client_input\" \"client_input\" \"$cid:$val\" 0\n" +" }\n" +"}\n" +"\n" "proc disconnect_dialog {client} {\n" " set cid \"\"\n" " set host \"\"\n" " set msg \"\\n\"\n" " append msg \"*** Client info string: $client\\n\"\n" -" if {[regexp {^(.*):(.*)/(.*)-(.*)$} $client m0 m1 m2 m3 m4]} {\n" -" if {$m4 == \"ro\"} {\n" -" set view \"(viewonly)\"\n" -" } else {\n" -" set view \"(interactive)\"\n" -" }\n" -" set host $m1\n" -" set cid $m3\n" -" append msg \"*** Host: $m1, Port: $m2 Id: $m3 $view\\n\"\n" +" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} $client m0 m1 m2 m3 m4 m5 m6]} {\n" +" set cid $m1\n" +" set ip $m2\n" +" set port $m3\n" +" set host $m4\n" +" regsub {\\..*$} $host \"\" host\n" +" set input $m5\n" +" set logvo $m6\n" +" append_text \"Host: $host, Port: $port, IP: $ip, Id: $cid\\n\"\n" " }\n" " if {$cid == \"\"} {\n" " append_text \"Invalid client info string: $client\\n\"\n" @@ -1740,7 +2053,7 @@ " }\n" " append msg \"*** To *DISCONNECT* this client press \\\"OK\\\", otherwise press \\\"Skip\\\"\\n\"\n" " bell\n" -" if [warning_dialog $msg \"current\"] {\n" +" if {[warning_dialog $msg \"current\"]} {\n" " push_new_value \"disconnect\" \"disconnect\" $cid 1\n" " } else {\n" " append_text \"disconnect cancelled.\\n\"\n" @@ -1762,7 +2075,7 @@ " continue\n" " }\n" " set name [$casc entrycget $i -label]\n" -" if {[regexp {^#} $name]} {\n" +" if {[regexp {^num-clients} $name]} {\n" " continue\n" " }\n" " if {[regexp {^refresh-list} $name]} {\n" @@ -1789,12 +2102,20 @@ " $subm add separator\n" " set count 0\n" " foreach client [split $list \",\"] {\n" -" regsub {:[0-9][0-9]*/} $client {/} lab\n" -" $subm add command -label \"$client\" \\\n" -" -command \"disconnect_dialog $client\"\n" +" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$} \\\n" +" $client m0 m1 m2 m3 m4 m5 m6]} {\n" +" # id:ip:port:hostname:input:loginvo\n" +" set host $m4\n" +" regsub {\\..*$} $host \"\" host\n" +" set clabel \"$host $m1\"\n" +" } else {\n" +" regsub {:.*$} $client \"\" clabel\n" +" }\n" +" $subm add command -label \"$clabel\" \\\n" +" -command \"client_dialog $client\"\n" " incr count\n" " }\n" -" $subm entryconfigure 0 -label \"#clients: $count\"\n" +" $subm entryconfigure 0 -label \"num-clients: $count\"\n" "}\n" "\n" "proc set_widgets {} {\n" @@ -2082,7 +2403,7 @@ " pack $df -side top -fill x\n" "\n" " # text area\n" -" text .text -height 11 -relief ridge -font $ffont\n" +" text .text -height 12 -relief ridge -font $ffont\n" " set text_area .text\n" " pack .text -side top -fill both -expand 1\n" "\n" @@ -2628,7 +2949,7 @@ "\n" "set_template\n" "\n" -"wm title . \"tkx11vnc\"\n" +"set_name \"tkx11vnc\"\n" "make_widgets;\n" "\n" "menu_bindings;\n" diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index 66bb57d..fcafe51 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -2,7 +2,7 @@ .TH X11VNC "1" "February 2005" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.7.1pre, lastmod: 2005-02-08 + version: 0.7.1pre, lastmod: 2005-02-10 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -250,6 +250,21 @@ out with the "#" character in the usual way. .IP Same as \fB-allow\fR 127.0.0.1 .PP +\fB-input\fR \fIstring\fR +.IP +Fine tuning of allowed user input. If \fIstring\fR does +not contain a comma "," the tuning applies only to +normal clients. Otherwise the part before "," is +for normal clients and the part after for view-only +clients. "K" is for Keystroke input, "M" for +Mouse-motion input, and "B" for Button-click input. +Their presence in the string enables that type of input. +E.g. "\fB-input\fR \fIM\fR" means normal users can only move +the mouse and "\fB-input\fR \fIKMB,M\fR" lets normal users do +anything and enables view-only users to move the mouse. +This option is ignored when a global \fB-viewonly\fR is in +effect (all input is discarded). +.PP \fB-viewpasswd\fR \fIstring\fR .IP Supply a 2nd password for view-only logins. The \fB-passwd\fR @@ -394,7 +409,8 @@ the switch succeeds (i.e. a user logs in). To make it switch immediately regardless if the display can be reopened or not prefix the username with the + character. E.g. "\fB-users\fR \fI+bob\fR" or "\fB-users\fR \fI+nobody\fR". -The latter is probably the only use of this option +The latter (i.e. switching immediately to user +"nobody") is probably the only use of this option that increases security. To switch to a user *before* connections to the display are made or any files opened use the "=" character: "\fB-users\fR \fI=username\fR". @@ -402,7 +418,7 @@ use the "=" character: "\fB-users\fR \fI=username\fR". The special user "guess" means to examine the utmpx database looking for a user attached to the display number and try him/her. To limit the list of guesses, -use: "\fB-users\fR \fIguess=bob,fred\fR". Be especially careful +use: "\fB-users\fR \fIguess=bob,betty\fR". Be especially careful using this mode. .PP \fB-noshm\fR @@ -1143,6 +1159,13 @@ localhost enable \fB-localhost\fR mode .IP nolocalhost disable \fB-localhost\fR mode .IP +input:str set \fB-input\fR to "str", empty to disable. +.IP +client_input:str set the K, M, B \fB-input\fR on a per-client + basis. select which client as for + disconnect, e.g. client_input:host:MB + or client_input:0x2:K +.IP accept:cmd set \fB-accept\fR "cmd" (empty to disable). .IP gone:cmd set \fB-gone\fR "cmd" (empty to disable). @@ -1424,13 +1447,13 @@ nosolid blackout xinerama noxinerama xrandr noxrandr xrandr_mode padgeom quiet q noquiet modtweak nomodtweak xkb noxkb skip_keycodes add_keysyms noadd_keysyms clear_mods noclear_mods clear_keys noclear_keys -remap repeat norepeat fb nofb bell nobell sel nosel -primary noprimary cursorshape nocursorshape cursorpos -nocursorpos cursor show_cursor noshow_cursor -nocursor xfixes noxfixes alphacut alphafrac -alpharemove noalpharemove alphablend noalphablend -xwarp xwarppointer noxwarp noxwarppointer buttonmap -dragging nodragging pointer_mode pm input_skip speeds +remap repeat norepeat fb nofb bell nobell sel +nosel primary noprimary cursorshape nocursorshape +cursorpos nocursorpos cursor show_cursor noshow_cursor +nocursor xfixes noxfixes alphacut alphafrac alpharemove +noalpharemove alphablend noalphablend xwarp xwarppointer +noxwarp noxwarppointer buttonmap dragging nodragging +pointer_mode pm input_skip input client_input speeds debug_pointer dp nodebug_pointer nodp debug_keyboard dk nodebug_keyboard nodk deferupdate defer wait rfbwait nap nonap sb screen_blank fs gaps grow fuzz snapfb @@ -1482,10 +1505,13 @@ x11vnc. Normally access to the X display is protected. Note that if they can modify VNC_CONNECT, they could also run their own x11vnc and have complete control of the desktop. If the "\fB-connect\fR \fI/path/to/file\fR" -channel is being used, obviously anyone who can write -to /path/to/file can remotely control x11vnc. So be -sure to protect the X display and that file's write -permissions. +channel is being used, obviously anyone who can +write to /path/to/file can remotely control x11vnc. +So be sure to protect the X display and that file's +write permissions. +.IP +To disable the VNC_CONNECT property channel completely +use \fB-novncconnect.\fR .PP \fB-unsafe\fR .IP diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index af15260..89bef9d 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -290,7 +290,7 @@ static int xdamage_base_event_type; #endif /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.1pre lastmod: 2005-02-08"; +char lastmod[] = "0.7.1pre lastmod: 2005-02-10"; /* X display info */ @@ -346,13 +346,19 @@ unsigned short main_red_shift, main_green_shift, main_blue_shift; /* we now have a struct with client specific data: */ #define RATE_SAMPLES 5 +#define CILEN 10 typedef struct _ClientData { - int had_cursor_shape_updates; - int had_cursor_pos_updates; int uid; + char *hostname; int client_port; int server_port; char *server_ip; + char input[CILEN]; + int login_viewonly; + + int had_cursor_shape_updates; + int had_cursor_pos_updates; + double timer; double send_cmp_rate; double send_raw_rate; @@ -445,6 +451,7 @@ double dtime(double *); void initialize_blackouts(char *); void initialize_blackouts_and_xinerama(void); void initialize_keyboard_and_pointer(void); +void initialize_allowed_input(void); void initialize_modtweak(void); void initialize_pointer_map(char *); void initialize_cursors_mode(void); @@ -519,6 +526,8 @@ void check_xevents(void); char *this_host(void); void set_vnc_desktop_name(void); +char *short_kmb(char *); + int get_cmp_rate(void); int get_raw_rate(void); int get_read_rate(void); @@ -574,6 +583,9 @@ char *allow_once = NULL; /* one time -allow */ char *accept_cmd = NULL; /* for -accept */ char *gone_cmd = NULL; /* for -gone */ int view_only = 0; /* clients can only watch. */ +char *allowed_input_view_only = NULL; +char *allowed_input_normal = NULL; +char *allowed_input_str = NULL; char *viewonly_passwd = NULL; /* view only passwd. */ int inetd = 0; /* spawned from inetd(1) */ int connect_once = 1; /* disconnect after first connection session. */ @@ -776,6 +788,18 @@ void lowercase(char *str) { } } +void uppercase(char *str) { + char *p; + if (str == NULL) { + return; + } + p = str; + while (*p != '\0') { + *p = toupper(*p); + p++; + } +} + char *lblanks(char *str) { char *p = str; while (*p) { @@ -1325,6 +1349,31 @@ char *host2ip(char *host) { return str; } +char *ip2host(char *ip) { + char *str; +#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H + struct hostent *hp; +#ifndef in_addr_t +typedef unsigned int in_addr_t; +#endif + in_addr_t iaddr; + + iaddr = inet_addr(ip); + if (iaddr == INADDR_NONE) { + return strdup("unknown"); + } + + hp = gethostbyaddr((char *)&iaddr, sizeof(in_addr_t), AF_INET); + if (!hp) { + return strdup("unknown"); + } + str = strdup(hp->h_name); +#else + str = strdup("unknown"); +#endif + return str; +} + int dotted_ip(char *host) { char *p = host; while (*p != '\0') { @@ -1337,20 +1386,34 @@ int dotted_ip(char *host) { return 1; } -int get_remote_port(int sock) { +int get_port(int sock, int remote) { struct sockaddr_in saddr; int saddr_len, saddr_port; saddr_len = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); saddr_port = -1; - if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); + if (remote) { + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } + } else { + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } } return saddr_port; } -char *get_remote_host(int sock) { +int get_remote_port(int sock) { + return get_port(sock, 1); +} + +int get_local_port(int sock) { + return get_port(sock, 0); +} + +char *get_host(int sock, int remote) { struct sockaddr_in saddr; int saddr_len, saddr_port; char *saddr_ip_str = NULL; @@ -1358,47 +1421,29 @@ char *get_remote_host(int sock) { saddr_len = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); saddr_port = -1; - if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { #if LIBVNCSERVER_HAVE_NETINET_IN_H - saddr_ip_str = inet_ntoa(saddr.sin_addr); -#endif + if (remote) { + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_ip_str = inet_ntoa(saddr.sin_addr); + } + } else { + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_ip_str = inet_ntoa(saddr.sin_addr); + } } +#endif if (! saddr_ip_str) { - saddr_ip_str = strdup("unknown"); + saddr_ip_str = "unknown"; } - return saddr_ip_str; + return strdup(saddr_ip_str); } -int get_local_port(int sock) { - struct sockaddr_in saddr; - int saddr_len, saddr_port; - - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; - if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); - } - return saddr_port; +char *get_remote_host(int sock) { + return get_host(sock, 1); } char *get_local_host(int sock) { - struct sockaddr_in saddr; - int saddr_len, saddr_port; - char *saddr_ip_str = NULL; - - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; - if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { -#if LIBVNCSERVER_HAVE_NETINET_IN_H - saddr_ip_str = inet_ntoa(saddr.sin_addr); -#endif - } - if (! saddr_ip_str) { - saddr_ip_str = strdup("unknown"); - } - return saddr_ip_str; + return get_host(sock, 0); } /* @@ -2072,10 +2117,13 @@ char *list_clients(void) { rfbReleaseClientIterator(iter); /* - * each client: 123.123.123.123:60000/0x11111111-rw, = 36 bytes - * so count+1 * 100 must cover it. + * each client: + * :::::, + * 8+1+16+1+5+1+256+1+5+1+1+1 + * 123.123.123.123:60000/0x11111111-rw, = 297 bytes + * so count+1 * 400 must cover it. */ - list = (char *) malloc((count+1)*100); + list = (char *) malloc((count+1)*400); list[0] = '\0'; @@ -2085,14 +2133,18 @@ char *list_clients(void) { if (*list != '\0') { strcat(list, ","); } + sprintf(tmp, "0x%x:", cd->uid); + strcat(list, tmp); strcat(list, cl->host); - sprintf(tmp, ":%d/0x%x", get_remote_port(cl->sock), cd->uid); + strcat(list, ":"); + sprintf(tmp, "%d:", cd->client_port); + strcat(list, tmp); + strcat(list, cd->hostname); + strcat(list, ":"); + strcat(list, cd->input); + strcat(list, ":"); + sprintf(tmp, "%d", cd->login_viewonly); strcat(list, tmp); - if (cl->viewOnly) { - strcat(list, "-ro"); - } else { - strcat(list, "-rw"); - } } rfbReleaseClientIterator(iter); return list; @@ -2114,23 +2166,18 @@ void close_all_clients(void) { rfbReleaseClientIterator(iter); } -void close_clients(char *str) { +rfbClientPtr *client_match(char *str) { rfbClientIteratorPtr iter; - rfbClientPtr cl; - int host_warn = 0, hex_warn = 0; - - if (!strcmp(str, "all") || !strcmp(str, "*")) { - close_all_clients(); - return; - } - - if (! screen) { - return; - } + rfbClientPtr cl, *cl_list; + int i, n, host_warn = 0, hex_warn = 0; + n = client_count + 10; + cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr)); + + i = 0; iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { - if (strstr(str, "0x")) { + if (strstr(str, "0x") == str) { int id; ClientData *cd = (ClientData *) cl->clientData; if (sscanf(str, "0x%x", &id) != 1) { @@ -2141,8 +2188,7 @@ void close_clients(char *str) { continue; } if ( cd->uid == id) { - rfbCloseClient(cl); - rfbClientConnectionGone(cl); + cl_list[i++] = cl; } } else { char *rstr = str; @@ -2159,15 +2205,77 @@ void close_clients(char *str) { rfbLog("lookup: %s -> %s\n", str, rstr); } if (!strcmp(rstr, cl->host)) { - rfbCloseClient(cl); - rfbClientConnectionGone(cl); + cl_list[i++] = cl; } if (rstr != str) { free(rstr); } } + if (i >= n - 1) { + break; + } } rfbReleaseClientIterator(iter); + + cl_list[i] = NULL; + + return cl_list; +} + +void close_clients(char *str) { + rfbClientPtr *cl_list, *cp; + + if (!strcmp(str, "all") || !strcmp(str, "*")) { + close_all_clients(); + return; + } + + if (! screen) { + return; + } + + cl_list = client_match(str); + + cp = cl_list; + while (*cp) { + rfbCloseClient(*cp); + rfbClientConnectionGone(*cp); + cp++; + } + free(cl_list); +} + +void set_client_input(char *str) { + rfbClientPtr *cl_list, *cp; + char *p, *val; + + /* str is "match:value" */ + + if (! screen) { + return; + } + + p = strchr(str, ':'); + if (! p) { + return; + } + *p = '\0'; + p++; + val = short_kmb(p); + + cl_list = client_match(str); + + cp = cl_list; + while (*cp) { + ClientData *cd = (ClientData *) (*cp)->clientData; + cd->input[0] = '\0'; + strcat(cd->input, "_"); + strcat(cd->input, val); + cp++; + } + + free(val); + free(cl_list); } /* @@ -2221,7 +2329,9 @@ static int run_user_command(char *cmd, rfbClientPtr client, char *mode) { if (cd && cd->server_ip) { set_env("RFB_SERVER_IP", cd->server_ip); } else { - set_env("RFB_SERVER_IP", get_local_host(client->sock)); + char *sip = get_local_host(client->sock); + set_env("RFB_SERVER_IP", sip); + free(sip); } if (cd && cd->server_port > 0) { @@ -2287,8 +2397,13 @@ static void client_gone(rfbClientPtr client) { if (client->clientData) { ClientData *cd = (ClientData *) client->clientData; - if (cd && cd->server_ip) { - free(cd->server_ip); + if (cd) { + if (cd->server_ip) { + free(cd->server_ip); + } + if (cd->hostname) { + free(cd->hostname); + } } free(client->clientData); } @@ -3288,9 +3403,6 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { return(RFB_CLIENT_REFUSE); } - if (view_only) { - client->viewOnly = TRUE; - } client->clientData = (void *) calloc(sizeof(ClientData), 1); cd = (ClientData *) client->clientData; @@ -3298,7 +3410,11 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { cd->client_port = get_remote_port(client->sock); cd->server_port = get_local_port(client->sock); - cd->server_ip = strdup(get_remote_host(client->sock)); + cd->server_ip = get_local_host(client->sock); + cd->hostname = ip2host(client->host); + + cd->input[0] = '-'; + cd->login_viewonly = -1; client->clientGoneHook = client_gone; client_count++; @@ -3327,6 +3443,54 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { return(RFB_CLIENT_ACCEPT); } +void check_new_clients(void) { + static int last_count = 0; + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + if (client_count == last_count) { + return; + } + + if (! all_clients_initialized()) { + return; + } + + last_count = client_count; + if (! client_count) { + return; + } + if (! screen) { + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->login_viewonly < 0) { + /* this is a general trigger to initialize things */ + if (cl->viewOnly) { + cd->login_viewonly = 1; + if (allowed_input_view_only) { + cl->viewOnly = FALSE; + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_view_only, CILEN); + } + } else { + cd->login_viewonly = 0; + if (allowed_input_normal) { + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_normal, CILEN); + } + } + } + } + rfbReleaseClientIterator(iter); +} + /* -- keyboard.c -- */ /* * Routine to retreive current state keyboard. 1 means down, 0 up. @@ -4494,6 +4658,99 @@ if (sym >> 8 == 0) { \ } #endif +char *short_kmb(char *str) { + int i, saw_k = 0, saw_m = 0, saw_b = 0, n = 10; + char *p, tmp[10]; + + for (i=0; iclientData; + + if (cd->input[0] == '=') { + ; /* custom setting */ + } else if (cd->login_viewonly) { + if (*allowed_input_view_only != '\0') { + cl->viewOnly = FALSE; + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_view_only, CILEN); + } else { + cl->viewOnly = TRUE; + } + } else { + if (allowed_input_normal) { + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_normal, CILEN); + } + } + } + rfbReleaseClientIterator(iter); + } +} + void initialize_keyboard_and_pointer(void) { if (use_modifier_tweak) { initialize_modtweak(); @@ -4664,13 +4921,6 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, down ? "down" : "up", (int) keysym); } - if (view_only) { - return; - } - if (client && client->viewOnly) { - return; - } - #define ADJUSTMOD(sym, state) \ if (keysym == sym) { \ if (down) { \ @@ -4715,6 +4965,54 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, } } +typedef struct allowed_input { + int keystroke; + int motion; + int button; +} allowed_input_t; + +void get_allowed_input(rfbClientPtr client, allowed_input_t *input) { + ClientData *cd; + char *str; + + input->keystroke = 0; + input->motion = 0; + input->button = 0; + + if (! client) { + return; + } + + cd = (ClientData *) client->clientData; + + if (cd->input[0] != '-') { + str = cd->input; + } else if (client->viewOnly) { + if (allowed_input_view_only) { + str = allowed_input_view_only; + } else { + str = ""; + } + } else { + if (allowed_input_normal) { + str = allowed_input_normal; + } else { + str = "KMB"; + } + } + + while (*str) { + if (*str == 'K') { + input->keystroke = 1; + } else if (*str == 'M') { + input->motion = 1; + } else if (*str == 'B') { + input->button = 1; + } + str++; + } +} + /* * key event handler. See the above functions for contortions for * running under -modtweak. @@ -4724,6 +5022,7 @@ static rfbClientPtr last_keyboard_client = NULL; void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { KeyCode k; int isbutton = 0; + allowed_input_t input; if (debug_keyboard) { char *str; @@ -4737,7 +5036,8 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { if (view_only) { return; } - if (client && client->viewOnly) { + get_allowed_input(client, &input); + if (! input.keystroke) { return; } @@ -5074,10 +5374,9 @@ void initialize_pointer_map(char *pointer_remap) { } /* - * Send a pointer event to the X server. + * Send a pointer position event to the X server. */ -static void update_x11_pointer(int mask, int x, int y) { - int i, mb; +static void update_x11_pointer_position(int x, int y) { X_LOCK; if (use_xwarppointer) { @@ -5100,6 +5399,25 @@ static void update_x11_pointer(int mask, int x, int y) { last_event = last_input = time(0); + if (nofb) { + /* + * nofb is for, e.g. Win2VNC, where fastest pointer + * updates are desired. + */ + X_LOCK; + XFlush(dpy); + X_UNLOCK; + } +} + +/* + * Send a pointer position event to the X server. + */ +static void update_x11_pointer_mask(int mask) { + int i, mb; + + last_event = last_input = time(0); + X_LOCK; /* look for buttons that have be clicked or released: */ for (i=0; i < MAX_BUTTONS; i++) { @@ -5165,14 +5483,6 @@ static void update_x11_pointer(int mask, int x, int y) { } } - if (nofb) { - /* - * nofb is for, e.g. Win2VNC, where fastest pointer - * updates are desired. - */ - XFlush(dpy); - } - X_UNLOCK; /* @@ -5185,9 +5495,10 @@ static void update_x11_pointer(int mask, int x, int y) { /* * Actual callback from libvncserver when it gets a pointer event. * This may queue pointer events rather than sending them immediately - * to the X server. (see update_x11_pointer()) + * to the X server. (see update_x11_pointer*()) */ void pointer(int mask, int x, int y, rfbClientPtr client) { + allowed_input_t input; if (debug_pointer && mask >= 0) { static int show_motion = -1; @@ -5207,7 +5518,8 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { if (view_only) { return; } - if (client && client->viewOnly) { + get_allowed_input(client, &input); + if (! input.motion && ! input.button) { return; } if (scaling) { @@ -5277,6 +5589,13 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { ev[i][0] = mask; ev[i][1] = x; ev[i][2] = y; + if (! input.button) { + ev[i][0] = -1; + } + if (! input.motion) { + ev[i][1] = -1; + ev[i][2] = -1; + } UNLOCK(pointerMutex); if (debug_pointer) { rfbLog("pointer(): deferring event " @@ -5291,7 +5610,12 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { if (debug_pointer) { rfbLog("pointer(): sending event %d\n", i+1); } - update_x11_pointer(ev[i][0], ev[i][1], ev[i][2]); + if (ev[i][1] >= 0) { + update_x11_pointer_position(ev[i][1], ev[i][2]); + } + if (ev[i][0] >= 0) { + update_x11_pointer_mask(ev[i][0]); + } } if (nevents && dt > maxwait) { X_LOCK; @@ -5313,7 +5637,12 @@ void pointer(int mask, int x, int y, rfbClientPtr client) { } /* update the X display with the event: */ - update_x11_pointer(mask, x, y); + if (input.motion) { + update_x11_pointer_position(x, y); + } + if (input.button) { + update_x11_pointer_mask(mask); + } } /* -- xkb_bell.c -- */ @@ -6098,16 +6427,22 @@ void check_xevents(void) { * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). */ void xcut_receive(char *text, int len, rfbClientPtr cl) { + allowed_input_t input; if (!watch_selection) { return; } - if (cl && cl->viewOnly) { + if (view_only) { return; } if (text == NULL || len == 0) { return; } + get_allowed_input(cl, &input); + if (!input.keystroke && !input.motion && !input.button) { + /* maybe someday KMBC for cut text... */ + return; + } X_LOCK; @@ -8013,6 +8348,34 @@ char *process_remote_cmd(char *cmd, int stringonly) { rfbLog("process_remote_cmd: setting input_skip %d\n", is); ui_skip = is; + } else if (strstr(p, "input") == p) { + int doit = 1; + COLON_CHECK("input:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(allowed_input_str)); + goto qry; + } + p += strlen("input:"); + if (allowed_input_str && !strcmp(p, allowed_input_str)) { + doit = 0; + } + rfbLog("process_remote_cmd: setting input %s\n", p); + if (allowed_input_str) free(allowed_input_str); + if (*p == '\0') { + allowed_input_str = NULL; + } else { + allowed_input_str = strdup(p); + } + if (doit) { + initialize_allowed_input(); + } + } else if (strstr(p, "client_input") == p) { + NOTAPP + COLON_CHECK("client_input:") + p += strlen("client_input:"); + set_client_input(p); + } else if (strstr(p, "speeds") == p) { COLON_CHECK("speeds:") if (query) { @@ -15328,6 +15691,7 @@ static void watch_loop(void) { copy_screen(); } + check_new_clients(); check_xevents(); check_connect_inputs(); check_padded_fb(); @@ -15366,10 +15730,7 @@ static void watch_loop(void) { } if (watch_bell) { - /* - * check for any bell events. - * n.b. assumes -nofb folks do not want bell... - */ + /* n.b. assumes -nofb folks do not want bell... */ check_bell_event(); } @@ -15579,6 +15940,19 @@ static void print_help(int mode) { " each time a new client connects. Lines can be commented\n" " out with the \"#\" character in the usual way.\n" "-localhost Same as -allow 127.0.0.1\n" +"\n" +"-input string Fine tuning of allowed user input. If \"string\" does\n" +" not contain a comma \",\" the tuning applies only to\n" +" normal clients. Otherwise the part before \",\" is\n" +" for normal clients and the part after for view-only\n" +" clients. \"K\" is for Keystroke input, \"M\" for\n" +" Mouse-motion input, and \"B\" for Button-click input.\n" +" Their presence in the string enables that type of input.\n" +" E.g. \"-input M\" means normal users can only move\n" +" the mouse and \"-input KMB,M\" lets normal users do\n" +" anything and enables view-only users to move the mouse.\n" +" This option is ignored when a global -viewonly is in\n" +" effect (all input is discarded).\n" "-viewpasswd string Supply a 2nd password for view-only logins. The -passwd\n" " (full-access) password must also be supplied.\n" "-passwdfile filename Specify libvncserver -passwd via the first line of\n" @@ -15692,7 +16066,8 @@ static void print_help(int mode) { " it switch immediately regardless if the display can\n" " be reopened or not prefix the username with the +\n" " character. E.g. \"-users +bob\" or \"-users +nobody\".\n" -" The latter is probably the only use of this option\n" +" The latter (i.e. switching immediately to user\n" +" \"nobody\") is probably the only use of this option\n" " that increases security. To switch to a user *before*\n" " connections to the display are made or any files opened\n" " use the \"=\" character: \"-users =username\".\n" @@ -15700,7 +16075,7 @@ static void print_help(int mode) { " The special user \"guess\" means to examine the utmpx\n" " database looking for a user attached to the display\n" " number and try him/her. To limit the list of guesses,\n" -" use: \"-users guess=bob,fred\". Be especially careful\n" +" use: \"-users guess=bob,betty\". Be especially careful\n" " using this mode.\n" " \n" "-noshm Do not use the MIT-SHM extension for the polling.\n" @@ -16258,6 +16633,11 @@ static void print_help(int mode) { " use \"-host\" to delete a single host\n" " localhost enable -localhost mode\n" " nolocalhost disable -localhost mode\n" +" input:str set -input to \"str\", empty to disable.\n" +" client_input:str set the K, M, B -input on a per-client\n" +" basis. select which client as for\n" +" disconnect, e.g. client_input:host:MB\n" +" or client_input:0x2:K\n" /* ext. cmd. */ " accept:cmd set -accept \"cmd\" (empty to disable).\n" " gone:cmd set -gone \"cmd\" (empty to disable).\n" @@ -16429,13 +16809,13 @@ static void print_help(int mode) { " xrandr_mode padgeom quiet q noquiet modtweak nomodtweak\n" " xkb noxkb skip_keycodes add_keysyms noadd_keysyms\n" " clear_mods noclear_mods clear_keys noclear_keys\n" -" remap repeat norepeat fb nofb bell nobell sel nosel\n" -" primary noprimary cursorshape nocursorshape cursorpos\n" -" nocursorpos cursor show_cursor noshow_cursor\n" -" nocursor xfixes noxfixes alphacut alphafrac\n" -" alpharemove noalpharemove alphablend noalphablend\n" -" xwarp xwarppointer noxwarp noxwarppointer buttonmap\n" -" dragging nodragging pointer_mode pm input_skip speeds\n" +" remap repeat norepeat fb nofb bell nobell sel\n" +" nosel primary noprimary cursorshape nocursorshape\n" +" cursorpos nocursorpos cursor show_cursor noshow_cursor\n" +" nocursor xfixes noxfixes alphacut alphafrac alpharemove\n" +" noalpharemove alphablend noalphablend xwarp xwarppointer\n" +" noxwarp noxwarppointer buttonmap dragging nodragging\n" +" pointer_mode pm input_skip input client_input speeds\n" " debug_pointer dp nodebug_pointer nodp debug_keyboard dk\n" " nodebug_keyboard nodk deferupdate defer wait rfbwait\n" " nap nonap sb screen_blank fs gaps grow fuzz snapfb\n" @@ -16483,10 +16863,13 @@ static void print_help(int mode) { " Note that if they can modify VNC_CONNECT, they could\n" " also run their own x11vnc and have complete control\n" " of the desktop. If the \"-connect /path/to/file\"\n" -" channel is being used, obviously anyone who can write\n" -" to /path/to/file can remotely control x11vnc. So be\n" -" sure to protect the X display and that file's write\n" -" permissions.\n" +" channel is being used, obviously anyone who can\n" +" write to /path/to/file can remotely control x11vnc.\n" +" So be sure to protect the X display and that file's\n" +" write permissions.\n" +"\n" +" To disable the VNC_CONNECT property channel completely\n" +" use -novncconnect.\n" "\n" "-unsafe If x11vnc is running as root (e.g. inetd or Xsetup for\n" " a display manager) a few remote commands are disabled\n" @@ -17044,6 +17427,9 @@ int main(int argc, char* argv[]) { allow_list = strdup(argv[++i]); } else if (!strcmp(arg, "-localhost")) { allow_list = strdup("127.0.0.1"); + } else if (!strcmp(arg, "-input")) { + CHECK_ARGC + allowed_input_str = strdup(argv[++i]); } else if (!strcmp(arg, "-viewpasswd")) { vpw_loc = i; CHECK_ARGC @@ -17987,6 +18373,8 @@ int main(int argc, char* argv[]) { initialize_keyboard_and_pointer(); + initialize_allowed_input(); + if (! inetd) { if (! screen->port || screen->listenSock < 0) { rfbLog("Error: could not obtain listening port.\n");