autoproxy.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package require http;
00026 package require uri;
00027 package require base64;
00028
00029 namespace ::autoproxy {
00030 variable rcsid {$Id: autoproxy.tcl,v 1.9 2007/03/12 22:53:25 patthoyts Exp $}
00031 variable version 1.4
00032 variable options
00033
00034 if {! [info exists options]} {
00035 array options = {
00036 proxy_host ""
00037 proxy_port 80
00038 no_proxy {}
00039 basic {}
00040 authProc {}
00041 }
00042 }
00043
00044 variable winregkey
00045 winregkey = [join {
00046 HKEY_CURRENT_USER
00047 Software Microsoft Windows
00048 CurrentVersion "Internet Settings"
00049 } \\]
00050 }
00051
00052
00053
00054
00055
00056 ret ::autoproxy::cget (type option) {
00057 variable options
00058 switch -glob -- $option] {
00059 -host -
00060 -proxy_h* { set options(proxy_host) }
00061 -port -
00062 -proxy_p* { set options(proxy_port) }
00063 -no* { set options(no_proxy) }
00064 -basic { set options(basic) }
00065 -authProc { set options(authProc) }
00066 default {
00067 set err [join [lsort [array names options]] ", -"]
00068 return -code error "bad option \"$option\":\
00069 must be one of -$err"
00070 }
00071 }
00072 }
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 ret ::autoproxy::configure (type args) {
00086 variable options
00087
00088 if {[llength $args] == 0} {
00089 foreach {opt value} [array get options] {
00090 lappend r -$opt $value
00091 }
00092 return $r
00093 }
00094
00095 while {[string match "-*" [set option [lindex $args 0]]]} {
00096 switch -glob -- $option {
00097 -host -
00098 -proxy_h* { set options(proxy_host) [Pop args 1]}
00099 -port -
00100 -proxy_p* { set options(proxy_port) [Pop args 1]}
00101 -no* { set options(no_proxy) [Pop args 1] }
00102 -basic { Pop args; configure:basic $args ; break }
00103 -authProc { set options(authProc) [Pop args] }
00104 -- { Pop args; break }
00105 default {
00106 set opts [join [lsort [array names options]] ", -"]
00107 return -code error "bad option \"$option\":\
00108 must be one of -$opts"
00109 }
00110 }
00111 Pop args
00112 }
00113 }
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 ret ::autoproxy::init (optional httpproxy ={) {no_proxy {}}} {
00127 global tcl_platform
00128 global env
00129 variable winregkey
00130 variable options
00131
00132
00133 if {[string length $httpproxy] > 0} {
00134
00135
00136
00137 } elseif {[info exists env(http_proxy)]} {
00138 httpproxy = $env(http_proxy)
00139 if {[info exists env(no_proxy)]} {
00140 no = _proxy $env(no_proxy)
00141 }
00142 } else {
00143 if {$tcl_platform(platform) == "windows"} {
00144 package require registry 1.0
00145 array reg = {ProxyEnable 0 ProxyServer "" ProxyOverride {}}
00146 catch {
00147
00148 switch -exact -- [registry type $winregkey "ProxyEnable"] {
00149 dword {
00150 reg = (ProxyEnable) [registry get $winregkey "ProxyEnable"]
00151 }
00152 binary {
00153 v = [registry get $winregkey "ProxyEnable"]
00154 binary scan $v i reg(ProxyEnable)
00155 }
00156 default {
00157 return -code error "unexpected type found for\
00158 ProxyEnable registry item"
00159 }
00160 }
00161 reg = (ProxyServer) [GetWin32Proxy http]
00162 reg = (ProxyOverride) [registry get $winregkey "ProxyOverride"]
00163 }
00164 if {![string is bool $reg(ProxyEnable)]} {
00165 reg = (ProxyEnable) 0
00166 }
00167 if {$reg(ProxyEnable)} {
00168 httpproxy = $reg(ProxyServer)
00169 no = _proxy $reg(ProxyOverride)
00170 }
00171 }
00172 }
00173
00174
00175 if {[string length $httpproxy] > 0} {
00176
00177 if {![regexp {\w:
00178 httpproxy = "http:
00179 }
00180
00181
00182 array proxy = [uri::split $httpproxy]
00183
00184
00185 no = _proxy [string map {; " " , " "} $no_proxy]
00186
00187
00188 configure -proxy_host $proxy(host) \
00189 -proxy_port $proxy(port) \
00190 -no_proxy $no_proxy
00191
00192
00193 if {[string length $proxy(user)] < 1 \
00194 && [info exists env(http_proxy_user)] \
00195 && [info exists env(http_proxy_pass)]} {
00196 proxy = (user) $env(http_proxy_user)
00197 proxy = (pwd) $env(http_proxy_pass)
00198 }
00199
00200
00201
00202 if {[string length $proxy(user)] > 0} {
00203 configure -basic -username $proxy(user) -password $proxy(pwd)
00204 }
00205
00206
00207 http::config -proxyfilter [namespace origin filter]
00208 }
00209 return $httpproxy
00210 }
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220 ret ::autoproxy::GetWin32Proxy (type protocol) {
00221 variable winregkey
00222 set proxies [split [registry get $winregkey "ProxyServer"] ";"]
00223 foreach proxy $proxies {
00224 if {[string first = $proxy] == -1} {
00225 return $proxy
00226 } else {
00227 foreach {prot host} [split $proxy =] break
00228 if {[string compare $protocol $prot] == 0} {
00229 return $host
00230 }
00231 }
00232 }
00233 return -code error "failed to identify an '$protocol' proxy"
00234 }
00235
00236
00237
00238
00239 ret ::autoproxy::Pop (type varname , optional nth =0) {
00240 upvar $varname args
00241 set r [lindex $args $nth]
00242 set args [lreplace $args $nth $nth]
00243 return $r
00244 }
00245
00246
00247
00248
00249
00250
00251
00252 ret ::autoproxy::defAuthProc (optional user ={) {passwd {}} {realm {}}} {
00253 if {[string length $realm] > 0} {
00254 title = "Realm: $realm"
00255 } else {
00256 title = {}
00257 }
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 dlg = [toplevel .autoproxy_defAuthProc -class Dialog]
00268 wm title $dlg "$realm"
00269 label $dlg.ll -text Login -underline 0 -anchor w
00270 entry $dlg.le -textvariable [namespace current]::${dlg}:l
00271 label $dlg.pl -text Password -underline 0 -anchor w
00272 entry $dlg.pe -show * -textvariable [namespace current]::${dlg}:p
00273 button $dlg.ok -text OK -default active -width -11 \
00274 -command [list [namespace = current]::${dlg}:ok 1]
00275 grid $dlg.ll $dlg.le -sticky news
00276 grid $dlg.pl $dlg.pe -sticky news
00277 grid $dlg.ok - -sticky e
00278 grid columnconfigure $dlg 1 -weight 1
00279 bind $dlg <Return> [list $dlg.ok invoke]
00280 bind $dlg <Alt-l> [list focus $dlg.le]
00281 bind $dlg <Alt-p> [list focus $dlg.pe]
00282 variable ${dlg}:l $user; variable ${dlg}:p $passwd
00283 variable ${dlg}:ok 0
00284 wm deiconify $dlg; focus $dlg.pe; update idletasks
00285 old = [::grab current]; grab $dlg
00286 tkwait variable [namespace current]::${dlg}:ok
00287 grab release $dlg ; if {[llength $old] > 0} {::grab $old}
00288 r = [list [ ${dlg = }:l] [ ${dlg = }:p]]
00289 un ${dlg = }:l; un ${dlg = }:p; un ${dlg = }:ok
00290 destroy $dlg
00291 return $r
00292 }
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 ret ::autoproxy::configure:basic (type arglist) {
00305 variable options
00306 array set opts {user {} passwd {} realm {}}
00307 foreach {opt value} $arglist {
00308 switch -glob -- $opt {
00309 -u* { set opts(user) $value}
00310 -p* { set opts(passwd) $value}
00311 -r* { set opts(realm) $value}
00312 default {
00313 return -code error "invalid option \"$opt\": must be one of\
00314 -username or -password or -realm"
00315 }
00316 }
00317 }
00318
00319 # If nothing was provided, try calling the authProc
00320 if {$options(authProc) != {} \
00321 && ($opts(user) == {} || $opts(passwd) == {})} {
00322 set r [$options(authProc) $opts(user) $opts(passwd) $opts(realm)]
00323 set opts(user) [lindex $r 0]
00324 set opts(passwd) [lindex $r 1]
00325 }
00326
00327 # Store the encoded string to avoid re-encoding all the time.
00328 set options(basic) [list "Proxy-Authorization" \
00329 [concat "Basic" \
00330 [base64::encode $opts(user):$opts(passwd)]]]
00331 return
00332 }
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344 ret ::autoproxy::filter (type host) {
00345 variable options
00346
00347 if {$options(proxy_host) == {}} {
00348 return {}
00349 }
00350
00351 foreach domain $options(no_proxy) {
00352 if {[string match $domain $host]} {
00353 return {}
00354 }
00355 }
00356
00357 # Add authorisation header to the request (by Anders Ramdahl)
00358 catch {
00359 upvar state State
00360 if {$options(basic) != {}} {
00361 set State(-headers) [concat $options(basic) $State(-headers)]
00362 }
00363 }
00364 return [list $options(proxy_host) $options(proxy_port)]
00365 }
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 ret ::autoproxy::tls_connect (type host , type port , optional useragent ={)} {
00376 variable options
00377 if {[string length $options(proxy_host)] > 0} {
00378 s = [::socket $options(proxy_host) $options(proxy_port)]
00379 fconfigure $s -blocking 1 -buffering line -translation crlf
00380 puts $s "CONNECT $host:$port HTTP/1.1"
00381 puts $s "Host: $host"
00382 if {[string length $useragent] > 0} {
00383 puts $s "User-Agent: $useragent"
00384 }
00385 puts $s "Proxy-Connection: keep-alive"
00386 puts $s "Connection: keep-alive"
00387 if {[string length $options(basic)] > 0} {
00388 puts $s [join $options(basic) ": "]
00389 }
00390 puts $s ""
00391
00392 block = ""
00393 while {[gets $s r] > 0} {
00394 lappend block $r
00395 }
00396 result = [lindex $block 0]
00397 code = [lindex [split $result { }] 1]
00398
00399 if {$code >= 200 && $code < 300} {
00400 fconfigure $s -blocking 1 -buffering none -translation binary
00401 tls::import $s
00402 } else {
00403 close $s
00404 return -code error $result
00405 }
00406 } else {
00407 s = [eval [linsert $args 0 ::tls::socket]]
00408 }
00409 return $s
00410 }
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 ret ::autoproxy::tls_socket (type args) {
00422 variable options
00423
00424 # Look into the http package for the actual target. The function
00425 # has unfortunately not passed these as parameters.
00426 upvar host host port port
00427 set s [tls_connect $host $port [http::config -useragent]]
00428
00429 # record the tls connection status in the http state array.
00430 upvar state state
00431 tls::handshake $s
00432 set state(tls_status) [tls::status $s]
00433
00434 return $s
00435 }
00436
00437
00438
00439 package provide autoproxy $::autoproxy::version
00440
00441
00442
00443
00444
00445
00446
00447