GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
url-transfer.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2006-2023 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 <fstream>
31 #include <iomanip>
32 #include <iostream>
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 "unwind-prot.h"
40 #include "url-transfer.h"
41 #include "version.h"
42 
43 #if defined (HAVE_CURL)
44 # include <curl/curl.h>
45 # include <curl/curlver.h>
46 # include <curl/easy.h>
47 #endif
48 
50 
52  : m_host_or_url (), m_valid (false), m_ftp (false),
53  m_ascii_mode (false), m_ok (true), m_errmsg (),
54  m_curr_istream (&std::cin), m_curr_ostream (&std::cout)
55 { }
56 
57 base_url_transfer::base_url_transfer (const std::string& host,
58  const std::string& /* user_arg */,
59  const std::string& /* passwd */,
60  std::ostream& os)
61  : m_host_or_url (host), m_valid (false), m_ftp (true),
62  m_ascii_mode (false), m_ok (true), m_errmsg (),
63  m_curr_istream (&std::cin), m_curr_ostream (&os)
64 { }
65 
66 base_url_transfer::base_url_transfer (const std::string& url,
67  std::ostream& os)
68  : m_host_or_url (url), m_valid (false), m_ftp (false),
69  m_ascii_mode (false), m_ok (true), m_errmsg (),
70  m_curr_istream (&std::cin), m_curr_ostream (&os)
71 { }
72 
73 void
74 base_url_transfer::mget_directory (const std::string& directory,
75  const std::string& target)
76 {
77  std::string sep = sys::file_ops::dir_sep_str ();
78  sys::file_stat fs (directory);
79 
80  if (! fs || ! fs.is_dir ())
81  {
82  std::string msg;
83  int status = sys::mkdir (directory, 0777, msg);
84 
85  if (status < 0)
86  {
87  m_ok = false;
88  m_errmsg = "__ftp_mget__: can not create directory '"
89  + target + sep + directory + "': " + msg;
90  return;
91  }
92  }
93 
94  cwd (directory);
95 
96  if (good ())
97  {
98  unwind_action_safe reset_path (&base_url_transfer::cwd, this, "..");
99 
100  string_vector sv = list ();
101 
102  for (octave_idx_type i = 0; i < sv.numel (); i++)
103  {
104  OCTAVE_TIME_T ftime;
105  bool fisdir;
106  double fsize;
107 
108  get_fileinfo (sv(i), fsize, ftime, fisdir);
109 
110  if (fisdir)
111  mget_directory (sv(i), target + directory + sep);
112  else
113  {
114  std::string realfile = target + directory + sep + sv(i);
115 
116  std::ofstream ofile =
117  sys::ofstream (realfile.c_str (),
118  std::ios::out | std::ios::binary);
119 
120  if (! ofile.is_open ())
121  {
122  m_ok = false;
123  m_errmsg = "__ftp_mget__: unable to open file";
124  break;
125  }
126  int(*unlink_fptr)(const std::string&) = sys::unlink;
127  unwind_action_safe delete_file (unlink_fptr, realfile);
128 
129  get (sv(i), ofile);
130 
131  ofile.close ();
132 
133  if (good ())
134  delete_file.discard ();
135  }
136 
137  if (! good ())
138  break;
139  }
140  }
141 }
142 
144 base_url_transfer::mput_directory (const std::string& base,
145  const std::string& directory)
146 {
147  string_vector file_list;
148 
149  std::string realdir
150  = (base.empty ()
151  ? directory : base + sys::file_ops::dir_sep_str () + directory);
152 
153  mkdir (directory);
154 
155  if (! good ())
156  return file_list;
157 
158  cwd (directory);
159 
160  if (good ())
161  {
162  unwind_action_safe reset_path (&base_url_transfer::cwd, this, "..");
163 
164  string_vector files;
165  std::string msg;
166 
167  if (sys::get_dirlist (realdir, files, msg))
168  for (octave_idx_type i = 0; i < files.numel (); i++)
169  {
170  std::string file = files (i);
171 
172  if (file == "." || file == "..")
173  continue;
174 
175  std::string realfile
176  = realdir + sys::file_ops::dir_sep_str () + file;
177 
178  sys::file_stat fs (realfile);
179 
180  if (! fs.exists ())
181  {
182  m_ok = false;
183  m_errmsg = "__ftp__mput: file '" + realfile
184  + "' does not exist";
185  break;
186  }
187 
188  if (fs.is_dir ())
189  {
190  file_list.append (mput_directory (realdir, file));
191 
192  if (! good ())
193  break;
194  }
195  else
196  {
197  // FIXME: Does ascii mode need to be flagged here?
198  std::ifstream ifile =
199  sys::ifstream (realfile.c_str (),
200  std::ios::in | std::ios::binary);
201 
202  if (! ifile.is_open ())
203  {
204  m_ok = false;
205  m_errmsg = "__ftp_mput__: unable to open file '"
206  + realfile + "'";
207  break;
208  }
209 
210  put (file, ifile);
211 
212  ifile.close ();
213 
214  if (! good ())
215  break;
216 
217  file_list.append (realfile);
218  }
219  }
220  else
221  {
222  m_ok = false;
223  m_errmsg = "__ftp_mput__: can not read the directory '"
224  + realdir + "'";
225  }
226  }
227 
228  return file_list;
229 }
230 
231 #if defined (HAVE_CURL)
232 
233 static int
234 write_data (void *buffer, std::size_t size, std::size_t nmemb, void *streamp)
235 {
236  std::ostream& stream = *(static_cast<std::ostream *> (streamp));
237  stream.write (static_cast<const char *> (buffer), size*nmemb);
238  return (stream.fail () ? 0 : size * nmemb);
239 }
240 
241 static int
242 read_data (void *buffer, std::size_t size, std::size_t nmemb, void *streamp)
243 {
244  std::istream& stream = *(static_cast<std::istream *> (streamp));
245  stream.read (static_cast<char *> (buffer), size*nmemb);
246  if (stream.eof ())
247  return stream.gcount ();
248  else
249  return (stream.fail () ? 0 : size * nmemb);
250 }
251 
252 static std::size_t
253 throw_away (void *, std::size_t size, std::size_t nmemb, void *)
254 {
255  return static_cast<std::size_t> (size * nmemb);
256 }
257 
258 // I'd love to rewrite this as a private method of the url_transfer
259 // class, but you can't pass the va_list from the wrapper SETOPT to
260 // the curl_easy_setopt function.
261 #define SETOPT(option, parameter) \
262  do \
263  { \
264  CURLcode res = curl_easy_setopt (m_curl, option, parameter); \
265  if (res != CURLE_OK) \
266  { \
267  m_ok = false; \
268  m_errmsg = curl_easy_strerror (res); \
269  return; \
270  } \
271  } \
272  while (0)
273 
274 // Same as above but with a return value.
275 #define SETOPTR(option, parameter) \
276  do \
277  { \
278  CURLcode res = curl_easy_setopt (m_curl, option, parameter); \
279  if (res != CURLE_OK) \
280  { \
281  m_ok = false; \
282  m_errmsg = curl_easy_strerror (res); \
283  return retval; \
284  } \
285  } \
286  while (0)
287 
289 {
290 public:
291 
293  : base_url_transfer (), m_curl (curl_easy_init ()), m_errnum (), m_url (),
294  m_userpwd ()
295  {
296  if (m_curl)
297  m_valid = true;
298  else
299  m_errmsg = "can not create curl object";
300  }
301 
302  curl_transfer (const std::string& host, const std::string& user_arg,
303  const std::string& passwd, std::ostream& os)
304  : base_url_transfer (host, user_arg, passwd, os),
305  m_curl (curl_easy_init ()), m_errnum (), m_url (), m_userpwd ()
306  {
307  if (m_curl)
308  m_valid = true;
309  else
310  {
311  m_errmsg = "can not create curl object";
312  return;
313  }
314 
315  init (user_arg, passwd, std::cin, os);
316 
317  m_url = "ftp://" + host;
318  SETOPT (CURLOPT_URL, m_url.c_str ());
319 
320  // Set up the link, with no transfer.
321  perform ();
322  }
323 
324  curl_transfer (const std::string& url_str, std::ostream& os)
325  : base_url_transfer (url_str, os), m_curl (curl_easy_init ()),
326  m_errnum (), m_url (), m_userpwd ()
327  {
328  if (m_curl)
329  m_valid = true;
330  else
331  {
332  m_errmsg = "can not create curl object";
333  return;
334  }
335 
336  init ("", "", std::cin, os);
337 
338  std::string cainfo = sys::env::getenv ("CURLOPT_CAINFO");
339  if (! cainfo.empty ())
340  SETOPT (CURLOPT_CAINFO, cainfo.c_str ());
341 
342  std::string capath = sys::env::getenv ("CURLOPT_CAPATH");
343  if (! capath.empty ())
344  SETOPT (CURLOPT_CAPATH, capath.c_str ());
345 
346  SETOPT (CURLOPT_NOBODY, 0);
347 
348  // Restore the default HTTP request method to GET after setting
349  // NOBODY to true (in the init method) and back to false (above).
350  // This is needed for backward compatibility with versions of
351  // libcurl < 7.18.2.
352  SETOPT (CURLOPT_HTTPGET, 1);
353  }
354 
355  // No copying!
356 
357  curl_transfer (const curl_transfer&) = delete;
358 
360 
362  {
363  if (m_curl)
364  curl_easy_cleanup (m_curl);
365  }
366 
367  void perform (void)
368  {
369  m_errnum = curl_easy_perform (m_curl);
370 
371  if (m_errnum != CURLE_OK)
372  {
373  m_ok = false;
374  m_errmsg = curl_easy_strerror (m_errnum);
375  }
376  }
377 
378  std::string lasterror (void) const
379  {
380  return std::string (curl_easy_strerror (m_errnum));
381  }
382 
383  std::ostream& set_ostream (std::ostream& os)
384  {
385  std::ostream& retval = *m_curr_ostream;
386  m_curr_ostream = &os;
387  SETOPTR (CURLOPT_WRITEDATA, static_cast<void *> (m_curr_ostream));
388  return retval;
389  }
390 
391  std::istream& set_istream (std::istream& is)
392  {
393  std::istream& retval = *m_curr_istream;
394  m_curr_istream = &is;
395  SETOPTR (CURLOPT_READDATA, static_cast<void *> (m_curr_istream));
396  return retval;
397  }
398 
399  void ascii (void)
400  {
401  m_ascii_mode = true;
402  SETOPT (CURLOPT_TRANSFERTEXT, 1);
403  }
404 
405  void binary (void)
406  {
407  m_ascii_mode = false;
408  SETOPT (CURLOPT_TRANSFERTEXT, 0);
409  }
410 
411  void cwd (const std::string& path)
412  {
413  ftp_file_or_dir_action (path, "cwd");
414  }
415 
416  void del (const std::string& file)
417  {
418  ftp_file_or_dir_action (file, "dele");
419  }
420 
421  void rmdir (const std::string& path)
422  {
423  ftp_file_or_dir_action (path, "rmd");
424  }
425 
426  void mkdir (const std::string& path)
427  {
428  ftp_file_or_dir_action (path, "mkd");
429  }
430 
431  void rename (const std::string& oldname, const std::string& newname)
432  {
433  struct curl_slist *slist = nullptr;
434 
435  unwind_action cleanup_slist ([=] () { curl_slist_free_all (slist); });
436 
437  std::string cmd = "rnfr " + oldname;
438  slist = curl_slist_append (slist, cmd.c_str ());
439  cmd = "rnto " + newname;
440  slist = curl_slist_append (slist, cmd.c_str ());
441  SETOPT (CURLOPT_POSTQUOTE, slist);
442 
443  perform ();
444  if (! good ())
445  return;
446 
447  SETOPT (CURLOPT_POSTQUOTE, 0);
448  }
449 
450  void put (const std::string& file, std::istream& is)
451  {
452  m_url = "ftp://" + m_host_or_url + '/' + file;
453  SETOPT (CURLOPT_URL, m_url.c_str ());
454  SETOPT (CURLOPT_UPLOAD, 1);
455  SETOPT (CURLOPT_NOBODY, 0);
456  std::istream& old_is = set_istream (is);
457 
458  perform ();
459  if (! good ())
460  return;
461 
462  set_istream (old_is);
463  SETOPT (CURLOPT_NOBODY, 1);
464  SETOPT (CURLOPT_UPLOAD, 0);
465  m_url = "ftp://" + m_host_or_url;
466  SETOPT (CURLOPT_URL, m_url.c_str ());
467  }
468 
469  void get (const std::string& file, std::ostream& os)
470  {
471  m_url = "ftp://" + m_host_or_url + '/' + file;
472  SETOPT (CURLOPT_URL, m_url.c_str ());
473  SETOPT (CURLOPT_NOBODY, 0);
474  std::ostream& old_os = set_ostream (os);
475 
476  perform ();
477  if (! good ())
478  return;
479 
480  set_ostream (old_os);
481  SETOPT (CURLOPT_NOBODY, 1);
482  m_url = "ftp://" + m_host_or_url;
483  SETOPT (CURLOPT_URL, m_url.c_str ());
484  }
485 
486  void dir (void)
487  {
488  m_url = "ftp://" + m_host_or_url + '/';
489  SETOPT (CURLOPT_URL, m_url.c_str ());
490  SETOPT (CURLOPT_NOBODY, 0);
491 
492  perform ();
493  if (! good ())
494  return;
495 
496  SETOPT (CURLOPT_NOBODY, 1);
497  m_url = "ftp://" + m_host_or_url;
498  SETOPT (CURLOPT_URL, m_url.c_str ());
499  }
500 
502  {
503  string_vector retval;
504 
505  std::ostringstream buf;
506  m_url = "ftp://" + m_host_or_url + '/';
507  SETOPTR (CURLOPT_WRITEDATA, static_cast<void *> (&buf));
508  SETOPTR (CURLOPT_URL, m_url.c_str ());
509  SETOPTR (CURLOPT_DIRLISTONLY, 1);
510  SETOPTR (CURLOPT_NOBODY, 0);
511 
512  perform ();
513  if (! good ())
514  return retval;
515 
516  SETOPTR (CURLOPT_NOBODY, 1);
517  m_url = "ftp://" + m_host_or_url;
518  SETOPTR (CURLOPT_WRITEDATA, static_cast<void *> (m_curr_ostream));
519  SETOPTR (CURLOPT_DIRLISTONLY, 0);
520  SETOPTR (CURLOPT_URL, m_url.c_str ());
521 
522  // Count number of directory entries
523  std::string str = buf.str ();
524  octave_idx_type n = 0;
525  std::size_t pos = 0;
526  while (true)
527  {
528  pos = str.find_first_of ('\n', pos);
529  if (pos == std::string::npos)
530  break;
531  pos++;
532  n++;
533  }
534  retval.resize (n);
535  pos = 0;
536  for (octave_idx_type i = 0; i < n; i++)
537  {
538  std::size_t newpos = str.find_first_of ('\n', pos);
539  if (newpos == std::string::npos)
540  break;
541 
542  retval(i) = str.substr(pos, newpos - pos);
543  pos = newpos + 1;
544  }
545 
546  return retval;
547  }
548 
549  void get_fileinfo (const std::string& filename, double& filesize,
550  OCTAVE_TIME_T& filetime, bool& fileisdir)
551  {
552  std::string path = pwd ();
553 
554  m_url = "ftp://" + m_host_or_url + '/' + path + '/' + filename;
555  SETOPT (CURLOPT_URL, m_url.c_str ());
556  SETOPT (CURLOPT_FILETIME, 1);
557  SETOPT (CURLOPT_HEADERFUNCTION, throw_away);
558  SETOPT (CURLOPT_WRITEFUNCTION, throw_away);
559 
560  // FIXME
561  // The MDTM command fails for a directory on the servers I tested
562  // so this is a means of testing for directories. It also means
563  // I can't get the date of directories!
564 
565  perform ();
566  if (! good ())
567  {
568  fileisdir = true;
569  filetime = -1;
570  filesize = 0;
571 
572  return;
573  }
574 
575  fileisdir = false;
576  OCTAVE_TIME_T ft;
577  curl_easy_getinfo (m_curl, CURLINFO_FILETIME, &ft);
578  filetime = ft;
579  double fs;
580  curl_easy_getinfo (m_curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs);
581  filesize = fs;
582 
583  SETOPT (CURLOPT_WRITEFUNCTION, write_data);
584  SETOPT (CURLOPT_HEADERFUNCTION, 0);
585  SETOPT (CURLOPT_FILETIME, 0);
586  m_url = "ftp://" + m_host_or_url;
587  SETOPT (CURLOPT_URL, m_url.c_str ());
588 
589  // The MDTM command seems to reset the path to the root with the
590  // servers I tested with, so cd again into the correct path. Make
591  // the path absolute so that this will work even with servers that
592  // don't end up in the root after an MDTM command.
593  cwd ('/' + path);
594  }
595 
596  std::string pwd (void)
597  {
598  std::string retval;
599 
600  struct curl_slist *slist = nullptr;
601 
602  unwind_action cleanup_slist ([=] () { curl_slist_free_all (slist); });
603 
604  slist = curl_slist_append (slist, "pwd");
605  SETOPTR (CURLOPT_POSTQUOTE, slist);
606  SETOPTR (CURLOPT_HEADERFUNCTION, write_data);
607 
608  std::ostringstream buf;
609  SETOPTR (CURLOPT_WRITEHEADER, static_cast<void *>(&buf));
610 
611  perform ();
612  if (! good ())
613  return retval;
614 
615  retval = buf.str ();
616 
617  // Can I assume that the path is always in "" on the last line
618  std::size_t pos2 = retval.rfind ('"');
619  std::size_t pos1 = retval.rfind ('"', pos2 - 1);
620  retval = retval.substr (pos1 + 1, pos2 - pos1 - 1);
621 
622  SETOPTR (CURLOPT_HEADERFUNCTION, 0);
623  SETOPTR (CURLOPT_WRITEHEADER, 0);
624  SETOPTR (CURLOPT_POSTQUOTE, 0);
625 
626  return retval;
627  }
628 
629  void http_get (const Array<std::string>& param)
630  {
631  http_action (param, "get");
632  }
633 
634  void http_post (const Array<std::string>& param)
635  {
636  http_action (param, "post");
637  }
638 
639  void http_action (const Array<std::string>& param, const std::string& action)
640  {
642 
643  std::string query_string;
644 
645  query_string = form_query_string (param);
646 
647  if (action.empty () || action == "get")
648  {
649  if (! query_string.empty ())
650  m_url += '?' + query_string;
651 
652  SETOPT (CURLOPT_URL, m_url.c_str ());
653  }
654  else if (action == "post" || action == "put" || action == "delete")
655  {
656  SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ());
657 
658  if (action == "put")
659  {
660  SETOPT (CURLOPT_CUSTOMREQUEST, "PUT");
661  }
662 
663  if (action == "delete")
664  {
665  SETOPT (CURLOPT_CUSTOMREQUEST, "DELETE");
666  }
667 
668  SETOPT (CURLOPT_URL, m_url.c_str ());
669  }
670  else
671  {
672  m_ok = false;
673  m_errmsg = "curl_transfer: unknown http action";
674  }
675 
676  if (m_ok)
677  perform ();
678  }
679 
680  void cookie_jar (const std::string& filename)
681  {
682  SETOPT (CURLOPT_COOKIEJAR, filename.c_str ());
683 
684  SETOPT (CURLOPT_COOKIEFILE, filename.c_str ());
685  }
686 
687  // Sets the header fields in a transfer. Input should be in the form
688  // of an array of strings with pairs of keys and values together
690  {
691  struct curl_slist *slist = nullptr;
692 
693  unwind_action cleanup_slist ([=] () { curl_slist_free_all (slist); });
694 
695  if (param.numel () >= 2)
696  {
697  for (int i = 0; i < param.numel (); i += 2)
698  {
699  std::string header = param(i) + ": " + param(i+1);
700 
701  slist = curl_slist_append (slist, header.c_str ());
702  }
703 
704  SETOPT (CURLOPT_HTTPHEADER, slist);
705  }
706  }
707 
708  // Sets and sends the form data associated with a transfer.
709  // Input should be an array of strings with each pair of strings
710  // corresponding to the fieldname and it's value.
711  // To attach a file, you should use 'file' as the fieldname with the
712  // path of the file as its value.
713  void form_data_post (const Array<std::string>& param)
714  {
715  struct curl_httppost *post = nullptr;
716  struct curl_httppost *last = nullptr;
717 
718  SETOPT (CURLOPT_URL, m_host_or_url.c_str ());
719 
720  unwind_action cleanup_httppost ([=] () { curl_formfree (post); });
721 
722  if (param.numel () >= 2)
723  {
724  for (int i = 0; i < param.numel (); i += 2)
725  {
726  std::string name = param(i);
727  std::string data = param(i+1);
728 
729  if (name == "file")
730  curl_formadd (&post, &last, CURLFORM_COPYNAME, name.c_str (),
731  CURLFORM_FILE, data.c_str (), CURLFORM_END);
732  else
733  curl_formadd(&post, &last, CURLFORM_COPYNAME, name.c_str (),
734  CURLFORM_COPYCONTENTS, data.c_str (), CURLFORM_END);
735  }
736 
737  SETOPT (CURLOPT_HTTPPOST, post);
738  }
739 
740  perform ();
741  }
742 
743  // Sets the various options specified by weboptions object.
744  void set_weboptions (const struct weboptions& options)
745  {
746  // Remove this after completing fixmes.
747  std::string temp = "";
748 
750 
751  SETOPT (CURLOPT_TIMEOUT, options.Timeout);
752 
753  if (! options.UserAgent.empty ())
754  SETOPT (CURLOPT_USERAGENT, options.UserAgent.c_str ());
755 
756  if (! options.Username.empty ())
757  {
758  if (! options.Password.empty ())
759  {
760  std::string tmp = options.Username + ":" + options.Password;
761  SETOPT (CURLOPT_USERPWD, tmp.c_str ());
762  }
763  else
764  {
765  std::string tmp = options.Username + ":";
766  SETOPT (CURLOPT_USERPWD, tmp.c_str ());
767  }
768  }
769 
770  // Unimplemented. Only for MATLAB compatibility.
771  if (! options.ContentReader.empty ())
772  temp = options.ContentReader;
773 
774  // Unimplemented. Only for MATLAB compatibility.
775  if (! options.ArrayFormat.empty ())
776  temp = options.ArrayFormat;
777 
778  // Unimplemented. Only for MATLAB compatibility.
779  if (! options.CertificateFilename.empty ())
780  temp = options.CertificateFilename;
781  }
782 
783 private:
784 
785  // Pointer to cURL object.
786  CURL *m_curl;
787 
788  // cURL error code.
789  CURLcode m_errnum;
790 
791  // The cURL library changed the curl_easy_setopt call to make an
792  // internal copy of string parameters in version 7.17.0. Prior
793  // versions only held a pointer to a string provided by the caller
794  // that must persist for the lifetime of the CURL handle.
795  //
796  // The associated API did not change, only the behavior of the library
797  // implementing the function call.
798  //
799  // To be compatible with any version of cURL, the caller must keep a
800  // copy of all string parameters associated with a CURL handle until
801  // the handle is released. The curl_handle::curl_handle_rep class
802  // contains the pointer to the CURL handle and so is the best
803  // candidate for storing the strings as well. (bug #36717)
804  std::string m_url;
805  std::string m_userpwd;
806 
807  void init (const std::string& user, const std::string& passwd,
808  std::istream& is, std::ostream& os)
809  {
810  // No data transfer by default
811  SETOPT (CURLOPT_NOBODY, 1);
812 
813  // Set the username and password
814  m_userpwd = user;
815  if (! passwd.empty ())
816  m_userpwd += ':' + passwd;
817  if (! m_userpwd.empty ())
818  SETOPT (CURLOPT_USERPWD, m_userpwd.c_str ());
819 
820  // Define our callback to get called when there's data to be written.
821  SETOPT (CURLOPT_WRITEFUNCTION, write_data);
822 
823  // Set a pointer to our struct to pass to the callback.
824  SETOPT (CURLOPT_WRITEDATA, static_cast<void *> (&os));
825 
826  // Define our callback to get called when there's data to be read
827  SETOPT (CURLOPT_READFUNCTION, read_data);
828 
829  // Set a pointer to our struct to pass to the callback.
830  SETOPT (CURLOPT_READDATA, static_cast<void *> (&is));
831 
832  // Follow redirects.
833  SETOPT (CURLOPT_FOLLOWLOCATION, true);
834 
835  // Don't use EPSV since connecting to sites that don't support it
836  // will hang for some time (3 minutes?) before moving on to try PASV
837  // instead.
838  SETOPT (CURLOPT_FTP_USE_EPSV, false);
839 
840  // Set the user agent for the curl request
841  // Needed by mediaWiki API.
842  curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
843  const char *lib_ver = data->version;
844  std::string user_agent
845  ("GNU Octave/"
846  + std::string (OCTAVE_VERSION)
847  + " (https://www.gnu.org/software/octave/ ; help@octave.org) libcurl/"
848  + std::string (lib_ver));
849 
850  SETOPT (CURLOPT_USERAGENT, user_agent.c_str ());
851 
852  SETOPT (CURLOPT_NOPROGRESS, true);
853  SETOPT (CURLOPT_FAILONERROR, true);
854 
855  SETOPT (CURLOPT_POSTQUOTE, 0);
856  SETOPT (CURLOPT_QUOTE, 0);
857  }
858 
859  std::string form_query_string (const Array<std::string>& param)
860  {
861  std::ostringstream query;
862 
863  if (param.numel () >= 2)
864  for (int i = 0; i < param.numel (); i += 2)
865  {
866  std::string name = param(i);
867  std::string text = param(i+1);
868 
869  // Encode strings.
870  char *enc_name = curl_easy_escape (m_curl, name.c_str (),
871  name.length ());
872  char *enc_text = curl_easy_escape (m_curl, text.c_str (),
873  text.length ());
874 
875  query << enc_name << '=' << enc_text;
876 
877  curl_free (enc_name);
878  curl_free (enc_text);
879 
880  if (i < param.numel ()-2)
881  query << '&';
882  }
883 
884  query.flush ();
885 
886  return query.str ();
887  }
888 
889  void ftp_file_or_dir_action (const std::string& file_or_dir,
890  const std::string& action)
891  {
892  struct curl_slist *slist = nullptr;
893 
894  unwind_action cleanup_slist ([=] () { curl_slist_free_all (slist); });
895 
896  std::string cmd = action + ' ' + file_or_dir;
897 
898  slist = curl_slist_append (slist, cmd.c_str ());
899 
900  SETOPT (CURLOPT_POSTQUOTE, slist);
901 
902  perform ();
903 
904  if (! good ())
905  return;
906 
907  SETOPT (CURLOPT_POSTQUOTE, 0);
908  }
909 };
910 
911 #undef SETOPT
912 
913 #endif
914 
915 #if defined (HAVE_CURL)
916 # define REP_CLASS curl_transfer
917 #else
918 # define REP_CLASS base_url_transfer
919 #endif
920 
921 url_transfer::url_transfer (void) : m_rep (new REP_CLASS ())
922 { }
923 
924 url_transfer::url_transfer (const std::string& host, const std::string& user,
925  const std::string& passwd, std::ostream& os)
926  : m_rep (new REP_CLASS (host, user, passwd, os))
927 { }
928 
929 url_transfer::url_transfer (const std::string& url, std::ostream& os)
930  : m_rep (new REP_CLASS (url, os))
931 { }
932 
933 #undef REP_CLASS
934 
OCTAVE_END_NAMESPACE(octave)
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
std::string m_host_or_url
Definition: url-transfer.h:157
bool good(void) const
Definition: url-transfer.h:81
std::string m_errmsg
Definition: url-transfer.h:162
virtual void get(const std::string &, std::ostream &)
Definition: url-transfer.h:119
virtual void put(const std::string &, std::istream &)
Definition: url-transfer.h:116
virtual void cwd(const std::string &)
Definition: url-transfer.h:105
std::istream * m_curr_istream
Definition: url-transfer.h:163
void mget_directory(const std::string &directory, const std::string &target)
Definition: url-transfer.cc:74
string_vector mput_directory(const std::string &base, const std::string &directory)
virtual void get_fileinfo(const std::string &, double &, OCTAVE_TIME_T &, bool &)
Definition: url-transfer.h:132
virtual string_vector list(void)
Definition: url-transfer.h:130
std::ostream * m_curr_ostream
Definition: url-transfer.h:164
virtual void mkdir(const std::string &)
Definition: url-transfer.h:111
std::string m_url
curl_transfer & operator=(const curl_transfer &)=delete
std::ostream & set_ostream(std::ostream &os)
void rmdir(const std::string &path)
std::string pwd(void)
void del(const std::string &file)
void http_post(const Array< std::string > &param)
void set_header_fields(const Array< std::string > &param)
void form_data_post(const Array< std::string > &param)
void set_weboptions(const struct weboptions &options)
std::string m_userpwd
void http_action(const Array< std::string > &param, const std::string &action)
curl_transfer(const curl_transfer &)=delete
std::istream & set_istream(std::istream &is)
std::string form_query_string(const Array< std::string > &param)
curl_transfer(const std::string &host, const std::string &user_arg, const std::string &passwd, std::ostream &os)
void cookie_jar(const std::string &filename)
std::string lasterror(void) const
void ftp_file_or_dir_action(const std::string &file_or_dir, const std::string &action)
void http_get(const Array< std::string > &param)
CURLcode m_errnum
string_vector list(void)
void init(const std::string &user, const std::string &passwd, std::istream &is, std::ostream &os)
void ascii(void)
void mkdir(const std::string &path)
void put(const std::string &file, std::istream &is)
void dir(void)
void cwd(const std::string &path)
void get(const std::string &file, std::ostream &os)
void get_fileinfo(const std::string &filename, double &filesize, OCTAVE_TIME_T &filetime, bool &fileisdir)
~curl_transfer(void)
curl_transfer(const std::string &url_str, std::ostream &os)
void perform(void)
void binary(void)
void rename(const std::string &oldname, const std::string &newname)
OCTINTERP_API bool eof(void) const
Definition: oct-stream.cc:7300
OCTINTERP_API octave_value read(const Array< double > &size, octave_idx_type block_size, oct_data_conv::data_type input_type, oct_data_conv::data_type output_type, octave_idx_type skip, mach_info::float_format flt_fmt, octave_idx_type &count)
Definition: oct-stream.cc:6617
OCTINTERP_API octave_idx_type write(const octave_value &data, octave_idx_type block_size, oct_data_conv::data_type output_type, octave_idx_type skip, mach_info::float_format flt_fmt)
Definition: oct-stream.cc:6810
string_vector & append(const std::string &s)
Definition: str-vec.cc:110
void resize(octave_idx_type n, const std::string &rfv="")
Definition: str-vec.h:95
octave_idx_type numel(void) const
Definition: str-vec.h:100
void discard(void)
Definition: unwind-prot.h:257
url_transfer(void)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
int unlink(const std::string &name)
Definition: file-ops.cc:677
std::string dir_sep_str(void)
Definition: file-ops.cc:240
int mkdir(const std::string &nm, mode_t md)
Definition: file-ops.cc:402
std::ofstream ofstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:438
std::ifstream ifstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:424
bool get_dirlist(const std::string &dirname, string_vector &dirlist, std::string &msg)
Definition: lo-sysdep.cc:119
#define OCTAVE_VERSION
Definition: main.in.cc:63
octave_idx_type n
Definition: mx-inlines.cc:753
std::string UserAgent
Definition: url-transfer.h:43
std::string ArrayFormat
Definition: url-transfer.h:50
std::string ContentReader
Definition: url-transfer.h:48
Array< std::string > HeaderFields
Definition: url-transfer.h:47
std::string Password
Definition: url-transfer.h:46
long Timeout
Definition: url-transfer.h:44
std::string CertificateFilename
Definition: url-transfer.h:51
std::string Username
Definition: url-transfer.h:45
#define SETOPT(option, parameter)
#define SETOPTR(option, parameter)
static int write_data(void *buffer, std::size_t size, std::size_t nmemb, void *streamp)
static int read_data(void *buffer, std::size_t size, std::size_t nmemb, void *streamp)
static std::size_t throw_away(void *, std::size_t size, std::size_t nmemb, void *)
#define REP_CLASS