diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
commit | ea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch) | |
tree | 6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/http | |
parent | b5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff) | |
parent | 7c621da28f07e449ad90c387344f07a453927569 (diff) | |
download | blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2 |
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/http')
-rw-r--r-- | tcllib/modules/http/ChangeLog | 159 | ||||
-rw-r--r-- | tcllib/modules/http/autoproxy.man | 199 | ||||
-rw-r--r-- | tcllib/modules/http/autoproxy.pcx | 62 | ||||
-rw-r--r-- | tcllib/modules/http/autoproxy.tcl | 539 | ||||
-rw-r--r-- | tcllib/modules/http/pkgIndex.tcl | 2 |
5 files changed, 961 insertions, 0 deletions
diff --git a/tcllib/modules/http/ChangeLog b/tcllib/modules/http/ChangeLog new file mode 100644 index 0000000..74f393e --- /dev/null +++ b/tcllib/modules/http/ChangeLog @@ -0,0 +1,159 @@ +2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.15 ======================== + * + +2012-05-28 Andreas Kupries <andreask@activestate.com> + + * autoproxy.tcl: Fixed bug where https proxying was attempted + * autoproxy.man: in the face of a domain exception. The TLS + * pkgIndex.tcl: setup code has to 'filter' properly. Further + check for existence of state(code), it may not exist (Server + accepts initial connection, then eof's during the TLS + handshake). Bumped version number to 1.5.3. + +2012-02-08 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Applied suggested fix from bug #3313923 to fix + handling of the -authProc option. + +2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.14 ======================== + * + +2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.13 ======================== + * + +2010-10-26 Andreas Kupries <andreask@activestate.com> + + * autoproxy.man: [Bug 3094654]: Made the spelling of options + consistent, all using their '-'. + +2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.12 ======================== + * + +2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11.1 ======================== + * + +2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11 ======================== + * + +2008-06-14 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * autoproxy.pcx: New file. Syntax definitions for the public + commands of the autoproxy package. + +2008-02-29 Andreas Kupries <andreask@activestate.com> + + * autoproxy.tcl (::autoproxy::init, ::autoproxy::GetWin32Proxy): + Added tclchecker pragmas to suppress false non-portable command + warnings. + + * autoproxy.tcl (::autoproxy::cget): Removed bogus closing + * autoproxy.man: bracket. Bumped version to 1.5.1 + * pkgIndex.tcl: + +2008-02-27 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Fix title usage in defAuthProc + +2008-02-05 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.man: Increment version to 1.5 and document. + * autoproxy.tcl: Fixed architectural problems that prevented the + http registered command from having tls package options + appended. This makes an incompatible change to the tls_connect + command and the host and port must now be the last two options. + * autoproxy.tcl: Re-organised the tls_connect code to split out + the connect and the tls parts to create a tunnel_connect command + that can form a non-SSL tunnel through a proxy. + +2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.10 ======================== + * + +2007-03-21 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * autoproxy.man: Fixed all warnings due to use of now deprecated + commands. Added a section about how to give feedback. + +2007-03-12 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Removed even the demo reference to BWidgets to + avoid confising the auto-dependency checker. Rearranged the tls + connection code to permit use outside of the http package as + tls_connect. + * autoproxy.man: Documented the tls functions. + * pkgIndex.tcl: Increment version to 1.4 + +2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.9 ======================== + * + +2006-09-19 Andreas Kupries <andreask@activestate.com> + + * autoproxy.man: Bumped version to 1.3 + * autoproxy.tcl: + * pkgIndex.tcl: + +2006-04-20 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Added a tls_socket procedure that can use + registered as the protocol handler for https + with the core http package and will do the right + thing when a proxy is in use. + +2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.8 ======================== + * + +2005-02-17 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Dealt with the parsing requirements as mentioned + * autoproxy.man: in bug #1099162. In theory we may have different + * pkgIndex.tcl: proxy settings for https and http (and other + protocols) but to deal with these we will need to change the http + package so we do not deal with them now. + +2004-10-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.7 ======================== + * + +2004-07-19 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Removed the stuff for Digest and NTLM until it + is working properly. Added an authProc configuration option to + permit application code to specify a procedure to get + authentication information from the user. + +2004-07-19 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Fix the version number. + +2004-07-17 Pat Thoyts <patthoyts@users.sourceforge.net> + + * autoproxy.tcl: Import of the wiki version. + * autoproxy.man: Wrote a manual page. diff --git a/tcllib/modules/http/autoproxy.man b/tcllib/modules/http/autoproxy.man new file mode 100644 index 0000000..2401cf1 --- /dev/null +++ b/tcllib/modules/http/autoproxy.man @@ -0,0 +1,199 @@ +[manpage_begin autoproxy n 1.5.3] +[see_also http(n)] +[keywords authentication] +[keywords http] +[keywords proxy] +[moddesc {HTTP protocol helper modules}] +[titledesc {Automatic HTTP proxy usage and authentication}] +[category Networking] +[require Tcl 8.2] +[require http [opt 2.0]] +[require autoproxy [opt 1.5.3]] +[description] +[para] + +This package attempts to automate the use of HTTP proxy servers in Tcl +HTTP client code. It tries to initialize the web access settings from +system standard locations and can be configured to negotiate +authentication with the proxy if required. + +[para] + +On Unix the standard for identifying the local HTTP proxy server +seems to be to use the environment variable http_proxy or ftp_proxy and +no_proxy to list those domains to be excluded from proxying. +On Windows we can retrieve the Internet Settings values from the registry +to obtain pretty much the same information. +With this information we can setup a suitable filter procedure for the +Tcl http package and arrange for automatic use of the proxy. + +[para] + +There seem to be a number of ways that the http_proxy environment +variable may be set up. Either a plain host:port or more commonly a +URL and sometimes the URL may contain authentication parameters or +these may be requested from the user or provided via http_proxy_user +and http_proxy_pass. This package attempts to deal with all these +schemes. It will do it's best to get the required parameters from the +environment or registry and if it fails can be reconfigured. + +[include ../common-text/tls-security-notes.inc] + +[section "COMMANDS"] + +[list_begin definitions] + +[call [cmd ::autoproxy::init]] + +Initialize the autoproxy package from system resources. Under unix +this means we look for environment variables. Under windows we look +for the same environment variables but also look at the registry +settings used by Internet Explorer. + +[call [cmd ::autoproxy::cget] [arg "-option"]] + +Retrieve individual package configuration options. See [sectref OPTIONS]. + +[call [cmd ::autoproxy::configure] [opt "-option [arg value]"]] + +Configure the autoproxy package. Calling [cmd configure] with no +options will return a list of all option names and values. +See [sectref OPTIONS]. + +[call [cmd ::autoproxy::tls_connect] [arg args]] + +Connect to a secure socket through a proxy. HTTP proxy servers permit +the use of the CONNECT HTTP command to open a link through the proxy +to the target machine. This function hides the details. For use with +the http package see [cmd tls_socket]. +[para] +The [arg args] list may contain any of the [package tls] package options but +must end with the host and port as the last two items. + +[call [cmd ::autoproxy::tunnel_connect] [arg args]] + +Connect to a target host throught a proxy. This uses the same CONNECT +HTTP command as the [cmd tls_connect] but does not promote the link +security once the connection is established. +[para] +The [arg args] list may contain any of the [package tls] package options but +must end with the host and port as the last two items. +[para] +Note that many proxy servers will permit CONNECT calls to a limited +set of ports - typically only port 443 (the secure HTTP port). + +[call [cmd ::autoproxy::tls_socket] [arg args]] + +This function is to be used to register a proxy-aware secure socket +handler for the https protocol. It may only be used with the Tcl http +package and should be registered using the http::register command (see +the examples below). The job of actually creating the tunnelled +connection is done by the tls_connect command and this may be used +when not registering with the http package. + +[list_end] + +[section {OPTIONS}] + +[list_begin options] + +[opt_def -host hostname] +[opt_def -proxy_host hostname] +Set the proxy hostname. This is normally set up by [cmd init] but may +be configured here as well. + +[opt_def -port number] +[opt_def -proxy_port number] +Set the proxy port number. This is normally set up by [cmd init]. +e.g. [cmd configure] [option -port] [arg 3128] + +[opt_def -no_proxy list] +You may manipulate the [option no_proxy] list that was setup by +[cmd init]. The value of this option is a tcl list of +strings that are matched against the http request host using the tcl +[cmd "string match"] command. Therefore glob patterns are permitted. +For instance, [cmd configure] [option -no_proxy] [arg "*.localdomain"] + +[opt_def -authProc procedure] +This option may be used to set an application defined procedure to be +called when [cmd configure] [option -basic] is called with either no or +insufficient authentication details. This can be used to present a +dialog to the user to request the additional information. + +[opt_def -basic] +Following options are for configuring the Basic authentication +scheme parameters. See [sectref "Basic Authentication"]. + +[list_end] + +[section "Basic Authentication"] + +Basic is the simplest and most commonly use HTTP proxy authentication +scheme. It is described in (1 section 11) and also in (2). It offers +no privacy whatsoever and its use should be discouraged in favour of +more secure alternatives like Digest. To perform Basic authentication +the client base64 encodes the username and plaintext password +separated by a colon. This encoded text is prefixed with the word +"Basic" and a space. + +[para] + +The following options exists for this scheme: +[list_begin options] +[opt_def "-username" "name"] +The username required to authenticate with the configured proxy. +[opt_def "-password" "password"] +The password required for the username specified. +[opt_def "-realm" "realm"] +This option is not used. +[list_end] + +[section "EXAMPLES"] + +[para] +[example { +package require autoproxy +autoproxy::init +autoproxy::configure -basic -username ME -password SEKRET +set tok [http::geturl http://wiki.tcl.tk/] +http::data $tok +}] + +[example { +package require http +package require tls +package require autoproxy +autoproxy::init +http::register https 443 autoproxy::tls_socket +set tok [http::geturl https://www.example.com/] +}] + +[section {REFERENCES}] + +[list_begin enumerated] + +[enum] + Berners-Lee, T., Fielding R. and Frystyk, H. + "Hypertext Transfer Protocol -- HTTP/1.0", + RFC 1945, May 1996, + ([uri http://www.rfc-editor.org/rfc/rfc1945.txt]) + +[enum] + Franks, J. et al. + "HTTP Authentication: Basic and Digest Access Authentication", + RFC 2617, June 1999 + ([uri http://www.rfc-editor.org/rfc/rfc2617.txt]) + +[list_end] + +[section {BUGS}] + +At this time only Basic authentication (1) (2) is supported. It is +planned to add support for Digest (2) and NTLM in the future. + +[section AUTHORS] +Pat Thoyts + +[vset CATEGORY {http :: autoproxy}] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/http/autoproxy.pcx b/tcllib/modules/http/autoproxy.pcx new file mode 100644 index 0000000..8edad89 --- /dev/null +++ b/tcllib/modules/http/autoproxy.pcx @@ -0,0 +1,62 @@ +# -*- tcl -*- autoproxy.pcx +# Syntax of the commands provided by package autoproxy. + +# For use by TclDevKit's static syntax checker. +# See http://www.activestate.com/solutions/tcl/ +# See http://aspn.activestate.com/ASPN/docs/Tcl_Dev_Kit/4.0/Checker.html#pcx_api +# for the documentation describing the format of the code contained in this file +# + +package require pcx +pcx::register autoproxy +pcx::tcldep 1.5.1 needs tcl 8.2 + +namespace eval ::autoproxy {} + +#pcx::message FOO {... text ...} type +#pcx::scan <VERSION> <NAME> <RULE> + +pcx::check 1.5.1 std ::autoproxy::cget \ + {checkSimpleArgs 1 1 { + checkKeyword 1 {-host -proxy_host -port -proxy_port -no_proxy -basic -authProc} + }} +pcx::check 1.5.1 std ::autoproxy::configure \ + {checkSimpleArgs 0 -1 { + {checkConfigure 1 { + {-host checkWord} + {-proy_host checkWord} + {-port checkWholeNum} + {-proxy_port checkWholeNum} + {-no_proxy checkList} + {-basic {checkConfigure 0 { + {-username checkWord} + {-password checkWord} + {-realm checkWord} + }}} + {-authProc checkProcName} + }} + }} +pcx::check 1.5.1 std ::autoproxy::init \ + {checkSimpleArgs 0 2 { + checkWord + checkList + }} +# TODO: Get options/syntax for tls::socket +pcx::check 1.5.1 std ::autoproxy::tls_connect \ + {checkSimpleArgs 0 -1 { + checkWord + }} +# TODO: Get options/syntax for tls_connect (s.a.) +pcx::check 1.5.1 std ::autoproxy::tls_socket \ + {checkSimpleArgs 0 -1 { + checkWord + }} +# TODO: Get options/syntax for tls::socket +pcx::check 1.5.1 std ::autoproxy::tunnel_connect \ + {checkSimpleArgs 0 -1 { + checkWord + }} + +# Initialization via pcx::init. +# Use a ::autoproxy::init procedure for non-standard initialization. +pcx::complete diff --git a/tcllib/modules/http/autoproxy.tcl b/tcllib/modules/http/autoproxy.tcl new file mode 100644 index 0000000..6cc7c08 --- /dev/null +++ b/tcllib/modules/http/autoproxy.tcl @@ -0,0 +1,539 @@ +# autoproxy.tcl - Copyright (C) 2002-2008 Pat Thoyts <patthoyts@users.sf.net> +# +# On Unix the standard for identifying the local HTTP proxy server +# seems to be to use the environment variable http_proxy or ftp_proxy and +# no_proxy to list those domains to be excluded from proxying. +# +# On Windows we can retrieve the Internet Settings values from the registry +# to obtain pretty much the same information. +# +# With this information we can setup a suitable filter procedure for the +# Tcl http package and arrange for automatic use of the proxy. +# +# Example: +# package require autoproxy +# autoproxy::init +# set tok [http::geturl http://wiki.tcl.tk/] +# http::data $tok +# +# To support https add: +# package require tls +# http::register https 443 ::autoproxy::tls_socket + +package require http; # tcl +package require uri; # tcllib +package require base64; # tcllib + +namespace eval ::autoproxy { + variable options + + if {! [info exists options]} { + array set options { + proxy_host "" + proxy_port 80 + no_proxy {} + basic {} + authProc {} + } + } + + variable uid + if {![info exists uid]} { set uid 0 } + + variable winregkey + set winregkey [join { + HKEY_CURRENT_USER + Software Microsoft Windows + CurrentVersion "Internet Settings" + } \\] +} + +# ------------------------------------------------------------------------- +# Description: +# Obtain configuration options for the server. +# +proc ::autoproxy::cget {option} { + variable options + switch -glob -- $option { + -host - + -proxy_h* { set options(proxy_host) } + -port - + -proxy_p* { set options(proxy_port) } + -no* { set options(no_proxy) } + -basic { set options(basic) } + -authProc { set options(authProc) } + default { + set err [join [lsort [array names options]] ", -"] + return -code error "bad option \"$option\":\ + must be one of -$err" + } + } +} + +# ------------------------------------------------------------------------- +# Description: +# Configure the autoproxy package settings. +# You may only configure one type of authorisation at a time as once we hit +# -basic, -digest or -ntlm - all further args are passed to the protocol +# specific script. +# +# Of course, most of the point of this package is to fill as many of these +# fields as possible automatically. You should call autoproxy::init to +# do automatic configuration and then call this method to refine the details. +# +proc ::autoproxy::configure {args} { + variable options + + if {[llength $args] == 0} { + foreach {opt value} [array get options] { + lappend r -$opt $value + } + return $r + } + + while {[string match "-*" [set option [lindex $args 0]]]} { + switch -glob -- $option { + -host - + -proxy_h* { set options(proxy_host) [Pop args 1]} + -port - + -proxy_p* { set options(proxy_port) [Pop args 1]} + -no* { set options(no_proxy) [Pop args 1] } + -basic { Pop args; configure:basic $args ; break } + -authProc { set options(authProc) [Pop args 1] } + -- { Pop args; break } + default { + set opts [join [lsort [array names options]] ", -"] + return -code error "bad option \"$option\":\ + must be one of -$opts" + } + } + Pop args + } +} + +# ------------------------------------------------------------------------- +# Description: +# Initialise the http proxy information from the environment or the +# registry (Win32) +# +# This procedure will load the http package and re-writes the +# http::geturl method to add in the authorisation header. +# +# A better solution will be to arrange for the http package to request the +# authorisation key on receiving an authorisation reqest. +# +proc ::autoproxy::init {{httpproxy {}} {no_proxy {}}} { + global tcl_platform + global env + variable winregkey + variable options + + # Look for standard environment variables. + if {[string length $httpproxy] > 0} { + + # nothing to do + + } elseif {[info exists env(http_proxy)]} { + set httpproxy $env(http_proxy) + if {[info exists env(no_proxy)]} { + set no_proxy $env(no_proxy) + } + } else { + if {$tcl_platform(platform) == "windows"} { + #checker -scope block exclude nonPortCmd + package require registry 1.0 + array set reg {ProxyEnable 0 ProxyServer "" ProxyOverride {}} + catch { + # IE5 changed ProxyEnable from a binary to a dword value. + switch -exact -- [registry type $winregkey "ProxyEnable"] { + dword { + set reg(ProxyEnable) [registry get $winregkey "ProxyEnable"] + } + binary { + set v [registry get $winregkey "ProxyEnable"] + binary scan $v i reg(ProxyEnable) + } + default { + return -code error "unexpected type found for\ + ProxyEnable registry item" + } + } + set reg(ProxyServer) [GetWin32Proxy http] + set reg(ProxyOverride) [registry get $winregkey "ProxyOverride"] + } + if {![string is bool $reg(ProxyEnable)]} { + set reg(ProxyEnable) 0 + } + if {$reg(ProxyEnable)} { + set httpproxy $reg(ProxyServer) + set no_proxy $reg(ProxyOverride) + } + } + } + + # If we found something ... + if {[string length $httpproxy] > 0} { + # The http_proxy is supposed to be a URL - lets make sure. + if {![regexp {\w://.*} $httpproxy]} { + set httpproxy "http://$httpproxy" + } + + # decompose the string. + array set proxy [uri::split $httpproxy] + + # turn the no_proxy value into a tcl list + set no_proxy [string map {; " " , " "} $no_proxy] + + # configure ourselves + configure -proxy_host $proxy(host) \ + -proxy_port $proxy(port) \ + -no_proxy $no_proxy + + # Lift the authentication details from the environment if present. + if {[string length $proxy(user)] < 1 \ + && [info exists env(http_proxy_user)] \ + && [info exists env(http_proxy_pass)]} { + set proxy(user) $env(http_proxy_user) + set proxy(pwd) $env(http_proxy_pass) + } + + # Maybe the proxy url has authentication parameters? + # At this time, only Basic is supported. + if {[string length $proxy(user)] > 0} { + configure -basic -username $proxy(user) -password $proxy(pwd) + } + + # setup and configure the http package to use our proxy info. + http::config -proxyfilter [namespace origin filter] + } + return $httpproxy +} + +# autoproxy::GetWin32Proxy -- +# +# Parse the Windows Internet Settings registry key and return the +# protocol proxy requested. If the same proxy is in use for all +# protocols, then that will be returned. Otherwise the string is +# parsed. Example: +# ftp=proxy:80;http=proxy:80;https=proxy:80 +# +proc ::autoproxy::GetWin32Proxy {protocol} { + variable winregkey + #checker exclude nonPortCmd + set proxies [split [registry get $winregkey "ProxyServer"] ";"] + foreach proxy $proxies { + if {[string first = $proxy] == -1} { + return $proxy + } else { + foreach {prot host} [split $proxy =] break + if {[string compare $protocol $prot] == 0} { + return $host + } + } + } + return -code error "failed to identify an '$protocol' proxy" +} + +# ------------------------------------------------------------------------- +# Description: +# Pop the nth element off a list. Used in options processing. +proc ::autoproxy::Pop {varname {nth 0}} { + upvar $varname args + set r [lindex $args $nth] + set args [lreplace $args $nth $nth] + return $r +} + +# ------------------------------------------------------------------------- +# Description +# An example user authentication procedure. +# Returns: +# A two element list consisting of the users authentication id and +# password. +proc ::autoproxy::defAuthProc {{user {}} {passwd {}} {realm {}}} { + if {[string length $realm] > 0} { + set title "Realm: $realm" + } else { + set title {} + } + + # If you are using BWidgets then the following will do: + # + # package require BWidget + # return [PasswdDlg .defAuthDlg -parent {} -transient 0 \ + # -title $title -logintext $user -passwdtext $passwd] + # + # if you just have Tk and no BWidgets -- + + set dlg [toplevel .autoproxy_defAuthProc -class Dialog] + wm title $dlg $title + wm withdraw $dlg + label $dlg.ll -text Login -underline 0 -anchor w + entry $dlg.le -textvariable [namespace current]::${dlg}:l + label $dlg.pl -text Password -underline 0 -anchor w + entry $dlg.pe -show * -textvariable [namespace current]::${dlg}:p + button $dlg.ok -text OK -default active -width -11 \ + -command [list set [namespace current]::${dlg}:ok 1] + grid $dlg.ll $dlg.le -sticky news + grid $dlg.pl $dlg.pe -sticky news + grid $dlg.ok - -sticky e + grid columnconfigure $dlg 1 -weight 1 + bind $dlg <Return> [list $dlg.ok invoke] + bind $dlg <Alt-l> [list focus $dlg.le] + bind $dlg <Alt-p> [list focus $dlg.pe] + variable ${dlg}:l $user; variable ${dlg}:p $passwd + variable ${dlg}:ok 0 + wm deiconify $dlg; focus $dlg.pe; update idletasks + set old [::grab current]; grab $dlg + tkwait variable [namespace current]::${dlg}:ok + grab release $dlg ; if {[llength $old] > 0} {::grab $old} + set r [list [set ${dlg}:l] [set ${dlg}:p]] + unset ${dlg}:l; unset ${dlg}:p; unset ${dlg}:ok + destroy $dlg + return $r +} + +# ------------------------------------------------------------------------- + +# Description: +# Implement support for the Basic authentication scheme (RFC 1945,2617). +# Options: +# -user userid - pass in the user ID (May require Windows NT domain +# as DOMAIN\\username) +# -password pwd - pass in the user's password. +# -realm realm - pass in the http realm. +# +proc ::autoproxy::configure:basic {arglist} { + variable options + array set opts {user {} passwd {} realm {}} + foreach {opt value} $arglist { + switch -glob -- $opt { + -u* { set opts(user) $value} + -p* { set opts(passwd) $value} + -r* { set opts(realm) $value} + default { + return -code error "invalid option \"$opt\": must be one of\ + -username or -password or -realm" + } + } + } + + # If nothing was provided, try calling the authProc + if {$options(authProc) != {} \ + && ($opts(user) == {} || $opts(passwd) == {})} { + set r [$options(authProc) $opts(user) $opts(passwd) $opts(realm)] + set opts(user) [lindex $r 0] + set opts(passwd) [lindex $r 1] + } + + # Store the encoded string to avoid re-encoding all the time. + set options(basic) [list "Proxy-Authorization" \ + [concat "Basic" \ + [base64::encode $opts(user):$opts(passwd)]]] + return +} + +# ------------------------------------------------------------------------- +# Description: +# An http package proxy filter. This attempts to work out if a request +# should go via the configured proxy using a glob comparison against the +# no_proxy list items. A typical no_proxy list might be +# [list localhost *.my.domain.com 127.0.0.1] +# +# If we are going to use the proxy - then insert the proxy authorization +# header. +# +proc ::autoproxy::filter {host} { + variable options + + if {$options(proxy_host) == {}} { + return {} + } + + foreach domain $options(no_proxy) { + if {[string match $domain $host]} { + return {} + } + } + + # Add authorisation header to the request (by Anders Ramdahl) + catch { + upvar state State + if {$options(basic) != {}} { + set State(-headers) [concat $options(basic) $State(-headers)] + } + } + return [list $options(proxy_host) $options(proxy_port)] +} + +# ------------------------------------------------------------------------- +# autoproxy::tls_connect -- +# +# Create a connection to a remote machine through a proxy +# if necessary. This is used by the tls_socket command for +# use with the http package but can also be used more generally +# provided your proxy will permit CONNECT attempts to ports +# other than port 443 (many will not). +# This command defers to 'tunnel_connect' to link to the target +# host and then upgrades the link to SSL/TLS +# +proc ::autoproxy::tls_connect {args} { + variable options + if {[string length $options(proxy_host)] > 0} { + set s [eval [linsert $args 0 tunnel_connect]] + fconfigure $s -blocking 1 -buffering none -translation binary + if {[string equal "-async" [lindex $args end-2]]} { + eval [linsert [lrange $args 0 end-3] 0 ::tls::import $s] + } else { + eval [linsert [lrange $args 0 end-2] 0 ::tls::import $s] + } + } else { + set s [eval [linsert $args 0 ::tls::socket]] + } + return $s +} + +# autoproxy::tunnel_connect -- +# +# Create a connection to a remote machine through a proxy +# if necessary. This is used by the tls_socket command for +# use with the http package but can also be used more generally +# provided your proxy will permit CONNECT attempts to ports +# other than port 443 (many will not). +# Note: this command just opens the socket through the proxy to +# the target machine -- no SSL/TLS negotiation is done yet. +# +proc ::autoproxy::tunnel_connect {args} { + variable options + variable uid + set code ok + + # args = ... host port + # and the host/port is the actual endpoint we want to talk to, + # regardless of any proxying. See our caller tls_connect for + # ensuring this by peeking into the http package internals. + + # To handle proxying properly we have to run through 'filter' + # (again), to ensure that proxy exceptions are correctly taken + # into account. + + set proxy [filter [lindex $args end-1]] + + if {[llength $proxy]} { + foreach {proxy_host proxy_port} $proxy break + + set token [namespace current]::[incr uid] + upvar #0 $token state + set state(endpoint) [lrange $args end-1 end] + set state(state) connect + set state(data) "" + set state(useragent) [http::config -useragent] + set state(sock) [::socket $proxy_host $proxy_port] + fileevent $state(sock) writable [namespace code [list tunnel_write $token]] + vwait [set token](state) + + if {[string length $state(error)] > 0} { + set result $state(error) + close $state(sock) + unset state + set code error + } elseif {[info exists state(code)] && + (($state(code) >= 300) || + ($state(code) < 200))} { + set result [lindex $state(headers) 0] + regexp {HTTP/\d.\d\s+\d+\s+(.*)} $result -> result + close $state(sock) + set code error + } else { + set result $state(sock) + } + unset state + } else { + set result [eval [linsert $args 0 ::socket]] + } + return -code $code $result +} + +proc ::autoproxy::tunnel_write {token} { + upvar #0 $token state + variable options + fileevent $state(sock) writable {} + if {[catch {set state(error) [fconfigure $state(sock) -error]} err]} { + set state(error) $err + } + if {[string length $state(error)] > 0} { + set state(state) error + return + } + fconfigure $state(sock) -blocking 0 -buffering line -translation crlf + foreach {host port} $state(endpoint) break + puts $state(sock) "CONNECT $host:$port HTTP/1.1" + puts $state(sock) "Host: $host" + if {[string length $state(useragent)] > 0} { + puts $state(sock) "User-Agent: $state(useragent)" + } + puts $state(sock) "Proxy-Connection: keep-alive" + puts $state(sock) "Connection: keep-alive" + if {[string length $options(basic)] > 0} { + puts $state(sock) [join $options(basic) ": "] + } + puts $state(sock) "" + + fileevent $state(sock) readable [namespace code [list tunnel_read $token]] + return +} + +proc ::autoproxy::tunnel_read {token} { + upvar #0 $token state + set len [gets $state(sock) line] + if {[eof $state(sock)]} { + fileevent $state(sock) readable {} + set state(state) eof + } elseif {$len == 0} { + set state(code) [lindex [split [lindex $state(headers) 0] { }] 1] + fileevent $state(sock) readable {} + set state(state) ok + } else { + lappend state(headers) $line + } +} + +# autoproxy::tls_socket -- +# +# This can be used to handle TLS connections independently of +# proxy presence. It can only be used with the Tcl http package +# and to use it you must do: +# http::register https 443 ::autoproxy::tls_socket +# After that you can use the http::geturl command to access +# secure web pages and any proxy details will be handled for you. +# +proc ::autoproxy::tls_socket {args} { + variable options + + # Look into the http package for the actual target. If a proxy is in use then + # The function appends the proxy host and port and not the target. + + upvar host uhost port uport + set args [lrange $args 0 end-2] + lappend args $uhost $uport + + set s [eval [linsert $args 0 tls_connect]] + + # record the tls connection status in the http state array. + upvar state state + tls::handshake $s + set state(tls_status) [tls::status $s] + + return $s +} + +# ------------------------------------------------------------------------- + +package provide autoproxy 1.5.3 + +# ------------------------------------------------------------------------- +# +# Local variables: +# mode: tcl +# indent-tabs-mode: nil +# End: diff --git a/tcllib/modules/http/pkgIndex.tcl b/tcllib/modules/http/pkgIndex.tcl new file mode 100644 index 0000000..c3ead43 --- /dev/null +++ b/tcllib/modules/http/pkgIndex.tcl @@ -0,0 +1,2 @@ +if {![package vsatisfies [package provide Tcl] 8.2]} {return} +package ifneeded autoproxy 1.5.3 [list source [file join $dir autoproxy.tcl]] |