Index: openacs-4/packages/acs-templating/acs-templating.info =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/acs-templating.info,v diff -u -N -r1.66.2.18 -r1.66.2.19 --- openacs-4/packages/acs-templating/acs-templating.info 15 Oct 2021 10:12:43 -0000 1.66.2.18 +++ openacs-4/packages/acs-templating/acs-templating.info 26 Jan 2022 16:06:08 -0000 1.66.2.19 @@ -9,7 +9,7 @@ f t - + OpenACS Templating library. 2021-09-15 @@ -27,7 +27,7 @@ GPL version 2 3 - + Index: openacs-4/packages/acs-templating/catalog/acs-templating.en_US.ISO-8859-1.xml =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/catalog/acs-templating.en_US.ISO-8859-1.xml,v diff -u -N -r1.25.2.5 -r1.25.2.6 --- openacs-4/packages/acs-templating/catalog/acs-templating.en_US.ISO-8859-1.xml 18 Aug 2021 16:32:28 -0000 1.25.2.5 +++ openacs-4/packages/acs-templating/catalog/acs-templating.en_US.ISO-8859-1.xml 26 Jan 2022 16:06:08 -0000 1.25.2.6 @@ -129,4 +129,5 @@ Year year Year must be positive + Your captcha is invalid Index: openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql,v diff -u -N -r1.1 -r1.1.32.1 --- openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql 5 Apr 2001 18:23:38 -0000 1.1 +++ openacs-4/packages/acs-templating/sql/oracle/acs-templating-create.sql 26 Jan 2022 16:06:08 -0000 1.1.32.1 @@ -10,3 +10,4 @@ -- http://www.fsf.org/copyleft/gpl.html @@ demo-create.sql +@@ captcha-create.sql Index: openacs-4/packages/acs-templating/sql/oracle/captcha-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/Attic/captcha-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/captcha-create.sql 26 Jan 2022 16:06:08 -0000 1.1.2.1 @@ -0,0 +1,9 @@ +-- Captcha data-model (untested) + +create table template_widget_captchas ( + image_checksum text primary key, + text text not null, + expiration timestamp not null default current_timestamp + cast('1 hour' as interval) +); + +create index template_widget_captchas_expiration_idx on template_widget_captchas(expiration); Index: openacs-4/packages/acs-templating/sql/oracle/upgrade/upgrade-5.10.1d0-5.10.1d1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/oracle/upgrade/Attic/upgrade-5.10.1d0-5.10.1d1.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/oracle/upgrade/upgrade-5.10.1d0-5.10.1d1.sql 26 Jan 2022 16:06:08 -0000 1.1.2.1 @@ -0,0 +1,14 @@ +-- (untested) +begin; + +create table if not exists template_widget_captchas ( + image_checksum text primary key, + text text not null, + expiration timestamp not null default current_timestamp + cast('1 hour' as interval) +); + +create index if not exists + template_widget_captchas_expiration_idx on + template_widget_captchas(expiration); + +end; Index: openacs-4/packages/acs-templating/sql/postgresql/acs-templating-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/postgresql/acs-templating-create.sql,v diff -u -N -r1.1 -r1.1.32.1 --- openacs-4/packages/acs-templating/sql/postgresql/acs-templating-create.sql 27 Apr 2001 02:27:09 -0000 1.1 +++ openacs-4/packages/acs-templating/sql/postgresql/acs-templating-create.sql 26 Jan 2022 16:06:08 -0000 1.1.32.1 @@ -10,3 +10,4 @@ -- http://www.fsf.org/copyleft/gpl.html \i demo-create.sql +\i captcha-create.sql Index: openacs-4/packages/acs-templating/sql/postgresql/captcha-create.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/postgresql/Attic/captcha-create.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/postgresql/captcha-create.sql 26 Jan 2022 16:06:08 -0000 1.1.2.1 @@ -0,0 +1,10 @@ +-- Captcha data-model + +create table template_widget_captchas ( + image_checksum text primary key, + text text not null, + expiration timestamp not null default current_timestamp + cast('1 hour' as interval) +); + +create index template_widget_captchas_expiration_idx on + template_widget_captchas(expiration); Index: openacs-4/packages/acs-templating/sql/postgresql/upgrade/upgrade-5.10.1d0-5.10.1d1.sql =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/sql/postgresql/upgrade/Attic/upgrade-5.10.1d0-5.10.1d1.sql,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/sql/postgresql/upgrade/upgrade-5.10.1d0-5.10.1d1.sql 26 Jan 2022 16:06:08 -0000 1.1.2.1 @@ -0,0 +1,13 @@ +begin; + +create table if not exists template_widget_captchas ( + image_checksum text primary key, + text text not null, + expiration timestamp not null default current_timestamp + cast('1 hour' as interval) +); + +create index if not exists + template_widget_captchas_expiration_idx on + template_widget_captchas(expiration); + +end; Index: openacs-4/packages/acs-templating/tcl/captcha-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/tcl/Attic/captcha-procs.tcl,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/acs-templating/tcl/captcha-procs.tcl 26 Jan 2022 16:06:08 -0000 1.1.2.1 @@ -0,0 +1,207 @@ +ad_library { + A captcha implementation for the template system based on + Imagemagick + + @author Antonio Pisano +} + +namespace eval captcha {} +namespace eval captcha::image {} + +ad_proc -private captcha::image::generate { + {-pointsize 40} + -text + {-background "#ffffff"} + {-fill "#000000"} +} { + Creates a distorted capcha image from a text. + + @param pointsize the font size as supported by convert + @param text the text to use for the captcha. When unspecified, a + random text will be used. The text can only contain + alphanumeric characters and spaces. + @param background the background color, as RGB 6 characters code. + @param fill the font color, background the background color, as + RGB 6 characters code. + + @see https://imagemagick.org/script/convert.php + + @return a dict of fields 'path' (path to the image), 'text' (the + text the image represents) and chec ksum (a checksum for + the image to use for matching). +} { + set convert [::util::which convert] + if {$convert eq ""} { + error {'convert' command not available.} + } + + if {![string is integer -strict $pointsize]} { + error {Invalid pointsize} + } + if {![regexp -nocase {^(\#([0-9]|[a-f]){6}){2}$} ${background}${fill}]} { + error {Invalid color} + } + if {[info exists text]} { + if {![regexp {^(\w| )*$} $text]} { + error {'text' can only contain alphanumerics and spaces} + } + } else { + set text [ad_generate_random_string 5] + } + + set path [ad_tmpnam].png + + exec $convert \ + -pointsize $pointsize \ + -background $background \ + -fill $fill \ + label:$text \ + -wave 25%x150% \ + $path + + if {![file exists $path]} { + error "File '$destination' was not generated" + } + + set checksum [ns_md file -digest sha1 $path] + + return [list \ + text $text \ + path $path \ + checksum $checksum] +} + +namespace eval template {} +namespace eval template::widget {} + +ad_proc -public template::widget::captcha { + element_reference + tag_attributes +} { + Generate a captcha text widget. This widget will display a captcha + image containing a text. On validation, the value supplied by the + user must match the value in the captcha. + + @param element_reference Reference variable to the form element + @param tag_attributes HTML attributes to add to the tag + + @return Form HTML for widget +} { + if {![ns_conn isconnected]} { + return + } + + upvar $element_reference element + + if {[info exists element(background)]} { + set background $element(background) + } else { + set background #ffffff + } + if {[info exists element(fill)]} { + set fill $element(fill) + } else { + set fill #000000 + } + if {[info exists element(pointsize)]} { + set pointsize $element(pointsize) + } else { + set pointsize 40 + } + + set captcha [captcha::image::generate \ + -background $background \ + -fill $fill \ + -pointsize $pointsize] + + set checksum [dict get $captcha checksum] + set text [dict get $captcha text] + + # The capcha image we are injecting directly into the page as + # base64 to not clutter the filesystem and mess around with + # request processor. + set captcha_path [dict get $captcha path] + set rfd [open $captcha_path r] + fconfigure $rfd -translation binary + set base64image [ns_base64encode -- [read $rfd]] + ::file delete -- $captcha_path + close $rfd + + # Store the checksum in the database together with the text we + # expect. + if {[info exists element(expire)]} { + set expiration $element(expire) + } else { + set expiration 3600 + } + db_dml store_captcha { + insert into template_widget_captchas + (image_checksum, text, expiration) + values + (:checksum, + :text, + current_timestamp + cast(:expiration || ' seconds' as interval) + ) + } + + set captcha_checksum_id $element(form_id):$element(name):image_checksum + return [subst { + +
+
[input text element $tag_attributes]
+ }] +} + +namespace eval template::data {} +namespace eval template::data::validate {} + +ad_proc -public template::data::validate::captcha { + value_ref + message_ref +} { + Validate the captcha widget by matching the image checksum against + the text that was supplied by the user. + + @param value_ref Reference variable to the submitted value. + @param message_ref Reference variable for returning an error + message. + + @return True (1) if valid, false (0) if not. +} { + if {![ns_conn isconnected]} { + return 1 + } + + upvar 2 \ + $message_ref message \ + $value_ref value \ + element element + + set checksum [ns_queryget $element(form_id):$element(name):image_checksum] + if {$checksum ne ""} { + # While we check for this particular captcha, we also sloppily + # cleanup the ones that have already expired. + db_dml check_captcha { + with cleanup_expired as ( + delete from template_widget_captchas + where expiration < current_timestamp + ) + delete from template_widget_captchas + where image_checksum = :checksum + and text = :value + } + set valid_p [db_resultrows] + } else { + set valid_p 0 + } + + if {!$valid_p} { + set message [_ acs-templating.Your_captcha_is_invalid] + } + + return $valid_p +} +