Index: openacs-4/packages/acs-tcl/tcl/request-processor-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/request-processor-procs.tcl,v diff -u -r1.118 -r1.119 --- openacs-4/packages/acs-tcl/tcl/request-processor-procs.tcl 19 Jun 2015 19:52:03 -0000 1.118 +++ openacs-4/packages/acs-tcl/tcl/request-processor-procs.tcl 7 Aug 2017 23:47:59 -0000 1.119 @@ -21,11 +21,11 @@ Tell the request processor to return some other page. - The path can either be relative to the current directory (e.g. "some-template") + The path can either be relative to the current directory (e.g. "some-template") relative to the server root (e.g. "/packages/my-package/www/some-template"), or - an absolute path (e.g. "/home/donb/openacs-4/templates/some-cms-template"). + an absolute path (e.g. "/home/donb/openacs-4/templates/some-cms-template"). - When there is no extension then the request processor will choose the + When there is no extension then the request processor will choose the matching file according to the extension preferences. Parameters will stay the same as in the initial request. @@ -35,7 +35,7 @@ browser may be broken (since the client will have the original URL). Use rp_form_put or rp_form_update if you want to feed query variables to the redirected page. - + @param absolute_path If set the path is an absolute path within the host filesystem @param path path to the file to serve @@ -70,7 +70,7 @@ # restore the file setting. we need to do this because # rp_serve_abstract_file sets it to the path we internally # redirected to, and rp_handler will cache the file setting - # internally in the tcl_url2file variable when PerformanceModeP is + # internally in the ::tcl_url2file variable when PerformanceModeP is # switched on. This way it caches the location that was originally # requested, not the path that we redirected to. ad_conn -set file $saved_file @@ -80,16 +80,16 @@ This proc is a simple wrapper around AOLserver's standard ns_getform proc, that will create the form if it doesn't exist, so that you - can then add values to that form. This is useful in conjunction - with rp_internal_redirect to redirect to a different page with + can then add values to that form. This is useful in conjunction + with rp_internal_redirect to redirect to a different page with certain query variables set. @author Lars Pind (lars@pinds.com) @creation-date August 20, 2002 - @return the form ns_set, just like ns_getform, except it will + @return the form ns_set, just like ns_getform, except it will always be non-empty. - + } { # The form may not exist, if there's nothing in it if { [ns_getform] ne "" } { @@ -98,7 +98,7 @@ } { # It doesn't exist, create a new one - # This is the magic global Tcl variable that AOLserver uses + # This is the magic global Tcl variable that AOLserver uses # to store the ns_set that contains the query args or form. global _ns_form @@ -111,7 +111,7 @@ ad_proc rp_form_put { name value } { This proc adds a query variable to AOLserver's internal ns_getform - form, so that it'll be picked up by ad_page_contract and other procs + form, so that it'll be picked up by ad_page_contract and other procs that look at the query variables or form supplied. This is useful when you do an rp_internal_redirect to a new page, and you want to feed that page with certain query variables. @@ -225,13 +225,13 @@ # Shortcut to allow registering filter for all methods. Just # call ad_register_proc again, with each of the three methods. foreach method { GET POST HEAD } { - ad_register_proc -debug $debug -noinherit $noinherit $method $path $proc $arg - } + ad_register_proc -debug $debug -noinherit $noinherit $method $path $proc $arg + } return } - if {$method ni { GET POST HEAD }} { - error "Method passed to ad_register_proc must be one of GET, POST, or HEAD" + if {$method ni { GET POST HEAD PUT DELETE }} { + error "Method passed to ad_register_proc must be one of GET, POST, HEAD, PUT and DELETE" } set proc_info [list $method $path $proc $arg $debug $noinherit $description [info script]] @@ -244,8 +244,7 @@ the browser if it fails (unless kind is trace). } { - set startclicks [clock clicks -milliseconds] - + set startclicks [clock clicks -microseconds] lassign $filter_info filter_index debug_p arg_count proc arg rp_debug -debug $debug_p "Invoking $why filter $proc" @@ -259,39 +258,39 @@ ad_try { set result [$proc $conn $arg $why] } ad_script_abort val { - set result "filter_return" + set result filter_return } - } error] + } error] } } if { $errno } { # Uh-oh - an error occurred. ds_add rp [list filter [list $why [ns_conn method] [ns_conn url] $proc $arg] \ - $startclicks [clock clicks -milliseconds] "error" $::errorInfo] + $startclicks [clock clicks -microseconds] "error" $::errorInfo] # make sure you report catching the error! rp_debug "error in filter $proc for [ns_conn method] [ns_conn url]?[ns_conn query] errno is $errno message is $::errorInfo" rp_report_error - set result "filter_return" + set result filter_return } elseif {$result ne "filter_ok" && $result ne "filter_break" && $result ne "filter_return" } { set error_msg "error in filter $proc for [ns_conn method] [ns_conn url]?[ns_conn query]. Filter returned invalid result \"$result\"" ds_add rp [list filter [list $why [ns_conn method] [ns_conn url] $proc $arg] \ - $startclicks [clock clicks -milliseconds] "error" $error_msg] + $startclicks [clock clicks -microseconds] "error" $error_msg] # report the bad filter_return message rp_debug -debug t -ns_log_level error $error_msg rp_report_error -message $error_msg - set result "filter_return" + set result filter_return } else { ds_add rp [list filter [list $why [ns_conn method] [ns_conn url] $proc $arg] \ - $startclicks [clock clicks -milliseconds] $result] + $startclicks [clock clicks -microseconds] $result] } rp_debug -debug $debug_p "Done invoking $why filter $proc (returning $result)" - # JCD: Why was this here? the rp_finish_serving_page is called inside the - # handlers and this handles trace filters + # JCD: Why was this here? the rp_finish_serving_page is called inside the + # handlers and this handles trace filters # if {$result ne "filter_return" } { # rp_finish_serving_page # } @@ -304,7 +303,7 @@ Invokes a registered procedure. } { - set startclicks [clock clicks -milliseconds] + set startclicks [clock clicks -microseconds] lassign $argv proc_index debug_p arg_count proc arg @@ -324,11 +323,11 @@ if { $errno } { # Uh-oh - an error occurred. - ds_add rp [list registered_proc [list $proc $arg] $startclicks [clock clicks -milliseconds] "error" $::errorInfo] + ds_add rp [list registered_proc [list $proc $arg] $startclicks [clock clicks -microseconds] "error" $::errorInfo] rp_debug "error in $proc for [ns_conn method] [ns_conn url]?[ns_conn query] errno is $errno message is $::errorInfo" rp_report_error } else { - ds_add rp [list registered_proc [list $proc $arg] $startclicks [clock clicks -milliseconds]] + ds_add rp [list registered_proc [list $proc $arg] $startclicks [clock clicks -microseconds]] } rp_debug -debug $debug_p "Done Invoking registered procedure $proc" @@ -339,24 +338,24 @@ ad_proc -private rp_finish_serving_page {} { global doc_properties if { [info exists doc_properties(body)] } { - rp_debug "Returning page:[info level [expr {[info level] - 1}]]: [ad_quotehtml [string range $doc_properties(body) 0 100]]" + rp_debug "Returning page:[info level [expr {[info level] - 1}]]: [ns_quotehtml [string range $doc_properties(body) 0 100]]" doc_return 200 text/html $doc_properties(body) } } ad_proc -public ad_register_filter { { -debug f } { -priority 10000 } - { -critical f } + { -critical f } { -description "" } kind method path proc { arg "" } } { Registers a filter that gets called during page serving. The filter - should return one of + should return one of - Should not be used in .vuh files or elsewhere, instead + Should not be used in .vuh files or elsewhere, instead use the public function rp_internal_redirect. - @see rp_internal_redirect + @see rp_internal_redirect } { if {[string index $path end] eq "/"} { if { [file isdirectory $path] } { @@ -1068,18 +1160,18 @@ } ad_conn -set file [rp_concrete_file -extension_pattern $extension_pattern $path] - + if { [ad_conn file] eq "" } { - + if { [file isdirectory $path] && !$noredirect_p } { # Directory name with no trailing slash. Redirect to the same # URL but with a trailing slash. - + set url "[ad_conn url]/" if { [ad_conn query] ne "" } { append url "?[ad_conn query]" } - + ad_raise redirect $url } else { if { [info exists dir_index] && !$nodirectory_p } { @@ -1099,7 +1191,7 @@ Serves a file. } { set extension [file extension $file] - set startclicks [clock clicks -milliseconds] + set startclicks [clock clicks -microseconds] if { [nsv_exists rp_extension_handlers $extension] } { set handler [nsv_get rp_extension_handlers $extension] @@ -1115,15 +1207,15 @@ # do nothing } rp_finish_serving_page - ds_add rp [list serve_file [list $file $handler] $startclicks [clock clicks -milliseconds]] + ds_add rp [list serve_file [list $file $handler] $startclicks [clock clicks -microseconds]] } error]] } { - ds_add rp [list serve_file [list $file $handler] $startclicks [clock clicks -milliseconds] \ + ds_add rp [list serve_file [list $file $handler] $startclicks [clock clicks -microseconds] \ error "$::errorCode: $::errorInfo"] return -code $errno -errorcode $::errorCode -errorinfo $::errorInfo $error } } elseif { [rp_file_can_be_public_p $file] } { set type [ns_guesstype $file] - ds_add rp [list serve_file [list $file $type] $startclicks [clock clicks -milliseconds]] + ds_add rp [list serve_file [list $file $type] $startclicks [clock clicks -microseconds]] ns_returnfile 200 $type $file } else { ad_raise notfound @@ -1144,18 +1236,18 @@ @return 0 (and close the connection!) if the file must not be served. 1 if the application should perform its own checks, if any. } { - # first check that we are not serving a forbidden file like a .xql, a backup or CVS file + # first check that we are not serving a forbidden file like a .xql, a backup or CVS file if {[file extension $path] eq ".xql" - && ![parameter::get -parameter ServeXQLFiles -package_id [ad_acs_kernel_id] -default 0] } { - # Can't use ad_return_exception_page because it depends upon an initialized ad_conn + && ![parameter::get -parameter ServeXQLFiles -package_id $::acs::kernel_id -default 0] } { + # Can't use ad_return_exception_page because it depends upon an initialized ad_conn ns_log Warning "An attempt was made to access an .XQL resource: {$path}." ns_return 404 "text/html" "Not Found" ns_conn close return 0 } - foreach match [parameter::get -parameter ExcludedFiles -package_id [ad_acs_kernel_id] -default {}] { + foreach match [parameter::get -parameter ExcludedFiles -package_id $::acs::kernel_id -default {}] { if {[string match $match $path]} { - # Can't use ad_return_exception_page because it depends upon an initialized ad_conn + # Can't use ad_return_exception_page because it depends upon an initialized ad_conn ns_log Warning "An attempt was made to access an ExcludedFiles resource: {$path}." ns_return 404 "text/html" "Not Found" ns_conn close @@ -1185,7 +1277,7 @@ # Search for files in the order specified in ExtensionPrecedence, # include always "vuh" - set precedence [parameter::get -package_id [ad_acs_kernel_id] -parameter ExtensionPrecedence -default tcl] + set precedence [parameter::get -package_id $::acs::kernel_id -parameter ExtensionPrecedence -default tcl] foreach extension [concat [split [string trim $precedence] ","] vuh] { if { [lsearch -glob $files "*.$extension"] != -1 } { return "$path.$extension" @@ -1217,7 +1309,7 @@ ad_proc -public ad_script_abort {} { Aborts the current running Tcl script, returning to the request processor. - Used to stop processing after doing ad_returnredirect or other commands + Used to stop processing after doing ad_returnredirect or other commands which have already returned output to the client. } { ad_raise ad_script_abort @@ -1232,31 +1324,66 @@ return [db_string acs_kernel_id_get {} -default 0] } -# use proc rather than ad_proc on redefine since we don't want to see a -# multiple define proc warning... ad_proc -public ad_acs_kernel_id {} { Returns the package_id of the kernel. } { set acs_kernel_id [ad_acs_kernel_id_mem] + # + # use proc rather than ad_proc on redefine since we don't want to see a + # multiple define proc warning... proc ad_acs_kernel_id {} "return $acs_kernel_id" + return $acs_kernel_id } ad_proc -public ad_conn {args} { Returns a property about the connection. See the request - processor documentation for an (almost complete) list of allowable values. + href="/doc/request-processor">request + processor documentation for an (incomplete) list of allowable values.

If -set is passed then it sets a property.

- If the property has not been set directly by OpenACS it will be passed on to aolservers ns_conn: http://www.aolserver.com/docs/devel/tcl/api/conn.html#ns_conn. If it is not a valid option for ns_conn either then it will throw an error. - - Valid options for ad_conn are: request, sec_validated, browser_id, session_id, user_id, token, last_issue, deferred_dml, start_clicks, node_id, object_id, object_url, object_type, package_id, package_url, instance_name, package_key, extra_url, system_p, path_info, recursion_count. + If the property has not been set directly by OpenACS it will be passed on to AOLservers/NaviServers ns_conn: http://www.aolserver.com/docs/devel/tcl/api/conn.html#ns_conn. If it is not a valid option for ns_conn either then it will throw an error. +

+ Valid options for ad_conn are: + ajax_p, + behind_proxy_p, + behind_secure_proxy_p, + browser_id, + deferred_dml, + extra_url, + instance_name, + last_issue, + mobile_p, + node_id, + object_id, + object_type, + object_url, + package_id, + package_key, + package_url, + path_info, + peeraddr, + recursion_count, + request, + sec_validated, + session_id, + start_clicks, + subsite_id, + subsite_node_id, + subsite_url, + system_p, + token, + untrusted_user_id, + user_id, + vhost_package_url, + vhost_subsite_url, + vhost_url.

Added recursion_count to properly deal with internalredirects. @@ -1295,6 +1422,7 @@ browser_id "" session_id "" user_id "" + untrusted_user_id 0 token "" last_issue "" deferred_dml "" @@ -1330,7 +1458,7 @@ switch $var { form { - return [ns_getform] + return [ns_getform] } all { return [array get ad_conn] @@ -1340,7 +1468,7 @@ return $ad_conn($var) } - # Fallback + # Fallback switch $var { locale { set ad_conn(locale) [parameter::get \ @@ -1420,8 +1548,8 @@ return $ad_conn(vhost_package_url) } recursion_count { - # sometimes recusion_count will be uninitialized and - # something will call ad_conn recursion_count - return 0 + # sometimes recusion_count will be uninitialized and + # something will call ad_conn recursion_count - return 0 # in that instance. This is filters ahead of rp_filter which throw # an ns_returnnotfound or something like that. set ad_conn(recursion_count) 0 @@ -1441,6 +1569,67 @@ # return the physical peer address return [ns_conn $var] } + + mobile_p { + # + # Check, if we are used from a mobile device (based on user_agent). + # + if {[ns_conn isconnected]} { + set user_agent [string tolower [ns_set get [ns_conn headers] User-Agent]] + set ad_conn(mobile_p) [regexp (android|webos|iphone|ipad) $user_agent] + } else { + set ad_conn(mobile_p) 0 + } + return $ad_conn(mobile_p) + } + + ajax_p { + # + # Check, if we are used from an ajax + # client (providing the header field + # "X-Requested-With: XMLHttpRequest") + # + set ad_conn(ajax_p) 0 + if {[ns_conn isconnected]} { + set headers [ns_conn headers] + set i [ns_set ifind $headers "X-Requested-With"] + if {$i > -1 } { + set ad_conn(ajax_p) [expr {[ns_set value $headers $i] eq "XMLHttpRequest"}] + } + } + return $ad_conn(ajax_p) + } + + behind_proxy_p { + # + # Check, if we are running behind a proxy: + # a) the parameter "ReverseProxyMode" has to be set + # b) the header-field X-Forwarded-For must be present + # + set ad_conn(behind_proxy_p) 0 + if {[ns_conn isconnected]} { + set headers [ns_conn headers] + if { [ns_config "ns/parameters" ReverseProxyMode false] + && [ns_set ifind $headers X-Forwarded-For] > -1} { + set ad_conn(behind_proxy_p) 1 + } + } + return $ad_conn(behind_proxy_p) + } + + behind_secure_proxy_p { + # + # Check, if we are running behind a secure proxy: + # a) [ad_conn behind_proxy_p] must be true + # b) the header-field X-SSL-Request must be 1 + # + set ad_conn(behind_secure_proxy_p) 0 + if {[ad_conn behind_proxy_p]} { + set ad_conn(behind_secure_proxy_p) [ns_set iget [ns_conn headers] X-SSL-Request] + } + return $ad_conn(behind_secure_proxy_p) + } + default { return [ns_conn $var] } @@ -1526,27 +1715,27 @@ ad_proc -private ad_http_cache_control { } { - This adds specific headers to the http output headers for the current - request in order to prevent user agents and proxies from caching + This adds specific headers to the http output headers for the current + request in order to prevent user agents and proxies from caching the page.

- It should be called only when the method to return the data to the + It should be called only when the method to return the data to the client is going to be ns_return. In other cases, e.g. ns_returnfile, one can assume that the returned content is not dynamic and can in - fact be cached. Besides that, aolserver implements its own handling + fact be cached. Besides that, AOLserver implements its own handling of Last-Modified headers with ns_returnfile. Also it should be - called as late as possible - shortly before ns_return, so that + called as late as possible - shortly before ns_return, so that other code has the chance to set no_cache_control_p to 1 before it runs. - +

This proc can be disabled per request by calling - "ad_conn -set no_http_cache_control_p 1" before this proc is reached. + "ad_conn -set no_http_cache_control_p 1" before this proc is reached. It will not modify any headers if this variable is set to 1. - +

If the acs-kernel parameter CacheControlP is set to 0 then @@ -1556,7 +1745,7 @@ } { - if { ![parameter::get -package_id [ad_acs_kernel_id] -parameter HttpCacheControlP -default 0]} { + if { ![parameter::get -package_id $::acs::kernel_id -parameter HttpCacheControlP -default 0]} { return } @@ -1567,16 +1756,16 @@ set headers [ad_conn outputheaders] # Check if any relevant header is already present - in this case - # don't touch anything. + # don't touch anything. set modify_p 1 - if { [ns_set ifind $headers "cache-control"] > -1 + if { [ns_set ifind $headers "cache-control"] > -1 || [ns_set ifind $headers "expires"] > -1 } { set modify_p 0 } else { for { set i 0 } { $i < [ns_set size $headers] } { incr i } { - if { [string tolower [ns_set key $headers $i]] eq "pragma" - && [string tolower [ns_set value $headers $i]] eq "no-cache" + if { [string tolower [ns_set key $headers $i]] eq "pragma" + && [string tolower [ns_set value $headers $i]] eq "no-cache" } { set modify_p 0 break @@ -1594,7 +1783,7 @@ ns_set put $headers "Pragma" "no-cache" ns_set put $headers "Cache-Control" "no-cache" } - + # Prevent subsequent calls of this proc from adding the same # headers again. ad_conn -set no_http_cache_control_p 1 @@ -1613,7 +1802,7 @@ if { [regexp {^([^:]+)} $host_and_port match host] } { return $host } else { - return "unknown host" + return "" } } @@ -1632,19 +1821,34 @@ namespace eval ::acs {} ad_proc root_of_host {host} { + Maps a hostname to the corresponding sub-directory. + } { set key ::acs::root_of_host($host) if {[info exists $key]} {return [set $key]} - set $key [root_of_host1 $host] + set $key [root_of_host_noncached $host] } -proc root_of_host1 {host} { +ad_proc -private root_of_host_noncached {host} { + + Helper function for root_of_host, which performs the actual work. + +} { + # # The main hostname is mounted at /. - if { $host eq [ns_config ns/server/[ns_info server]/module/nssock Hostname] } { - return "" + # + foreach driver {nssock nsssl} { + set driver_section [ns_driversection -driver $driver] + set configured_hostname [ns_config $driver_section hostname] + if { $host eq $configured_hostname } { + return "" + } } + + # # Other hostnames map to subsites. + # set node_id [util_memoize [list rp_lookup_node_from_host $host]] if {$node_id eq ""} { @@ -1663,8 +1867,16 @@ } ad_proc -private rp_lookup_node_from_host { host } { - return [db_string node_id { *SQL* } -default ""] -} + if {$host ne ""} { + if {![regexp {^[\w.@+/=$%!*~\[\]-]+$} $host]} { + binary scan [encoding convertto utf-8 $host] H* hex + ad_log error "rp_lookup_node_from_host: host <$host> (hex $hex) contains invalid characters" + ad_return_complaint 1 "invalid request" + ad_script_abort + } + return [db_string node_id {} -default ""] + } +} @@ -1686,14 +1898,14 @@ set cmd ${filter}_aolserver if {[info commands $cmd] ne ""} {rename $cmd ""} rename $filter $cmd - proc $filter {why} "$cmd \$why" + proc $filter {why} "$cmd \$why" } set cmd rp_invoke_filter_conn if {[info commands $cmd] ne ""} {rename $cmd ""} rename rp_invoke_filter $cmd proc rp_invoke_filter { why filter_info} "$cmd _ \$filter_info \$why" - + set cmd rp_invoke_proc_conn if {[info commands $cmd] ne ""} {rename $cmd ""} rename rp_invoke_proc $cmd