comparison src/DLD-FUNCTIONS/urlwrite.cc @ 6043:199f15a8d1fc

[project @ 2006-10-09 19:49:03 by jwe]
author jwe
date Mon, 09 Oct 2006 19:49:04 +0000
parents
children de4ae354c831
comparison
equal deleted inserted replaced
6042:40be03213eb5 6043:199f15a8d1fc
1 // urlwrite and urlread, a curl front-end for octave
2 /*
3
4 Copyright (C) 2006 Alexander Barth
5
6 This file is part of Octave.
7
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
11 later version.
12
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, write to the Free
20 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22
23 */
24
25 // Author: Alexander Barth <abarth@marine.usf.edu>
26 // Adapted-By: jwe
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <string>
33 #include <fstream>
34 #include <iomanip>
35
36 #include "oct-env.h"
37
38 #include "defun-dld.h"
39 #include "error.h"
40 #include "oct-obj.h"
41 #include "ov-cell.h"
42 #include "pager.h"
43
44 #if defined (HAVE_CURL)
45
46 #include <curl/curl.h>
47 #include <curl/types.h>
48 #include <curl/easy.h>
49
50 // Write callback function for curl.
51
52 int
53 write_data (void *buffer, size_t size, size_t nmemb, void *streamp)
54 {
55 // *stream is actually an ostream object.
56 std::ostream& stream = *(static_cast<std::ostream*> (streamp));
57 stream.write (static_cast<const char*> (buffer), size*nmemb);
58 return (stream.fail () ? 0 : size * nmemb);
59 }
60
61 // Progress callback function for curl.
62
63 int
64 progress_func (const char *url, double dltotal, double dlnow,
65 double /*ultotal*/, double /*ulnow*/)
66 {
67 // macro that picks up Ctrl-C signalling
68 OCTAVE_QUIT;
69
70 #if !defined (URL_QUITE_DOWNLOAD)
71 if (dltotal != 0)
72 {
73 // Is the carriage return character "\r" portable?
74 octave_stdout << "\r" << url << ": "
75 << std::fixed << std::setw(5) << std::setprecision(1)
76 << dlnow*100.0/dltotal << " %";
77
78 octave_stdout.flush ();
79 }
80 #endif
81
82 return 0;
83 }
84
85 // Form the query string based on param.
86
87 std::string
88 form_query_string (CURL *curl, const Cell& param)
89 {
90 std::ostringstream query;
91
92 for (int i = 0; i < param.numel (); i += 2)
93 {
94 std::string name = param(i).string_value ();
95 std::string text = param(i+1).string_value ();
96
97 // Encode strings.
98 char *enc_name = curl_easy_escape (curl, name.c_str (), name.length ());
99 char *enc_text = curl_easy_escape (curl, text.c_str (), text.length ());
100
101 query << enc_name << "=" << enc_text;
102
103 curl_free (enc_name);
104 curl_free (enc_text);
105
106 if (i < param.numel()-1)
107 query << "&";
108 }
109
110 query.flush ();
111
112 return query.str ();
113 }
114
115 // curl front-end
116
117 CURLcode
118 urlget (const std::string& url, const std::string& method,
119 const Cell& param, std::ostream& stream)
120 {
121 CURL *curl;
122
123 curl_global_init(CURL_GLOBAL_DEFAULT);
124
125 curl = curl_easy_init();
126
127 if (! curl)
128 return CURLE_FAILED_INIT;
129
130 // handle paramters of GET or POST request
131
132 std::string query_string = form_query_string (curl,param);
133 //octave_stdout << "query_string " << query_string << std::endl;
134
135 if (method == "get")
136 {
137 query_string = url + "?" + query_string;
138 curl_easy_setopt (curl, CURLOPT_URL, query_string.c_str ());
139 }
140 else if (method == "post")
141 {
142 curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
143 curl_easy_setopt (curl, CURLOPT_POSTFIELDS, query_string.c_str ());
144 }
145 else
146 {
147 curl_easy_setopt (curl, CURLOPT_URL,url.c_str());
148 }
149
150 // Define our callback to get called when there's data to be written.
151 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data);
152
153 // Set a pointer to our struct to pass to the callback.
154 curl_easy_setopt (curl, CURLOPT_WRITEDATA, static_cast<void*> (&stream));
155
156 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, false);
157 curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_func);
158 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, url.c_str ());
159 curl_easy_setopt (curl, CURLOPT_FAILONERROR, true);
160
161 // Switch on full protocol/debug output
162 // curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
163
164 CURLcode res = curl_easy_perform(curl);
165
166 #if !defined (URL_QUITE_DOWNLOAD)
167 if (res == CURLE_OK)
168 {
169 // download is complete
170 progress_func (url.c_str (), 1, 1, 0, 0);
171 // new line after progress_func
172 octave_stdout << std::endl;
173 }
174 #endif
175
176 // always cleanup
177 curl_easy_cleanup (curl);
178
179 curl_global_cleanup ();
180
181 return res;
182 }
183
184 #endif
185
186 DEFUN_DLD (urlwrite, args, nargout,
187 "-*- texinfo -*-\n\
188 @deftypefn {Loadable Function} {} urlwrite (@var{URL}, @var{localfile})\n\
189 @deftypefnx {Loadable Function} {@var{f} =} urlwrite (@var{url}, @var{localfile})\n\
190 @deftypefnx {Loadable Function} {[@var{f}, @var{success}] =} urlwrite (@var{url}, @var{localfile})\n\
191 @deftypefnx {Loadable Function} {[@var{f}, @var{success}, @var{message}] =} urlwrite (@var{url}, @var{localfile})\n\
192 Download a remote file specified by its @var{URL} and save it as\n\
193 @var{localfile}. For example,\n\
194 \n\
195 @example\n\
196 urlwrite ('ftp://ftp.octave.org/pub/octave/README', 'README.txt');\n\
197 @end example\n\
198 \n\
199 The full path of the downloaded file is returned in @var{f}. The\n\
200 variable @var{success} is 1 if the download was successful,\n\
201 otherwise it is 0 in which case @var{message} contains an error\n\
202 message. If no output argument is specified and if an error occurs,\n\
203 then the error is signaled through octave's error handling mechanism.\n\
204 \n\
205 This function uses libcurl. Curl supports, among others, the HTTP,\n\
206 FTP and FILE protocols. Username and password may be specified in\n\
207 the URL, for example:\n\
208 \n\
209 @example\n\
210 urlwrite ('http://username:password@@example.com/file.txt', 'file.txt');\n\
211 @end example\n\
212 \n\
213 GET and POST requests can be specified by @var{method} and @var{param}.\n\
214 The parameter @var{method} is either 'get' or 'post' and @var{param} is a\n\
215 cell array of parameter and value pairs. For example:\n\
216 \n\
217 @example\n\
218 urlwrite ('http://www.google.com/search', 'search.html', 'get', @{'query', 'octave'@});\n\
219 @end example\n\
220 @seealso{urlread}\n\
221 @end deftypefn")
222 {
223 octave_value_list retval;
224
225 #if defined (HAVE_CURL)
226
227 int nargin = args.length ();
228
229 // verify arguments
230 if (nargin != 2 && nargin != 4)
231 {
232 print_usage ();
233 return retval;
234 }
235
236 std::string url = args(0).string_value();
237
238 if (error_state)
239 {
240 error ("urlwrite: url must be a character string");
241 return retval;
242 }
243
244 // name to store the file if download is succesful
245 std::string filename = args(1).string_value();
246
247 if (error_state)
248 {
249 error ("urlwrite: localfile must be a character string");
250 return retval;
251 }
252
253 std::string method;
254 Cell param; // empty cell array
255
256 if (nargin == 4)
257 {
258 method = args(2).string_value();
259
260 if (error_state)
261 {
262 error ("urlwrite: method can only be \"get\" or \"post\"");
263 return retval;
264 }
265
266 if (method != "get" && method != "post")
267 {
268 error ("urlwrite: method can only be \"get\" or \"post\"");
269 return retval;
270 }
271
272 param = args(3).cell_value();
273
274 if (error_state)
275 {
276 error ("urlwrite: parameters for get and post requests must be given as a cell");
277 return retval;
278 }
279
280
281 if (param.numel () % 2 == 1 )
282 {
283 error ("urlwrite: number of elements in param must be even");
284 return retval;
285 }
286 }
287
288 // create and open file stream
289
290 std::ofstream stream (filename.c_str(), std::ios::out | std::ios::binary);
291
292 if (! stream.is_open ())
293 {
294 error ("urlwrite: unable to open file");
295 return retval;
296 }
297
298 CURLcode res = urlget (url, method, param, stream);
299
300 // close the local file
301 stream.close ();
302
303 if (nargout > 0)
304 {
305 // FIXME: urlwrite should return full file path
306 retval(0) = octave_env::make_absolute (filename, octave_env::getcwd ());
307 // retval(0) = filename;
308 retval(1) = res == CURLE_OK;
309 // return empty string if no error occured
310 retval(2) = std::string (res == CURLE_OK ? "" : curl_easy_strerror (res));
311 }
312
313 if (nargout < 2 & res != CURLE_OK)
314 error ("urlwrite: curl: %s", curl_easy_strerror (res));
315
316 #else
317 error ("urlwrite: not available in this version of Octave");
318 #endif
319
320 return retval;
321 }
322
323 DEFUN_DLD (urlread, args, nargout,
324 "-*- texinfo -*-\n\
325 @deftypefn {Loadable Function} {@var{s} =} urlread(@var{url})\n\
326 @deftypefnx {Loadable Function} {[@var{s}, @var{success}] =} urlread (@var{url})\n\
327 @deftypefnx {Loadable Function} {[@var{s}, @var{success}, @var{message}] =} urlread(@var{url})\n\
328 @deftypefnx {Loadable Function} {[...] =} urlread (@var{url}, @var{method}, @var{param})\n\
329 Download a remote file specified by its @var{URL} and return its content\n\
330 in string @var{s}. For example,\n\
331 \n\
332 @example\n\
333 s = urlread ('ftp://ftp.octave.org/pub/octave/README');\n\
334 @end example\n\
335 \n\
336 The variable @var{success} is 1 if the download was successful,\n\
337 otherwise it is 0 in which case @var{message} contains an error\n\
338 message. If no output argument is specified and if an error occurs,\n\
339 then the error is signaled through octave's error handling mechanism.\n\
340 \n\
341 This function uses libcurl. Curl supports, among others, the HTTP,\n\
342 FTP and FILE protocols. Username and password may be specified in the\n\
343 URL. For example,\n\
344 \n\
345 @example\n\
346 s = urlread ('http://username:password@@example.com/file.txt');\n\
347 @end example\n\
348 \n\
349 GET and POST requests can be specified by @var{method} and @var{param}.\n\
350 The parameter @var{method} is either 'get' or 'post' and @var{param} is a\n\
351 cell array of parameter and value pairs. For example,\n\
352 \n\
353 @example\n\
354 s = urlread ('http://www.google.com/search', 'get', @{'query', 'octave'@});\n\
355 @end example\n\
356 @seealso{urlwrite}\n\
357 @end deftypefn")
358 {
359 // octave's return value
360 octave_value_list retval;
361
362 #if defined (HAVE_CURL)
363
364 int nargin = args.length ();
365
366 // verify arguments
367 if (nargin != 1 && nargin != 3)
368 {
369 print_usage ();
370 return retval;
371 }
372
373 std::string url = args(0).string_value();
374
375 if (error_state)
376 {
377 error ("urlread: url must be a character string");
378 return retval;
379 }
380
381 std::string method;
382 Cell param; // empty cell array
383
384 if (nargin == 3)
385 {
386 method = args(1).string_value();
387
388 if (error_state)
389 {
390 error ("urlread: method can only be \"get\" or \"post\"");
391 return retval;
392 }
393
394 if (method != "get" && method != "post")
395 {
396 error ("urlread: method can only be \"get\" or \"post\"");
397 return retval;
398 }
399
400 param = args(2).cell_value();
401
402 if (error_state)
403 {
404 error ("urlread: parameters for get and post requests must be given as a cell");
405 return retval;
406 }
407
408 if (param.numel () % 2 == 1 )
409 {
410 error ("urlread: number of elements in param must be even");
411 return retval;
412 }
413 }
414
415 // string stream for output
416 std::ostringstream stream;
417
418 CURLcode res = urlget (url, method, param, stream);
419
420 if (nargout > 0)
421 {
422 retval(0) = stream.str ();
423 retval(1) = res == CURLE_OK;
424 // return empty string if no error occured
425 retval(2) = std::string (res == CURLE_OK ? "" : curl_easy_strerror (res));
426 }
427
428 if (nargout < 2 & res != CURLE_OK)
429 error ("urlread: curl: %s", curl_easy_strerror (res));
430
431 #else
432 error ("urlread: not available in this version of Octave");
433 #endif
434
435 return retval;
436 }