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