Mercurial > octave-nkf
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 } |