GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
urlwrite.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2006-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <string>
31 #include <fstream>
32 #include <iomanip>
33 
34 #include "dir-ops.h"
35 #include "file-ops.h"
36 #include "file-stat.h"
37 #include "lo-sysdep.h"
38 #include "oct-env.h"
39 #include "oct-handle.h"
40 #include "glob-match.h"
41 #include "url-transfer.h"
42 
43 #include "defun.h"
44 #include "error.h"
45 #include "interpreter.h"
46 #include "oct-map.h"
47 #include "oct-refcount.h"
48 #include "ov-cell.h"
49 #include "ov-classdef.h"
50 #include "ovl.h"
51 #include "pager.h"
52 #include "unwind-prot.h"
53 #include "url-handle-manager.h"
54 
56 
57 DEFUN (urlwrite, args, nargout,
58  doc: /* -*- texinfo -*-
59 @deftypefn {} {} urlwrite (@var{url}, @var{localfile})
60 @deftypefnx {} {@var{f} =} urlwrite (@var{url}, @var{localfile})
61 @deftypefnx {} {[@var{f}, @var{success}] =} urlwrite (@var{url}, @var{localfile})
62 @deftypefnx {} {[@var{f}, @var{success}, @var{message}] =} urlwrite (@var{url}, @var{localfile})
63 Download a remote file specified by its @var{url} and save it as
64 @var{localfile}.
65 
66 For example:
67 
68 @example
69 @group
70 urlwrite ("http://ftp.octave.org/pub/README",
71  "README.txt");
72 @end group
73 @end example
74 
75 The full path of the downloaded file is returned in @var{f}.
76 
77 The variable @var{success} is 1 if the download was successful,
78 otherwise it is 0 in which case @var{message} contains an error message.
79 
80 If no output argument is specified and an error occurs, then the error is
81 signaled through Octave's error handling mechanism.
82 
83 This function uses libcurl. The curl library supports, among others, the HTTP,
84 FTP, and FILE protocols. Username and password may be specified in the URL,
85 for example:
86 
87 @example
88 @group
89 urlwrite ("http://username:password@@example.com/file.txt",
90  "file.txt");
91 @end group
92 @end example
93 
94 GET and POST requests can be specified by @var{method} and @var{param}.
95 The parameter @var{method} is either @samp{get} or @samp{post} and
96 @var{param} is a cell array of parameter and value pairs.
97 For example:
98 
99 @example
100 @group
101 urlwrite ("http://www.google.com/search", "search.html",
102  "get", @{"query", "octave"@});
103 @end group
104 @end example
105 @seealso{urlread}
106 @end deftypefn */)
107 {
108  int nargin = args.length ();
109 
110  // verify arguments
111  if (nargin != 2 && nargin != 4)
112  print_usage ();
113 
114  std::string url = args(0).xstring_value ("urlwrite: URL must be a string");
115 
116  // name to store the file if download is successful
117  std::string filename = args(1).xstring_value ("urlwrite: LOCALFILE must be a string");
118 
119  std::string method;
120  Array<std::string> param;
121 
122  if (nargin == 4)
123  {
124  method = args(2).xstring_value ("urlwrite: METHOD must be a string");
125 
126  if (method != "get" && method != "post")
127  error (R"(urlwrite: METHOD must be "get" or "post")");
128 
129  param = args(3).xcellstr_value ("urlwrite: parameters (PARAM) for get and post requests must be given as a cell array of strings");
130 
131  if (param.numel () % 2 == 1)
132  error ("urlwrite: number of elements in PARAM must be even");
133  }
134 
135  // The file should only be deleted if it doesn't initially exist, we
136  // create it, and the download fails. We use unwind_protect to do
137  // it so that the deletion happens no matter how we exit the function.
138 
139  std::ofstream ofile =
140  sys::ofstream (filename.c_str (), std::ios::out | std::ios::binary);
141 
142  if (! ofile.is_open ())
143  error ("urlwrite: unable to open file");
144 
145  int(*unlink_fptr)(const std::string&) = sys::unlink;
146  unwind_action_safe unlink_action (unlink_fptr, filename);
147 
148  url_transfer url_xfer (url, ofile);
149 
150  octave_value_list retval;
151 
152  if (! url_xfer.is_valid ())
153  error ("support for URL transfers was disabled when Octave was built");
154 
155  url_xfer.http_action (param, method);
156 
157  ofile.close ();
158 
159  if (url_xfer.good ())
160  unlink_action.discard ();
161 
162  if (nargout > 0)
163  {
164  if (url_xfer.good ())
165  retval = ovl (sys::env::make_absolute (filename), true, "");
166  else
167  retval = ovl ("", false, url_xfer.lasterror ());
168  }
169 
170  if (nargout < 2 && ! url_xfer.good ())
171  error ("urlwrite: %s", url_xfer.lasterror ().c_str ());
172 
173  return retval;
174 }
175 
176 DEFUN (urlread, args, nargout,
177  doc: /* -*- texinfo -*-
178 @deftypefn {} {@var{s} =} urlread (@var{url})
179 @deftypefnx {} {[@var{s}, @var{success}] =} urlread (@var{url})
180 @deftypefnx {} {[@var{s}, @var{success}, @var{message}] =} urlread (@var{url})
181 @deftypefnx {} {[@dots{}] =} urlread (@var{url}, @var{method}, @var{param})
182 Download a remote file specified by its @var{url} and return its content
183 in string @var{s}.
184 
185 For example:
186 
187 @example
188 s = urlread ("http://ftp.octave.org/pub/README");
189 @end example
190 
191 The variable @var{success} is 1 if the download was successful,
192 otherwise it is 0 in which case @var{message} contains an error
193 message.
194 
195 If no output argument is specified and an error occurs, then the error is
196 signaled through Octave's error handling mechanism.
197 
198 This function uses libcurl. The curl library supports, among others, the HTTP,
199 FTP, and FILE protocols. Username and password may be specified in the URL@.
200 For example:
201 
202 @example
203 s = urlread ("http://user:password@@example.com/file.txt");
204 @end example
205 
206 GET and POST requests can be specified by @var{method} and @var{param}.
207 The parameter @var{method} is either @samp{get} or @samp{post} and
208 @var{param} is a cell array of parameter and value pairs.
209 For example:
210 
211 @example
212 @group
213 s = urlread ("http://www.google.com/search",
214  "get", @{"query", "octave"@});
215 @end group
216 @end example
217 @seealso{urlwrite}
218 @end deftypefn */)
219 {
220  int nargin = args.length ();
221 
222  // verify arguments
223  if (nargin != 1 && nargin != 3)
224  print_usage ();
225 
226  std::string url = args(0).xstring_value ("urlread: URL must be a string");
227 
228  std::string method;
229  Array<std::string> param;
230 
231  if (nargin == 3)
232  {
233  method = args(1).xstring_value ("urlread: METHOD must be a string");
234 
235  if (method != "get" && method != "post")
236  error (R"(urlread: METHOD must be "get" or "post")");
237 
238  param = args(2).xcellstr_value ("urlread: parameters (PARAM) for get and post requests must be given as a cell array of strings");
239 
240  if (param.numel () % 2 == 1)
241  error ("urlread: number of elements in PARAM must be even");
242  }
243 
244  std::ostringstream buf;
245 
246  url_transfer url_xfer = url_transfer (url, buf);
247 
248  if (! url_xfer.is_valid ())
249  error ("support for URL transfers was disabled when Octave was built");
250 
251  url_xfer.http_action (param, method);
252 
253  if (nargout < 2 && ! url_xfer.good ())
254  error ("urlread: %s", url_xfer.lasterror ().c_str ());
255 
256  octave_value_list retval (std::max (1, std::min (nargout, 3)));
257 
258  retval(0) = buf.str ();
259  if (nargout > 1)
260  retval(1) = url_xfer.good ();
261  if (nargout > 2)
262  retval(2) = url_xfer.good () ? "" : url_xfer.lasterror ();
263 
264  return retval;
265 }
266 
267 DEFUN (__restful_service__, args, nargout,
268  doc: /* -*- texinfo -*-
269 @deftypefn {} {@var{response} =} __restful_service__ (@var{url}, @var{param}, @var{weboptions})
270 Undocumented internal function.
271 @end deftypefn */)
272 {
273  int nargin = args.length ();
274 
275  if (nargin < 1)
276  print_usage ();
277 
278  std::string url = args(0).xstring_value ("__restful_service__: URL must be a string");
279 
280  std::ostringstream content;
281 
282  url_transfer url_xfer (url, content);
283 
284  if (! url_xfer.is_valid ())
285  error ("support for URL transfers was disabled when Octave was built");
286 
287  Array<std::string> param = args(1).cellstr_value ();
288 
289  std::string data, method;
290 
291  struct weboptions options;
292 
293  cdef_object object
294  = args (nargin - 1).classdef_object_value () -> get_object ();
295 
296  // We could've used object.map_value () instead to return a map but that
297  // shows a warning about about overriding access restrictions.
298  // Nevertheless, we are keeping checking that here if the keys are not
299  // equal to "delete" and "display", getting away with the warning.
300  string_vector keys = object.map_keys ();
301 
302  for (int i = 0; i < keys.numel (); i++)
303  {
304  if (keys(i) == "Timeout")
305  {
306  float timeout = object.get (keys(i)).float_value ();
307  options.Timeout = static_cast<long> (timeout * 1000);
308  }
309 
310  if (keys(i) == "HeaderFields")
311  {
312  options.HeaderFields = object.get (keys(i)).cellstr_value ();
313  }
314 
315  // FIXME: 'delete' and 'display', auto-generated, probably by cdef_object
316  // class? Remaining fields have already been adjusted elsewhere in the
317  // m-script. Set 'value' as the Value of the Key wherever it's a string.
318  if (keys(i) != "Timeout" && keys(i) != "HeaderFields"
319  && keys(i) != "delete" && keys(i) != "display")
320  {
321  std::string value = object.get (keys(i)).string_value ();
322 
323  if (keys(i) == "UserAgent")
324  options.UserAgent = value;
325 
326  if (keys(i) == "Username")
327  options.Username = value;
328 
329  if (keys(i) == "Password")
330  options.Password = value;
331 
332  if (keys(i) == "ContentReader")
333  // Unimplemented. Only for MATLAB compatibility.
334  options.ContentReader = "";
335 
336  if (keys(i) == "RequestMethod")
337  method = value;
338 
339  if (keys(i) == "ArrayFormat")
340  options.ArrayFormat = value;
341 
342  if (keys(i) == "CertificateFilename")
343  options.CertificateFilename = "";
344  }
345  }
346 
347  url_xfer.set_weboptions (options);
348 
349  url_xfer.http_action (param, method);
350 
351  if (nargout < 2 && ! url_xfer.good ())
352  error ("__restful_service__: %s", url_xfer.lasterror ().c_str ());
353 
354  return ovl (content.str ());
355 }
356 
357 OCTAVE_END_NAMESPACE(octave)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
octave_idx_type numel() const
Definition: str-vec.h:100
void set_weboptions(const struct weboptions &param)
Definition: url-transfer.h:284
bool is_valid() const
Definition: url-transfer.h:182
std::string lasterror() const
Definition: url-transfer.h:186
void http_action(const Array< std::string > &param, const std::string &action)
Definition: url-transfer.h:263
bool good() const
Definition: url-transfer.h:184
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void() error(const char *fmt,...)
Definition: error.cc:988
std::ofstream ofstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:635
int unlink(const std::string &name)
Definition: file-ops.cc:727
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219
std::string UserAgent
Definition: url-transfer.h:43
std::string ArrayFormat
Definition: url-transfer.h:50
std::string ContentReader
Definition: url-transfer.h:48
Array< std::string > HeaderFields
Definition: url-transfer.h:47
std::string Password
Definition: url-transfer.h:46
long Timeout
Definition: url-transfer.h:44
std::string CertificateFilename
Definition: url-transfer.h:51
std::string Username
Definition: url-transfer.h:45