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 @@
ft
-
+ OpenACSTemplating library.2021-09-15
@@ -27,7 +27,7 @@
GPL version 23
-
+
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 @@
YearyearYear 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
+}
+