Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gap-curlinterface for 
openSUSE:Factory checked in at 2026-09-18 22:08:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gap-curlinterface (Old)
 and      /work/SRC/openSUSE:Factory/.gap-curlinterface.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gap-curlinterface"

Fri Sep 18 22:08:54 2026 rev:8 rq:1378830 version:2.5.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/gap-curlinterface/gap-curlinterface.changes      
2026-07-21 23:13:18.008246473 +0200
+++ 
/work/SRC/openSUSE:Factory/.gap-curlinterface.new.383539/gap-curlinterface.changes
  2026-09-18 22:09:17.003495631 +0200
@@ -1,0 +2,10 @@
+Fri Sep 18 13:00:02 UTC 2026 - Jan Engelhardt <[email protected]>
+
+- Update to release 2.5.0
+  * Add a `targetFile` option which writes the response body
+    directly to a file, so downloads are no longer bounded by
+    available memory.
+  * Fix inverted `verifyCert` check which disabled TLS certificate
+    verification by default.
+
+-------------------------------------------------------------------

Old:
----
  curlInterface-2.4.4.tar.gz

New:
----
  curlInterface-2.5.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gap-curlinterface.spec ++++++
--- /var/tmp/diff_new_pack.MIdR5s/_old  2026-09-18 22:09:17.702524927 +0200
+++ /var/tmp/diff_new_pack.MIdR5s/_new  2026-09-18 22:09:17.705525053 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           gap-curlinterface
-Version:        2.4.4
+Version:        2.5.0
 Release:        0
 Summary:        GAP: Web Access via curl
 License:        GPL-2.0-or-later

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.MIdR5s/_old  2026-09-18 22:09:17.736526352 +0200
+++ /var/tmp/diff_new_pack.MIdR5s/_new  2026-09-18 22:09:17.739526478 +0200
@@ -1,5 +1,5 @@
-mtime: 1784644609
-commit: 3c24c9df4b8adb4810b780461ef744d14ee694c08f15ad0debf9695e59ae2f57
+mtime: 1789736428
+commit: 5af870767ae4af7729ad5db4811a8f48457c243d0455cad15e0f79d0f8b84d77
 url: https://src.opensuse.org/jengelh/gap-curlinterface
 revision: master
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-18 15:00:28.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ curlInterface-2.4.4.tar.gz -> curlInterface-2.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/.release 
new/curlInterface-2.5.0/.release
--- old/curlInterface-2.4.4/.release    2026-07-19 02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/.release    1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-#!/bin/sh
-# This file is executed by the `release` script from
-# https://github.com/gap-system/ReleaseTools
-rm -f .clang-format
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/CHANGES 
new/curlInterface-2.5.0/CHANGES
--- old/curlInterface-2.4.4/CHANGES     2026-07-19 02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/CHANGES     2026-09-18 02:00:00.000000000 +0200
@@ -1,5 +1,11 @@
 This file describes changes in the curlInterface package.
 
+2.5.0 (2026-09-18)
+  - Add a `targetFile` option which writes the response body directly to
+    a file, so downloads are no longer bounded by available memory
+  - Fix inverted `verifyCert` check which disabled TLS certificate
+    verification by default
+
 2.4.4 (2026-07-19)
   - Remove dependency on GAPDoc
   - Prefer a Homebrew libcurl installation on macOS when available
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/PackageInfo.g 
new/curlInterface-2.5.0/PackageInfo.g
--- old/curlInterface-2.4.4/PackageInfo.g       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/PackageInfo.g       2026-09-18 02:00:00.000000000 
+0200
@@ -10,8 +10,8 @@
 
 PackageName := "curlInterface",
 Subtitle := "Simple Web Access",
-Version := "2.4.4",
-Date := "19/07/2026", # dd/mm/yyyy format
+Version := "2.5.0",
+Date := "18/09/2026", # dd/mm/yyyy format
 License := "GPL-2.0-or-later",
 
 Persons := [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/_Chapter_Overview.xml 
new/curlInterface-2.5.0/doc/_Chapter_Overview.xml
--- old/curlInterface-2.4.4/doc/_Chapter_Overview.xml   2026-07-19 
02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/doc/_Chapter_Overview.xml   2026-09-18 
02:00:00.000000000 +0200
@@ -72,6 +72,7 @@
    For descriptions of the output and the additional argument <A>opts</A>, see
    <Ref Func="CurlRequest"/>.
 <P/>
+<!--AutoDocSource gap/curl.gd:57-63-->
 <Example><![CDATA[
 gap> r := DownloadURL("www.gap-system.org");;
 gap> r.success;
@@ -95,6 +96,7 @@
    For descriptions of the output and the additional argument <A>opts</A>, see
    <Ref Func="CurlRequest"/>.
 <P/>
+<!--AutoDocSource gap/curl.gd:77-83-->
 <Example><![CDATA[
 gap> r := PostToURL("httpbun.com/post", "animal=tiger");;
 gap> r.success;
@@ -118,6 +120,7 @@
    For descriptions of the output and the additional argument <A>opts</A>, see
    <Ref Func="CurlRequest"/>.
 <P/>
+<!--AutoDocSource gap/curl.gd:97-103-->
 <Example><![CDATA[
 gap> r := DeleteURL("www.google.com");;
 gap> r.success;
@@ -174,6 +177,24 @@
 <C>maxTime</C>: Maximum time in seconds that you allow each transfer
        to take. 0 means no limitation. (default <K>0</K>).
 </Item>
+<Item>
+<C>targetFile</C>: the name of a file to write the body of the response
+       to, as a string, or <K>false</K> to have it returned as a string
+       (the default).  The data is written as it arrives, so the size of
+       the response is not limited by the available memory.
+       <P/>
+       The body first goes to a temporary file next to
+       <C>targetFile</C>, which is renamed into place once the transfer
+       has succeeded.  A failed request thus leaves an existing file at
+       <C>targetFile</C> untouched, and never leaves a partial one behind.
+       A file that cannot be replaced, such as a directory or a file
+       without write permission, is reported before the transfer starts.
+       <P/>
+       Beware that with the default <C>failOnError := false</C> a 404
+       response counts as success, and its empty body then replaces the
+       contents of <C>targetFile</C>.  Pass <C>failOnError := true</C>
+       when writing to a file, unless error pages are wanted.
+</Item>
 </List>
 <P/>
    As output, this function returns a record containing some of the following
@@ -185,7 +206,7 @@
 </Item>
 <Item>
 <C>result</C>: body of the information sent by the server (only if
-       <C>success = true</C>);
+       <C>success = true</C> and no <C>targetFile</C> was given);
 </Item>
 <Item>
 <C>error</C>: human-readable string saying what went wrong (only if
@@ -209,6 +230,7 @@
 </Item>
 </List>
 <P/>
+<!--AutoDocSource gap/curl.gd:173-184-->
 <Example><![CDATA[
 gap> r := CurlRequest("https://www.google.com";,
 >                     "HEAD",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/_entities.xml 
new/curlInterface-2.5.0/doc/_entities.xml
--- old/curlInterface-2.4.4/doc/_entities.xml   2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/_entities.xml   2026-09-18 02:00:00.000000000 
+0200
@@ -1,4 +1,4 @@
-<!ENTITY RELEASEDATE '19 July 2026'>
+<!ENTITY RELEASEDATE '18 September 2026'>
 <!ENTITY RELEASEYEAR '2026'>
-<!ENTITY VERSION '2.4.4'>
+<!ENTITY VERSION '2.5.0'>
 <!ENTITY curl '<Package>curl</Package>'>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/_main.tex 
new/curlInterface-2.5.0/doc/_main.tex
--- old/curlInterface-2.4.4/doc/_main.tex       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/_main.tex       2026-09-18 02:00:00.000000000 
+0200
@@ -100,8 +100,8 @@
 \mbox{}}}\\
 \vfill
 
-{\Huge  2.4.4 \mbox{}}\\[1cm]
-{ 19 July 2026 \mbox{}}\\[1cm]
+{\Huge  2.5.0 \mbox{}}\\[1cm]
+{ 18 September 2026 \mbox{}}\\[1cm]
 \mbox{}\\[2cm]
 {\Large \textbf{\strut  Christopher Jefferson     \strut\mbox{}}}\\
 {\Large \textbf{\strut  Michael Young     \strut\mbox{}}}\\
@@ -230,7 +230,7 @@
 "http://"; or "https://";. For descriptions of the output and the additional
 argument \mbox{\texttt{\mdseries\slshape opts}}, see \texttt{CurlRequest} 
(\ref{CurlRequest}). 
 
- 
+  
 \begin{Verbatim}[commandchars=@|A,fontsize=\small,frame=single,label=Example]
   @gapprompt|gap>A @gapinput|r := DownloadURL("www.gap-system.org");;A
   @gapprompt|gap>A @gapinput|r.success;A
@@ -257,7 +257,7 @@
 "http://"; or "https://";. \mbox{\texttt{\mdseries\slshape str}} should be the 
string which will be sent to the server as a POST request. For
 descriptions of the output and the additional argument 
\mbox{\texttt{\mdseries\slshape opts}}, see \texttt{CurlRequest} 
(\ref{CurlRequest}). 
 
- 
+  
 \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example]
   !gapprompt@gap>| !gapinput@r := PostToURL("httpbun.com/post", 
"animal=tiger");;|
   !gapprompt@gap>| [email protected];|
@@ -285,7 +285,7 @@
 either "http://"; or "https://";. For descriptions of the output and the
 additional argument \mbox{\texttt{\mdseries\slshape opts}}, see 
\texttt{CurlRequest} (\ref{CurlRequest}). 
 
- 
+  
 \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example]
   !gapprompt@gap>| !gapinput@r := DeleteURL("www.google.com");;|
   !gapprompt@gap>| [email protected];|
@@ -324,6 +324,16 @@
 error (corresponds to the curl option 
\texttt{CURLOPT{\textunderscore}FAILONERROR}, the default is \texttt{false}). 
 \item  \texttt{maxTime}: Maximum time in seconds that you allow each transfer 
to take. 0 means no
 limitation. (default \texttt{0}). 
+\item  \texttt{targetFile}: the name of a file to write the body of the 
response to, as a string, or \texttt{false} to have it returned as a string 
(the default). The data is written as it
+arrives, so the size of the response is not limited by the available memory. 
+
+ The body first goes to a temporary file next to \texttt{targetFile}, which is 
renamed into place once the transfer has succeeded. A failed
+request thus leaves an existing file at \texttt{targetFile} untouched, and 
never leaves a partial one behind. A file that cannot be
+replaced, such as a directory or a file without write permission, is reported
+before the transfer starts. 
+
+ Beware that with the default \texttt{failOnError := false} a 404 response 
counts as success, and its empty body then replaces the
+contents of \texttt{targetFile}. Pass \texttt{failOnError := true} when 
writing to a file, unless error pages are wanted. 
 \end{itemize}
  
 
@@ -332,7 +342,7 @@
 \begin{itemize}
 \item  \texttt{success}: a boolean describing whether the request was 
successfully received by the
 server; 
-\item  \texttt{result}: body of the information sent by the server (only if 
\texttt{success = true}); 
+\item  \texttt{result}: body of the information sent by the server (only if 
\texttt{success = true} and no \texttt{targetFile} was given); 
 \item  \texttt{error}: human\texttt{\symbol{45}}readable string saying what 
went wrong (only if \texttt{success = false}). 
 \end{itemize}
  
@@ -347,7 +357,7 @@
 \end{itemize}
  
 
- 
+  
 \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example]
   !gapprompt@gap>| !gapinput@r := CurlRequest("https://www.google.com";,|
   !gapprompt@>| !gapinput@                    "HEAD",|
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap0.html 
new/curlInterface-2.5.0/doc/chap0.html
--- old/curlInterface-2.4.4/doc/chap0.html      2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap0.html      2026-09-18 02:00:00.000000000 
+0200
@@ -29,10 +29,10 @@
 <h2>Simple Web Access</h2>
 
 <p>
-    2.4.4</p>
+    2.5.0</p>
 
 <p>
-    19 July 2026
+    18 September 2026
   </p>
 
 </div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap0.txt 
new/curlInterface-2.5.0/doc/chap0.txt
--- old/curlInterface-2.4.4/doc/chap0.txt       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap0.txt       2026-09-18 02:00:00.000000000 
+0200
@@ -6,10 +6,10 @@
                                Simple Web Access 
   
   
-                                     2.4.4
+                                     2.5.0
   
   
-                                  19 July 2026
+                               18 September 2026
   
   
                              Christopher Jefferson
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap0_mj.html 
new/curlInterface-2.5.0/doc/chap0_mj.html
--- old/curlInterface-2.4.4/doc/chap0_mj.html   2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap0_mj.html   2026-09-18 02:00:00.000000000 
+0200
@@ -32,10 +32,10 @@
 <h2>Simple Web Access</h2>
 
 <p>
-    2.4.4</p>
+    2.5.0</p>
 
 <p>
-    19 July 2026
+    18 September 2026
   </p>
 
 </div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap1.html 
new/curlInterface-2.5.0/doc/chap1.html
--- old/curlInterface-2.4.4/doc/chap1.html      2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap1.html      2026-09-18 02:00:00.000000000 
+0200
@@ -177,6 +177,13 @@
 <li><p><code class="code">maxTime</code>: Maximum time in seconds that you 
allow each transfer to take. 0 means no limitation. (default <code 
class="keyw">0</code>).</p>
 
 </li>
+<li><p><code class="code">targetFile</code>: the name of a file to write the 
body of the response to, as a string, or <code class="keyw">false</code> to 
have it returned as a string (the default). The data is written as it arrives, 
so the size of the response is not limited by the available memory.</p>
+
+<p>The body first goes to a temporary file next to <code 
class="code">targetFile</code>, which is renamed into place once the transfer 
has succeeded. A failed request thus leaves an existing file at <code 
class="code">targetFile</code> untouched, and never leaves a partial one 
behind. A file that cannot be replaced, such as a directory or a file without 
write permission, is reported before the transfer starts.</p>
+
+<p>Beware that with the default <code class="code">failOnError := false</code> 
a 404 response counts as success, and its empty body then replaces the contents 
of <code class="code">targetFile</code>. Pass <code class="code">failOnError := 
true</code> when writing to a file, unless error pages are wanted.</p>
+
+</li>
 </ul>
 <p>As output, this function returns a record containing some of the following 
components, which describe the outcome of the request:</p>
 
@@ -185,7 +192,7 @@
 <li><p><code class="code">success</code>: a boolean describing whether the 
request was successfully received by the server;</p>
 
 </li>
-<li><p><code class="code">result</code>: body of the information sent by the 
server (only if <code class="code">success = true</code>);</p>
+<li><p><code class="code">result</code>: body of the information sent by the 
server (only if <code class="code">success = true</code> and no <code 
class="code">targetFile</code> was given);</p>
 
 </li>
 <li><p><code class="code">error</code>: human-readable string saying what went 
wrong (only if <code class="code">success = false</code>).</p>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap1.txt 
new/curlInterface-2.5.0/doc/chap1.txt
--- old/curlInterface-2.4.4/doc/chap1.txt       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap1.txt       2026-09-18 02:00:00.000000000 
+0200
@@ -132,6 +132,23 @@
       maxTime: Maximum time in seconds that you 
allow each transfer to take.
         0 means no limitation. (default 0).
   
+      targetFile:  the  name of a file to write the 
body of the response to,
+        as  a  string, or false to have it returned as a string (the 
default).
+        The  data is written as it arrives, so the size of the response is not
+        limited by the available memory.
+  
+        The  body  first goes to a temporary file next to 
targetFile, which is
+        renamed  into  place once the transfer has succeeded. A failed request
+        thus leaves an existing file at targetFile untouched, and 
never leaves
+        a  partial  one  behind.  A  file  that  cannot be replaced, such as a
+        directory  or  a file without write permission, is reported before the
+        transfer starts.
+  
+        Beware  that  with  the  default  failOnError  := 
false a 404 response
+        counts  as  success,  and its empty body then replaces the contents of
+        targetFile.  Pass  failOnError  := true when 
writing to a file, unless
+        error pages are wanted.
+  
   As  output,  this function returns a record containing some of 
the following
   components, which describe the outcome of the request:
   
@@ -139,7 +156,7 @@
         received by the server;
   
       result:  body of the information sent by the 
server (only if success =
-        true);
+        true and no targetFile was given);
   
       error: human-readable string saying what went 
wrong (only if success =
         false).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/chap1_mj.html 
new/curlInterface-2.5.0/doc/chap1_mj.html
--- old/curlInterface-2.4.4/doc/chap1_mj.html   2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/chap1_mj.html   2026-09-18 02:00:00.000000000 
+0200
@@ -180,6 +180,13 @@
 <li><p><code class="code">maxTime</code>: Maximum time in seconds that you 
allow each transfer to take. 0 means no limitation. (default <code 
class="keyw">0</code>).</p>
 
 </li>
+<li><p><code class="code">targetFile</code>: the name of a file to write the 
body of the response to, as a string, or <code class="keyw">false</code> to 
have it returned as a string (the default). The data is written as it arrives, 
so the size of the response is not limited by the available memory.</p>
+
+<p>The body first goes to a temporary file next to <code 
class="code">targetFile</code>, which is renamed into place once the transfer 
has succeeded. A failed request thus leaves an existing file at <code 
class="code">targetFile</code> untouched, and never leaves a partial one 
behind. A file that cannot be replaced, such as a directory or a file without 
write permission, is reported before the transfer starts.</p>
+
+<p>Beware that with the default <code class="code">failOnError := false</code> 
a 404 response counts as success, and its empty body then replaces the contents 
of <code class="code">targetFile</code>. Pass <code class="code">failOnError := 
true</code> when writing to a file, unless error pages are wanted.</p>
+
+</li>
 </ul>
 <p>As output, this function returns a record containing some of the following 
components, which describe the outcome of the request:</p>
 
@@ -188,7 +195,7 @@
 <li><p><code class="code">success</code>: a boolean describing whether the 
request was successfully received by the server;</p>
 
 </li>
-<li><p><code class="code">result</code>: body of the information sent by the 
server (only if <code class="code">success = true</code>);</p>
+<li><p><code class="code">result</code>: body of the information sent by the 
server (only if <code class="code">success = true</code> and no <code 
class="code">targetFile</code> was given);</p>
 
 </li>
 <li><p><code class="code">error</code>: human-readable string saying what went 
wrong (only if <code class="code">success = false</code>).</p>
Binary files old/curlInterface-2.4.4/doc/manual.pdf and 
new/curlInterface-2.5.0/doc/manual.pdf differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/manual.six 
new/curlInterface-2.5.0/doc/manual.six
--- old/curlInterface-2.4.4/doc/manual.six      2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/manual.six      2026-09-18 02:00:00.000000000 
+0200
@@ -20,7 +20,7 @@
       28, 3, "macos", "X7FF92C7E7A41189D" ], 
   [ "\033[1X\033[33X\033[0;-2YFunctions\033[133X\033[101X", "1.2", 
       [ 1, 2, 0 ], 39, 3, "functions", "X86FA580F8055B274" ], 
-  [ "Index", "ind", [ "Ind", 0, 0 ], 1, 6, "index", "X83A0356F839C696F" ], 
+  [ "Index", "ind", [ "Ind", 0, 0 ], 1, 7, "index", "X83A0356F839C696F" ], 
   [ "\033[2XDownloadURL\033[102X", "1.2-1", [ 1, 2, 1 ], 45, 4, 
       "downloadurl", "X7827F7DE85113D7E" ], 
   [ "\033[2XPostToURL\033[102X", "1.2-2", [ 1, 2, 2 ], 63, 4, "posttourl", 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/doc/title.xml 
new/curlInterface-2.5.0/doc/title.xml
--- old/curlInterface-2.4.4/doc/title.xml       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/doc/title.xml       2026-09-18 02:00:00.000000000 
+0200
@@ -9,7 +9,7 @@
     Simple Web Access
   </Subtitle>
   <Version>
-    2.4.4
+    2.5.0
   </Version>
   <Author>
     Christopher Jefferson
@@ -36,6 +36,6 @@
 <Homepage>http://mct25.host.cs.st-andrews.ac.uk/</Homepage>
   </Author>
   <Date>
-    19 July 2026
+    18 September 2026
   </Date>
   </TitlePage>
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/gap/curl.gd 
new/curlInterface-2.5.0/gap/curl.gd
--- old/curlInterface-2.4.4/gap/curl.gd 2026-07-19 02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/gap/curl.gd 2026-09-18 02:00:00.000000000 +0200
@@ -136,13 +136,29 @@
 #!       the default is <K>false</K>).
 #!     * <C>maxTime</C>: Maximum time in seconds that you allow each transfer
 #!       to take. 0 means no limitation. (default <K>0</K>).
+#!     * <C>targetFile</C>: the name of a file to write the body of the 
response
+#!       to, as a string, or <K>false</K> to have it returned as a string
+#!       (the default).  The data is written as it arrives, so the size of
+#!       the response is not limited by the available memory.
+#!       <P/>
+#!       The body first goes to a temporary file next to
+#!       <C>targetFile</C>, which is renamed into place once the transfer
+#!       has succeeded.  A failed request thus leaves an existing file at
+#!       <C>targetFile</C> untouched, and never leaves a partial one behind.
+#!       A file that cannot be replaced, such as a directory or a file
+#!       without write permission, is reported before the transfer starts.
+#!       <P/>
+#!       Beware that with the default <C>failOnError := false</C> a 404
+#!       response counts as success, and its empty body then replaces the
+#!       contents of <C>targetFile</C>.  Pass <C>failOnError := true</C>
+#!       when writing to a file, unless error pages are wanted.
 #!
 #!   As output, this function returns a record containing some of the following
 #!   components, which describe the outcome of the request:
 #!     * <C>success</C>: a boolean describing whether the request was
 #!       successfully received by the server;
 #!     * <C>result</C>: body of the information sent by the server (only if
-#!       <C>success = true</C>);
+#!       <C>success = true</C> and no <C>targetFile</C> was given);
 #!     * <C>error</C>: human-readable string saying what went wrong (only if
 #!       <C>success = false</C>).
 #!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/gap/curl.gi 
new/curlInterface-2.5.0/gap/curl.gi
--- old/curlInterface-2.4.4/gap/curl.gi 2026-07-19 02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/gap/curl.gi 2026-09-18 02:00:00.000000000 +0200
@@ -9,7 +9,7 @@
 
     # Get options
     r := rec(verifyCert := true, verbose := false, followRedirect := true,
-             failOnError:= false, maxTime := 0);
+             failOnError:= false, maxTime := 0, targetFile := false);
     if Length(opts) = 1 then
         if not IsRecord(opts[1]) then
             ErrorNoReturn("CurlRequest: <opts> must be a record");
@@ -42,13 +42,18 @@
                           " must be a non-negative integer");
         fi;
     od;
+    if r.targetFile <> false and not IsString(r.targetFile) then
+        ErrorNoReturn("CurlRequest: <opts>.targetFile must be a string or ",
+                      "false");
+    fi;
 
     return CURL_REQUEST(URL, type, out_string,
                         r.verifyCert,
                         r.verbose,
                         r.followRedirect,
                         r.failOnError,
-                        r.maxTime);
+                        r.maxTime,
+                        r.targetFile);
 end);
 
 InstallGlobalFunction("DownloadURL",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/src/curl.c 
new/curlInterface-2.5.0/src/curl.c
--- old/curlInterface-2.4.4/src/curl.c  2026-07-19 02:00:00.000000000 +0200
+++ new/curlInterface-2.5.0/src/curl.c  2026-09-18 02:00:00.000000000 +0200
@@ -6,6 +6,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 #include <curl/curl.h>
 
 #undef PACKAGE
@@ -33,6 +36,29 @@
     return size * nmemb;
 }
 
+// Write straight to a file, for CURLOPT_WRITEDATA when a target is given.
+size_t write_file(char * ptr, size_t size, size_t nmemb, void * outstream)
+{
+    return fwrite(ptr, size, nmemb, (FILE *)outstream);
+}
+
+// room for the ".<pid>.part" that names the temporary file
+#define TARGET_TEMP_SUFFIX_LEN 32
+
+// Could a completed download be moved onto <path>?  A directory cannot be
+// replaced by a file, and an existing file we may not write should not be
+// replaced either, even though 'rename' would happily do it.  Saying so here
+// costs a 'stat' and saves downloading a body that has nowhere to go.
+static int can_replace_file(const char * path)
+{
+    struct stat st;
+
+    if (stat(path, &st) != 0)
+        return 1;    // nothing there yet; opening the temporary file decides
+
+    return S_ISDIR(st.st_mode) ? 0 : access(path, W_OK) == 0;
+}
+
 Obj FuncCURL_REQUEST(Obj self, Obj input_list)
 {
     CURL *     curl;
@@ -42,9 +68,12 @@
     curl_off_t len;
     char       urlbuf[4096] = { 0 };
     char *     typebuf = NULL;
+    char *     targetbuf = NULL;
+    char *     tempbuf = NULL;
+    FILE *     targetfile = NULL;
 
     const int n = LEN_PLIST(input_list);
-    GAP_ASSERT(n == 8);    // paranoia check, GAP enforces this
+    GAP_ASSERT(n == 9);    // paranoia check, GAP enforces this
 
     Obj URL = ELM_PLIST(input_list, 1);
     if (!IS_STRING_REP(URL)) {
@@ -70,6 +99,42 @@
     }
     memcpy(urlbuf, CONST_CSTR_STRING(URL), len);
 
+    // If a target file was given, write the body straight into it instead of
+    // building it up in memory.  Copy the name out of the GAP string for the
+    // same reason as the URL above.
+    Obj target = ELM_PLIST(input_list, 9);
+    if (target != False) {
+        if (!IS_STRING_REP(target)) {
+            target = CopyToStringRep(target);
+        }
+        len = GET_LEN_STRING(target) + 1;
+        targetbuf = (char *)malloc(len);
+        memcpy(targetbuf, CONST_CSTR_STRING(target), len);
+
+        // Write to a temporary file beside the target and rename it into
+        // place once the transfer succeeded, so that a failure leaves
+        // whatever was at the target alone instead of truncating it.  The
+        // pid keeps two GAP processes fetching the same target apart.
+        tempbuf = (char *)malloc(len + TARGET_TEMP_SUFFIX_LEN);
+        snprintf(tempbuf, len + TARGET_TEMP_SUFFIX_LEN, "%s.%ld.part",
+                 targetbuf, (long)getpid());
+
+        if (can_replace_file(targetbuf))
+            targetfile = fopen(tempbuf, "wb");
+        if (targetfile == NULL) {
+            Obj prec = NEW_PREC(2);
+            SET_LEN_PREC(prec, 2);
+            SET_RNAM_PREC(prec, 1, RNamName("success"));
+            SET_ELM_PREC(prec, 1, False);
+            SET_RNAM_PREC(prec, 2, RNamName("error"));
+            SET_ELM_PREC(prec, 2, MakeImmString("cannot open target file"));
+            CHANGED_BAG(prec);
+            free(tempbuf);
+            free(targetbuf);
+            return prec;
+        }
+    }
+
     res = curl_global_init(CURL_GLOBAL_DEFAULT);
     if (res != 0) {
         ErrorMayQuit("CURL_REQUEST: failed to initialize libcurl (error %d)",
@@ -82,8 +147,14 @@
         curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
 
         curl_easy_setopt(curl, CURLOPT_URL, urlbuf);
-        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_string);
-        curl_easy_setopt(curl, CURLOPT_WRITEDATA, in_string);
+        if (targetfile != NULL) {
+            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file);
+            curl_easy_setopt(curl, CURLOPT_WRITEDATA, targetfile);
+        }
+        else {
+            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_string);
+            curl_easy_setopt(curl, CURLOPT_WRITEDATA, in_string);
+        }
         curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1L);
         curl_easy_setopt(curl, CURLOPT_USERAGENT, "curlInterface/GAP package");
 
@@ -130,7 +201,7 @@
             curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, typebuf);
         }
 
-        if (verifyCert == True) {
+        if (verifyCert == False) {
             //
             // If you want to connect to a site who isn't using a certificate
             // that is signed by one of the certs in the CA bundle you have,
@@ -171,6 +242,19 @@
     curl_global_cleanup();
     free(typebuf);
 
+    if (targetfile != NULL) {
+        if (fclose(targetfile) != 0 && errorstring == 0)
+            errorstring = MakeImmString("cannot write target file");
+        if (errorstring == 0 && rename(tempbuf, targetbuf) != 0)
+            errorstring = MakeImmString("cannot write target file");
+        if (errorstring)
+            remove(tempbuf);
+        free(tempbuf);
+        free(targetbuf);
+    }
+
+    // With a target file there is no body to hand back, so the result record
+    // has just 'success', or 'success' and 'error'.
     Obj prec = NEW_PREC(2);
     SET_LEN_PREC(prec, 2);
     SET_RNAM_PREC(prec, 1, RNamName("success"));
@@ -179,6 +263,10 @@
         SET_RNAM_PREC(prec, 2, RNamName("error"));
         SET_ELM_PREC(prec, 2, errorstring);
     }
+    else if (targetfile != NULL) {
+        SET_LEN_PREC(prec, 1);
+        SET_ELM_PREC(prec, 1, True);
+    }
     else {
         SET_ELM_PREC(prec, 1, True);
         SET_RNAM_PREC(prec, 2, RNamName("result"));
@@ -195,8 +283,8 @@
 
 // Table of functions to export
 static StructGVarFunc GVarFuncs[] = {
-    GVAR_FUNC(CURL_REQUEST, 8,
-              "url, type, out_string, verifyCert, verbose, followRedirect, 
failOnError, maxTime"),
+    GVAR_FUNC(CURL_REQUEST, 9,
+              "url, type, out_string, verifyCert, verbose, followRedirect, 
failOnError, maxTime, targetFile"),
     GVAR_FUNC(CURL_VERSION, 0, ""),
     { 0 }
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/tst/basic.tst 
new/curlInterface-2.5.0/tst/basic.tst
--- old/curlInterface-2.4.4/tst/basic.tst       2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/tst/basic.tst       2026-09-18 02:00:00.000000000 
+0200
@@ -1,4 +1,4 @@
-#@local r, url, postString, requestType, server, baseurl
+#@local r, url, postString, requestType, server, baseurl, file
 gap> LoadPackage( "curlInterface", false );
 true
 gap> LoadPackage( "io", false );
@@ -110,4 +110,72 @@
 false
 gap> DownloadURL( url, rec( maxTime := 5 ) ).result;
 "download test response\n"
+
+# Downloading to a file
+gap> file := Filename( DirectoryTemporary(), "target" );;
+gap> r := DownloadURL( Concatenation( baseurl, "/success" ),
+>                      rec( targetFile := file ) );;
+gap> r.success;
+true
+
+# with a target file there is no body to hand back
+gap> RecNames( r );
+[ "success" ]
+gap> StringFile( file );
+"download test response\n"
+
+# a failed request must not leave the file behind
+gap> RemoveFile( file );;
+gap> r := DownloadURL( Concatenation( baseurl, "/missing" ),
+>                      rec( targetFile := file, failOnError := true ) );;
+gap> r.success;
+false
+gap> IsExistingFile( file );
+false
+
+# nor after the connection drops mid-transfer
+gap> r := DownloadURL( Concatenation( baseurl, "/disconnect" ),
+>                      rec( targetFile := file ) );;
+gap> r.success;
+false
+gap> IsExistingFile( file );
+false
+
+# an existing file survives a failed download, contents and all
+gap> FileString( file, "do not touch\n" );;
+gap> r := DownloadURL( Concatenation( baseurl, "/missing" ),
+>                      rec( targetFile := file, failOnError := true ) );;
+gap> r.success;
+false
+gap> StringFile( file );
+"do not touch\n"
+
+# and likewise when the connection drops mid-transfer
+gap> r := DownloadURL( Concatenation( baseurl, "/disconnect" ),
+>                      rec( targetFile := file ) );;
+gap> r.success;
+false
+gap> StringFile( file );
+"do not touch\n"
+
+# a successful download replaces it
+gap> r := DownloadURL( Concatenation( baseurl, "/success" ),
+>                      rec( targetFile := file ) );;
+gap> r.success;
+true
+gap> StringFile( file );
+"download test response\n"
+gap> RemoveFile( file );;
+
+# a target file that cannot be opened is reported, not fatal
+gap> r := DownloadURL( Concatenation( baseurl, "/success" ),
+>                      rec( targetFile := "/no/such/directory/target" ) );;
+gap> r.success;
+false
+gap> r.error;
+"cannot open target file"
+
+# argument checking
+gap> DownloadURL( baseurl, rec( targetFile := 42 ) );
+Error, CurlRequest: <opts>.targetFile must be a string or false
 gap> CURLINTERFACE_StopHTTPTestServer( server );;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/tst/errors.tst 
new/curlInterface-2.5.0/tst/errors.tst
--- old/curlInterface-2.4.4/tst/errors.tst      2026-07-19 02:00:00.000000000 
+0200
+++ new/curlInterface-2.5.0/tst/errors.tst      2026-09-18 02:00:00.000000000 
+0200
@@ -63,4 +63,4 @@
 
 # number of arguments
 gap> CURL_REQUEST();
-Error, Function: number of arguments must be 8 (not 0)
+Error, Function: number of arguments must be 9 (not 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/tst/https-server.g 
new/curlInterface-2.5.0/tst/https-server.g
--- old/curlInterface-2.4.4/tst/https-server.g  1970-01-01 01:00:00.000000000 
+0100
+++ new/curlInterface-2.5.0/tst/https-server.g  2026-09-18 02:00:00.000000000 
+0200
@@ -0,0 +1,125 @@
+#############################################################################
+##
+##  An HTTPS server with a self-signed certificate for the curlInterface
+##  tests, used to check that certificates really are verified unless
+##  `verifyCert` is false. Implemented via `openssl s_server`; if openssl is
+##  missing, the tests using it are skipped.
+##
+
+BindGlobal( "CURLINTERFACE_OpenSSL", function()
+  local openssl;
+
+  openssl := Filename( DirectoriesSystemPrograms(), "openssl" );
+  if openssl = fail or not IsExecutableFile( openssl ) then
+    return fail;
+  fi;
+  return openssl;
+end );
+
+# Bind port 0 to have the kernel pick a free port, then release it again.
+BindGlobal( "CURLINTERFACE_FreePort", function()
+  local listener, address, port;
+
+  listener := IO_socket( IO.PF_INET, IO.SOCK_STREAM, "tcp" );
+  if listener = fail then
+    return fail;
+  fi;
+  if IO_bind( listener, IO_MakeIPAddressPort( "127.0.0.1", 0 ) ) = fail then
+    IO_close( listener );
+    return fail;
+  fi;
+  address := IO_getsockname( listener );
+  port := 256 * INT_CHAR( address[3] ) + INT_CHAR( address[4] );
+  IO_close( listener );
+  return port;
+end );
+
+# Waits for the server to accept requests. Tries both settings of
+# `verifyCert`, so that this succeeds no matter which of them rejects a
+# self-signed certificate.
+BindGlobal( "CURLINTERFACE_WaitForHTTPSServer", function( url )
+  local i;
+
+  for i in [ 1 .. 10 ] do
+    if DownloadURL( url, rec( verifyCert := false ) ).success or
+       DownloadURL( url ).success then
+      return true;
+    fi;
+    Sleep( 1 );
+  od;
+  return false;
+end );
+
+# Returns a record with components `pid` and `url`, or `fail` if no HTTPS
+# server could be started.
+BindGlobal( "CURLINTERFACE_StartHTTPSTestServer", function()
+  local openssl, sh, dir, cert, key, port, url, pid, devnull;
+
+  openssl := CURLINTERFACE_OpenSSL();
+  sh := Filename( DirectoriesSystemPrograms(), "sh" );
+  if openssl = fail or sh = fail then
+    return fail;
+  fi;
+
+  # LibreSSL's `req` has no `-quiet`, so silence it via the shell
+  dir := DirectoryTemporary();
+  cert := Filename( dir, "cert.pem" );
+  key := Filename( dir, "key.pem" );
+  if Process( dir, sh, InputTextNone(), OutputTextNone(),
+              [ "-c", Concatenation(
+                  "'", openssl, "' req -x509 -newkey rsa:2048 -nodes",
+                  " -keyout '", key, "' -out '", cert, "'",
+                  " -days 1 -subj /CN=localhost 2>/dev/null" ) ] ) <> 0 then
+    return fail;
+  fi;
+
+  port := CURLINTERFACE_FreePort();
+  if port = fail then
+    return fail;
+  fi;
+  url := Concatenation( "https://localhost:";, String( port ), "/" );
+
+  pid := IO_fork();
+  if pid = 0 then
+    devnull := IO_open( "/dev/null", IO.O_WRONLY, 0 );
+    if devnull <> fail then
+      IO_dup2( devnull, 1 );
+      IO_dup2( devnull, 2 );
+    fi;
+    IO_execv( openssl,
+              [ "s_server", "-quiet", "-www", "-accept", String( port ),
+                "-cert", cert, "-key", key ] );
+    IO_exit( 1 );
+  elif pid < 0 then
+    return fail;
+  fi;
+
+  if CURLINTERFACE_WaitForHTTPSServer( url ) <> true then
+    IO_kill( pid, IO.SIGTERM );
+    IO_WaitPid( pid, true );
+    return fail;
+  fi;
+
+  return rec( pid := pid, url := url );
+end );
+
+BindGlobal( "CURLINTERFACE_StopHTTPSTestServer", function( server )
+  if server <> fail then
+    IO_kill( server.pid, IO.SIGTERM );
+    IO_WaitPid( server.pid, true );
+  fi;
+end );
+
+# Downloads from the test server once with the default options and once with
+# `verifyCert := false`, and returns the two `success` values.
+BindGlobal( "CURLINTERFACE_VerifyCertResults", function( server )
+  if server = fail then
+    if CURLINTERFACE_OpenSSL() = fail then
+      # skip the test on systems without openssl
+      return [ false, true ];
+    fi;
+    return "could not start the HTTPS test server";
+  fi;
+  return [ DownloadURL( server.url ).success,
+           DownloadURL( server.url, rec( verifyCert := false ) ).success ];
+end );
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/curlInterface-2.4.4/tst/https.tst 
new/curlInterface-2.5.0/tst/https.tst
--- old/curlInterface-2.4.4/tst/https.tst       1970-01-01 01:00:00.000000000 
+0100
+++ new/curlInterface-2.5.0/tst/https.tst       2026-09-18 02:00:00.000000000 
+0200
@@ -0,0 +1,13 @@
+#@local server
+gap> LoadPackage( "curlInterface", false );
+true
+gap> LoadPackage( "io", false );
+true
+gap> ReadPackage( "curlInterface", "tst/https-server.g" );;
+gap> server := CURLINTERFACE_StartHTTPSTestServer();;
+
+# A self-signed certificate is rejected by default, and accepted only if
+# verification is turned off explicitly
+gap> CURLINTERFACE_VerifyCertResults( server );
+[ false, true ]
+gap> CURLINTERFACE_StopHTTPSTestServer( server );

Reply via email to