comparison libinterp/dldfcn/urlwrite.cc @ 17550:77127a3badaa

move curl_object actions to rep class * urlwrite.cc (class curl_object): Move curl actions to curl_object_rep class. Forward all non-refcount actions to rep class. (mput_directory): Move to curl_object_rep class. Create wrapper in curl_object_class. (getallfiles): Move to curl_object_rep_class, renaming to mget_directory. Create wrapper in curl_object class. (setopt): Delete macro. (SETOPT, SETOPTR): New macros. Return on error.
author John W. Eaton <jwe@octave.org>
date Thu, 03 Oct 2013 09:51:56 -0400
parents a646665cd574
children 8fca0bbdd4c1
comparison
equal deleted inserted replaced
17549:a646665cd574 17550:77127a3badaa
48 #include "ov-cell.h" 48 #include "ov-cell.h"
49 #include "pager.h" 49 #include "pager.h"
50 #include "oct-map.h" 50 #include "oct-map.h"
51 #include "oct-refcount.h" 51 #include "oct-refcount.h"
52 #include "unwind-prot.h" 52 #include "unwind-prot.h"
53 #include "gripes.h" 53
54 static void
55 delete_file (const std::string& file)
56 {
57 octave_unlink (file);
58 }
54 59
55 #ifdef HAVE_CURL 60 #ifdef HAVE_CURL
56 61
57 #include <curl/curl.h> 62 #include <curl/curl.h>
58 #include <curl/curlver.h> 63 #include <curl/curlver.h>
84 } 89 }
85 90
86 class curl_object 91 class curl_object
87 { 92 {
88 private: 93 private:
94
95 // I'd love to rewrite this as a private method of the curl_object
96 // class, but you can't pass the va_list from the wrapper SETOPT to
97 // the curl_easy_setopt function.
98 #define SETOPT(option, parameter) \
99 do \
100 { \
101 CURLcode res = curl_easy_setopt (curl, option, parameter); \
102 if (res != CURLE_OK) \
103 { \
104 error ("%s", curl_easy_strerror (res)); \
105 return; \
106 } \
107 } \
108 while (0)
109
110 // Sames as above, but return retval.
111 #define SETOPTR(option, parameter) \
112 do \
113 { \
114 CURLcode res = curl_easy_setopt (curl, option, parameter); \
115 if (res != CURLE_OK) \
116 { \
117 error ("%s", curl_easy_strerror (res)); \
118 return retval; \
119 } \
120 } \
121 while (0)
122
89 class curl_object_rep 123 class curl_object_rep
90 { 124 {
125 private:
126
127 static void reset_path (const curl_object_rep *curl_rep)
128 {
129 curl_rep->cwd ("..");
130 }
131
91 public: 132 public:
92 curl_object_rep (void) : count (1), valid (true), ascii (false) 133
93 { 134 curl_object_rep (void)
94 curl = curl_easy_init (); 135 : count (1), curl (curl_easy_init ()), host (), userpwd (),
95 if (!curl) 136 valid (false), ascii_mode (false), errnum ()
137 {
138 if (! curl)
139 error ("can not create curl object");
140 }
141
142 curl_object_rep (const std::string& host_arg, const std::string& user_arg,
143 const std::string& passwd)
144 : count (1), curl (curl_easy_init ()), host (host_arg),
145 userpwd (), valid (true), ascii_mode (false), errnum ()
146 {
147 if (!curl)
148 {
149 valid = false;
96 error ("can not create curl object"); 150 error ("can not create curl object");
97 } 151 return;
98 152 }
99 ~curl_object_rep (void) 153
100 { 154 init (user_arg, passwd, std::cin, octave_stdout);
101 if (curl) 155
102 curl_easy_cleanup (curl); 156 std::string url ("ftp://" + host_arg);
103 } 157 SETOPT (CURLOPT_URL, url.c_str ());
104 158
105 bool is_valid (void) const 159 // Setup the link, with no transfer.
106 { 160 perform ();
107 return valid; 161 }
108 } 162
109 163 curl_object_rep (const std::string& url, const std::string& method,
110 bool perform (bool curlerror) const 164 const Cell& param, std::ostream& os, bool& retval)
111 { 165 : count (1), curl (curl_easy_init ()), host (), userpwd (),
112 bool retval = false; 166 valid (true), ascii_mode (false), errnum ()
113 if (!error_state)
114 {
115 BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
116
117 errnum = curl_easy_perform (curl);
118 if (errnum != CURLE_OK)
119 {
120 if (curlerror)
121 error ("%s", curl_easy_strerror (errnum));
122 }
123 else
124 retval = true;
125
126 END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
127 }
128 return retval;
129 }
130
131 CURL *object (void) const
132 {
133 return curl;
134 }
135
136 bool is_ascii (void) const
137 {
138 return ascii;
139 }
140
141 bool is_binary (void) const
142 {
143 return !ascii;
144 }
145
146 octave_refcount<size_t> count;
147 std::string host;
148 std::string url;
149 std::string userpwd;
150 bool valid;
151 bool ascii;
152 mutable CURLcode errnum;
153
154 private:
155 CURL *curl;
156
157 // No copying!
158
159 curl_object_rep (const curl_object_rep& ov);
160
161 curl_object_rep& operator = (const curl_object_rep&);
162 };
163
164 public:
165
166 // I'd love to rewrite this as a private method of the curl_object
167 // class, but you can't pass the va_list from the wrapper setopt to
168 // the curl_easy_setopt function.
169 #define setopt(option, parameter) \
170 { \
171 CURLcode res = curl_easy_setopt (rep->object (), option, parameter); \
172 if (res != CURLE_OK) \
173 error ("%s", curl_easy_strerror (res)); \
174 }
175
176 curl_object (void) : rep (new curl_object_rep ())
177 {
178 rep->valid = false;
179 }
180
181 curl_object (const std::string& _host, const std::string& user,
182 const std::string& passwd) :
183 rep (new curl_object_rep ())
184 {
185 rep->host = _host;
186 init (user, passwd, std::cin, octave_stdout);
187
188 rep->url = "ftp://" + _host;
189 setopt (CURLOPT_URL, rep->url.c_str ());
190
191 // Setup the link, with no transfer
192 if (!error_state)
193 perform ();
194 }
195
196 curl_object (const std::string& url, const std::string& method,
197 const Cell& param, std::ostream& os, bool& retval) :
198 rep (new curl_object_rep ())
199 { 167 {
200 retval = false; 168 retval = false;
201 169
170 if (!curl)
171 {
172 valid = false;
173 error ("can not create curl object");
174 return;
175 }
176
202 init ("", "", std::cin, os); 177 init ("", "", std::cin, os);
203 178
204 setopt (CURLOPT_NOBODY, 0); 179 SETOPT (CURLOPT_NOBODY, 0);
205 180
206 // Restore the default HTTP request method to GET after setting 181 // Restore the default HTTP request method to GET after setting
207 // NOBODY to true and back to false. This is needed for backward 182 // NOBODY to true and back to false. This is needed for backward
208 // compatibility with versions of libcurl < 7.18.2. 183 // compatibility with versions of libcurl < 7.18.2.
209 setopt (CURLOPT_HTTPGET, 1); 184 SETOPT (CURLOPT_HTTPGET, 1);
210 185
211 // Don't need to store the parameters here as we can't change 186 // Don't need to store the parameters here as we can't change
212 // the URL after the object is created 187 // the URL after the object is created
213 std::string query_string = form_query_string (param); 188 std::string query_string = form_query_string (param);
214 189
215 if (method == "get") 190 if (method == "get")
216 { 191 {
217 query_string = url + "?" + query_string; 192 query_string = url + "?" + query_string;
218 setopt (CURLOPT_URL, query_string.c_str ()); 193 SETOPT (CURLOPT_URL, query_string.c_str ());
219 } 194 }
220 else if (method == "post") 195 else if (method == "post")
221 { 196 {
222 setopt (CURLOPT_URL, url.c_str ()); 197 SETOPT (CURLOPT_URL, url.c_str ());
223 setopt (CURLOPT_POSTFIELDS, query_string.c_str ()); 198 SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ());
224 } 199 }
225 else 200 else
226 setopt (CURLOPT_URL, url.c_str ()); 201 SETOPT (CURLOPT_URL, url.c_str ());
227 202
228 if (!error_state) 203 retval = perform (false);
229 retval = perform (false); 204 }
230 } 205
231 206 ~curl_object_rep (void)
232 curl_object (const curl_object& h) : rep (h.rep) 207 {
233 { 208 if (curl)
234 rep->count++; 209 curl_easy_cleanup (curl);
235 } 210 }
236 211
237 ~curl_object (void) 212 bool is_valid (void) const { return valid; }
238 { 213
239 if (--rep->count == 0) 214 bool perform (bool curlerror = true) const
240 delete rep; 215 {
241 } 216 bool retval = false;
242 217
243 curl_object& operator = (const curl_object& h) 218 BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
244 { 219
245 if (this != &h) 220 errnum = curl_easy_perform (curl);
246 { 221 if (errnum != CURLE_OK)
247 if (--rep->count == 0) 222 {
248 delete rep; 223 if (curlerror)
249 224 error ("%s", curl_easy_strerror (errnum));
250 rep = h.rep; 225 }
251 rep->count++; 226 else
252 } 227 retval = true;
253 return *this; 228
254 } 229 END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE;
255 230
256 bool is_valid (void) const 231 return retval;
257 { 232 }
258 return rep->is_valid (); 233
259 } 234 std::string lasterror (void) const
260 235 {
261 std::string lasterror (void) const 236 return std::string (curl_easy_strerror (errnum));
262 { 237 }
263 return std::string (curl_easy_strerror (rep->errnum)); 238
264 } 239 void set_ostream (std::ostream& os) const
265 240 {
266 void set_ostream (std::ostream& os) const 241 SETOPT (CURLOPT_WRITEDATA, static_cast<void*> (&os));
267 { 242 }
268 setopt (CURLOPT_WRITEDATA, static_cast<void*> (&os)); 243
269 } 244 void set_istream (std::istream& is) const
270 245 {
271 void set_istream (std::istream& is) const 246 SETOPT (CURLOPT_READDATA, static_cast<void*> (&is));
272 { 247 }
273 setopt (CURLOPT_READDATA, static_cast<void*> (&is)); 248
274 } 249 void ascii (void)
275 250 {
276 void ascii (void) const 251 SETOPT (CURLOPT_TRANSFERTEXT, 1);
277 { 252 ascii_mode = true;
278 setopt (CURLOPT_TRANSFERTEXT, 1); 253 }
279 rep->ascii = true; 254
280 } 255 void binary (void)
281 256 {
282 void binary (void) const 257 SETOPT (CURLOPT_TRANSFERTEXT, 0);
283 { 258 ascii_mode = false;
284 setopt (CURLOPT_TRANSFERTEXT, 0); 259 }
285 rep->ascii = false; 260
286 } 261 bool is_ascii (void) const { return ascii_mode; }
287 262
288 bool is_ascii (void) const 263 bool is_binary (void) const { return !ascii_mode; }
289 { 264
290 return rep->is_ascii (); 265 void cwd (const std::string& path) const
291 }
292
293 bool is_binary (void) const
294 {
295 return rep->is_binary ();
296 }
297
298 void cwd (const std::string& path) const
299 { 266 {
300 struct curl_slist *slist = 0; 267 struct curl_slist *slist = 0;
301 std::string cmd = "cwd " + path; 268 std::string cmd = "cwd " + path;
302 slist = curl_slist_append (slist, cmd.c_str ()); 269 slist = curl_slist_append (slist, cmd.c_str ());
303 setopt (CURLOPT_POSTQUOTE, slist); 270 SETOPT (CURLOPT_POSTQUOTE, slist);
304 if (! error_state) 271 perform ();
305 perform (); 272 SETOPT (CURLOPT_POSTQUOTE, 0);
306 setopt (CURLOPT_POSTQUOTE, 0);
307 curl_slist_free_all (slist); 273 curl_slist_free_all (slist);
308 } 274 }
309 275
310 void del (const std::string& file) const 276 void del (const std::string& file) const
311 { 277 {
312 struct curl_slist *slist = 0; 278 struct curl_slist *slist = 0;
313 std::string cmd = "dele " + file; 279 std::string cmd = "dele " + file;
314 slist = curl_slist_append (slist, cmd.c_str ()); 280 slist = curl_slist_append (slist, cmd.c_str ());
315 setopt (CURLOPT_POSTQUOTE, slist); 281 SETOPT (CURLOPT_POSTQUOTE, slist);
316 if (! error_state) 282 perform ();
317 perform (); 283 SETOPT (CURLOPT_POSTQUOTE, 0);
318 setopt (CURLOPT_POSTQUOTE, 0);
319 curl_slist_free_all (slist); 284 curl_slist_free_all (slist);
320 } 285 }
321 286
322 void rmdir (const std::string& path) const 287 void rmdir (const std::string& path) const
323 { 288 {
324 struct curl_slist *slist = 0; 289 struct curl_slist *slist = 0;
325 std::string cmd = "rmd " + path; 290 std::string cmd = "rmd " + path;
326 slist = curl_slist_append (slist, cmd.c_str ()); 291 slist = curl_slist_append (slist, cmd.c_str ());
327 setopt (CURLOPT_POSTQUOTE, slist); 292 SETOPT (CURLOPT_POSTQUOTE, slist);
328 if (! error_state) 293 perform ();
329 perform (); 294 SETOPT (CURLOPT_POSTQUOTE, 0);
330 setopt (CURLOPT_POSTQUOTE, 0);
331 curl_slist_free_all (slist); 295 curl_slist_free_all (slist);
332 } 296 }
333 297
334 bool mkdir (const std::string& path, bool curlerror = true) const 298 bool mkdir (const std::string& path, bool curlerror = true) const
335 { 299 {
336 bool retval = false; 300 bool retval = false;
337 struct curl_slist *slist = 0; 301 struct curl_slist *slist = 0;
338 std::string cmd = "mkd " + path; 302 std::string cmd = "mkd " + path;
339 slist = curl_slist_append (slist, cmd.c_str ()); 303 slist = curl_slist_append (slist, cmd.c_str ());
340 setopt (CURLOPT_POSTQUOTE, slist); 304 SETOPTR (CURLOPT_POSTQUOTE, slist);
341 if (! error_state) 305 retval = perform (curlerror);
342 retval = perform (curlerror); 306 SETOPTR (CURLOPT_POSTQUOTE, 0);
343 setopt (CURLOPT_POSTQUOTE, 0);
344 curl_slist_free_all (slist); 307 curl_slist_free_all (slist);
345 return retval; 308 return retval;
346 } 309 }
347 310
348 void rename (const std::string& oldname, const std::string& newname) const 311 void rename (const std::string& oldname, const std::string& newname) const
349 { 312 {
350 struct curl_slist *slist = 0; 313 struct curl_slist *slist = 0;
351 std::string cmd = "rnfr " + oldname; 314 std::string cmd = "rnfr " + oldname;
352 slist = curl_slist_append (slist, cmd.c_str ()); 315 slist = curl_slist_append (slist, cmd.c_str ());
353 cmd = "rnto " + newname; 316 cmd = "rnto " + newname;
354 slist = curl_slist_append (slist, cmd.c_str ()); 317 slist = curl_slist_append (slist, cmd.c_str ());
355 setopt (CURLOPT_POSTQUOTE, slist); 318 SETOPT (CURLOPT_POSTQUOTE, slist);
319 perform ();
320 SETOPT (CURLOPT_POSTQUOTE, 0);
321 curl_slist_free_all (slist);
322 }
323
324 void put (const std::string& file, std::istream& is) const
325 {
326 std::string url = "ftp://" + host + "/" + file;
327 SETOPT (CURLOPT_URL, url.c_str ());
328 SETOPT (CURLOPT_UPLOAD, 1);
329 SETOPT (CURLOPT_NOBODY, 0);
330 set_istream (is);
331 perform ();
332 set_istream (std::cin);
333 SETOPT (CURLOPT_NOBODY, 1);
334 SETOPT (CURLOPT_UPLOAD, 0);
335 url = "ftp://" + host;
336 SETOPT (CURLOPT_URL, url.c_str ());
337 }
338
339 void get (const std::string& file, std::ostream& os) const
340 {
341 std::string url = "ftp://" + host + "/" + file;
342 SETOPT (CURLOPT_URL, url.c_str ());
343 SETOPT (CURLOPT_NOBODY, 0);
344 set_ostream (os);
345 perform ();
346 set_ostream (octave_stdout);
347 SETOPT (CURLOPT_NOBODY, 1);
348 url = "ftp://" + host;
349 SETOPT (CURLOPT_URL, url.c_str ());
350 }
351
352 void mget_directory (const std::string& directory,
353 const std::string& target) const
354 {
355 std::string sep = file_ops::dir_sep_str ();
356 file_stat fs (directory);
357
358 if (!fs || !fs.is_dir ())
359 {
360 std::string msg;
361 int status = octave_mkdir (directory, 0777, msg);
362
363 if (status < 0)
364 {
365 error ("__ftp_mget__: can't create directory %s%s%s. %s",
366 target.c_str (), sep.c_str (), directory.c_str (),
367 msg.c_str ());
368
369 return;
370 }
371 }
372
373 cwd (directory);
374
356 if (! error_state) 375 if (! error_state)
357 perform (); 376 {
358 setopt (CURLOPT_POSTQUOTE, 0); 377 unwind_protect_safe frame;
359 curl_slist_free_all (slist); 378
360 } 379 frame.add_fcn (reset_path, this);
361 380
362 void put (const std::string& file, std::istream& is) const 381 string_vector sv = list ();
363 { 382
364 rep->url = "ftp://" + rep->host + "/" + file; 383 for (octave_idx_type i = 0; i < sv.length (); i++)
365 setopt (CURLOPT_URL, rep->url.c_str ()); 384 {
366 setopt (CURLOPT_UPLOAD, 1); 385 time_t ftime;
367 setopt (CURLOPT_NOBODY, 0); 386 bool fisdir;
368 set_istream (is); 387 double fsize;
388
389 get_fileinfo (sv(i), fsize, ftime, fisdir);
390
391 if (fisdir)
392 mget_directory (sv(i), target + directory + sep);
393 else
394 {
395 std::string realfile = target + directory + sep + sv(i);
396
397 std::ofstream ofile (realfile.c_str (),
398 std::ios::out | std::ios::binary);
399
400 if (! ofile.is_open ())
401 {
402 error ("__ftp_mget__: unable to open file");
403 break;
404 }
405
406 unwind_protect_safe frame2;
407
408 frame2.add_fcn (delete_file, realfile);
409
410 get (sv(i), ofile);
411
412 ofile.close ();
413
414 if (!error_state)
415 frame2.discard ();
416 else
417 frame2.run ();
418 }
419
420 if (error_state)
421 break;
422 }
423 }
424 }
425
426 string_vector mput_directory (const std::string& base,
427 const std::string& directory) const
428 {
429 string_vector retval;
430
431 std::string realdir
432 = (base.length () == 0
433 ? directory : base + file_ops::dir_sep_str () + directory);
434
435 if (! mkdir (directory, false))
436 warning ("__ftp_mput__: can not create the remote directory ""%s""",
437 realdir.c_str ());
438
439 cwd (directory);
440
369 if (! error_state) 441 if (! error_state)
370 perform (); 442 {
371 set_istream (std::cin); 443 unwind_protect_safe frame;
372 setopt (CURLOPT_NOBODY, 1); 444
373 setopt (CURLOPT_UPLOAD, 0); 445 frame.add_fcn (reset_path, this);
374 rep->url = "ftp://" + rep->host; 446
375 setopt (CURLOPT_URL, rep->url.c_str ()); 447 dir_entry dirlist (realdir);
376 } 448
377 449 if (dirlist)
378 void get (const std::string& file, std::ostream& os) const 450 {
379 { 451 string_vector files = dirlist.read ();
380 rep->url = "ftp://" + rep->host + "/" + file; 452
381 setopt (CURLOPT_URL, rep->url.c_str ()); 453 for (octave_idx_type i = 0; i < files.length (); i++)
382 setopt (CURLOPT_NOBODY, 0); 454 {
383 set_ostream (os); 455 std::string file = files (i);
384 if (! error_state) 456
385 perform (); 457 if (file == "." || file == "..")
386 set_ostream (octave_stdout); 458 continue;
387 setopt (CURLOPT_NOBODY, 1); 459
388 rep->url = "ftp://" + rep->host; 460 std::string realfile = realdir + file_ops::dir_sep_str () + file;
389 setopt (CURLOPT_URL, rep->url.c_str ()); 461 file_stat fs (realfile);
390 } 462
391 463 if (! fs.exists ())
392 void dir (void) const 464 {
393 { 465 error ("__ftp__mput: file ""%s"" does not exist",
394 rep->url = "ftp://" + rep->host + "/"; 466 realfile.c_str ());
395 setopt (CURLOPT_URL, rep->url.c_str ()); 467 break;
396 setopt (CURLOPT_NOBODY, 0); 468 }
397 if (! error_state) 469
398 perform (); 470 if (fs.is_dir ())
399 setopt (CURLOPT_NOBODY, 1); 471 {
400 rep->url = "ftp://" + rep->host; 472 retval.append (mput_directory (realdir, file));
401 setopt (CURLOPT_URL, rep->url.c_str ()); 473
402 } 474 if (error_state)
403 475 break;
404 string_vector list (void) const 476 }
405 { 477 else
478 {
479 // FIXME Does ascii mode need to be flagged here?
480 std::ifstream ifile (realfile.c_str (), std::ios::in |
481 std::ios::binary);
482
483 if (! ifile.is_open ())
484 {
485 error ("__ftp_mput__: unable to open file ""%s""",
486 realfile.c_str ());
487 break;
488 }
489
490 put (file, ifile);
491
492 ifile.close ();
493
494 if (error_state)
495 break;
496
497 retval.append (realfile);
498 }
499 }
500 }
501 else
502 error ("__ftp_mput__: can not read the directory ""%s""",
503 realdir.c_str ());
504 }
505
506 return retval;
507 }
508
509 void dir (void) const
510 {
511 std::string url = "ftp://" + host + "/";
512 SETOPT (CURLOPT_URL, url.c_str ());
513 SETOPT (CURLOPT_NOBODY, 0);
514 perform ();
515 SETOPT (CURLOPT_NOBODY, 1);
516 url = "ftp://" + host;
517 SETOPT (CURLOPT_URL, url.c_str ());
518 }
519
520 string_vector list (void) const
521 {
522 string_vector retval;
406 std::ostringstream buf; 523 std::ostringstream buf;
407 rep->url = "ftp://" + rep->host + "/"; 524 std::string url = "ftp://" + host + "/";
408 setopt (CURLOPT_WRITEDATA, static_cast<void*> (&buf)); 525 SETOPTR (CURLOPT_WRITEDATA, static_cast<void*> (&buf));
409 setopt (CURLOPT_URL, rep->url.c_str ()); 526 SETOPTR (CURLOPT_URL, url.c_str ());
410 setopt (CURLOPT_DIRLISTONLY, 1); 527 SETOPTR (CURLOPT_DIRLISTONLY, 1);
411 setopt (CURLOPT_NOBODY, 0); 528 SETOPTR (CURLOPT_NOBODY, 0);
412 if (! error_state) 529 perform ();
413 perform (); 530 SETOPTR (CURLOPT_NOBODY, 1);
414 setopt (CURLOPT_NOBODY, 1); 531 url = "ftp://" + host;
415 rep->url = "ftp://" + rep->host; 532 SETOPTR (CURLOPT_WRITEDATA, static_cast<void*> (&octave_stdout));
416 setopt (CURLOPT_WRITEDATA, static_cast<void*> (&octave_stdout)); 533 SETOPTR (CURLOPT_DIRLISTONLY, 0);
417 setopt (CURLOPT_DIRLISTONLY, 0); 534 SETOPTR (CURLOPT_URL, url.c_str ());
418 setopt (CURLOPT_URL, rep->url.c_str ());
419 535
420 // Count number of directory entries 536 // Count number of directory entries
421 std::string str = buf.str (); 537 std::string str = buf.str ();
422 octave_idx_type n = 0; 538 octave_idx_type n = 0;
423 size_t pos = 0; 539 size_t pos = 0;
427 if (pos == std::string::npos) 543 if (pos == std::string::npos)
428 break; 544 break;
429 pos++; 545 pos++;
430 n++; 546 n++;
431 } 547 }
432 string_vector retval (n); 548 retval.resize (n);
433 pos = 0; 549 pos = 0;
434 for (octave_idx_type i = 0; i < n; i++) 550 for (octave_idx_type i = 0; i < n; i++)
435 { 551 {
436 size_t newpos = str.find_first_of ('\n', pos); 552 size_t newpos = str.find_first_of ('\n', pos);
437 if (newpos == std::string::npos) 553 if (newpos == std::string::npos)
441 pos = newpos + 1; 557 pos = newpos + 1;
442 } 558 }
443 return retval; 559 return retval;
444 } 560 }
445 561
446 void get_fileinfo (const std::string& filename, double& filesize, 562 void get_fileinfo (const std::string& filename, double& filesize,
447 time_t& filetime, bool& fileisdir) const 563 time_t& filetime, bool& fileisdir) const
448 { 564 {
449 std::string path = pwd (); 565 std::string path = pwd ();
450 566
451 rep->url = "ftp://" + rep->host + "/" + path + "/" + filename; 567 std::string url = "ftp://" + host + "/" + path + "/" + filename;
452 setopt (CURLOPT_URL, rep->url.c_str ()); 568 SETOPT (CURLOPT_URL, url.c_str ());
453 setopt (CURLOPT_FILETIME, 1); 569 SETOPT (CURLOPT_FILETIME, 1);
454 setopt (CURLOPT_HEADERFUNCTION, throw_away); 570 SETOPT (CURLOPT_HEADERFUNCTION, throw_away);
455 setopt (CURLOPT_WRITEFUNCTION, throw_away); 571 SETOPT (CURLOPT_WRITEFUNCTION, throw_away);
456 572
457 // FIXME 573 // FIXME
458 // The MDTM command fails for a directory on the servers I tested 574 // The MDTM command fails for a directory on the servers I tested
459 // so this is a means of testing for directories. It also means 575 // so this is a means of testing for directories. It also means
460 // I can't get the date of directories! 576 // I can't get the date of directories!
461 if (! error_state) 577
462 { 578 if (! perform (false))
463 if (! perform (false)) 579 {
464 { 580 fileisdir = true;
465 fileisdir = true; 581 filetime = -1;
466 filetime = -1; 582 filesize = 0;
467 filesize = 0; 583 }
468 } 584 else
469 else 585 {
470 { 586 fileisdir = false;
471 fileisdir = false; 587 time_t ft;
472 time_t ft; 588 curl_easy_getinfo (curl, CURLINFO_FILETIME, &ft);
473 curl_easy_getinfo (rep->object (), CURLINFO_FILETIME, &ft); 589 filetime = ft;
474 filetime = ft; 590 double fs;
475 double fs; 591 curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs);
476 curl_easy_getinfo (rep->object (), 592 filesize = fs;
477 CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs); 593 }
478 filesize = fs; 594
479 } 595 SETOPT (CURLOPT_WRITEFUNCTION, write_data);
480 } 596 SETOPT (CURLOPT_HEADERFUNCTION, 0);
481 597 SETOPT (CURLOPT_FILETIME, 0);
482 setopt (CURLOPT_WRITEFUNCTION, write_data); 598 url = "ftp://" + host;
483 setopt (CURLOPT_HEADERFUNCTION, 0); 599 SETOPT (CURLOPT_URL, url.c_str ());
484 setopt (CURLOPT_FILETIME, 0);
485 rep->url = "ftp://" + rep->host;
486 setopt (CURLOPT_URL, rep->url.c_str ());
487 600
488 // The MDTM command seems to reset the path to the root with the 601 // The MDTM command seems to reset the path to the root with the
489 // servers I tested with, so cd again into the correct path. Make 602 // servers I tested with, so cd again into the correct path. Make
490 // the path absolute so that this will work even with servers that 603 // the path absolute so that this will work even with servers that
491 // don't end up in the root after an MDTM command. 604 // don't end up in the root after an MDTM command.
492 cwd ("/" + path); 605 cwd ("/" + path);
493 } 606 }
494 607
495 std::string pwd (void) const 608 std::string pwd (void) const
496 { 609 {
497 struct curl_slist *slist = 0; 610 struct curl_slist *slist = 0;
498 std::string retval; 611 std::string retval;
499 std::ostringstream buf; 612 std::ostringstream buf;
500 613
501 slist = curl_slist_append (slist, "pwd"); 614 slist = curl_slist_append (slist, "pwd");
502 setopt (CURLOPT_POSTQUOTE, slist); 615 SETOPTR (CURLOPT_POSTQUOTE, slist);
503 setopt (CURLOPT_HEADERFUNCTION, write_data); 616 SETOPTR (CURLOPT_HEADERFUNCTION, write_data);
504 setopt (CURLOPT_WRITEHEADER, static_cast<void *>(&buf)); 617 SETOPTR (CURLOPT_WRITEHEADER, static_cast<void *>(&buf));
505 618
506 if (! error_state) 619 perform ();
507 { 620 retval = buf.str ();
508 perform (); 621
509 retval = buf.str (); 622 // Can I assume that the path is alway in "" on the last line
510 623 size_t pos2 = retval.rfind ('"');
511 // Can I assume that the path is alway in "" on the last line 624 size_t pos1 = retval.rfind ('"', pos2 - 1);
512 size_t pos2 = retval.rfind ('"'); 625 retval = retval.substr (pos1 + 1, pos2 - pos1 - 1);
513 size_t pos1 = retval.rfind ('"', pos2 - 1); 626
514 retval = retval.substr (pos1 + 1, pos2 - pos1 - 1); 627 SETOPTR (CURLOPT_HEADERFUNCTION, 0);
515 } 628 SETOPTR (CURLOPT_WRITEHEADER, 0);
516 setopt (CURLOPT_HEADERFUNCTION, 0); 629 SETOPTR (CURLOPT_POSTQUOTE, 0);
517 setopt (CURLOPT_WRITEHEADER, 0);
518 setopt (CURLOPT_POSTQUOTE, 0);
519 curl_slist_free_all (slist); 630 curl_slist_free_all (slist);
520 631
521 return retval; 632 return retval;
522 } 633 }
523 634
524 bool perform (bool curlerror = true) const 635 octave_refcount<size_t> count;
525 { 636
526 return rep->perform (curlerror); 637 private:
527 } 638
528 639 CURL *curl;
529 private: 640 std::string host;
530 curl_object_rep *rep; 641 std::string userpwd;
531 642 bool valid;
532 std::string form_query_string (const Cell& param) 643 bool ascii_mode;
533 { 644 mutable CURLcode errnum;
534 std::ostringstream query; 645
535 646 // No copying!
536 for (int i = 0; i < param.numel (); i += 2) 647
537 { 648 curl_object_rep (const curl_object_rep& ov);
538 std::string name = param(i).string_value (); 649
539 std::string text = param(i+1).string_value (); 650 curl_object_rep& operator = (const curl_object_rep&);
540 651
541 // Encode strings. 652 void init (const std::string& user, const std::string& passwd,
542 char *enc_name = curl_easy_escape (rep->object (), name.c_str (), 653 std::istream& is, std::ostream& os)
543 name.length ());
544 char *enc_text = curl_easy_escape (rep->object (), text.c_str (),
545 text.length ());
546
547 query << enc_name << "=" << enc_text;
548
549 curl_free (enc_name);
550 curl_free (enc_text);
551
552 if (i < param.numel ()-1)
553 query << "&";
554 }
555
556 query.flush ();
557
558 return query.str ();
559 }
560
561 void init (const std::string& user, const std::string& passwd,
562 std::istream& is, std::ostream& os)
563 { 654 {
564 // No data transfer by default 655 // No data transfer by default
565 setopt (CURLOPT_NOBODY, 1); 656 SETOPT (CURLOPT_NOBODY, 1);
566 657
567 // Set the username and password 658 // Set the username and password
568 rep->userpwd = user; 659 userpwd = user;
569 if (! passwd.empty ()) 660 if (! passwd.empty ())
570 rep->userpwd += ":" + passwd; 661 userpwd += ":" + passwd;
571 if (! rep->userpwd.empty ()) 662 if (! userpwd.empty ())
572 setopt (CURLOPT_USERPWD, rep->userpwd.c_str ()); 663 SETOPT (CURLOPT_USERPWD, userpwd.c_str ());
573 664
574 // Define our callback to get called when there's data to be written. 665 // Define our callback to get called when there's data to be written.
575 setopt (CURLOPT_WRITEFUNCTION, write_data); 666 SETOPT (CURLOPT_WRITEFUNCTION, write_data);
576 667
577 // Set a pointer to our struct to pass to the callback. 668 // Set a pointer to our struct to pass to the callback.
578 setopt (CURLOPT_WRITEDATA, static_cast<void*> (&os)); 669 SETOPT (CURLOPT_WRITEDATA, static_cast<void*> (&os));
579 670
580 // Define our callback to get called when there's data to be read 671 // Define our callback to get called when there's data to be read
581 setopt (CURLOPT_READFUNCTION, read_data); 672 SETOPT (CURLOPT_READFUNCTION, read_data);
582 673
583 // Set a pointer to our struct to pass to the callback. 674 // Set a pointer to our struct to pass to the callback.
584 setopt (CURLOPT_READDATA, static_cast<void*> (&is)); 675 SETOPT (CURLOPT_READDATA, static_cast<void*> (&is));
585 676
586 // Follow redirects. 677 // Follow redirects.
587 setopt (CURLOPT_FOLLOWLOCATION, true); 678 SETOPT (CURLOPT_FOLLOWLOCATION, true);
588 679
589 // Don't use EPSV since connecting to sites that don't support it 680 // Don't use EPSV since connecting to sites that don't support it
590 // will hang for some time (3 minutes?) before moving on to try PASV 681 // will hang for some time (3 minutes?) before moving on to try PASV
591 // instead. 682 // instead.
592 setopt (CURLOPT_FTP_USE_EPSV, false); 683 SETOPT (CURLOPT_FTP_USE_EPSV, false);
593 684
594 setopt (CURLOPT_NOPROGRESS, true); 685 SETOPT (CURLOPT_NOPROGRESS, true);
595 setopt (CURLOPT_FAILONERROR, true); 686 SETOPT (CURLOPT_FAILONERROR, true);
596 687
597 setopt (CURLOPT_POSTQUOTE, 0); 688 SETOPT (CURLOPT_POSTQUOTE, 0);
598 setopt (CURLOPT_QUOTE, 0); 689 SETOPT (CURLOPT_QUOTE, 0);
599 } 690 }
600 691
601 #undef setopt 692 std::string form_query_string (const Cell& param)
693 {
694 std::ostringstream query;
695
696 for (int i = 0; i < param.numel (); i += 2)
697 {
698 std::string name = param(i).string_value ();
699 std::string text = param(i+1).string_value ();
700
701 // Encode strings.
702 char *enc_name = curl_easy_escape (curl, name.c_str (),
703 name.length ());
704 char *enc_text = curl_easy_escape (curl, text.c_str (),
705 text.length ());
706
707 query << enc_name << "=" << enc_text;
708
709 curl_free (enc_name);
710 curl_free (enc_text);
711
712 if (i < param.numel ()-1)
713 query << "&";
714 }
715
716 query.flush ();
717
718 return query.str ();
719 }
720 #undef SETOPT
721 #undef SETOPTR
722 };
723
724 public:
725
726 curl_object (void) : rep (new curl_object_rep ()) { }
727
728 curl_object (const std::string& host, const std::string& user,
729 const std::string& passwd)
730 : rep (new curl_object_rep (host, user, passwd)) { }
731
732 curl_object (const std::string& url, const std::string& method,
733 const Cell& param, std::ostream& os, bool& retval)
734 : rep (new curl_object_rep (url, method, param, os, retval)) { }
735
736 curl_object (const curl_object& h) : rep (h.rep)
737 {
738 rep->count++;
739 }
740
741 ~curl_object (void)
742 {
743 if (--rep->count == 0)
744 delete rep;
745 }
746
747 curl_object& operator = (const curl_object& h)
748 {
749 if (this != &h)
750 {
751 if (--rep->count == 0)
752 delete rep;
753
754 rep = h.rep;
755 rep->count++;
756 }
757
758 return *this;
759 }
760
761 bool is_valid (void) const { return rep->is_valid (); }
762
763 std::string lasterror (void) const { return rep->lasterror (); }
764
765 void set_ostream (std::ostream& os) const { rep->set_ostream (os); }
766
767 void set_istream (std::istream& is) const { rep->set_istream (is); }
768
769 void ascii (void) const { rep->ascii (); }
770
771 void binary (void) const { rep->binary (); }
772
773 bool is_ascii (void) const { return rep->is_ascii (); }
774
775 bool is_binary (void) const { return rep->is_binary (); }
776
777 void cwd (const std::string& path) const { rep->cwd (path); }
778
779 void del (const std::string& file) const { rep->del (file); }
780
781 void rmdir (const std::string& path) const { rep->rmdir (path); }
782
783 bool mkdir (const std::string& path, bool curlerror = true) const
784 {
785 return rep->mkdir (path, curlerror);
786 }
787
788 void rename (const std::string& oldname, const std::string& newname) const
789 {
790 rep->rename (oldname, newname);
791 }
792
793 void put (const std::string& file, std::istream& is) const
794 {
795 rep->put (file, is);
796 }
797
798 void get (const std::string& file, std::ostream& os) const
799 {
800 rep->get (file, os);
801 }
802
803 void mget_directory (const std::string& directory,
804 const std::string& target) const
805 {
806 return rep->mget_directory (directory, target);
807 }
808
809 string_vector mput_directory (const std::string& base,
810 const std::string& directory) const
811 {
812 return mput_directory (base, directory);
813 }
814
815 void dir (void) const { rep->dir (); }
816
817 string_vector list (void) const { return rep->list (); }
818
819 void get_fileinfo (const std::string& filename, double& filesize,
820 time_t& filetime, bool& fileisdir) const
821 {
822 rep->get_fileinfo (filename, filesize, filetime, fileisdir);
823 }
824
825 std::string pwd (void) const { return rep->pwd (); }
826
827 private:
828
829 curl_object_rep *rep;
602 }; 830 };
603 831
604 typedef octave_handle curl_handle; 832 typedef octave_handle curl_handle;
605 833
606 class OCTINTERP_API ch_manager 834 class OCTINTERP_API ch_manager
824 error ("ch_manager::free: invalid object %g", h.value ()); 1052 error ("ch_manager::free: invalid object %g", h.value ());
825 } 1053 }
826 } 1054 }
827 1055
828 ch_manager *ch_manager::instance = 0; 1056 ch_manager *ch_manager::instance = 0;
829
830 static void
831 reset_path (const curl_object& curl)
832 {
833 curl.cwd ("..");
834 }
835
836 static void
837 delete_file (std::string file)
838 {
839 octave_unlink (file);
840 }
841 1057
842 #endif 1058 #endif
843 1059
844 DEFUN_DLD (urlwrite, args, nargout, 1060 DEFUN_DLD (urlwrite, args, nargout,
845 "-*- texinfo -*-\n\ 1061 "-*- texinfo -*-\n\
1561 #endif 1777 #endif
1562 1778
1563 return octave_value (); 1779 return octave_value ();
1564 } 1780 }
1565 1781
1566 #ifdef HAVE_CURL
1567 static string_vector
1568 mput_directory (const curl_object& curl, const std::string& base,
1569 const std::string& dir)
1570 {
1571 string_vector retval;
1572
1573 if (! curl.mkdir (dir, false))
1574 warning ("__ftp_mput__: can not create the remote directory ""%s""",
1575 (base.length () == 0 ? dir : base +
1576 file_ops::dir_sep_str () + dir).c_str ());
1577
1578 curl.cwd (dir);
1579
1580 if (! error_state)
1581 {
1582 unwind_protect_safe frame;
1583
1584 frame.add_fcn (reset_path, curl);
1585
1586 std::string realdir = base.length () == 0 ? dir : base +
1587 file_ops::dir_sep_str () + dir;
1588
1589 dir_entry dirlist (realdir);
1590
1591 if (dirlist)
1592 {
1593 string_vector files = dirlist.read ();
1594
1595 for (octave_idx_type i = 0; i < files.length (); i++)
1596 {
1597 std::string file = files (i);
1598
1599 if (file == "." || file == "..")
1600 continue;
1601
1602 std::string realfile = realdir + file_ops::dir_sep_str () + file;
1603 file_stat fs (realfile);
1604
1605 if (! fs.exists ())
1606 {
1607 error ("__ftp__mput: file ""%s"" does not exist",
1608 realfile.c_str ());
1609 break;
1610 }
1611
1612 if (fs.is_dir ())
1613 {
1614 retval.append (mput_directory (curl, realdir, file));
1615
1616 if (error_state)
1617 break;
1618 }
1619 else
1620 {
1621 // FIXME Does ascii mode need to be flagged here?
1622 std::ifstream ifile (realfile.c_str (), std::ios::in |
1623 std::ios::binary);
1624
1625 if (! ifile.is_open ())
1626 {
1627 error ("__ftp_mput__: unable to open file ""%s""",
1628 realfile.c_str ());
1629 break;
1630 }
1631
1632 curl.put (file, ifile);
1633
1634 ifile.close ();
1635
1636 if (error_state)
1637 break;
1638
1639 retval.append (realfile);
1640 }
1641 }
1642 }
1643 else
1644 error ("__ftp_mput__: can not read the directory ""%s""",
1645 realdir.c_str ());
1646 }
1647
1648 return retval;
1649 }
1650 #endif
1651
1652 DEFUN_DLD (__ftp_mput__, args, nargout, 1782 DEFUN_DLD (__ftp_mput__, args, nargout,
1653 "-*- texinfo -*-\n\ 1783 "-*- texinfo -*-\n\
1654 @deftypefn {Loadable Function} {} __ftp_mput__ (@var{handle}, @var{files})\n\ 1784 @deftypefn {Loadable Function} {} __ftp_mput__ (@var{handle}, @var{files})\n\
1655 Undocumented internal function\n\ 1785 Undocumented internal function\n\
1656 @end deftypefn") 1786 @end deftypefn")
1687 break; 1817 break;
1688 } 1818 }
1689 1819
1690 if (fs.is_dir ()) 1820 if (fs.is_dir ())
1691 { 1821 {
1692 retval.append (mput_directory (curl, "", file)); 1822 retval.append (curl.mput_directory ("", file));
1693 if (error_state) 1823 if (error_state)
1694 break; 1824 break;
1695 } 1825 }
1696 else 1826 else
1697 { 1827 {
1727 #endif 1857 #endif
1728 1858
1729 return (nargout > 0 ? octave_value (retval) : octave_value ()); 1859 return (nargout > 0 ? octave_value (retval) : octave_value ());
1730 } 1860 }
1731 1861
1732 #ifdef HAVE_CURL
1733 static void
1734 getallfiles (const curl_object& curl, const std::string& dir,
1735 const std::string& target)
1736 {
1737 std::string sep = file_ops::dir_sep_str ();
1738 file_stat fs (dir);
1739
1740 if (!fs || !fs.is_dir ())
1741 {
1742 std::string msg;
1743 int status = octave_mkdir (dir, 0777, msg);
1744
1745 if (status < 0)
1746 error ("__ftp_mget__: can't create directory %s%s%s. %s",
1747 target.c_str (), sep.c_str (), dir.c_str (), msg.c_str ());
1748 }
1749
1750 if (! error_state)
1751 {
1752 curl.cwd (dir);
1753
1754 if (! error_state)
1755 {
1756 unwind_protect_safe frame;
1757
1758 frame.add_fcn (reset_path, curl);
1759
1760 string_vector sv = curl.list ();
1761
1762 for (octave_idx_type i = 0; i < sv.length (); i++)
1763 {
1764 time_t ftime;
1765 bool fisdir;
1766 double fsize;
1767
1768 curl.get_fileinfo (sv(i), fsize, ftime, fisdir);
1769
1770 if (fisdir)
1771 getallfiles (curl, sv(i), target + dir + sep);
1772 else
1773 {
1774 std::string realfile = target + dir + sep + sv(i);
1775 std::ofstream ofile (realfile.c_str (),
1776 std::ios::out |
1777 std::ios::binary);
1778
1779 if (! ofile.is_open ())
1780 {
1781 error ("__ftp_mget__: unable to open file");
1782 break;
1783 }
1784
1785 unwind_protect_safe frame2;
1786
1787 frame2.add_fcn (delete_file, realfile);
1788
1789 curl.get (sv(i), ofile);
1790
1791 ofile.close ();
1792
1793 if (!error_state)
1794 frame2.discard ();
1795 else
1796 frame2.run ();
1797 }
1798
1799 if (error_state)
1800 break;
1801 }
1802 }
1803 }
1804 }
1805 #endif
1806
1807 DEFUN_DLD (__ftp_mget__, args, , 1862 DEFUN_DLD (__ftp_mget__, args, ,
1808 "-*- texinfo -*-\n\ 1863 "-*- texinfo -*-\n\
1809 @deftypefn {Loadable Function} {} __ftp_mget__ (@var{handle}, @var{files})\n\ 1864 @deftypefn {Loadable Function} {} __ftp_mget__ (@var{handle}, @var{files})\n\
1810 Undocumented internal function\n\ 1865 Undocumented internal function\n\
1811 @end deftypefn") 1866 @end deftypefn")
1844 double fsize; 1899 double fsize;
1845 1900
1846 curl.get_fileinfo (sv(i), fsize, ftime, fisdir); 1901 curl.get_fileinfo (sv(i), fsize, ftime, fisdir);
1847 1902
1848 if (fisdir) 1903 if (fisdir)
1849 getallfiles (curl, sv(i), target); 1904 curl.mget_directory (sv(i), target);
1850 else 1905 else
1851 { 1906 {
1852 std::ofstream ofile ((target + sv(i)).c_str (), 1907 std::ofstream ofile ((target + sv(i)).c_str (),
1853 std::ios::out | 1908 std::ios::out |
1854 std::ios::binary); 1909 std::ios::binary);