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