GNU Octave  8.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-2023 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(
130  3).xcellstr_value ("urlwrite: parameters (PARAM) for get and post requests must be given as a cell array of strings");
131 
132  if (param.numel () % 2 == 1)
133  error ("urlwrite: number of elements in PARAM must be even");
134  }
135 
136  // The file should only be deleted if it doesn't initially exist, we
137  // create it, and the download fails. We use unwind_protect to do
138  // it so that the deletion happens no matter how we exit the function.
139 
140  sys::file_stat fs (filename);
141 
142  std::ofstream ofile =
143  sys::ofstream (filename.c_str (), std::ios::out | std::ios::binary);
144 
145  if (! ofile.is_open ())
146  error ("urlwrite: unable to open file");
147 
148  int(*unlink_fptr)(const std::string&) = sys::unlink;
149  unwind_action_safe unlink_action (unlink_fptr, filename);
150 
151  url_transfer url_xfer (url, ofile);
152 
153  octave_value_list retval;
154 
155  if (! url_xfer.is_valid ())
156  error ("support for URL transfers was disabled when Octave was built");
157 
158  url_xfer.http_action (param, method);
159 
160  ofile.close ();
161 
162  if (url_xfer.good ())
163  unlink_action.discard ();
164 
165  if (nargout > 0)
166  {
167  if (url_xfer.good ())
168  retval = ovl (sys::env::make_absolute (filename), true, "");
169  else
170  retval = ovl ("", false, url_xfer.lasterror ());
171  }
172 
173  if (nargout < 2 && ! url_xfer.good ())
174  error ("urlwrite: %s", url_xfer.lasterror ().c_str ());
175 
176  return retval;
177 }
178 
179 DEFUN (urlread, args, nargout,
180  doc: /* -*- texinfo -*-
181 @deftypefn {} {@var{s} =} urlread (@var{url})
182 @deftypefnx {} {[@var{s}, @var{success}] =} urlread (@var{url})
183 @deftypefnx {} {[@var{s}, @var{success}, @var{message}] =} urlread (@var{url})
184 @deftypefnx {} {[@dots{}] =} urlread (@var{url}, @var{method}, @var{param})
185 Download a remote file specified by its @var{url} and return its content
186 in string @var{s}.
187 
188 For example:
189 
190 @example
191 s = urlread ("http://ftp.octave.org/pub/README");
192 @end example
193 
194 The variable @var{success} is 1 if the download was successful,
195 otherwise it is 0 in which case @var{message} contains an error
196 message.
197 
198 If no output argument is specified and an error occurs, then the error is
199 signaled through Octave's error handling mechanism.
200 
201 This function uses libcurl. The curl library supports, among others, the HTTP,
202 FTP, and FILE protocols. Username and password may be specified in the URL@.
203 For example:
204 
205 @example
206 s = urlread ("http://user:password@@example.com/file.txt");
207 @end example
208 
209 GET and POST requests can be specified by @var{method} and @var{param}.
210 The parameter @var{method} is either @samp{get} or @samp{post} and
211 @var{param} is a cell array of parameter and value pairs.
212 For example:
213 
214 @example
215 @group
216 s = urlread ("http://www.google.com/search",
217  "get", @{"query", "octave"@});
218 @end group
219 @end example
220 @seealso{urlwrite}
221 @end deftypefn */)
222 {
223  int nargin = args.length ();
224 
225  // verify arguments
226  if (nargin != 1 && nargin != 3)
227  print_usage ();
228 
229  std::string url = args(0).xstring_value ("urlread: URL must be a string");
230 
231  std::string method;
232  Array<std::string> param;
233 
234  if (nargin == 3)
235  {
236  method = args(1).xstring_value ("urlread: METHOD must be a string");
237 
238  if (method != "get" && method != "post")
239  error (R"(urlread: METHOD must be "get" or "post")");
240 
241  param = args(
242  2).xcellstr_value ("urlread: parameters (PARAM) for get and post requests must be given as a cell array of strings");
243 
244  if (param.numel () % 2 == 1)
245  error ("urlread: number of elements in PARAM must be even");
246  }
247 
248  std::ostringstream buf;
249 
250  url_transfer url_xfer = url_transfer (url, buf);
251 
252  if (! url_xfer.is_valid ())
253  error ("support for URL transfers was disabled when Octave was built");
254 
255  url_xfer.http_action (param, method);
256 
257  octave_value_list retval;
258 
259  if (nargout > 0)
260  {
261  // Return empty string if no error occurred.
262  retval = ovl (buf.str (), url_xfer.good (),
263  url_xfer.good () ? "" : url_xfer.lasterror ());
264  }
265 
266  if (nargout < 2 && ! url_xfer.good ())
267  error ("urlread: %s", url_xfer.lasterror ().c_str ());
268 
269  return retval;
270 }
271 
272 DEFUN (__restful_service__, args, nargout,
273  doc: /* -*- texinfo -*-
274 @deftypefn {} {@var{response} =} __restful_service__ (@var{url}, @var{param}, @var{weboptions})
275 Undocumented internal function.
276 @end deftypefn */)
277 {
278  int nargin = args.length ();
279 
280  if (nargin < 1)
281  print_usage ();
282 
283  std::string url = args(0).xstring_value ("__restful_service__: URL must be a string");
284 
285  std::ostringstream content;
286 
287  url_transfer url_xfer (url, content);
288 
289  if (! url_xfer.is_valid ())
290  error ("support for URL transfers was disabled when Octave was built");
291 
292  Array<std::string> param = args(1).cellstr_value ();
293 
294  std::string data, method;
295 
296  struct weboptions options;
297 
298  cdef_object object
299  = args (nargin - 1).classdef_object_value () -> get_object ();
300 
301  // We could've used object.map_value () instead to return a map but that
302  // shows a warning about about overriding access restrictions.
303  // Nevertheless, we are keeping checking that here if the keys are not
304  // equal to "delete" and "display", getting away with the warning.
305  string_vector keys = object.map_keys ();
306 
307  for (int i = 0; i < keys.numel (); i++)
308  {
309  if (keys(i) == "Timeout")
310  {
311  float timeout = object.get (keys(i)).float_value ();
312  options.Timeout = static_cast<long>(timeout * 1000);
313  }
314 
315  if (keys(i) == "HeaderFields")
316  {
317  options.HeaderFields = object.get (keys(i)).cellstr_value ();
318  }
319 
320  // FIXME: 'delete' and 'display', auto-generated, probably by cdef_object
321  // class? Remaining fields have already been adjusted elsewhere in the
322  // m-script. Set 'value' as the Value of the Key wherever it's a string.
323  if (keys(i) != "Timeout" && keys(i) != "HeaderFields"
324  && keys(i) != "delete" && keys(i) != "display")
325  {
326  std::string value = object.get (keys(i)).string_value ();
327 
328  if (keys(i) == "UserAgent")
329  options.UserAgent = value;
330 
331  if (keys(i) == "Username")
332  options.Username = value;
333 
334  if (keys(i) == "Password")
335  options.Password = value;
336 
337  if (keys(i) == "ContentReader")
338  // Unimplemented. Only for MATLAB compatibility.
339  options.ContentReader = "";
340 
341  if (keys(i) == "RequestMethod")
342  method = value;
343 
344  if (keys(i) == "ArrayFormat")
345  options.ArrayFormat = value;
346 
347  if (keys(i) == "CertificateFilename")
348  options.CertificateFilename = "";
349  }
350  }
351 
352  url_xfer.set_weboptions (options);
353 
354  url_xfer.http_action (param, method);
355 
356  if (nargout < 2 && ! url_xfer.good ())
357  error ("__restful_service__: %s", url_xfer.lasterror ().c_str ());
358 
359  return ovl (content.str ());
360 }
361 
OCTAVE_END_NAMESPACE(octave)
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
octave_idx_type numel(void) const
Definition: str-vec.h:100
void discard(void)
Definition: unwind-prot.h:257
void set_weboptions(const struct weboptions &param)
Definition: url-transfer.h:288
bool is_valid(void) const
Definition: url-transfer.h:186
bool good(void) const
Definition: url-transfer.h:188
void http_action(const Array< std::string > &param, const std::string &action)
Definition: url-transfer.h:267
std::string lasterror(void) const
Definition: url-transfer.h:190
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTINTERP_API 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:979
int unlink(const std::string &name)
Definition: file-ops.cc:677
std::ofstream ofstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:438
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
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
static string_vector make_absolute(const string_vector &sv)
Definition: utils.cc:536