Mercurial > octave-nkf
annotate src/DLD-FUNCTIONS/urlwrite.cc @ 7481:78f3811155f7
use exceptions in liboctave error handler
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 14 Feb 2008 17:14:23 -0500 |
parents | 120f3135952f |
children | 3725f819b5b3 |
rev | line source |
---|---|
6043 | 1 // urlwrite and urlread, a curl front-end for octave |
2 /* | |
3 | |
7017 | 4 Copyright (C) 2006, 2007 Alexander Barth |
6043 | 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 | |
7016 | 10 Free Software Foundation; either version 3 of the License, or (at your |
11 option) any later version. | |
6043 | 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 | |
7016 | 19 along with Octave; see the file COPYING. If not, see |
20 <http://www.gnu.org/licenses/>. | |
6043 | 21 |
22 */ | |
23 | |
24 // Author: Alexander Barth <abarth@marine.usf.edu> | |
25 // Adapted-By: jwe | |
26 | |
27 #ifdef HAVE_CONFIG_H | |
28 #include <config.h> | |
29 #endif | |
30 | |
31 #include <string> | |
32 #include <fstream> | |
33 #include <iomanip> | |
34 | |
35 #include "oct-env.h" | |
36 | |
37 #include "defun-dld.h" | |
38 #include "error.h" | |
39 #include "oct-obj.h" | |
40 #include "ov-cell.h" | |
41 #include "pager.h" | |
42 | |
43 #if defined (HAVE_CURL) | |
44 | |
45 #include <curl/curl.h> | |
46 #include <curl/types.h> | |
47 #include <curl/easy.h> | |
48 | |
49 // Write callback function for curl. | |
50 | |
6986 | 51 static int |
6043 | 52 write_data (void *buffer, size_t size, size_t nmemb, void *streamp) |
53 { | |
54 // *stream is actually an ostream object. | |
55 std::ostream& stream = *(static_cast<std::ostream*> (streamp)); | |
56 stream.write (static_cast<const char*> (buffer), size*nmemb); | |
57 return (stream.fail () ? 0 : size * nmemb); | |
58 } | |
59 | |
60 // Form the query string based on param. | |
61 | |
6986 | 62 static std::string |
6043 | 63 form_query_string (CURL *curl, const Cell& param) |
64 { | |
65 std::ostringstream query; | |
66 | |
67 for (int i = 0; i < param.numel (); i += 2) | |
68 { | |
69 std::string name = param(i).string_value (); | |
70 std::string text = param(i+1).string_value (); | |
71 | |
72 // Encode strings. | |
73 char *enc_name = curl_easy_escape (curl, name.c_str (), name.length ()); | |
74 char *enc_text = curl_easy_escape (curl, text.c_str (), text.length ()); | |
75 | |
76 query << enc_name << "=" << enc_text; | |
77 | |
78 curl_free (enc_name); | |
79 curl_free (enc_text); | |
80 | |
81 if (i < param.numel()-1) | |
82 query << "&"; | |
83 } | |
84 | |
85 query.flush (); | |
86 | |
87 return query.str (); | |
88 } | |
89 | |
90 // curl front-end | |
91 | |
6992 | 92 static void |
93 urlget_cleanup (CURL *curl) | |
94 { | |
95 curl_easy_cleanup (curl); | |
96 curl_global_cleanup (); | |
97 } | |
98 | |
6986 | 99 static CURLcode |
6043 | 100 urlget (const std::string& url, const std::string& method, |
101 const Cell& param, std::ostream& stream) | |
102 { | |
103 CURL *curl; | |
104 | |
105 curl_global_init(CURL_GLOBAL_DEFAULT); | |
106 | |
107 curl = curl_easy_init(); | |
108 | |
109 if (! curl) | |
110 return CURLE_FAILED_INIT; | |
111 | |
112 // handle paramters of GET or POST request | |
113 | |
114 std::string query_string = form_query_string (curl,param); | |
115 //octave_stdout << "query_string " << query_string << std::endl; | |
116 | |
117 if (method == "get") | |
118 { | |
119 query_string = url + "?" + query_string; | |
120 curl_easy_setopt (curl, CURLOPT_URL, query_string.c_str ()); | |
121 } | |
122 else if (method == "post") | |
123 { | |
124 curl_easy_setopt (curl, CURLOPT_URL, url.c_str ()); | |
125 curl_easy_setopt (curl, CURLOPT_POSTFIELDS, query_string.c_str ()); | |
126 } | |
127 else | |
7013 | 128 curl_easy_setopt (curl, CURLOPT_URL, url.c_str()); |
6043 | 129 |
130 // Define our callback to get called when there's data to be written. | |
131 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data); | |
132 | |
133 // Set a pointer to our struct to pass to the callback. | |
134 curl_easy_setopt (curl, CURLOPT_WRITEDATA, static_cast<void*> (&stream)); | |
135 | |
6390 | 136 // Follow redirects. |
7013 | 137 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, true); |
138 | |
139 // Don't use EPSV since connecting to sites that don't support it | |
140 // will hang for some time (3 minutes?) before moving on to try PASV | |
141 // instead. | |
142 curl_easy_setopt (curl, CURLOPT_FTP_USE_EPSV, false); | |
6390 | 143 |
6992 | 144 curl_easy_setopt (curl, CURLOPT_NOPROGRESS, true); |
6043 | 145 curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, url.c_str ()); |
146 curl_easy_setopt (curl, CURLOPT_FAILONERROR, true); | |
147 | |
7013 | 148 // Switch on full protocol/debug output. |
149 // curl_easy_setopt (curl, CURLOPT_VERBOSE, true); | |
6043 | 150 |
6992 | 151 CURLcode res = CURLE_OK; |
152 | |
153 // To understand the following, see the definitions of these macros | |
154 // in libcruft/misc/quit.h. The idea is that we call sigsetjmp here | |
155 // then the signal handler calls siglongjmp to get back here | |
156 // immediately. Then we perform some cleanup and throw an interrupt | |
157 // exception which will get us back to the top level, cleaning up | |
158 // any local C++ objects on the stack as we go. | |
159 | |
160 BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1; | |
161 | |
162 // We were interrupted (this code is inside a block that is only | |
163 // called when siglongjmp is called from a signal handler). | |
6043 | 164 |
6992 | 165 // Is there a better error code to use? Maybe it doesn't matter |
166 // because we are about to throw an execption. | |
167 | |
168 res = CURLE_ABORTED_BY_CALLBACK; | |
169 urlget_cleanup (curl); | |
7481
78f3811155f7
use exceptions in liboctave error handler
John W. Eaton <jwe@octave.org>
parents:
7031
diff
changeset
|
170 octave_rethrow_exception (); |
6992 | 171 |
172 BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2; | |
6043 | 173 |
6992 | 174 res = curl_easy_perform (curl); |
175 | |
176 END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; | |
177 | |
178 // If we are not interuppted, we will end up here, so we still need | |
179 // to clean up. | |
180 | |
181 urlget_cleanup (curl); | |
6043 | 182 |
183 return res; | |
184 } | |
185 | |
186 #endif | |
187 | |
188 DEFUN_DLD (urlwrite, args, nargout, | |
189 "-*- texinfo -*-\n\ | |
190 @deftypefn {Loadable Function} {} urlwrite (@var{URL}, @var{localfile})\n\ | |
191 @deftypefnx {Loadable Function} {@var{f} =} urlwrite (@var{url}, @var{localfile})\n\ | |
192 @deftypefnx {Loadable Function} {[@var{f}, @var{success}] =} urlwrite (@var{url}, @var{localfile})\n\ | |
193 @deftypefnx {Loadable Function} {[@var{f}, @var{success}, @var{message}] =} urlwrite (@var{url}, @var{localfile})\n\ | |
194 Download a remote file specified by its @var{URL} and save it as\n\ | |
195 @var{localfile}. For example,\n\ | |
196 \n\ | |
197 @example\n\ | |
7031 | 198 urlwrite (\"ftp://ftp.octave.org/pub/octave/README\", \n\ |
199 \"README.txt\");\n\ | |
6043 | 200 @end example\n\ |
201 \n\ | |
202 The full path of the downloaded file is returned in @var{f}. The\n\ | |
203 variable @var{success} is 1 if the download was successful,\n\ | |
204 otherwise it is 0 in which case @var{message} contains an error\n\ | |
205 message. If no output argument is specified and if an error occurs,\n\ | |
6588 | 206 then the error is signaled through Octave's error handling mechanism.\n\ |
6043 | 207 \n\ |
208 This function uses libcurl. Curl supports, among others, the HTTP,\n\ | |
209 FTP and FILE protocols. Username and password may be specified in\n\ | |
210 the URL, for example:\n\ | |
211 \n\ | |
212 @example\n\ | |
6588 | 213 urlwrite (\"http://username:password@@example.com/file.txt\",\n\ |
214 \"file.txt\");\n\ | |
6043 | 215 @end example\n\ |
216 \n\ | |
217 GET and POST requests can be specified by @var{method} and @var{param}.\n\ | |
6589 | 218 The parameter @var{method} is either @samp{get} or @samp{post}\n\ |
6588 | 219 and @var{param} is a cell array of parameter and value pairs.\n\ |
220 For example:\n\ | |
6043 | 221 \n\ |
222 @example\n\ | |
6588 | 223 urlwrite (\"http://www.google.com/search\", \"search.html\",\n\ |
224 \"get\", @{\"query\", \"octave\"@});\n\ | |
6043 | 225 @end example\n\ |
226 @seealso{urlread}\n\ | |
227 @end deftypefn") | |
228 { | |
229 octave_value_list retval; | |
230 | |
231 #if defined (HAVE_CURL) | |
232 | |
233 int nargin = args.length (); | |
234 | |
235 // verify arguments | |
236 if (nargin != 2 && nargin != 4) | |
237 { | |
238 print_usage (); | |
239 return retval; | |
240 } | |
241 | |
242 std::string url = args(0).string_value(); | |
243 | |
244 if (error_state) | |
245 { | |
246 error ("urlwrite: url must be a character string"); | |
247 return retval; | |
248 } | |
249 | |
250 // name to store the file if download is succesful | |
251 std::string filename = args(1).string_value(); | |
252 | |
253 if (error_state) | |
254 { | |
255 error ("urlwrite: localfile must be a character string"); | |
256 return retval; | |
257 } | |
258 | |
259 std::string method; | |
260 Cell param; // empty cell array | |
261 | |
262 if (nargin == 4) | |
263 { | |
264 method = args(2).string_value(); | |
265 | |
266 if (error_state) | |
267 { | |
268 error ("urlwrite: method can only be \"get\" or \"post\""); | |
269 return retval; | |
270 } | |
271 | |
272 if (method != "get" && method != "post") | |
273 { | |
274 error ("urlwrite: method can only be \"get\" or \"post\""); | |
275 return retval; | |
276 } | |
277 | |
278 param = args(3).cell_value(); | |
279 | |
280 if (error_state) | |
281 { | |
282 error ("urlwrite: parameters for get and post requests must be given as a cell"); | |
283 return retval; | |
284 } | |
285 | |
286 | |
287 if (param.numel () % 2 == 1 ) | |
288 { | |
289 error ("urlwrite: number of elements in param must be even"); | |
290 return retval; | |
291 } | |
292 } | |
293 | |
6986 | 294 std::ofstream ofile (filename.c_str(), std::ios::out | std::ios::binary); |
6043 | 295 |
6986 | 296 if (! ofile.is_open ()) |
6043 | 297 { |
298 error ("urlwrite: unable to open file"); | |
299 return retval; | |
300 } | |
301 | |
6986 | 302 CURLcode res = urlget (url, method, param, ofile); |
6043 | 303 |
6986 | 304 ofile.close (); |
6043 | 305 |
306 if (nargout > 0) | |
307 { | |
308 retval(0) = octave_env::make_absolute (filename, octave_env::getcwd ()); | |
309 retval(1) = res == CURLE_OK; | |
310 retval(2) = std::string (res == CURLE_OK ? "" : curl_easy_strerror (res)); | |
311 } | |
312 | |
6484 | 313 if (nargout < 2 && res != CURLE_OK) |
6043 | 314 error ("urlwrite: curl: %s", curl_easy_strerror (res)); |
315 | |
316 #else | |
6981 | 317 error ("urlwrite: not available in this version of Octave"); |
6043 | 318 #endif |
319 | |
320 return retval; | |
321 } | |
322 | |
323 DEFUN_DLD (urlread, args, nargout, | |
324 "-*- texinfo -*-\n\ | |
6549 | 325 @deftypefn {Loadable Function} {@var{s} =} urlread (@var{url})\n\ |
6043 | 326 @deftypefnx {Loadable Function} {[@var{s}, @var{success}] =} urlread (@var{url})\n\ |
6549 | 327 @deftypefnx {Loadable Function} {[@var{s}, @var{success}, @var{message}] =} urlread (@var{url})\n\ |
6547 | 328 @deftypefnx {Loadable Function} {[@dots{}] =} urlread (@var{url}, @var{method}, @var{param})\n\ |
6043 | 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\ | |
6588 | 333 s = urlread (\"ftp://ftp.octave.org/pub/octave/README\");\n\ |
6043 | 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\ | |
6588 | 339 then the error is signaled through Octave's error handling mechanism.\n\ |
6043 | 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\ | |
7031 | 346 s = urlread (\"http://user:password@@example.com/file.txt\");\n\ |
6043 | 347 @end example\n\ |
348 \n\ | |
349 GET and POST requests can be specified by @var{method} and @var{param}.\n\ | |
6588 | 350 The parameter @var{method} is either @samp{get} or @samp{post}\n\ |
351 and @var{param} is a cell array of parameter and value pairs.\n\ | |
6650 | 352 For example,\n\ |
6043 | 353 \n\ |
354 @example\n\ | |
6588 | 355 s = urlread (\"http://www.google.com/search\", \"get\",\n\ |
356 @{\"query\", \"octave\"@});\n\ | |
6043 | 357 @end example\n\ |
358 @seealso{urlwrite}\n\ | |
359 @end deftypefn") | |
360 { | |
6588 | 361 // Octave's return value |
6043 | 362 octave_value_list retval; |
363 | |
364 #if defined (HAVE_CURL) | |
365 | |
366 int nargin = args.length (); | |
367 | |
368 // verify arguments | |
369 if (nargin != 1 && nargin != 3) | |
370 { | |
371 print_usage (); | |
372 return retval; | |
373 } | |
374 | |
375 std::string url = args(0).string_value(); | |
376 | |
377 if (error_state) | |
378 { | |
379 error ("urlread: url must be a character string"); | |
380 return retval; | |
381 } | |
382 | |
383 std::string method; | |
384 Cell param; // empty cell array | |
385 | |
386 if (nargin == 3) | |
387 { | |
388 method = args(1).string_value(); | |
389 | |
390 if (error_state) | |
391 { | |
392 error ("urlread: method can only be \"get\" or \"post\""); | |
393 return retval; | |
394 } | |
395 | |
396 if (method != "get" && method != "post") | |
397 { | |
398 error ("urlread: method can only be \"get\" or \"post\""); | |
399 return retval; | |
400 } | |
401 | |
402 param = args(2).cell_value(); | |
403 | |
404 if (error_state) | |
405 { | |
406 error ("urlread: parameters for get and post requests must be given as a cell"); | |
407 return retval; | |
408 } | |
409 | |
410 if (param.numel () % 2 == 1 ) | |
411 { | |
412 error ("urlread: number of elements in param must be even"); | |
413 return retval; | |
414 } | |
415 } | |
416 | |
6986 | 417 std::ostringstream buf; |
6043 | 418 |
6986 | 419 CURLcode res = urlget (url, method, param, buf); |
6043 | 420 |
421 if (nargout > 0) | |
422 { | |
6986 | 423 retval(0) = buf.str (); |
6043 | 424 retval(1) = res == CURLE_OK; |
6986 | 425 // Return empty string if no error occured. |
6043 | 426 retval(2) = std::string (res == CURLE_OK ? "" : curl_easy_strerror (res)); |
427 } | |
428 | |
6484 | 429 if (nargout < 2 && res != CURLE_OK) |
6043 | 430 error ("urlread: curl: %s", curl_easy_strerror (res)); |
431 | |
432 #else | |
6981 | 433 error ("urlread: not available in this version of Octave"); |
6043 | 434 #endif |
435 | |
436 return retval; | |
437 } |