diff libinterp/corefcn/urlwrite.cc @ 17555:0946b0e06544

move url_transfer classes to liboctave * liboctave/util/url-transfer.h, liboctave/util/url-transfer.cc: New files, extracted from libinterp/dldfcn/urlwrite.cc. * libinterp/corefcn/urlwrite.cc: Move here from libinterp/dldfcn/urlwrite.cc. * libinterp/corefcn/module.mk, libinterp/dldfcn/module-files, liboctave/link-deps.mk liboctave/util/module.mk: Update for new and renamed files.
author John W. Eaton <jwe@octave.org>
date Thu, 03 Oct 2013 15:52:49 -0400
parents libinterp/dldfcn/urlwrite.cc@f0d21e7d4653
children 9e8a9f043944
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/urlwrite.cc	Thu Oct 03 15:52:49 2013 -0400
@@ -0,0 +1,1178 @@
+// urlwrite and urlread, a curl front-end for octave
+/*
+
+Copyright (C) 2006-2012 Alexander Barth
+Copyright (C) 2009 David Bateman
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+Octave is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+// Author: Alexander Barth <abarth@marine.usf.edu>
+// Adapted-By: jwe
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+#include "dir-ops.h"
+#include "file-ops.h"
+#include "file-stat.h"
+#include "oct-env.h"
+#include "oct-handle.h"
+#include "glob-match.h"
+#include "singleton-cleanup.h"
+#include "url-transfer.h"
+
+#include "defun.h"
+#include "error.h"
+#include "oct-obj.h"
+#include "ov-cell.h"
+#include "pager.h"
+#include "oct-map.h"
+#include "oct-refcount.h"
+#include "unwind-prot.h"
+
+static void
+delete_file (const std::string& file)
+{
+  octave_unlink (file);
+}
+
+typedef octave_handle curl_handle;
+
+class OCTINTERP_API ch_manager
+{
+protected:
+
+  ch_manager (void)
+    : handle_map (), handle_free_list (),
+      next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)) { }
+
+public:
+
+  static void create_instance (void);
+
+  static bool instance_ok (void)
+  {
+    bool retval = true;
+
+    if (! instance)
+      create_instance ();
+
+    if (! instance)
+      {
+        ::error ("unable to create ch_manager!");
+
+        retval = false;
+      }
+
+    return retval;
+  }
+
+  static void cleanup_instance (void) { delete instance; instance = 0; }
+
+  static curl_handle get_handle (void)
+  {
+    return instance_ok ()
+      ? instance->do_get_handle () : curl_handle ();
+  }
+
+  static void free (const curl_handle& h)
+  {
+    if (instance_ok ())
+      instance->do_free (h);
+  }
+
+  static curl_handle lookup (double val)
+  {
+    return instance_ok () ? instance->do_lookup (val) : curl_handle ();
+  }
+
+  static curl_handle lookup (const octave_value& val)
+  {
+    return val.is_real_scalar ()
+      ? lookup (val.double_value ()) : curl_handle ();
+  }
+
+  static url_transfer get_object (double val)
+  {
+    return get_object (lookup (val));
+  }
+
+  static url_transfer get_object (const octave_value& val)
+  {
+    return get_object (lookup (val));
+  }
+
+  static url_transfer get_object (const curl_handle& h)
+  {
+    return instance_ok () ? instance->do_get_object (h) : url_transfer ();
+  }
+
+  static curl_handle make_curl_handle (const std::string& host,
+                                       const std::string& user,
+                                       const std::string& passwd)
+  {
+    return instance_ok ()
+      ? instance->do_make_curl_handle (host, user, passwd) : curl_handle ();
+  }
+
+  static Matrix handle_list (void)
+  {
+    return instance_ok () ? instance->do_handle_list () : Matrix ();
+  }
+
+private:
+
+  static ch_manager *instance;
+
+  typedef std::map<curl_handle, url_transfer>::iterator iterator;
+  typedef std::map<curl_handle, url_transfer>::const_iterator const_iterator;
+
+  typedef std::set<curl_handle>::iterator free_list_iterator;
+  typedef std::set<curl_handle>::const_iterator const_free_list_iterator;
+
+  // A map of handles to curl objects.
+  std::map<curl_handle, url_transfer> handle_map;
+
+  // The available curl handles.
+  std::set<curl_handle> handle_free_list;
+
+  // The next handle available if handle_free_list is empty.
+  double next_handle;
+
+  curl_handle do_get_handle (void);
+
+  void do_free (const curl_handle& h);
+
+  curl_handle do_lookup (double val)
+  {
+    iterator p = (xisnan (val) ? handle_map.end () : handle_map.find (val));
+
+    return (p != handle_map.end ()) ? p->first : curl_handle ();
+  }
+
+  url_transfer do_get_object (const curl_handle& h)
+  {
+    iterator p = (h.ok () ? handle_map.find (h) : handle_map.end ());
+
+    return (p != handle_map.end ()) ? p->second : url_transfer ();
+  }
+
+  curl_handle do_make_curl_handle (const std::string& host,
+                                   const std::string& user,
+                                   const std::string& passwd)
+  {
+    curl_handle h = get_handle ();
+
+    url_transfer obj (host, user, passwd, octave_stdout);
+
+    if (! error_state)
+      handle_map[h] = obj;
+    else
+      h = curl_handle ();
+
+    return h;
+  }
+
+  Matrix do_handle_list (void)
+  {
+    Matrix retval (1, handle_map.size ());
+
+    octave_idx_type i = 0;
+    for (const_iterator p = handle_map.begin (); p != handle_map.end (); p++)
+      {
+        curl_handle h = p->first;
+
+        retval(i++) = h.value ();
+      }
+
+    return retval;
+  }
+};
+
+void
+ch_manager::create_instance (void)
+{
+  instance = new ch_manager ();
+
+  if (instance)
+    singleton_cleanup_list::add (cleanup_instance);
+}
+
+static double
+make_handle_fraction (void)
+{
+  static double maxrand = RAND_MAX + 2.0;
+
+  return (rand () + 1.0) / maxrand;
+}
+
+curl_handle
+ch_manager::do_get_handle (void)
+{
+  curl_handle retval;
+
+  // Curl handles are negative integers plus some random fractional
+  // part.  To avoid running out of integers, we recycle the integer
+  // part but tack on a new random part each time.
+
+  free_list_iterator p = handle_free_list.begin ();
+
+  if (p != handle_free_list.end ())
+    {
+      retval = *p;
+      handle_free_list.erase (p);
+    }
+  else
+    {
+      retval = curl_handle (next_handle);
+
+      next_handle = std::ceil (next_handle) - 1.0 - make_handle_fraction ();
+    }
+
+  return retval;
+}
+
+void
+ch_manager::do_free (const curl_handle& h)
+{
+  if (h.ok ())
+    {
+      iterator p = handle_map.find (h);
+
+      if (p != handle_map.end ())
+        {
+          // Curl handles are negative integers plus some random
+          // fractional part.  To avoid running out of integers, we
+          // recycle the integer part but tack on a new random part
+          // each time.
+
+          handle_map.erase (p);
+
+          if (h.value () < 0)
+            handle_free_list.insert (std::ceil (h.value ()) - make_handle_fraction ());
+        }
+      else
+        error ("ch_manager::free: invalid object %g", h.value ());
+    }
+}
+
+ch_manager *ch_manager::instance = 0;
+
+DEFUN (urlwrite, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {} urlwrite (@var{url}, @var{localfile})\n\
+@deftypefnx {Loadable Function} {@var{f} =} urlwrite (@var{url}, @var{localfile})\n\
+@deftypefnx {Loadable Function} {[@var{f}, @var{success}] =} urlwrite (@var{url}, @var{localfile})\n\
+@deftypefnx {Loadable Function} {[@var{f}, @var{success}, @var{message}] =} urlwrite (@var{url}, @var{localfile})\n\
+Download a remote file specified by its @var{url} and save it as\n\
+@var{localfile}.  For example:\n\
+\n\
+@example\n\
+@group\n\
+urlwrite (\"ftp://ftp.octave.org/pub/octave/README\",\n\
+          \"README.txt\");\n\
+@end group\n\
+@end example\n\
+\n\
+The full path of the downloaded file is returned in @var{f}.  The\n\
+variable @var{success} is 1 if the download was successful,\n\
+otherwise it is 0 in which case @var{message} contains an error\n\
+message.  If no output argument is specified and an error occurs,\n\
+then the error is signaled through Octave's error handling mechanism.\n\
+\n\
+This function uses libcurl.  Curl supports, among others, the HTTP,\n\
+FTP and FILE protocols.  Username and password may be specified in\n\
+the URL, for example:\n\
+\n\
+@example\n\
+@group\n\
+urlwrite (\"http://username:password@@example.com/file.txt\",\n\
+          \"file.txt\");\n\
+@end group\n\
+@end example\n\
+\n\
+GET and POST requests can be specified by @var{method} and @var{param}.\n\
+The parameter @var{method} is either @samp{get} or @samp{post}\n\
+and @var{param} is a cell array of parameter and value pairs.\n\
+For example:\n\
+\n\
+@example\n\
+@group\n\
+urlwrite (\"http://www.google.com/search\", \"search.html\",\n\
+          \"get\", @{\"query\", \"octave\"@});\n\
+@end group\n\
+@end example\n\
+@seealso{urlread}\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+  int nargin = args.length ();
+
+  // verify arguments
+  if (nargin != 2 && nargin != 4)
+    {
+      print_usage ();
+      return retval;
+    }
+
+  std::string url = args(0).string_value ();
+
+  if (error_state)
+    {
+      error ("urlwrite: URL must be a character string");
+      return retval;
+    }
+
+  // name to store the file if download is succesful
+  std::string filename = args(1).string_value ();
+
+  if (error_state)
+    {
+      error ("urlwrite: LOCALFILE must be a character string");
+      return retval;
+    }
+
+  std::string method;
+  Array<std::string> param;
+
+  if (nargin == 4)
+    {
+      method = args(2).string_value ();
+
+      if (error_state)
+        {
+          error ("urlwrite: METHOD must be \"get\" or \"post\"");
+          return retval;
+        }
+
+      if (method != "get" && method != "post")
+        {
+          error ("urlwrite: METHOD must be \"get\" or \"post\"");
+          return retval;
+        }
+
+      param = args(3).cellstr_value ();
+
+      if (error_state)
+        {
+          error ("urlwrite: parameters (PARAM) for get and post requests must be given as a cell array of character strings");
+          return retval;
+        }
+
+
+      if (param.numel () % 2 == 1 )
+        {
+          error ("urlwrite: number of elements in PARAM must be even");
+          return retval;
+        }
+    }
+
+  // The file should only be deleted if it doesn't initially exist, we
+  // create it, and the download fails.  We use unwind_protect to do
+  // it so that the deletion happens no matter how we exit the function.
+
+  file_stat fs (filename);
+
+  std::ofstream ofile (filename.c_str (), std::ios::out | std::ios::binary);
+
+  if (! ofile.is_open ())
+    {
+      error ("urlwrite: unable to open file");
+      return retval;
+    }
+
+  unwind_protect_safe frame;
+
+  frame.add_fcn (delete_file, filename);
+
+  url_transfer curl = url_transfer (url, method, param, ofile);
+
+  ofile.close ();
+
+  if (curl.good ())
+    frame.discard ();
+
+  if (nargout > 0)
+    {
+      if (curl.good ())
+        {
+          retval(2) = std::string ();
+          retval(1) = true;
+          retval(0) = octave_env::make_absolute (filename);
+        }
+      else
+        {
+          retval(2) = curl.lasterror ();
+          retval(1) = false;
+          retval(0) = std::string ();
+        }
+    }
+
+  if (nargout < 2 && ! curl.good ())
+    error ("urlwrite: %s", curl.lasterror ().c_str ());
+
+  return retval;
+}
+
+DEFUN (urlread, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {@var{s} =} urlread (@var{url})\n\
+@deftypefnx {Loadable Function} {[@var{s}, @var{success}] =} urlread (@var{url})\n\
+@deftypefnx {Loadable Function} {[@var{s}, @var{success}, @var{message}] =} urlread (@var{url})\n\
+@deftypefnx {Loadable Function} {[@dots{}] =} urlread (@var{url}, @var{method}, @var{param})\n\
+Download a remote file specified by its @var{url} and return its content\n\
+in string @var{s}.  For example:\n\
+\n\
+@example\n\
+s = urlread (\"ftp://ftp.octave.org/pub/octave/README\");\n\
+@end example\n\
+\n\
+The variable @var{success} is 1 if the download was successful,\n\
+otherwise it is 0 in which case @var{message} contains an error\n\
+message.  If no output argument is specified and an error occurs,\n\
+then the error is signaled through Octave's error handling mechanism.\n\
+\n\
+This function uses libcurl.  Curl supports, among others, the HTTP,\n\
+FTP and FILE protocols.  Username and password may be specified in the\n\
+URL@.  For example:\n\
+\n\
+@example\n\
+s = urlread (\"http://user:password@@example.com/file.txt\");\n\
+@end example\n\
+\n\
+GET and POST requests can be specified by @var{method} and @var{param}.\n\
+The parameter @var{method} is either @samp{get} or @samp{post}\n\
+and @var{param} is a cell array of parameter and value pairs.\n\
+For example:\n\
+\n\
+@example\n\
+@group\n\
+s = urlread (\"http://www.google.com/search\", \"get\",\n\
+            @{\"query\", \"octave\"@});\n\
+@end group\n\
+@end example\n\
+@seealso{urlwrite}\n\
+@end deftypefn")
+{
+  // Octave's return value
+  octave_value_list retval;
+
+  int nargin = args.length ();
+
+  // verify arguments
+  if (nargin != 1  && nargin != 3)
+    {
+      print_usage ();
+      return retval;
+    }
+
+  std::string url = args(0).string_value ();
+
+  if (error_state)
+    {
+      error ("urlread: URL must be a character string");
+      return retval;
+    }
+
+  std::string method;
+  Array<std::string> param;
+
+  if (nargin == 3)
+    {
+      method = args(1).string_value ();
+
+      if (error_state)
+        {
+          error ("urlread: METHOD must be \"get\" or \"post\"");
+          return retval;
+        }
+
+      if (method != "get" && method != "post")
+        {
+          error ("urlread: METHOD must be \"get\" or \"post\"");
+          return retval;
+        }
+
+      param = args(2).cellstr_value ();
+
+      if (error_state)
+        {
+          error ("urlread: parameters (PARAM) for get and post requests must be given as a cell array of character strings");
+          return retval;
+        }
+
+      if (param.numel () % 2 == 1 )
+        {
+          error ("urlread: number of elements in PARAM must be even");
+          return retval;
+        }
+    }
+
+  std::ostringstream buf;
+
+  url_transfer curl = url_transfer (url, method, param, buf);
+
+  if (curl.good ())
+    {
+      if (nargout > 0)
+        {
+          // Return empty string if no error occured.
+          retval(2) = curl.good () ? "" : curl.lasterror ();
+          retval(1) = curl.good ();
+          retval(0) = buf.str ();
+        }
+    }
+
+  if (nargout < 2 && ! curl.good ())
+    error ("urlread: %s", curl.lasterror().c_str());
+
+  return retval;
+}
+
+DEFUN (__ftp__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {@var{handle} =} __ftp__ (@var{host})\n\
+@deftypefnx {Loadable Function} {@var{handle} =} __ftp__ (@var{host}, @var{username}, @var{password})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+  std::string host;
+  std::string user = "anonymous";
+  std::string passwd = "";
+
+  if (nargin < 1 || nargin > 3)
+    {
+      print_usage ();
+      return retval;
+    }
+  else
+    {
+      host = args(0).string_value ();
+
+      if (nargin > 1)
+        user = args(1).string_value ();
+
+      if (nargin > 2)
+        passwd = args(2).string_value ();
+
+      if (! error_state)
+        {
+          curl_handle ch
+            = ch_manager::make_curl_handle (host, user, passwd, octave_stdout);
+
+          if (! error_state)
+            retval = ch.value ();
+        }
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_pwd__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_pwd__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_pwd__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        retval = curl.pwd ();
+      else
+        error ("__ftp_pwd__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_cwd__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_cwd__ (@var{handle}, @var{path})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1 && nargin != 2)
+    error ("__ftp_cwd__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string path = "";
+
+          if (nargin > 1)
+            path = args(1).string_value ();
+
+          if (! error_state)
+            curl.cwd (path);
+          else
+            error ("__ftp_cwd__: expecting path as second argument");
+        }
+      else
+        error ("__ftp_cwd__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_dir__, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_dir__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_dir__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          if (nargout == 0)
+            curl.dir ();
+          else
+            {
+              string_vector sv = curl.list ();
+              octave_idx_type n = sv.length ();
+
+              if (n == 0)
+                {
+                  string_vector flds (5);
+
+                  flds(0) = "name";
+                  flds(1) = "date";
+                  flds(2) = "bytes";
+                  flds(3) = "isdir";
+                  flds(4) = "datenum";
+
+                  retval = octave_map (flds);
+                }
+              else
+                {
+                  octave_map st;
+
+                  Cell filectime (dim_vector (n, 1));
+                  Cell filesize (dim_vector (n, 1));
+                  Cell fileisdir (dim_vector (n, 1));
+                  Cell filedatenum (dim_vector (n, 1));
+
+                  st.assign ("name", Cell (sv));
+
+                  for (octave_idx_type i = 0; i < n; i++)
+                    {
+                      time_t ftime;
+                      bool fisdir;
+                      double fsize;
+
+                      curl.get_fileinfo (sv(i), fsize, ftime, fisdir);
+
+                      fileisdir (i) = fisdir;
+                      filectime (i) = ctime (&ftime);
+                      filesize (i) = fsize;
+                      filedatenum (i) = double (ftime);
+                    }
+
+                  st.assign ("date", filectime);
+                  st.assign ("bytes", filesize);
+                  st.assign ("isdir", fileisdir);
+                  st.assign ("datenum", filedatenum);
+
+                  retval = st;
+                }
+            }
+        }
+      else
+        error ("__ftp_dir__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_ascii__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_ascii__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_ascii__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        curl.ascii ();
+      else
+        error ("__ftp_ascii__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_binary__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_binary__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_binary__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        curl.binary ();
+      else
+        error ("__ftp_binary__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_close__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_close__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_close__: incorrect number of arguments");
+  else
+    {
+      curl_handle h = ch_manager::lookup (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (h.ok ())
+        ch_manager::free (h);
+      else
+        error ("__ftp_close__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_mode__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_mode__ (@var{handle})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 1)
+    error ("__ftp_mode__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        retval = (curl.is_ascii () ? "ascii" : "binary");
+      else
+        error ("__ftp_binary__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_delete__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_delete__ (@var{handle}, @var{path})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 2)
+    error ("__ftp_delete__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string file = args(1).string_value ();
+
+          if (! error_state)
+            curl.del (file);
+          else
+            error ("__ftp_delete__: expecting file name as second argument");
+        }
+      else
+        error ("__ftp_delete__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_rmdir__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_rmdir__ (@var{handle}, @var{path})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 2)
+    error ("__ftp_rmdir__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string dir = args(1).string_value ();
+
+          if (! error_state)
+            curl.rmdir (dir);
+          else
+            error ("__ftp_rmdir__: expecting directory name as second argument");
+        }
+      else
+        error ("__ftp_rmdir__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_mkdir__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_mkdir__ (@var{handle}, @var{path})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 2)
+    error ("__ftp_mkdir__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string dir = args(1).string_value ();
+
+          if (! error_state)
+            curl.mkdir (dir);
+          else
+            error ("__ftp_mkdir__: expecting directory name as second argument");
+        }
+      else
+        error ("__ftp_mkdir__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_rename__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_rename__ (@var{handle}, @var{path})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 3)
+    error ("__ftp_rename__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string oldname = args(1).string_value ();
+          std::string newname = args(2).string_value ();
+
+          if (! error_state)
+            curl.rename (oldname, newname);
+          else
+            error ("__ftp_rename__: expecting file names for second and third arguments");
+        }
+      else
+        error ("__ftp_rename__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_mput__, args, nargout,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_mput__ (@var{handle}, @var{files})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 2)
+    error ("__ftp_mput__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string pat = args(1).string_value ();
+
+          if (! error_state)
+            {
+              string_vector file_list;
+
+              glob_match pattern (file_ops::tilde_expand (pat));
+              string_vector files = pattern.glob ();
+
+              for (octave_idx_type i = 0; i < files.length (); i++)
+                {
+                  std::string file = files (i);
+
+                  file_stat fs (file);
+
+                  if (! fs.exists ())
+                    {
+                      error ("__ftp__mput: file does not exist");
+                      break;
+                    }
+
+                  if (fs.is_dir ())
+                    {
+                      file_list.append (curl.mput_directory ("", file));
+
+                      if (! curl.good ())
+                        {
+                          error ("__ftp_mput__: %s", curl.lasterror().c_str());
+                          break;
+                        }
+                    }
+                  else
+                    {
+                      // FIXME Does ascii mode need to be flagged here?
+                      std::ifstream ifile (file.c_str (), std::ios::in |
+                                           std::ios::binary);
+
+                      if (! ifile.is_open ())
+                        {
+                          error ("__ftp_mput__: unable to open file");
+                          break;
+                        }
+
+                      curl.put (file, ifile);
+
+                      ifile.close ();
+
+                      if (! curl.good ())
+                        {
+                          error ("__ftp_mput__: %s", curl.lasterror().c_str());
+                          break;
+                        }
+
+                      file_list.append (file);
+                    }
+                }
+
+              if (nargout > 0)
+                retval = file_list;
+            }
+          else
+            error ("__ftp_mput__: expecting file name patter as second argument");
+        }
+      else
+        error ("__ftp_mput__: invalid ftp handle");
+    }
+
+  return retval;
+}
+
+DEFUN (__ftp_mget__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __ftp_mget__ (@var{handle}, @var{files})\n\
+Undocumented internal function\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  int nargin = args.length ();
+
+  if (nargin != 2 && nargin != 3)
+    error ("__ftp_mget__: incorrect number of arguments");
+  else
+    {
+      url_transfer curl = ch_manager::get_object (args(0));
+
+      if (error_state)
+        return retval;
+
+      if (curl.is_valid ())
+        {
+          std::string file = args(1).string_value ();
+          std::string target;
+
+          if (nargin == 3)
+            target = args(2).string_value () + file_ops::dir_sep_str ();
+
+          if (! error_state)
+            {
+              string_vector sv = curl.list ();
+              octave_idx_type n = 0;
+              glob_match pattern (file);
+
+
+              for (octave_idx_type i = 0; i < sv.length (); i++)
+                {
+                  if (pattern.match (sv(i)))
+                    {
+                      n++;
+
+                      time_t ftime;
+                      bool fisdir;
+                      double fsize;
+
+                      curl.get_fileinfo (sv(i), fsize, ftime, fisdir);
+
+                      if (fisdir)
+                        curl.mget_directory (sv(i), target);
+                      else
+                        {
+                          std::ofstream ofile ((target + sv(i)).c_str (),
+                                               std::ios::out |
+                                               std::ios::binary);
+
+                          if (! ofile.is_open ())
+                            {
+                              error ("__ftp_mget__: unable to open file");
+                              break;
+                            }
+
+                          unwind_protect_safe frame;
+
+                          frame.add_fcn (delete_file, target + sv(i));
+
+                          curl.get (sv(i), ofile);
+
+                          ofile.close ();
+
+                          if (curl.good ())
+                            frame.discard ();
+                        }
+
+                      if (! curl.good ())
+                        {
+                          error ("__ftp_mget__: %s", curl.lasterror().c_str());
+                          break;
+                        }
+                    }
+                }
+              if (n == 0)
+                error ("__ftp_mget__: file not found");
+            }
+          else
+            error ("__ftp_mget__: expecting file name and target as second and third arguments");
+        }
+      else
+        error ("__ftp_mget__: invalid ftp handle");
+    }
+
+  return retval;
+}