Index: openacs.org-dev/packages/acs-tcl/tcl/utilities-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs.org-dev/packages/acs-tcl/tcl/utilities-procs.tcl,v diff -u -r1.1 -r1.2 --- openacs.org-dev/packages/acs-tcl/tcl/utilities-procs.tcl 9 Jul 2002 17:34:59 -0000 1.1 +++ openacs.org-dev/packages/acs-tcl/tcl/utilities-procs.tcl 12 Dec 2002 12:21:05 -0000 1.2 @@ -3,7 +3,7 @@ Provides a variety of non-ACS-specific utilities @author Various (acs@arsdigita.com) - @date 13 April 2000 + @creation-date 13 April 2000 @cvs-id $Id$ } @@ -32,27 +32,27 @@ ns_log Notice $message } -# stuff to process the data that comes -# back from the users - -# if the form looked like -# and -# then after you run this function you'll have Tcl vars -# $foo and $bar set to whatever the user typed in the form - -# this uses the initially nauseating but ultimately delicious -# Tcl system function "uplevel" that lets a subroutine bash -# the environment and local vars of its caller. It ain't Common Lisp... - -# This is an ad-hoc check to make sure users aren't trying to pass in -# "naughty" form variables in an effort to hack the database by passing -# in SQL. It is called in all instances where a Tcl variable -# is set from a form variable. - proc_doc check_for_form_variable_naughtiness { name value } { + stuff to process the data that comes + back from the users + + if the form looked like + and + then after you run this function you'll have Tcl vars + $foo and $bar set to whatever the user typed in the form + + this uses the initially nauseating but ultimately delicious + tcl system function "uplevel" that lets a subroutine bash + the environment and local vars of its caller. It ain't Common Lisp... + + This is an ad-hoc check to make sure users aren't trying to pass in + "naughty" form variables in an effort to hack the database by passing + in SQL. It is called in all instances where a Tcl variable + is set from a form variable. + Checks the given variable for against known form variable exploits. If it finds anything objectionable, it throws an error. } { @@ -156,7 +156,10 @@ } -proc set_form_variables {{error_if_not_found_p 1}} { +ad_proc -deprecated -warn set_form_variables {{error_if_not_found_p 1}} { + use ad_page_contract for this functionality + @see ad_page_contract +} { if { $error_if_not_found_p == 1} { uplevel { if { [ns_getform] == "" } { ns_returnerror 500 "Missing form data" @@ -188,25 +191,38 @@ } } -proc DoubleApos {string} { +ad_proc -private DoubleApos {string} { + if the user types "O'Malley" and you try to insert that into an SQL + database, you will lose big time because the single quote is magic + in SQL and the insert has to look like 'O''Malley'. +
+ You should be using bind variables rather than + calling DoubleApos + + @return string with single quotes converted to a pair of single quotes +} { regsub -all ' "$string" '' result return $result } -# if the user types "O'Malley" and you try to insert that into an SQL -# database, you will lose big time because the single quote is magic -# in SQL and the insert has to look like 'O''Malley'. This function -# also trims white space off the ends of the user-typed data. -# if the form looked like -# and -# then after you run this function you'll have Tcl vars -# $QQfoo and $QQbar set to whatever the user typed in the form -# plus an extra single quote in front of the user's single quotes -# and maybe some missing white space -proc set_form_variables_string_trim_DoubleAposQQ {} { - # The variable names are prefixed with a V to avoid confusion with the form variables while checking for naughtiness. +ad_proc -deprecated -warn set_form_variables_string_trim_DoubleAposQQ {} { + if the user types "O'Malley" and you try to insert that into an SQL + database, you will lose big time because the single quote is magic + in SQL and the insert has to look like 'O''Malley'. This function + also trims white space off the ends of the user-typed data. + + if the form looked like + <input type=text name=yow> and <input type=text name=bar> + then after you run this function you'll have Tcl vars + $QQfoo and $QQbar set to whatever the user typed in the form + plus an extra single quote in front of the user's single quotes + and maybe some missing white space + + @see ad_page_contract +} { + #The variable names are prefixed with a V to avoid confusion with the form variables while checking for naughtiness. uplevel { set Vform [ns_getform] if {$Vform == ""} { @@ -227,7 +243,11 @@ # this one does both the regular and the QQ -proc set_the_usual_form_variables {{error_if_not_found_p 1}} { +ad_proc -deprecated -warn set_the_usual_form_variables {{error_if_not_found_p 1}} { + use ad_page_contract for this functionality + + @see ad_page_contract +} { if { [ns_getform] == "" } { if $error_if_not_found_p { uplevel { @@ -255,7 +275,10 @@ } } -proc set_form_variables_string_trim_DoubleApos {} { +ad_proc -deprecated -warn set_form_variables_string_trim_DoubleApos {} { + use ad_page_contract for this functionality + @see ad_page_contract +} { # The variable names are prefixed with a V to avoid confusion with the form variables while checking for naughtiness. uplevel { set Vform [ns_getform] @@ -275,7 +298,10 @@ } } -proc set_form_variables_string_trim {} { +ad_proc -deprecated -warn set_form_variables_string_trim {} { + use ad_page_contract for this functionality + @see ad_page_contract +} { # The variable names are prefixed with a V to avoid confusion with the form variables while checking for naughtiness. uplevel { set Vform [ns_getform] @@ -297,39 +323,22 @@ # debugging kludges -proc NsSettoTclString {set_id} { +ad_proc -public NsSettoTclString {set_id} { + returns a plain text version of the passed ns_set id +} { set result "" for {set i 0} {$i<[ns_set size $set_id]} {incr i} { append result "[ns_set key $set_id $i] : [ns_set value $set_id $i]\n" } return $result } -proc get_referrer {} { +ad_proc -public get_referrer {} { + gets the Referer for the headers +} { return [ns_set get [ad_conn headers] Referer] } -proc post_args_to_query_string {} { - set arg_form [ns_getform] - if {$arg_form!=""} { - set form_counter_i 0 - while {$form_counter_i<[ns_set size $arg_form]} { - append query_return "[ns_set key $arg_form $form_counter_i]=[ns_urlencode [ns_set value $arg_form $form_counter_i]]&" - incr form_counter_i - } - set query_return [string trim $query_return &] - } -} - -proc get_referrer_and_query_string {} { - if {[ad_conn method]!="GET"} { - set query_return [post_args_to_query_string] - return "[get_referrer]?${query_return}" - } else { - return [get_referrer] - } -} - ## # Database-related code ## @@ -385,32 +394,19 @@
$errmsg- " + + " return } ad_returnredirect $return_url + # should this be ad_script_abort? Should check how its being used. return } -proc nmc_IllustraDatetoPrettyDate {sql_date} { - - regexp {(.*)-(.*)-(.*)$} $sql_date match year month day - - set allthemonths {January February March April May June July August September October November December} - - # we have to trim the leading zero because Tcl has such a - # brain damaged model of numbers and decided that "09-1" - # was "8.0" - - set trimmed_month [string trimleft $month 0] - set pretty_month [lindex $allthemonths [expr $trimmed_month - 1]] - - return "$pretty_month $day, $year" - -} - -proc_doc util_AnsiDatetoPrettyDate {sql_date} "Converts 1998-09-05 to September 5, 1998" { +ad_proc -public util_AnsiDatetoPrettyDate {sql_date} { + Converts 1998-09-05 to September 5, 1998 +} { set sql_date [string range $sql_date 0 9] if ![regexp {(.*)-(.*)-(.*)$} $sql_date match year month day] { return "" @@ -430,8 +426,11 @@ } } -proc remove_nulls_from_ns_set {old_set_id} { +ad_proc -public remove_nulls_from_ns_set {old_set_id} { + Creates and returns a new ns_set without any null value fields + @return new ns_set +} { set new_set_id [ns_set new "no_nulls$old_set_id"] for {set i 0} {$i<[ns_set size $old_set_id]} {incr i} { @@ -447,23 +446,14 @@ } -proc merge_form_with_ns_set {form set_id} { - - for {set i 0} {$i<[ns_set size $set_id]} {incr i} { - set form [ns_formvalueput $form [ns_set key $set_id $i] [ns_set value $set_id $i]] - } - - return $form - -} - ad_proc -public merge_form_with_query { { -bind {} } form statement_name sql_qry } { Merges a form with a query string. + @param form the form to be stuffed. @param statement_name An identifier for the sql_qry to be executed. @param sql_qry The sql that must be executed. @@ -488,117 +478,8 @@ } -proc util_prepare_update {table_name primary_key_name primary_key_value form} { - set form_size [ns_set size $form] - set form_counter_i 0 - set column_list [db_columns $table_name] - set bind_vars [ad_tcl_list_list_to_ns_set [list [list $primary_key_name $primary_key_value]]] - while {$form_counter_i<$form_size} { - - set form_var_name [ns_set key $form $form_counter_i] - set value [string trim [ns_set value $form $form_counter_i]] - - if { ($form_var_name != $primary_key_name) && ([lsearch $column_list $form_var_name] != -1) } { - - ad_tcl_list_list_to_ns_set -set_id $bind_vars [list [list $form_var_name $value]] - lappend the_sets "$form_var_name = :$form_var_name" - - } - - incr form_counter_i - } - - return [list "update $table_name\nset [join $the_sets ",\n"] \n where $primary_key_name = :$primary_key_name" $bind_vars] - -} - -proc util_prepare_update_multi_key {table_name primary_key_name_list primary_key_value_list form} { - - set form_size [ns_set size $form] - set form_counter_i 0 - set bind_vars [ns_set create] - - while {$form_counter_i<$form_size} { - - set form_var_name [ns_set key $form $form_counter_i] - set value [string trim [ns_set value $form $form_counter_i]] - - if { [lsearch -exact $primary_key_name_list $form_var_name] == -1 } { - - # this is not one of the keys - ad_tcl_list_list_to_ns_set -set_id $bind_vars [list [list $form_var_name $value]] - lappend the_sets "$form_var_name = :$form_var_name" - - } - - incr form_counter_i - } - - for {set i 0} {$i<[llength $primary_key_name_list]} {incr i} { - - set this_key_name [lindex $primary_key_name_list $i] - set this_key_value [lindex $primary_key_value_list $i] - - ad_tcl_list_list_to_ns_set -set_id $bind_vars [list [list $this_key_name $this_key_value]] - lappend key_eqns "$this_key_name = :$this_key_name" - - } - - return [list "update $table_name\nset [join $the_sets ",\n"] \n where [join $key_eqns " AND "]" $bind_vars] -} - -proc util_prepare_insert {table_name form} { - - set form_size [ns_set size $form] - set form_counter_i 0 - set bind_vars [ns_set create] - - while { $form_counter_i < $form_size } { - - ns_set update $bind_vars [ns_set key $form $form_counter_i] [string trim [ns_set value $form $form_counter_i]] - incr form_counter_i - - } - - return [list "insert into $table_name\n([join [ad_ns_set_keys $bind_vars] ", "])\n values ([join [ad_ns_set_keys -colon $bind_vars] ", "])" $bind_vars] -} - -proc util_PrettySex {m_or_f { default "default" }} { - if { $m_or_f == "M" || $m_or_f == "m" } { - return "Male" - } elseif { $m_or_f == "F" || $m_or_f == "f" } { - return "Female" - } else { - # Note that we can't compare default to the empty string as in - # many cases, we are going want the default to be the empty - # string - if { [string compare $default "default"] == 0 } { - return "Unknown (\"$m_or_f\")" - } else { - return $default - } - } -} - -proc util_PrettySexManWoman {m_or_f { default "default"} } { - if { $m_or_f == "M" || $m_or_f == "m" } { - return "Man" - } elseif { $m_or_f == "F" || $m_or_f == "f" } { - return "Woman" - } else { - # Note that we can't compare default to the empty string as in - # many cases, we are going want the default to be the empty - # string - if { [string compare $default "default"] == 0 } { - return "Person of Unknown Sex" - } else { - return $default - } - } -} - proc util_PrettyBoolean {t_or_f { default "default" } } { if { $t_or_f == "t" || $t_or_f == "T" } { return "Yes" @@ -624,22 +505,31 @@ } } -proc randomInit {seed} { +ad_proc -public randomInit {seed} { + seed the random number generator. +} { nsv_set rand ia 9301 nsv_set rand ic 49297 nsv_set rand im 233280 nsv_set rand seed $seed } -# initialize the random number generator -randomInit [ns_time] - -proc random {} { +ad_proc -public random {} { + Return a pseudo-random number between 0 and 1. +} { nsv_set rand seed [expr ([nsv_get rand seed] * [nsv_get rand ia] + [nsv_get rand ic]) % [nsv_get rand im]] return [expr [nsv_get rand seed]/double([nsv_get rand im])] } +ad_proc -public randomRange {range} { + Returns a pseudo-random number between 0 and range. + + @return integer +} { + return [expr int([random] * $range)] +} + ad_proc -public db_html_select_options { { -bind "" } { -select_option "" } @@ -664,9 +554,9 @@ foreach option $options { if { [string compare $option $select_option] == 0 } { - append select_options "\n" } else { - append select_options "\n" } } return $select_options @@ -699,9 +589,9 @@ foreach option $options { if { [string compare $select_option [lindex $option $value_index]] == 0 } { - append select_options "\n" } else { - append select_options "\n" } } return $select_options @@ -989,7 +879,7 @@ set export_string [join $export_list "&"] } else { for { set i 0 } { $i < $export_size } { incr i } { - append export_string "\n" + append export_string "\n" } } @@ -1067,6 +957,8 @@ @author Lars Pind (lars@pinds.com) @creation-date 21 July 2000 + + @see export_vars } { #################### @@ -1140,8 +1032,8 @@ } else { set export_list [list] foreach varname [array names export] { - lappend export_list "" + lappend export_list "" } return [join $export_list \n] } @@ -1151,7 +1043,7 @@ -ad_proc export_form_vars { +ad_proc -deprecated export_form_vars { -sign:boolean args } { @@ -1160,8 +1052,9 @@ to grab the value of the variables. Variables that are not defined are silently ignored. You can append :multiple to the name of a variable. In this case, the value will be treated as a list, and each of the elements output separately. -
+ export_vars is now the prefered interface. +
Example usage: [export_form_vars -sign foo bar:multiple baz]
@@ -1174,6 +1067,7 @@
href="/api-doc/proc-view?proc=ad_verify_signature">ad_verify_signature
. This
ensures that the value hasn't been tampered with at the user's end.
+ @see export_vars
} {
set hidden ""
foreach var_spec $args {
@@ -1185,43 +1079,56 @@
switch $type {
multiple {
foreach item $value {
- append hidden "\n"
+ append hidden "\n"
}
}
default {
- append hidden "\n"
+ append hidden "\n"
}
}
if { $sign_p } {
- append hidden "\n"
+ append hidden "\n"
}
}
}
return $hidden
}
-ad_proc export_entire_form {} {
- Exports everything in ns_getform to the ns_set.
- This should generally not be used. It's much better to explicitly
- name the variables you want to export.
+ad_proc -public export_entire_form {} {
+
+ Exports everything in ns_getform to the ns_set. This should
+ generally not be used. It's much better to explicitly name
+ the variables you want to export.
+
+ export_vars is now the prefered interface.
+
+ @see export_vars
} {
set hidden ""
set the_form [ns_getform]
if { ![empty_string_p $the_form] } {
for {set i 0} {$i<[ns_set size $the_form]} {incr i} {
set varname [ns_set key $the_form $i]
set varvalue [ns_set value $the_form $i]
- append hidden "\n"
+ append hidden "\n"
}
}
return $hidden
}
-ad_proc export_ns_set_vars {{format "url"} {exclusion_list ""} {setid ""}} {
+ad_proc export_ns_set_vars {{format "url"} {exclusion_list ""} {setid ""}} {
Returns all the params in an ns_set with the exception of those in
- exclusion_list. If no setid is provide, ns_getform is used. If format
- = url, a url parameter string will be returned. If format = form, a
+ exclusion_list. If no setid is provide, ns_getform is used. If
+ format = url, a url parameter string will be returned. If format = form, a
block of hidden form fragments will be returned.
+
+ export_vars is now the prefered interface.
+
+ @param format either url or form
+ @param exclusion_list list of fields to exclude
+ @param setid if null then it is ns_getform
+
+ @see export_vars
} {
if [empty_string_p $setid] {
@@ -1248,14 +1155,15 @@
if {$format == "url"} {
return [join $return_list "&"]
} else {
- return "\n "
+ return "\n "
}
}
ad_proc export_url_vars {
-sign:boolean
args
} {
+ export_vars is now the prefered interface.
Returns a string of key=value pairs suitable for inclusion in a
URL; you can pass it any number of variables as arguments. If any are
@@ -1287,6 +1195,8 @@
which in turn uses ad_verify_signature
. This
ensures that the value hasn't been tampered with at the user's end.
+
+ @see export_vars
} {
set params {}
foreach var_spec $args {
@@ -1326,13 +1236,17 @@
return [join $params "&"]
}
-ad_proc export_entire_form_as_url_vars {
+ad_proc -public export_entire_form_as_url_vars {
{vars_to_passthrough ""}
} {
+ export_vars is now the prefered interface.
+
Returns a URL parameter string of name-value pairs of all the form
parameters passed to this page. If vars_to_passthrough is given, it
should be a list of parameter names that will be the only ones passed
through.
+
+ @see export_vars
} {
set params [list]
set the_form [ns_getform]
@@ -1347,54 +1261,14 @@
lappend params "[ns_urlencode $varname]=[ns_urlencode $varvalue]"
}
}
- return [join $params "&"]
+ return [join $params "&"]
}
}
-# Perform the dml statements in sql_list in a transaction.
-# Aborts the transaction and returns an error message if
-# an error occurred for any of the statements, otherwise
-# returns null string. -jsc
-proc do_dml_transactions {dml_stmt_list} {
- db_transaction {
- foreach dml_stmt $dml_stmt_list {
- if { [catch {db_dml $dml_stmt} errmsg] } {
- db_abort_transaction
- return $errmsg
- }
- }
- }
- return ""
-}
-# Perform body within a database transaction.
-# Execute on_error if there was some error caught
-# within body, with errmsg bound.
-# This procedure will clobber errmsg in the caller.
-# -jsc
-proc with_transaction {body on_error} {
- upvar errmsg errmsg
- global errorInfo errorCode
- if { [catch {db_transaction { uplevel $body }} errmsg] } {
- db_abort_transaction
- set code [catch {uplevel $on_error} string]
- # Return out of the caller appropriately.
- if { $code == 1 } {
- return -code error -errorinfo $errorInfo -errorcode $errorCode $string
- } elseif { $code == 2 } {
- return -code return $string
- } elseif { $code == 3 } {
- return -code break
- } elseif { $code == 4 } {
- return -code continue
- } elseif { $code > 4 } {
- return -code $code $string
- }
- }
-}
proc with_catch {error_var body on_error} {
upvar 1 $error_var $error_var
@@ -1416,33 +1290,15 @@
}
}
-proc_doc string_contains_p {small_string big_string} {Returns 1 if the BIG_STRING contains the SMALL_STRING, 0 otherwise; syntactic sugar for string first != -1} {
- if { [string first $small_string $big_string] == -1 } {
- return 0
- } else {
- return 1
- }
-}
-proc remove_whitespace {input_string} {
- if [regsub -all "\[\015\012\t \]" $input_string "" output_string] {
- return $output_string
- } else {
- return $input_string
- }
-}
-proc util_just_the_digits {input_string} {
- if [regsub -all {[^0-9]} $input_string "" output_string] {
- return $output_string
- } else {
- return $input_string
- }
-}
-
# putting commas into numbers (thank you, Michael Bryzek)
-proc_doc util_commify_number { num } {Returns the number with commas inserted where appropriate. Number can be positive or negative and can have a decimal point. e.g. -1465.98 => -1,465.98} {
+ad_proc -public util_commify_number { num } {
+ Returns the number with commas inserted where appropriate. Number can be
+ positive or negative and can have a decimal point.
+ e.g. -1465.98 => -1,465.98
+} {
while { 1 } {
# Regular Expression taken from Mastering Regular Expressions (Jeff Friedl)
# matches optional leading negative sign plus any
@@ -1454,11 +1310,9 @@
return $num
}
-proc leap_year_p {year} {
- expr ( $year % 4 == 0 ) && ( ( $year % 100 != 0 ) || ( $year % 400 == 0 ) )
-}
-
-proc_doc util_search_list_of_lists {list_of_lists query_string {sublist_element_pos 0}} "Returns position of sublist that contains QUERY_STRING at SUBLIST_ELEMENT_POS." {
+ad_proc -public util_search_list_of_lists {list_of_lists query_string {sublist_element_pos 0}} {
+ Returns position of sublist that contains QUERY_STRING at SUBLIST_ELEMENT_POS.
+} {
set sublist_index 0
foreach sublist $list_of_lists {
set comparison_element [lindex $sublist $sublist_element_pos]
@@ -1473,7 +1327,14 @@
# --- network stuff
-proc_doc util_get_http_status {url {use_get_p 1} {timeout 30}} "Returns the HTTP status code, e.g., 200 for a normal response or 500 for an error, of a URL. By default this uses the GET method instead of HEAD since not all servers will respond properly to a HEAD request even when the URL is perfectly valid. Note that this means AOLserver may be sucking down a lot of bits that it doesn't need." {
+ad_proc -public util_get_http_status {url {use_get_p 1} {timeout 30}} {
+ Returns the HTTP status code, e.g., 200 for a normal response
+ or 500 for an error, of a URL. By default this uses the GET method
+ instead of HEAD since not all servers will respond properly to a
+ HEAD request even when the URL is perfectly valid. Note that
+ this means AOLserver may be sucking down a lot of bits that it
+ doesn't need.
+} {
if $use_get_p {
set http [ns_httpopen GET $url "" $timeout]
} else {
@@ -1490,7 +1351,14 @@
return $status
}
-proc_doc util_link_responding_p {url {list_of_bad_codes "404"}} "Returns 1 if the URL is responding (generally we think that anything other than 404 (not found) is okay)." {
+ad_proc -public util_link_responding_p {
+ url
+ {list_of_bad_codes "404"}
+} {
+ Returns 1 if the URL is responding (generally we think that anything other than 404 (not found) is okay).
+
+ @see util_get_http_status
+} {
if [catch { set status [util_get_http_status $url] } errmsg] {
# got an error; definitely not valid
return 0
@@ -1507,43 +1375,51 @@
# system by Tracy Adams (teadams@arsdigita.com) to permit AOLserver to POST
# to another Web server; sort of like ns_httpget
-proc_doc util_httpopen {method url {rqset ""} {timeout 30} {http_referer ""}} "Like ns_httpopen but works for POST as well; called by util_httppost" {
+ad_proc -public util_httpopen {
+ method
+ url
+ {rqset ""}
+ {timeout 30}
+ {http_referer ""}
+} {
+ Like ns_httpopen but works for POST as well; called by util_httppost
+} {
- if ![string match http://* $url] {
- return -code error "Invalid url \"$url\": _httpopen only supports HTTP"
- }
- set url [split $url /]
- set hp [split [lindex $url 2] :]
- set host [lindex $hp 0]
- set port [lindex $hp 1]
- if [string match $port ""] {set port 80}
- set uri /[join [lrange $url 3 end] /]
- set fds [ns_sockopen -nonblock $host $port]
- set rfd [lindex $fds 0]
- set wfd [lindex $fds 1]
- if [catch {
- _ns_http_puts $timeout $wfd "$method $uri HTTP/1.0\r"
- if {$rqset != ""} {
- for {set i 0} {$i < [ns_set size $rqset]} {incr i} {
- _ns_http_puts $timeout $wfd \
- "[ns_set key $rqset $i]: [ns_set value $rqset $i]\r"
- }
- } else {
- _ns_http_puts $timeout $wfd \
- "Accept: */*\r"
+ if ![string match http://* $url] {
+ return -code error "Invalid url \"$url\": _httpopen only supports HTTP"
+ }
+ set url [split $url /]
+ set hp [split [lindex $url 2] :]
+ set host [lindex $hp 0]
+ set port [lindex $hp 1]
+ if [string match $port ""] {set port 80}
+ set uri /[join [lrange $url 3 end] /]
+ set fds [ns_sockopen -nonblock $host $port]
+ set rfd [lindex $fds 0]
+ set wfd [lindex $fds 1]
+ if [catch {
+ _ns_http_puts $timeout $wfd "$method $uri HTTP/1.0\r"
+ if {$rqset != ""} {
+ for {set i 0} {$i < [ns_set size $rqset]} {incr i} {
+ _ns_http_puts $timeout $wfd \
+ "[ns_set key $rqset $i]: [ns_set value $rqset $i]\r"
+ }
+ } else {
+ _ns_http_puts $timeout $wfd \
+ "Accept: */*\r"
- _ns_http_puts $timeout $wfd "User-Agent: Mozilla/1.01 \[en\] (Win95; I)\r"
- _ns_http_puts $timeout $wfd "Referer: $http_referer \r"
+ _ns_http_puts $timeout $wfd "User-Agent: Mozilla/1.01 \[en\] (Win95; I)\r"
+ _ns_http_puts $timeout $wfd "Referer: $http_referer \r"
}
} errMsg] {
- global errorInfo
- #close $wfd
- #close $rfd
- if [info exists rpset] {ns_set free $rpset}
- return -1
- }
- return [list $rfd $wfd ""]
+ global errorInfo
+ #close $wfd
+ #close $rfd
+ if [info exists rpset] {ns_set free $rpset}
+ return -1
+ }
+ return [list $rfd $wfd ""]
}
@@ -1554,7 +1430,15 @@
# in the event of an error or timeout, -1 is returned
-proc_doc util_httppost {url formvars {timeout 30} {depth 0} {http_referer ""}} "Returns the result of POSTing to another Web server or -1 if there is an error or timeout. formvars should be in the form \"arg1=value1&arg2=value2\"" {
+ad_proc -public util_httppost {url formvars {timeout 30} {depth 0} {http_referer ""}} {
+ Returns the result of POSTing to another Web server or -1 if there is an error or timeout.
+ formvars should be in the form \"arg1=value1&arg2=value2\".
+
+ post is encoded as application/x-www-form-urlencoded. See util_http_file_upload + for file uploads via post (encoded multipart/form-data). +
+ @see util_http_file_upload +} { if [catch { if {[incr depth] > 10} { return -code error "util_httppost: Recursive redirection: $url" @@ -1613,7 +1497,11 @@ return $page } -proc_doc util_report_successful_library_load {{extra_message ""}} "Should be called at end of private Tcl library files so that it is easy to see in the error log whether or not private Tcl library files contain errors." { +ad_proc -public util_report_successful_library_load {{extra_message ""}} { + Should be called at end of private Tcl library files so that it is + easy to see in the error log whether or not private Tcl library + files contain errors. +} { set tentative_path [info script] regsub -all {/\./} $tentative_path {/} scrubbed_path if { [string compare $extra_message ""] == 0 } { @@ -1624,12 +1512,20 @@ ns_log Notice $message } -proc_doc exists_and_not_null { varname } {Returns 1 if the variable name exists in the caller's environment and is not the empty string.} { +ad_proc -public exists_and_not_null { varname } { + Returns 1 if the variable name exists in the caller's environment and + is not the empty string. +} { upvar 1 $varname var return [expr { [info exists var] && ![empty_string_p $var] }] } -proc_doc util_httpget {url {headers ""} {timeout 30} {depth 0}} "Just like ns_httpget, but first optional argument is an ns_set of headers to send during the fetch." { +ad_proc -public util_httpget { + url {headers ""} {timeout 30} {depth 0} +} { + Just like ns_httpget, but first optional argument is an ns_set of + headers to send during the fetch. +} { if {[incr depth] > 10} { return -code error "util_httpget: Recursive redirection: $url" } @@ -1674,34 +1570,15 @@ # added by philg@mit.edu on October 30, 1999 proc_doc util_escape_quotes_for_csv {string} "Returns its argument with double quote replaced by backslash double quote" { - regsub -all {"} $string {\"} result + regsub -all \" $string {\"} result + return $result } -proc_doc set_csv_variables_after_query {} { - You can call this after an ns_db getrow or ns_db 1row to set local - Tcl variables to values from the database. You get $foo, $EQfoo - (the same thing but with double quotes escaped), and $QEQQ - (same thing as $EQfoo but with double quotes around the entire - she-bang). +ad_proc -deprecated -warn ad_page_variables {variable_specs} { + use ad_page_contract now. -} { - uplevel { - set set_variables_after_query_i 0 - set set_variables_after_query_limit [ns_set size $selection] - while {$set_variables_after_query_i<$set_variables_after_query_limit} { - set [ns_set key $selection $set_variables_after_query_i] [ns_set value $selection $set_variables_after_query_i] - set EQ[ns_set key $selection $set_variables_after_query_i] [util_escape_quotes_for_csv [string trim [ns_set value $selection $set_variables_after_query_i]]] - set QEQQ[ns_set key $selection $set_variables_after_query_i] "\"[util_escape_quotes_for_csv [string trim [ns_set value $selection $set_variables_after_query_i]]]\"" - incr set_variables_after_query_i - } - } -} - -#" - -proc_doc ad_page_variables {variable_specs} {
Current syntax: @@ -1744,6 +1621,7 @@ {end {[expr $start + 20]}} }+ @see ad_page_contract } { set exception_list [list] set form [ns_getform] @@ -1863,12 +1741,14 @@ ns_returnerror 500 [lindex $exception_list 0] return -code return } elseif { $n_exceptions > 1 } { - ns_returnerror 500 "
This proc allows page arg, etc. validation. It accepts a bunch of code blocks. Each one is executed, and any error signalled is appended to the list of exceptions. @@ -1887,6 +1767,8 @@ nesting of procs which do the validation tests. In addition, validation functions can return useful values, such as trimmed or otherwise munged versions of the input. + + @see ad_page_contract } { if { [info exists {%%exception_list}] } { error "Something's wrong" @@ -1908,15 +1790,19 @@ if { $n_exceptions == 1 } { $complain_proc $n_exceptions [lindex $exception_list 0] } else { - $complain_proc $n_exceptions "
Use this inside a page_validation block which needs to check more than one thing. Put this around each part that might signal an error. + + @see ad_page_contract } { # to allow this to be at any level, we search up the stack for {%%exception_list} set depth [info level] @@ -1936,10 +1822,14 @@ } } -proc_doc validate_integer {field_name string} { +ad_proc -deprecated validate_integer {field_name string} { Throws an error if the string isn't a decimal integer; otherwise strips any leading zeros (so this won't work for octals) and returns the result. +
+ validate via ad_page_contract + + @see ad_page_contract } { if { ![regexp {^[0-9]+$} $string] } { error "$field_name is not an integer" @@ -1953,10 +1843,13 @@ return $string } -proc_doc validate_zip_code {field_name zip_string country_code} { - +ad_proc -deprecated validate_zip_code {field_name zip_string country_code} { Given a string, signals an error if it's not a legal zip code +
+ validate via ad_page_contract + @see ad_page_contract + } { if { $country_code == "" || [string toupper $country_code] == "US" } { if { [regexp {^[0-9][0-9][0-9][0-9][0-9](-[0-9][0-9][0-9][0-9])?$} $zip_string] } { @@ -1983,7 +1876,11 @@ return $zip_string } -proc_doc validate_ad_dateentrywidget {field_name column form {allow_null 0}} { +ad_proc -deprecated validate_ad_dateentrywidget {field_name column form {allow_null 0}} { +
+ validate via ad_page_contract + + @see ad_page_contract } { set col $column set day [ns_set get $form "$col.day"] @@ -2008,7 +1905,13 @@ return $date } -proc_doc util_WriteWithExtraOutputHeaders {headers_so_far {first_part_of_page ""}} "Takes in a string of headers to write to an HTTP connection, terminated by a newline. Checks \[ad_conn outputheaders\] and adds those headers if appropriate. Adds two newlines at the end and writes out to the connection. May optionally be used to write the first part of the page as well (saves a packet)" { +ad_proc -private util_WriteWithExtraOutputHeaders {headers_so_far {first_part_of_page ""}} { + Takes in a string of headers to write to an HTTP connection, + terminated by a newline. Checks \[ad_conn outputheaders\] and adds + those headers if appropriate. Adds two newlines at the end and writes + out to the connection. May optionally be used to write the first part + of the page as well (saves a packet). +} { ns_set put [ad_conn outputheaders] Server "[ns_info name]/[ns_info version]" set set_headers_i 0 set set_headers_limit [ns_set size [ad_conn outputheaders]] @@ -2020,22 +1923,24 @@ ns_write $entire_string_to_write } -# we use this when we want to send out just the headers -# and then do incremental ns_writes. This way the user -# doesn't have to wait like if you used a single ns_return -proc ReturnHeaders {{content_type text/html}} { +ad_proc -public ReturnHeaders {{content_type text/html}} { + we use this when we want to send out just the headers + nd then do incremental ns_writes. This way the user + doesn't have to wait like if you used a single ns_return +} { set all_the_headers "HTTP/1.0 200 OK MIME-Version: 1.0 Content-Type: $content_type\r\n" util_WriteWithExtraOutputHeaders $all_the_headers ns_startcontent -type $content_type } -# All the following ReturnHeaders versions are obsolete; -# just set [ad_conn outputheaders]. -proc ReturnHeadersNoCache {{content_type text/html}} { +ad_proc -deprecated -warn ReturnHeadersNoCache {{content_type text/html}} { + Deprecated. just set [ad_conn outputheaders]. + @see ad_conn +} { ns_write "HTTP/1.0 200 OK MIME-Version: 1.0 @@ -2045,8 +1950,10 @@ ns_startcontent -type $content_type } -proc ReturnHeadersWithCookie {cookie_content {content_type text/html}} { - +ad_proc -deprecated -warn ReturnHeadersWithCookie {cookie_content {content_type text/html}} { + Deprecated. just set [ad_conn outputheaders]. + @see ad_conn +} { ns_write "HTTP/1.0 200 OK MIME-Version: 1.0 Content-Type: $content_type @@ -2055,7 +1962,10 @@ ns_startcontent -type $content_type } -proc ReturnHeadersWithCookieNoCache {cookie_content {content_type text/html}} { +ad_proc -deprecated -warn ReturnHeadersWithCookieNoCache {cookie_content {content_type text/html}} { + Deprecated. just set [ad_conn outputheaders]. + @see ad_conn +} { ns_write "HTTP/1.0 200 OK MIME-Version: 1.0 @@ -2066,11 +1976,15 @@ ns_startcontent -type $content_type } -proc_doc ad_return_top_of_page {first_part_of_page {content_type text/html}} "Returns HTTP headers plus the top of the user-ivisible page. Saves a TCP packet (and therefore some overhead) compared to using ReturnHeaders and an ns_write." { +ad_proc -public ad_return_top_of_page {first_part_of_page {content_type text/html}} { + Returns HTTP headers plus the top of the user-visible page. Saves a + TCP packet (and therefore some overhead) compared to using + ReturnHeaders and an ns_write. +} { set all_the_headers "HTTP/1.0 200 OK MIME-Version: 1.0 Content-Type: $content_type\r\n" - util_WriteWithExtraOutputHeaders $all_the_headers + util_WriteWithExtraOutputHeaders $all_the_headers ns_startcontent -type $content_type @@ -2079,17 +1993,17 @@ } } -proc_doc apply {func arglist} { +ad_proc -public apply {func arglist} { Evaluates the first argument with ARGLIST as its arguments, in the environment of its caller. Analogous to the Lisp function of the same name. } { set func_and_args [concat $func $arglist] return [uplevel $func_and_args] } -proc_doc safe_eval args { - Version of eval that checks its arguments for brackets that may be -used to execute unsafe code. +ad_proc -public safe_eval args { + Version of eval that checks its arguments for brackets + that may be used to execute unsafe code. } { foreach arg $args { if { [regexp {[\[;]} $arg] } { @@ -2099,15 +2013,25 @@ return [apply uplevel $args] } -proc_doc lmap {list proc_name} {Applies proc_name to each item of the list, appending the result of each call to a new list that is the return value.} { +ad_proc -public lmap {list proc_name} { + Applies proc_name to each item of the list, appending the result of + each call to a new list that is the return value. +} { set lmap [list] foreach item $list { lappend lmap [safe_eval $proc_name $item] } return $lmap } -proc_doc ad_decode { args } "this procedure is analogus to sql decode procedure. first parameter is the value we want to decode. this parameter is followed by a list of pairs where first element in the pair is convert from value and second element is convert to value. last value is default value, which will be returned in the case convert from values matches the given value to be decoded" { +ad_proc -public ad_decode { args } { + this procedure is analogus to sql decode procedure. first parameter is + the value we want to decode. this parameter is followed by a list of + pairs where first element in the pair is convert from value and second + element is convert to value. last value is default value, which will + be returned in the case convert from values matches the given value to + be decoded +} { set num_args [llength $args] set input_value [lindex $args 0] @@ -2135,17 +2059,21 @@ } } -proc_doc ad_urlencode { string } "same as ns_urlencode except that dash and underscore are left unencoded." { +ad_proc -public ad_urlencode { string } { + same as ns_urlencode except that dash and underscore are left unencoded. +} { set encoded_string [ns_urlencode $string] regsub -all {%2d} $encoded_string {-} encoded_string regsub -all {%5f} $encoded_string {_} ad_encoded_string return $ad_encoded_string } -ad_proc ad_get_cookie { +ad_proc -public ad_get_cookie { { -include_set_cookies t } name { default "" } -} { "Returns the value of a cookie, or $default if none exists." } { +} { + Returns the value of a cookie, or $default if none exists. +} { if { $include_set_cookies == "t" } { set headers [ad_conn outputheaders] for { set i 0 } { $i < [ns_set size $headers] } { incr i } { @@ -2172,7 +2100,7 @@ return $default } -ad_proc ad_set_cookie { +ad_proc -public ad_set_cookie { { -replace f -secure f @@ -2211,6 +2139,7 @@ @param value is autmatically URL encoded. + @see ad_get_cookie } { set headers [ad_conn outputheaders] if { $replace != "f" } { @@ -2259,7 +2188,9 @@ ns_set put $headers "Set-Cookie" $cookie } -proc_doc ad_run_scheduled_proc { proc_info } { Runs a scheduled procedure and updates monitoring information in the shared variables. } { +ad_proc -private ad_run_scheduled_proc { proc_info } { + Runs a scheduled procedure and updates monitoring information in the shared variables. +} { # Grab information about the scheduled procedure. set thread [lindex $proc_info 0] set once [lindex $proc_info 1] @@ -2313,17 +2244,35 @@ nsv_set ad_procs . "" } -ad_proc ad_schedule_proc { +ad_proc -public ad_schedule_proc { { -thread f -once f -debug t -all_servers f + -schedule_proc "" } interval proc args -} { Replacement for ns_schedule_proc, allowing us to track what's going on. Can be monitored via /admin/monitoring/schedule-procs.tcl. The procedure defaults to run on only the canonical server unless the all_servers flag is set to true. } { +} { + Replacement for ns_schedule_proc and friends, allowing us to track what's going + on. Can be monitored via /admin/monitoring/schedule-procs.tcl. The + procedure defaults to run on only the canonical server unless the + all_servers flag is set to true. + + @param thread If true run scheduled proc in its own thread + @param once If true only run the scheduled proc once + @param debug If true log debugging information + @param all_servers If true run on all servers in a cluster + @param schedule_proc ns_schedule_daily, ns_schedule_weekly or blank + @param interval If schedule_proc is empty, the interval to run the proc + in seconds, otherwise a list of interval arguments to pass to + ns_schedule_daily or ns_schedule_weekly + @param proc The proc to schedule + @param args And the args to pass it + +} { # we don't schedule a proc to run if we have enabled server clustering, # we're not the canonical server, and the procedure was not requested to run on all servers. if { [server_cluster_enabled_p] && ![ad_canonical_server_p] && $all_servers == "f" } { @@ -2350,17 +2299,22 @@ } # Schedule the wrapper procedure (ad_run_scheduled_proc). - eval [concat [list ns_schedule_proc] $my_args [list $interval ad_run_scheduled_proc [list $proc_info]]] + + if { [empty_string_p $schedule_proc] } { + eval [concat [list ns_schedule_proc] $my_args [list $interval ad_run_scheduled_proc [list $proc_info]]] + } else { + eval [concat [list $schedule_proc] $my_args $interval [list ad_run_scheduled_proc [list $proc_info]]] + } } -proc util_ReturnMetaRefresh { url { seconds_delay 0 }} { +ad_proc -deprecated util_ReturnMetaRefresh { url { seconds_delay 0 }} { ReturnHeaders ns_write "
- + - If your browser does not automatically redirect you, please go here. + If your browser does not automatically redirect you, please go here. " } @@ -2369,7 +2323,7 @@ # util_current_directory # See: http://www.arsdigita.com/bboard/q-and-a-fetch-msg.tcl?msg_id=0003eV -ad_proc ad_returnredirect {{} target_url} { +ad_proc -public ad_returnredirect {{} target_url} { A replacement for ns_returnredirect. It uses ns_returnredirect but is better in two important aspects:
We can't just use [file dirname [ad_conn url]] because
@@ -2481,23 +2483,9 @@
}
}
-proc util_aolserver_2_p {} {
- if {[string index [ns_info version] 0] == "2"} {
- return 1
- } else {
- return 0
- }
-}
-proc_doc ad_chdir_and_exec { dir arg_list } { chdirs to $dir and executes the command in $arg_list. We'll probably want to improve this to be thread-safe. } {
- cd $dir
- eval exec $arg_list
-}
-
-proc_doc ad_call_proc_if_exists { proc args } {
-
-Calls a procedure with particular arguments, only if the procedure is defined.
-
+ad_proc -public ad_call_proc_if_exists { proc args } {
+ Calls a procedure with particular arguments, only if the procedure is defined.
} {
if { [llength [info procs $proc]] == 1 } {
eval $proc $args
@@ -2775,23 +2763,23 @@
return [regexp "^\[^@<>\"\t ]+@\[^@<>\".\t ]+(\\.\[^@<>\".\n ]+)+$" $query_email]
}
-ad_proc util_email_unique_p { email } {
+ad_proc -public util_email_unique_p { email } {
Returns 1 if the email passed in does not yet exist in the system.
@author yon (yon@openforce.net)
} {
return [db_string email_unique_p {}]
}
-ad_proc util_url_valid_p { query_url } {
+ad_proc -public util_url_valid_p { query_url } {
Returns 1 if a URL is a web URL (HTTP or HTTPS).
@author Philip Greenspun (philg@mit.edu)
} {
- return [regexp {https?://.+} $query_url]
+ return [regexp {https?://[^ ].+} [string trim $query_url]]
}
-ad_proc value_if_exists { var_name } {
+ad_proc -public value_if_exists { var_name } {
If the specified variable exists in the calling environment,
returns the value of that variable. Otherwise, returns the
empty_string.
@@ -2802,7 +2790,23 @@
}
}
-ad_proc max { args } {
+ad_proc -public min { args } {
+ Returns the minimum of a list of numbers. Example: min 2 3 1.5
returns 1.5.
+
+ @author Ken Mayer (kmayer@bitwrangler.com)
+ @creation-date 26 September 2002
+} {
+ set min [lindex $args 0]
+ foreach arg $args {
+ if { $arg < $min } {
+ set min $arg
+ }
+ }
+ return $min
+}
+
+
+ad_proc -public max { args } {
Returns the maximum of a list of numbers. Example: max 2 3 1.5
returns 3.
@author Lars Pind (lars@pinds.com)
@@ -2817,7 +2821,7 @@
return $max
}
-proc_doc -deprecated ad_check_for_naughty_html {user_submitted_html} {
+ad_proc -deprecated -warn ad_check_for_naughty_html {user_submitted_html} {
This proc is deprecated. Please use ad_html_security_check
@@ -2829,7 +2833,9 @@
HTML tags marked as naughty in the antispam section of ad.ini, otherwise
returns an empty string.
+@see ad_html_security_check
} {
+
set tag_names [list div font]
# look for a less than sign, zero or more spaces, then the tag
if { ! [empty_string_p $tag_names]} {
@@ -2853,20 +2859,18 @@
proc ad_dateentrywidget {column {default_date "1940-11-03"}} {
ns_share NS
- set output " "
+
return [ns_dbformvalueput $output $column date $default_date]
}
-proc ad_dateentrywidget_default_to_today {column} {
+ad_proc -deprecated -warn ad_dateentrywidget_default_to_today {column} {
set today [lindex [split [ns_localsqltimestamp] " "] 0]
return [ad_dateentrywidget $column $today]
}
@@ -2905,3 +2909,599 @@
return $setid
}
+
+
+ad_proc -public util_http_file_upload { -file -data -binary:boolean -filename
+ -name {-mime_type */*} {-mode formvars}
+ {-rqset ""} url {formvars {}} {timeout 30}
+ {depth 10} {http_referer ""}
+} {
+ Implement client-side HTTP file uploads as multipart/form-data as per
+ RFC 1867.
+
+ + Similar to util_httppost, + but enhanced to be able to upload a file as multipart/form-data. + Also useful for posting to forms that require their input to be encoded + as multipart/form-data instead of as + application/x-www-form-urlencoded. + +
+ + The switches -file /path/to/file and -data $raw_data + are mutually exclusive. You can specify one or the other, but not + both. NOTE: it is perfectly valid to not specify either, in which + case no file is uploaded, but form variables are encoded using + multipart/form-data instead of the usual encoding (as + noted aboved). + +
+ + If you specify either -file or -data you + must supply a value for -name, which is + the name of the <INPUT TYPE="file" NAME="..."> form + tag. + +
+ + Specify the -binary switch if the file (or data) needs + to be base-64 encoded. Not all servers seem to be able to handle + this. (For example, http://mol-stage.usps.com/mml.adp, which + expects to receive an XML file doesn't seem to grok any kind of + Content-Transfer-Encoding.) + +
+ + If you specify -file then -filename is optional + (it can be infered from the name of the file). However, if you + specify -data then it is mandatory. + +
+ + If -mime_type is not specified then ns_guesstype + is used to try and find a mime type based on the filename. + If ns_guesstype returns */* the generic value + of application/octet-stream will be used. + +
+ + Any form variables may be specified in one of four formats: +
+ + -rqset specifies an ns_set of extra headers to send to + the server when doing the POST. + +
+ + timeout, depth, and http_referer are optional, and are included + as optional positional variables in the same order they are used + in util_httppost. NOTE: util_http_file_upload does + not (currently) follow any redirects, so depth is superfulous. + + @author Michael A. Cleverly (michael@cleverly.com) + @creation-date 3 September 2002 +} { + + # sanity checks on switches given + if {[lsearch -exact {formvars array ns_set vars} $mode] == -1} { + error "Invalid mode \"$mode\"; should be one of: formvars,\ + array, ns_set, vars" + } + + if {[info exists file] && [info exists data]} { + error "Both -file and -data are mutually exclusive; can't use both" + } + + if {[info exists file]} { + if {![file exists $file]} { + error "Error reading file: $file not found" + } + + if {![file readable $file]} { + error "Error reading file: $file permission denied" + } + + set fp [open $file] + fconfigure $fp -translation binary + set data [read $fp] + close $fp + + if {![info exists filename]} { + set filename [file tail $file] + } + + if {[string equal */* $mime_type] || [empty_string_p $mime_type]} { + set mime_type [ns_guesstype $file] + } + } + + set boundary [ns_sha1 [list [clock clicks] [clock seconds]]] + set payload {} + + if {[info exists data] && [string length $data]} { + if {![info exists name]} { + error "Cannot upload file without specifing form variable -name" + } + + if {![info exists filename]} { + error "Cannot upload file without specifing -filename" + } + + if {[string equal $mime_type */*] || [empty_string_p $mime_type]} { + set mime_type [ns_guesstype $filename] + + if {[string equal $mime_type */*] || [empty_string_p $mime_type]} { + set mime_type application/octet-stream + } + } + + if {$binary_p} { + set data [base64::encode base64] + set transfer_encoding base64 + } else { + set transfer_encoding binary + } + + append payload --$boundary \ + \r\n \ + "Content-Disposition: form-data; " \ + "name=\"$name\"; filename=\"$filename\"" \ + \r\n \ + "Content-Type: $mime_type" \ + \r\n \ + "Content-transfer-encoding: $transfer_encoding" \ + \r\n \ + \r\n \ + $data \ + \r\n + } + + + set variables [list] + switch -- $mode { + array { + set variables $formvars + } + + formvars { + foreach formvar [split $formvars &] { + set formvar [split $formvar =] + set key [lindex $formvar 0] + set val [join [lrange $formvar 1 end] =] + lappend variables $key $val + } + } + + ns_set { + for {set i 0} {$i < [ns_set size $formvars]} {incr i} { + set key [ns_set key $formvars $i] + set val [ns_set value $formvars $i] + lappend variables $key $val + } + } + + vars { + foreach key $formvars { + upvar 1 $key val + lappend variables $key $val + } + } + } + + foreach {key val} $variables { + append payload --$boundary \ + \r\n \ + "Content-Disposition: form-data; name=\"$key\"" \ + \r\n \ + \r\n \ + $val \ + \r\n + } + + append payload --$boundary-- \r\n + + if [catch { + if {[incr depth -1] <= 0} { + return -code error "util_http_file_upload:\ + Recursive redirection: $url" + } + + set http [util_httpopen POST $url $rqset $timeout $http_referer] + set rfd [lindex $http 0] + set wfd [lindex $http 1] + + _ns_http_puts $timeout $wfd \ + "Content-type: multipart/form-data; boundary=$boundary\r" + _ns_http_puts $timeout $wfd "Content-length: [string length $payload]\r" + _ns_http_puts $timeout $wfd \r + _ns_http_puts $timeout $wfd "$payload\r" + flush $wfd + close $wfd + + set rpset [ns_set new [_ns_http_gets $timeout $rfd]] + while 1 { + set line [_ns_http_gets $timeout $rfd] + if ![string length $line] break + ns_parseheader $rpset $line + } + + set headers $rpset + set response [ns_set name $headers] + set status [lindex $response 1] + set length [ns_set iget $headers content-length] + if [string match "" $length] { set length -1 } + set err [catch { + while 1 { + set buf [_ns_http_read $timeout $rfd $length] + append page $buf + if [string match "" $buf] break + if {$length > 0} { + incr length -[string length $buf] + if {$length <= 0} break + } + } + } errMsg] + + ns_set free $headers + close $rfd + + if $err { + global errorInfo + return -code error -errorinfo $errorInfo $errMsg + } + } errmsg] {return -1} + + return $page +} + + +# base64.tcl -- +# +# Encode/Decode base64 for a string +# Stephen Uhler / Brent Welch (c) 1997 Sun Microsystems +# The decoder was done for exmh by Chris Garrigues +# +# Copyright (c) 1998-2000 by Ajuba Solutions. +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id$ + +# Version 1.0 implemented Base64_Encode, Bae64_Decode +# Version 2.0 uses the base64 namespace +# Version 2.1 fixes various decode bugs and adds options to encode +# Version 2.2 is much faster, Tcl8.0 compatible + +package require Tcl 8 +namespace eval base64 { + namespace export encode decode +} + +if {![catch {package require Trf 2.0}]} { + # Trf is available, so implement the functionality provided here + # in terms of calls to Trf for speed. + + # base64::encode -- + # + # Base64 encode a given string. + # + # Arguments: + # args ?-maxlen maxlen? ?-wrapchar wrapchar? string + # + # If maxlen is 0, the output is not wrapped. + # + # Results: + # A Base64 encoded version of $string, wrapped at $maxlen characters + # by $wrapchar. + + ad_proc -public ::base64::encode {args} { + Tcl implementation of base64::encode from tcllib 1.1. + (Tcllib is distributed under the same terms as the Tcl core, i.e., + the BSD license). See http://tcllib.sourceforge.net for the latest. + +
+ + This version uses the Trf extension for speed. + } { + # Set the default wrapchar and maximum line length to match the output + # of GNU uuencode 4.2. Various RFC's allow for different wrapping + # characters and wraplengths, so these may be overridden by command line + # options. + set wrapchar "\n" + set maxlen 60 + + if { [llength $args] == 0 } { + error "wrong # args: should be \"[lindex [info level 0] 0]\ + ?-maxlen maxlen? ?-wrapchar wrapchar? string\"" + } + + set optionStrings [list "-maxlen" "-wrapchar"] + for {set i 0} {$i < [llength $args] - 1} {incr i} { + set arg [lindex $args $i] + set index [lsearch -glob $optionStrings "${arg}*"] + if { $index == -1 } { + error "unknown option \"$arg\": must be -maxlen or -wrapchar" + } + incr i + if { $i >= [llength $args] - 1 } { + error "value for \"$arg\" missing" + } + set val [lindex $args $i] + + # The name of the variable to assign the value to is extracted + # from the list of known options, all of which have an + # associated variable of the same name as the option without + # a leading "-". The [string range] command is used to strip + # of the leading "-" from the name of the option. + # + # FRINK: nocheck + set [string range [lindex $optionStrings $index] 1 end] $val + } + + # [string is] requires Tcl8.2; this works with 8.0 too + if {[catch {expr {$maxlen % 2}}]} { + error "expected integer but got \"$maxlen\"" + } + + set string [lindex $args end] + set result [::base64 -mode encode -- $string] + regsub -all -- \n $result {} result + + if {$maxlen > 0} { + set res "" + set edge [expr {$maxlen - 1}] + while {[string length $result] > $maxlen} { + append res [string range $result 0 $edge]$wrapchar + set result [string range $result $maxlen end] + } + if {[string length $result] > 0} { + append res $result + } + set result $res + } + + return $result + } + + # base64::decode -- + # + # Base64 decode a given string. + # + # Arguments: + # string The string to decode. Characters not in the base64 + # alphabet are ignored (e.g., newlines) + # + # Results: + # The decoded value. + + ad_proc -public ::base64::decode {string} { + Tcl implementation of base64::encode from tcllib 1.1. + (Tcllib is distributed under the same terms as the Tcl core, i.e., + the BSD license). See http://tcllib.sourceforge.net for the latest. + +
+ + This version uses the Trf extension for speed. + } { + ::base64 -mode decode -- $string + } + +} else { + # Without Trf use a pure tcl implementation + + namespace eval base64 { + variable base64 {} + variable base64_en {} + + # We create the auxiliary array base64_tmp, it will be unset later. + + set i 0 + foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ + a b c d e f g h i j k l m n o p q r s t u v w x y z \ + 0 1 2 3 4 5 6 7 8 9 + /} { + set base64_tmp($char) $i + lappend base64_en $char + incr i + } + + # + # Create base64 as list: to code for instance C<->3, specify + # that [lindex $base64 67] be 3 (C is 67 in ascii); non-coded + # ascii chars get a {}. we later use the fact that lindex on a + # non-existing index returns {}, and that [expr {} < 0] is true + # + + # the last ascii char is 'z' + scan z %c len + for {set i 0} {$i <= $len} {incr i} { + set char [format %c $i] + set val {} + if {[info exists base64_tmp($char)]} { + set val $base64_tmp($char) + } else { + set val {} + } + lappend base64 $val + } + + # code the character "=" as -1; used to signal end of message + scan = %c i + set base64 [lreplace $base64 $i $i -1] + + # remove unneeded variables + unset base64_tmp i char len val + + namespace export encode decode + } + + # base64::encode -- + # + # Base64 encode a given string. + # + # Arguments: + # args ?-maxlen maxlen? ?-wrapchar wrapchar? string + # + # If maxlen is 0, the output is not wrapped. + # + # Results: + # A Base64 encoded version of $string, wrapped at $maxlen characters + # by $wrapchar. + + ad_proc -public ::base64::encode {args} { + Tcl implementation of base64::encode from tcllib 1.1. + (Tcllib is distributed under the same terms as the Tcl core, i.e., + the BSD license). See http://tcllib.sourceforge.net for the latest. + } { + set base64_en $::base64::base64_en + + # Set the default wrapchar and maximum line length to match the output + # of GNU uuencode 4.2. Various RFC's allow for different wrapping + # characters and wraplengths, so these may be overridden by command line + # options. + set wrapchar "\n" + set maxlen 60 + + if { [llength $args] == 0 } { + error "wrong # args: should be \"[lindex [info level 0] 0]\ + ?-maxlen maxlen? ?-wrapchar wrapchar? string\"" + } + + set optionStrings [list "-maxlen" "-wrapchar"] + for {set i 0} {$i < [llength $args] - 1} {incr i} { + set arg [lindex $args $i] + set index [lsearch -glob $optionStrings "${arg}*"] + if { $index == -1 } { + error "unknown option \"$arg\": must be -maxlen or -wrapchar" + } + incr i + if { $i >= [llength $args] - 1 } { + error "value for \"$arg\" missing" + } + set val [lindex $args $i] + + # The name of the variable to assign the value to is extracted + # from the list of known options, all of which have an + # associated variable of the same name as the option without + # a leading "-". The [string range] command is used to strip + # of the leading "-" from the name of the option. + # + # FRINK: nocheck + set [string range [lindex $optionStrings $index] 1 end] $val + } + + # [string is] requires Tcl8.2; this works with 8.0 too + if {[catch {expr {$maxlen % 2}}]} { + error "expected integer but got \"$maxlen\"" + } + + set string [lindex $args end] + + set result {} + set state 0 + set length 0 + + + # Process the input bytes 3-by-3 + + binary scan $string c* X + foreach {x y z} $X { + # Do the line length check before appending so that we don't get an + # extra newline if the output is a multiple of $maxlen chars long. + if {$maxlen && $length >= $maxlen} { + append result $wrapchar + set length 0 + } + + append result [lindex $base64_en [expr {($x >>2) & 0x3F}]] + if {$y != {}} { + append result [lindex $base64_en [expr {(($x << 4) & 0x30) | (($y >> 4) & 0xF)}]] + if {$z != {}} { + append result \ + [lindex $base64_en [expr {(($y << 2) & 0x3C) | (($z >> 6) & 0x3)}]] + append result [lindex $base64_en [expr {($z & 0x3F)}]] + } else { + set state 2 + break + } + } else { + set state 1 + break + } + incr length 4 + } + if {$state == 1} { + append result [lindex $base64_en [expr {(($x << 4) & 0x30)}]]== + } elseif {$state == 2} { + append result [lindex $base64_en [expr {(($y << 2) & 0x3C)}]]= + } + return $result + } + + # base64::decode -- + # + # Base64 decode a given string. + # + # Arguments: + # string The string to decode. Characters not in the base64 + # alphabet are ignored (e.g., newlines) + # + # Results: + # The decoded value. + + ad_proc -public ::base64::decode {string} { + Tcl implementation of base64::encode from tcllib 1.1. + (Tcllib is distributed under the same terms as the Tcl core, i.e., + the BSD license). See http://tcllib.sourceforge.net for the latest. + } { + set base64 $::base64::base64 + + binary scan $string c* X + foreach x $X { + set bits [lindex $base64 $x] + if {$bits >= 0} { + if {[llength [lappend nums $bits]] == 4} { + foreach {v w z y} $nums break + set a [expr {($v << 2) | ($w >> 4)}] + set b [expr {(($w & 0xF) << 4) | ($z >> 2)}] + set c [expr {(($z & 0x3) << 6) | $y}] + append output [binary format ccc $a $b $c] + set nums {} + } + } elseif {$bits == -1} { + # = indicates end of data. Output whatever chars are left. + # The encoding algorithm dictates that we can only have 1 or 2 + # padding characters. If x=={}, we have 12 bits of input + # (enough for 1 8-bit output). If x!={}, we have 18 bits of + # input (enough for 2 8-bit outputs). + + foreach {v w z} $nums break + set a [expr {($v << 2) | (($w & 0x30) >> 4)}] + + if {$z == {}} { + append output [binary format c $a ] + } else { + set b [expr {(($w & 0xF) << 4) | (($z & 0x3C) >> 2)}] + append output [binary format cc $a $b] + } + break + } else { + # RFC 2045 says that line breaks and other characters not part + # of the Base64 alphabet must be ignored, and that the decoder + # can optionally emit a warning or reject the message. We opt + # not to do so, but to just ignore the character. + continue + } + } + return $output + } +} + +# don't want to barf if, per chance, a newer version is already available +catch { package provide base64 2.2 }