GNU Octave  6.2.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-2021 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 
55 static void
56 delete_file (const std::string& file)
57 {
58  octave::sys::unlink (file);
59 }
60 
61 DEFUN (urlwrite, args, nargout,
62  doc: /* -*- texinfo -*-
63 @deftypefn {} {} urlwrite (@var{url}, @var{localfile})
64 @deftypefnx {} {@var{f} =} urlwrite (@var{url}, @var{localfile})
65 @deftypefnx {} {[@var{f}, @var{success}] =} urlwrite (@var{url}, @var{localfile})
66 @deftypefnx {} {[@var{f}, @var{success}, @var{message}] =} urlwrite (@var{url}, @var{localfile})
67 Download a remote file specified by its @var{url} and save it as
68 @var{localfile}.
69 
70 For example:
71 
72 @example
73 @group
74 urlwrite ("ftp://ftp.octave.org/pub/README",
75  "README.txt");
76 @end group
77 @end example
78 
79 The full path of the downloaded file is returned in @var{f}.
80 
81 The variable @var{success} is 1 if the download was successful,
82 otherwise it is 0 in which case @var{message} contains an error message.
83 
84 If no output argument is specified and an error occurs, then the error is
85 signaled through Octave's error handling mechanism.
86 
87 This function uses libcurl. Curl supports, among others, the HTTP, FTP, and
88 FILE protocols. Username and password may be specified in the URL, for
89 example:
90 
91 @example
92 @group
93 urlwrite ("http://username:password@@example.com/file.txt",
94  "file.txt");
95 @end group
96 @end example
97 
98 GET and POST requests can be specified by @var{method} and @var{param}.
99 The parameter @var{method} is either @samp{get} or @samp{post} and
100 @var{param} is a cell array of parameter and value pairs.
101 For example:
102 
103 @example
104 @group
105 urlwrite ("http://www.google.com/search", "search.html",
106  "get", @{"query", "octave"@});
107 @end group
108 @end example
109 @seealso{urlread}
110 @end deftypefn */)
111 {
112  int nargin = args.length ();
113 
114  // verify arguments
115  if (nargin != 2 && nargin != 4)
116  print_usage ();
117 
118  std::string url = args(0).xstring_value ("urlwrite: URL must be a string");
119 
120  // name to store the file if download is successful
121  std::string filename = args(1).xstring_value ("urlwrite: LOCALFILE must be a string");
122 
123  std::string method;
124  Array<std::string> param;
125 
126  if (nargin == 4)
127  {
128  method = args(2).xstring_value ("urlwrite: METHOD must be a string");
129 
130  if (method != "get" && method != "post")
131  error (R"(urlwrite: METHOD must be "get" or "post")");
132 
133  param = args(3).xcellstr_value ("urlwrite: parameters (PARAM) for get and post requests must be given as a cell array of strings");
134 
135  if (param.numel () % 2 == 1)
136  error ("urlwrite: number of elements in PARAM must be even");
137  }
138 
139  // The file should only be deleted if it doesn't initially exist, we
140  // create it, and the download fails. We use unwind_protect to do
141  // it so that the deletion happens no matter how we exit the function.
142 
143  octave::sys::file_stat fs (filename);
144 
145  std::ofstream ofile =
146  octave::sys::ofstream (filename.c_str (), std::ios::out | std::ios::binary);
147 
148  if (! ofile.is_open ())
149  error ("urlwrite: unable to open file");
150 
152 
153  frame.add_fcn (delete_file, filename);
154 
155  octave::url_transfer url_xfer (url, ofile);
156 
158 
159  if (! url_xfer.is_valid ())
160  error ("support for URL transfers was disabled when Octave was built");
161 
162  url_xfer.http_action (param, method);
163 
164  ofile.close ();
165 
166  if (url_xfer.good ())
167  frame.discard ();
168 
169  if (nargout > 0)
170  {
171  if (url_xfer.good ())
172  retval = ovl (octave::sys::env::make_absolute (filename), true, "");
173  else
174  retval = ovl ("", false, url_xfer.lasterror ());
175  }
176 
177  if (nargout < 2 && ! url_xfer.good ())
178  error ("urlwrite: %s", url_xfer.lasterror ().c_str ());
179 
180  return retval;
181 }
182 
183 DEFUN (urlread, args, nargout,
184  doc: /* -*- texinfo -*-
185 @deftypefn {} {@var{s} =} urlread (@var{url})
186 @deftypefnx {} {[@var{s}, @var{success}] =} urlread (@var{url})
187 @deftypefnx {} {[@var{s}, @var{success}, @var{message}] =} urlread (@var{url})
188 @deftypefnx {} {[@dots{}] =} urlread (@var{url}, @var{method}, @var{param})
189 Download a remote file specified by its @var{url} and return its content
190 in string @var{s}.
191 
192 For example:
193 
194 @example
195 s = urlread ("ftp://ftp.octave.org/pub/README");
196 @end example
197 
198 The variable @var{success} is 1 if the download was successful,
199 otherwise it is 0 in which case @var{message} contains an error
200 message.
201 
202 If no output argument is specified and an error occurs, then the error is
203 signaled through Octave's error handling mechanism.
204 
205 This function uses libcurl. Curl supports, among others, the HTTP, FTP, and
206 FILE protocols. Username and password may be specified in the URL@. For
207 example:
208 
209 @example
210 s = urlread ("http://user:password@@example.com/file.txt");
211 @end example
212 
213 GET and POST requests can be specified by @var{method} and @var{param}.
214 The parameter @var{method} is either @samp{get} or @samp{post} and
215 @var{param} is a cell array of parameter and value pairs.
216 For example:
217 
218 @example
219 @group
220 s = urlread ("http://www.google.com/search", "get",
221  @{"query", "octave"@});
222 @end group
223 @end example
224 @seealso{urlwrite}
225 @end deftypefn */)
226 {
227  int nargin = args.length ();
228 
229  // verify arguments
230  if (nargin != 1 && nargin != 3)
231  print_usage ();
232 
233  std::string url = args(0).xstring_value ("urlread: URL must be a string");
234 
235  std::string method;
236  Array<std::string> param;
237 
238  if (nargin == 3)
239  {
240  method = args(1).xstring_value ("urlread: METHOD must be a string");
241 
242  if (method != "get" && method != "post")
243  error (R"(urlread: METHOD must be "get" or "post")");
244 
245  param = args(2).xcellstr_value ("urlread: parameters (PARAM) for get and post requests must be given as a cell array of strings");
246 
247  if (param.numel () % 2 == 1)
248  error ("urlread: number of elements in PARAM must be even");
249  }
250 
251  std::ostringstream buf;
252 
253  octave::url_transfer url_xfer = octave::url_transfer (url, buf);
254 
255  if (! url_xfer.is_valid ())
256  error ("support for URL transfers was disabled when Octave was built");
257 
258  url_xfer.http_action (param, method);
259 
261 
262  if (nargout > 0)
263  {
264  // Return empty string if no error occurred.
265  retval = ovl (buf.str (), url_xfer.good (),
266  url_xfer.good () ? "" : url_xfer.lasterror ());
267  }
268 
269  if (nargout < 2 && ! url_xfer.good ())
270  error ("urlread: %s", url_xfer.lasterror ().c_str ());
271 
272  return retval;
273 }
274 
275 DEFUN (__restful_service__, args, nargout,
276  doc: /* -*- texinfo -*-
277 @deftypefn {} {@var{response} =} __restful_service__ (@var{url}, @var{param}, @var{weboptions})
278 Undocumented internal function.
279 @end deftypefn */)
280 {
281  int nargin = args.length ();
282 
283  if (nargin < 1)
284  print_usage ();
285 
286  std::string url = args(0).xstring_value ("__restful_service__: URL must be a string");
287 
288  std::ostringstream content;
289 
290  octave::url_transfer url_xfer (url, content);
291 
292  if (! url_xfer.is_valid ())
293  error ("support for URL transfers was disabled when Octave was built");
294 
295  Array<std::string> param = args(1).cellstr_value ();
296 
297  std::string data, method;
298 
299  struct octave::weboptions options;
300 
301  octave::cdef_object object = args (nargin - 1).classdef_object_value ()
302  -> get_object ();
303 
304  // We could've used object.map_value () instead to return a map but that
305  // shows a warning about about overriding access restrictions.
306  // Nevertheless, we are keeping checking that here if the keys are not
307  // equal to "delete" and "display", getting away with the warning.
308  string_vector keys = object.map_keys ();
309 
310  for (int i = 0; i < keys.numel (); i++)
311  {
312  if (keys(i) == "Timeout")
313  {
314  float timeout = object.get (keys(i)).float_value ();
315  options.Timeout = static_cast<long>(timeout * 1000);
316  }
317 
318  if (keys(i) == "HeaderFields")
319  {
320  options.HeaderFields = object.get (keys(i)).cellstr_value ();
321  }
322 
323  // FIXME: 'delete' and 'display', auto-generated, probably by cdef_object
324  // class? Remaining fields have already been adjusted elsewhere in the
325  // m-script. Set 'value' as the Value of the Key wherever it's a string.
326  if (keys(i) != "Timeout" && keys(i) != "HeaderFields"
327  && keys(i) != "delete" && keys(i) != "display")
328  {
329  std::string value = object.get (keys(i)).string_value ();
330 
331  if (keys(i) == "UserAgent")
332  options.UserAgent = value;
333 
334  if (keys(i) == "Username")
335  options.Username = value;
336 
337  if (keys(i) == "Password")
338  options.Password = value;
339 
340  if (keys(i) == "ContentReader")
341  // Unimplemented. Only for MATLAB compatibility.
342  options.ContentReader = "";
343 
344  if (keys(i) == "RequestMethod")
345  method = value;
346 
347  if (keys(i) == "ArrayFormat")
348  options.ArrayFormat = value;
349 
350  if (keys(i) == "CertificateFilename")
351  options.CertificateFilename = "";
352  }
353  }
354 
355  url_xfer.set_weboptions (options);
356 
357  url_xfer.http_action (param, method);
358 
359  if (nargout < 2 && ! url_xfer.good ())
360  error ("__restful_service__: %s", url_xfer.lasterror ().c_str ());
361 
362  return ovl (content.str ());
363 }
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
void add_fcn(void(*fcn)(Params...), Args &&... args)
static std::string make_absolute(const std::string &s, const std::string &dot_path=get_current_directory())
Definition: oct-env.cc:133
std::string lasterror(void) const
Definition: url-transfer.h:200
bool good(void) const
Definition: url-transfer.h:198
void http_action(const Array< std::string > &param, const std::string &action)
Definition: url-transfer.h:277
bool is_valid(void) const
Definition: url-transfer.h:196
void set_weboptions(const struct weboptions &param)
Definition: url-transfer.h:298
octave_idx_type numel(void) const
Definition: str-vec.h:100
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#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:968
int unlink(const std::string &name)
Definition: file-ops.cc:626
std::ofstream ofstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:395
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
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:44
std::string Password
Definition: url-transfer.h:47
std::string ArrayFormat
Definition: url-transfer.h:51
std::string ContentReader
Definition: url-transfer.h:49
std::string Username
Definition: url-transfer.h:46
Array< std::string > HeaderFields
Definition: url-transfer.h:48
std::string CertificateFilename
Definition: url-transfer.h:52
static void delete_file(const std::string &file)
Definition: urlwrite.cc:56