Index: openacs-4/packages/acs-tcl/tcl/http-client-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/http-client-procs.tcl,v
diff -u -r1.30.2.22 -r1.30.2.23
--- openacs-4/packages/acs-tcl/tcl/http-client-procs.tcl	28 Jun 2022 12:24:46 -0000	1.30.2.22
+++ openacs-4/packages/acs-tcl/tcl/http-client-procs.tcl	25 Aug 2022 13:09:19 -0000	1.30.2.23
@@ -380,7 +380,7 @@
                 -spool=$spool_p]
 }
 
-ad_proc util::http::post {
+ad_proc util::http::post_payload {
     -url
     {-files {}}
     -base64:boolean
@@ -398,7 +398,7 @@
     -spool:boolean
     {-preference {native curl}}
 } {
-    Implement client-side HTTP POST request.
+    Build the payload for a POST request
 
     @param body is the payload for the request and will be passed as
                 is (useful for many purposes, such as webDav).  A
@@ -451,73 +451,13 @@
     specified, and 'multipart/form-data' otherwise. If '-multipart'
     flag is set, format will be forced to multipart.
 
-    @param headers specifies an ns_set of extra headers to send to the
-                   server when doing the request.  Some options exist
-                   that allow one to avoid the need to specify headers
-                   manually, but headers will always take precedence
-                   over options.
+    @param headers Processing the payload might set some request
+                   headers. Provide yours to either override the
+                   default behavior, or to merge your headers with
+                   those from the payload. The resulting headers will
+                   be returned in the dict.
 
-    @param gzip_request informs the server that we are sending data in
-                        gzip format. Data will be automatically
-                        compressed.  Notice that not all servers can
-                        treat gzipped requests properly, and in such
-                        cases response will likely be an error.
-
-    @param gzip_response informs the server that we are capable of
-                         receiving gzipped responses.  If server
-                         complies to our indication, the result will
-                         be automatically decompressed.
-
-    @param force_ssl specifies whether we want to use SSL despite the
-                     url being in http:// form.  Default behavior is
-                     to use SSL on https:// URLs only.
-
-    @param spool enables file spooling of the request on the file
-                 specified. It is useful when we expect large
-                 responses from the server. The result is spooled to a
-                 temporary file, the name is returned in the file
-                 component of the result.
-
-    @param post_redirect decides what happens when we are POSTing and
-                         server replies with 301, 302 or 303
-                         redirects. RFC 2616/10.3.2 states that method
-                         should not change when 301 or 302 are
-                         returned, and that GET should be used on a
-                         303 response, but most HTTP clients fail in
-                         respecting this and switch to a GET request
-                         independently. This option forces this kinds
-                         of redirect to conserve their original
-                         method.
-
-    @param max_depth is the maximum number of redirects the proc is
-                     allowed to follow. A value of 0 disables
-                     redirection. When max depth for redirection has
-                     been reached, proc will return response from the
-                     last page we were redirected to. This is
-                     important if redirection response contains data
-                     such as cookies we need to obtain anyway. Be
-                     aware that when following redirects, unless it is
-                     a code 303 redirect, url and POST urlencoded
-                     variables will be sent again to the redirected
-                     host. Multipart variables won't be sent again.
-                     Sending to the redirected host can be dangerous,
-                     if such host is not trusted or uses a lower level
-                     of security.
-
-    @param preference decides which available implementation prefer in
-                      respective order. Choice is between 'native',
-                      based on ns_ api, available for NaviServer only
-                      and giving the best performances and 'curl',
-                      which wraps the command line utility (available
-                      on every system with curl installed).
-
-    @param timeout Timeout in seconds. The value can be an integer, a
-                   floating point number or an ns_time value.
-
-    @return the data as dict with elements 'headers', 'page', 'file',
-           'status', 'time' (elapsed request time in ns_time format),
-           and 'modified'.
-
+    @return a dict with fields 'payload', 'payload_file' and 'headers'
 } {
     set this_proc [lindex [info level 0] 0]
 
@@ -712,8 +652,169 @@
                  $payload_file_fd]
     lassign $app payload payload_file payload_file_fd
 
-    if {$payload_file_fd ne ""} {close $payload_file_fd}
+    if {$payload_file_fd ne ""} {
+        close $payload_file_fd
+    }
 
+    return [list \
+                payload $payload \
+                payload_file $payload_file \
+                headers $headers]
+}
+
+ad_proc util::http::post {
+    -url
+    {-files {}}
+    -base64:boolean
+    {-formvars ""}
+    {-body ""}
+    {-max_body_size 25000000}
+    {-headers ""}
+    {-timeout 30}
+    {-max_depth 10}
+    -force_ssl:boolean
+    -multipart:boolean
+    -gzip_request:boolean
+    -gzip_response:boolean
+    -post_redirect:boolean
+    -spool:boolean
+    {-preference {native curl}}
+} {
+    Implement client-side HTTP POST request.
+
+    @param body is the payload for the request and will be passed as
+                is (useful for many purposes, such as webDav).  A
+                convenient way to specify form variables through this
+                argument is passing a string obtained by 'export_vars
+                -url'.
+
+    @param max_body_size this value in number of characters will tell
+                         how big can the whole body payload get before
+                         we start spooling its content to a file. This
+                         is important in case of big file uploads,
+                         when keeping the entire request in memory is
+                         just not feasible. The handling of the
+                         spooling is taken care of in the API.  This
+                         value takes into account also the encoding
+                         required by the content type, so its value
+                         could not reflect the exact length of body's
+                         string representation.
+
+    @param files File upload can be specified using actual files on
+                 the filesystem or binary strings of data using the
+                 '-files' parameter.  '-files' must be a dict (flat
+                 list of key value pairs).  Keys of '-files' parameter
+                 are:
+
+     - data: binary data to be sent. If set, has precedence on 'file' key
+     - file: path for the actual file on filesystem
+     - filename: name the form will receive for this file
+     - fieldname: name the field this file will be sent as
+     - mime_type: mime_type the form will receive for this file
+
+    If 'filename' is missing and an actual file is being sent, it will
+    be set as the same name as the file. If 'mime_type' is missing, it
+    will be guessed from 'filename'. If result is */* or an empty
+    mime_type, 'application/octet-stream' will be used If '-base64'
+    flag is set, files will be base64 encoded (useful for some kind of
+    form).
+
+    @param formvars Other form variables can be passed easily
+                    through'-formvars' using 'export_vars -url' and
+                    will be translated for the proper type of
+                    form. This is useful when we intend to send files
+                    together with variables to a form. URL variables,
+                    as with GET requests, are also sent, but an error
+                    is thrown if URL variables conflict with those
+                    specified in other ways.
+
+     Default behavior is to build payload as an
+    'application/x-www-form-urlencoded' payload if no files are
+    specified, and 'multipart/form-data' otherwise. If '-multipart'
+    flag is set, format will be forced to multipart.
+
+    @param headers specifies an ns_set of extra headers to send to the
+                   server when doing the request.  Some options exist
+                   that allow one to avoid the need to specify headers
+                   manually, but headers will always take precedence
+                   over options.
+
+    @param gzip_request informs the server that we are sending data in
+                        gzip format. Data will be automatically
+                        compressed.  Notice that not all servers can
+                        treat gzipped requests properly, and in such
+                        cases response will likely be an error.
+
+    @param gzip_response informs the server that we are capable of
+                         receiving gzipped responses.  If server
+                         complies to our indication, the result will
+                         be automatically decompressed.
+
+    @param force_ssl specifies whether we want to use SSL despite the
+                     url being in http:// form.  Default behavior is
+                     to use SSL on https:// URLs only.
+
+    @param spool enables file spooling of the request on the file
+                 specified. It is useful when we expect large
+                 responses from the server. The result is spooled to a
+                 temporary file, the name is returned in the file
+                 component of the result.
+
+    @param post_redirect decides what happens when we are POSTing and
+                         server replies with 301, 302 or 303
+                         redirects. RFC 2616/10.3.2 states that method
+                         should not change when 301 or 302 are
+                         returned, and that GET should be used on a
+                         303 response, but most HTTP clients fail in
+                         respecting this and switch to a GET request
+                         independently. This option forces this kinds
+                         of redirect to conserve their original
+                         method.
+
+    @param max_depth is the maximum number of redirects the proc is
+                     allowed to follow. A value of 0 disables
+                     redirection. When max depth for redirection has
+                     been reached, proc will return response from the
+                     last page we were redirected to. This is
+                     important if redirection response contains data
+                     such as cookies we need to obtain anyway. Be
+                     aware that when following redirects, unless it is
+                     a code 303 redirect, url and POST urlencoded
+                     variables will be sent again to the redirected
+                     host. Multipart variables won't be sent again.
+                     Sending to the redirected host can be dangerous,
+                     if such host is not trusted or uses a lower level
+                     of security.
+
+    @param preference decides which available implementation prefer in
+                      respective order. Choice is between 'native',
+                      based on ns_ api, available for NaviServer only
+                      and giving the best performances and 'curl',
+                      which wraps the command line utility (available
+                      on every system with curl installed).
+
+    @param timeout Timeout in seconds. The value can be an integer, a
+                   floating point number or an ns_time value.
+
+    @return the data as dict with elements 'headers', 'page', 'file',
+           'status', 'time' (elapsed request time in ns_time format),
+           and 'modified'.
+
+} {
+    set payload_data [util::http::post_payload \
+                          -url $url \
+                          -files $files \
+                          -base64=$base64_p \
+                          -formvars $formvars \
+                          -body $body \
+                          -max_body_size $max_body_size \
+                          -headers $headers \
+                          -multipart=$multipart_p]
+
+    set payload      [dict get $payload_data payload]
+    set payload_file [dict get $payload_data payload_file]
+    set headers      [dict get $payload_data headers]
+
     return [util::http::request \
                 -method          POST \
                 -body            $payload \