# HG changeset patch # User John W. Eaton # Date 1380816900 14400 # Node ID 8fca0bbdd4c14bb96b178e2a4824b7c0c3f9336f # Parent 77127a3badaa01b6c34073cb2435c958a29a1be6 reorganize curl wrapper class * urlwrite.cc (url_transfer): Rename from curl_object (base_url_transfer): New base class. (curl_transfer): Rename from curl_base_rep. diff -r 77127a3badaa -r 8fca0bbdd4c1 libinterp/dldfcn/urlwrite.cc --- a/libinterp/dldfcn/urlwrite.cc Thu Oct 03 09:51:56 2013 -0400 +++ b/libinterp/dldfcn/urlwrite.cc Thu Oct 03 12:15:00 2013 -0400 @@ -57,6 +57,257 @@ octave_unlink (file); } +class base_url_transfer +{ +private: + + static void reset_path (const base_url_transfer *curl_xfer) + { + curl_xfer->cwd (".."); + } + +public: + + friend class url_transfer; + + base_url_transfer (void) + : count (1), host (), userpwd (), valid (false), ascii_mode (false) { } + + base_url_transfer (const std::string& host_arg, + const std::string& /* user_arg */, + const std::string& /* passwd */) + : count (1), host (host_arg), userpwd (), valid (false), + ascii_mode (false) { } + + base_url_transfer (const std::string& /* url */, + const std::string& /* method */, + const Cell& /* param */, std::ostream& /* os */, + bool& /* retval */) + : count (1), host (), userpwd (), valid (false), ascii_mode (false) { } + + virtual ~base_url_transfer (void) { } + + bool is_valid (void) const { return valid; } + + virtual bool perform (bool /* silent */ = false) const { return false; } + + virtual std::string lasterror (void) const { return std::string (); } + + virtual void set_ostream (std::ostream& /* os */) const { } + + virtual void set_istream (std::istream& /* is */) const { } + + virtual void ascii (void) { } + + virtual void binary (void) { } + + bool is_ascii (void) const { return ascii_mode; } + + bool is_binary (void) const { return !ascii_mode; } + + virtual void cwd (const std::string& /* path */) const { } + + virtual void del (const std::string& /* file */) const { } + + virtual void rmdir (const std::string& /* path */) const { } + + virtual bool mkdir (const std::string& /* path */, bool /* silent */) const + { return false; } + + virtual void rename (const std::string& /* oldname */, + const std::string& /* newname */) const { } + + virtual void put (const std::string& /* file */, + std::istream& /* is */) const { } + + virtual void get (const std::string& /* file */, + std::ostream& /* os */) const { } + + void mget_directory (const std::string& directory, + const std::string& target) const + { + std::string sep = file_ops::dir_sep_str (); + file_stat fs (directory); + + if (!fs || !fs.is_dir ()) + { + std::string msg; + int status = octave_mkdir (directory, 0777, msg); + + if (status < 0) + { + error ("__ftp_mget__: can't create directory %s%s%s. %s", + target.c_str (), sep.c_str (), directory.c_str (), + msg.c_str ()); + + return; + } + } + + cwd (directory); + + if (! error_state) + { + unwind_protect_safe frame; + + frame.add_fcn (reset_path, this); + + string_vector sv = list (); + + for (octave_idx_type i = 0; i < sv.length (); i++) + { + time_t ftime; + bool fisdir; + double fsize; + + get_fileinfo (sv(i), fsize, ftime, fisdir); + + if (fisdir) + mget_directory (sv(i), target + directory + sep); + else + { + std::string realfile = target + directory + sep + sv(i); + + std::ofstream ofile (realfile.c_str (), + std::ios::out | std::ios::binary); + + if (! ofile.is_open ()) + { + error ("__ftp_mget__: unable to open file"); + break; + } + + unwind_protect_safe frame2; + + frame2.add_fcn (delete_file, realfile); + + get (sv(i), ofile); + + ofile.close (); + + if (!error_state) + frame2.discard (); + else + frame2.run (); + } + + if (error_state) + break; + } + } + } + + string_vector mput_directory (const std::string& base, + const std::string& directory) const + { + string_vector retval; + + std::string realdir + = (base.length () == 0 + ? directory : base + file_ops::dir_sep_str () + directory); + + if (! mkdir (directory, false)) + warning ("__ftp_mput__: can not create the remote directory ""%s""", + realdir.c_str ()); + + cwd (directory); + + if (! error_state) + { + unwind_protect_safe frame; + + frame.add_fcn (reset_path, this); + + dir_entry dirlist (realdir); + + if (dirlist) + { + string_vector files = dirlist.read (); + + for (octave_idx_type i = 0; i < files.length (); i++) + { + std::string file = files (i); + + if (file == "." || file == "..") + continue; + + std::string realfile = realdir + file_ops::dir_sep_str () + file; + file_stat fs (realfile); + + if (! fs.exists ()) + { + error ("__ftp__mput: file ""%s"" does not exist", + realfile.c_str ()); + break; + } + + if (fs.is_dir ()) + { + retval.append (mput_directory (realdir, file)); + + if (error_state) + break; + } + else + { + // FIXME Does ascii mode need to be flagged here? + std::ifstream ifile (realfile.c_str (), std::ios::in | + std::ios::binary); + + if (! ifile.is_open ()) + { + error ("__ftp_mput__: unable to open file ""%s""", + realfile.c_str ()); + break; + } + + put (file, ifile); + + ifile.close (); + + if (error_state) + break; + + retval.append (realfile); + } + } + } + else + error ("__ftp_mput__: can not read the directory ""%s""", + realdir.c_str ()); + } + + return retval; + } + + virtual void dir (void) const { } + + virtual string_vector list (void) const { return string_vector (); } + + virtual void get_fileinfo (const std::string& /* filename */, + double& /* filesize */, + time_t& /* filetime */, + bool& /* fileisdir */) const { } + + virtual std::string pwd (void) const { return std::string (); } + +protected: + + octave_refcount count; + std::string host; + std::string userpwd; + bool valid; + bool ascii_mode; + +private: + + // No copying! + + base_url_transfer (const base_url_transfer&); + + base_url_transfer& operator = (const base_url_transfer&); +}; + #ifdef HAVE_CURL #include @@ -88,11 +339,7 @@ return static_cast(size * nmemb); } -class curl_object -{ -private: - -// I'd love to rewrite this as a private method of the curl_object +// I'd love to rewrite this as a private method of the url_transfer // class, but you can't pass the va_list from the wrapper SETOPT to // the curl_easy_setopt function. #define SETOPT(option, parameter) \ @@ -120,631 +367,492 @@ } \ while (0) - class curl_object_rep - { - private: +class curl_transfer : public base_url_transfer +{ +public: - static void reset_path (const curl_object_rep *curl_rep) - { - curl_rep->cwd (".."); - } + curl_transfer (void) + : base_url_transfer (), curl (curl_easy_init ()), errnum () + { + if (curl) + valid = true; + else + error ("can not create curl object"); + } - public: - - curl_object_rep (void) - : count (1), curl (curl_easy_init ()), host (), userpwd (), - valid (false), ascii_mode (false), errnum () - { - if (! curl) + curl_transfer (const std::string& host_arg, const std::string& user_arg, + const std::string& passwd) + : base_url_transfer (host_arg, user_arg, passwd), + curl (curl_easy_init ()), errnum () + { + if (curl) + valid = true; + else + { error ("can not create curl object"); - } + return; + } + + init (user_arg, passwd, std::cin, octave_stdout); + + std::string url ("ftp://" + host_arg); + SETOPT (CURLOPT_URL, url.c_str ()); + + // Setup the link, with no transfer. + perform (); + } + + curl_transfer (const std::string& url, const std::string& method, + const Cell& param, std::ostream& os, bool& retval) + : base_url_transfer (url, method, param, os, retval), + curl (curl_easy_init ()), errnum () + { + retval = false; - curl_object_rep (const std::string& host_arg, const std::string& user_arg, - const std::string& passwd) - : count (1), curl (curl_easy_init ()), host (host_arg), - userpwd (), valid (true), ascii_mode (false), errnum () - { - if (!curl) - { - valid = false; - error ("can not create curl object"); - return; - } + if (curl) + valid = true; + else + { + error ("can not create curl object"); + return; + } + + init ("", "", std::cin, os); + + SETOPT (CURLOPT_NOBODY, 0); - init (user_arg, passwd, std::cin, octave_stdout); + // Restore the default HTTP request method to GET after setting + // NOBODY to true and back to false. This is needed for backward + // compatibility with versions of libcurl < 7.18.2. + SETOPT (CURLOPT_HTTPGET, 1); + + // Don't need to store the parameters here as we can't change + // the URL after the object is created + std::string query_string = form_query_string (param); - std::string url ("ftp://" + host_arg); + if (method == "get") + { + query_string = url + "?" + query_string; + SETOPT (CURLOPT_URL, query_string.c_str ()); + } + else if (method == "post") + { + SETOPT (CURLOPT_URL, url.c_str ()); + SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ()); + } + else SETOPT (CURLOPT_URL, url.c_str ()); - // Setup the link, with no transfer. - perform (); - } - - curl_object_rep (const std::string& url, const std::string& method, - const Cell& param, std::ostream& os, bool& retval) - : count (1), curl (curl_easy_init ()), host (), userpwd (), - valid (true), ascii_mode (false), errnum () - { - retval = false; + retval = perform (true); + } - if (!curl) - { - valid = false; - error ("can not create curl object"); - return; - } + ~curl_transfer (void) + { + if (curl) + curl_easy_cleanup (curl); + } - init ("", "", std::cin, os); - - SETOPT (CURLOPT_NOBODY, 0); + bool perform (bool silent = false) const + { + bool retval = false; - // Restore the default HTTP request method to GET after setting - // NOBODY to true and back to false. This is needed for backward - // compatibility with versions of libcurl < 7.18.2. - SETOPT (CURLOPT_HTTPGET, 1); + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; - // Don't need to store the parameters here as we can't change - // the URL after the object is created - std::string query_string = form_query_string (param); + errnum = curl_easy_perform (curl); + if (errnum != CURLE_OK) + { + if (! silent) + error ("%s", curl_easy_strerror (errnum)); + } + else + retval = true; - if (method == "get") - { - query_string = url + "?" + query_string; - SETOPT (CURLOPT_URL, query_string.c_str ()); - } - else if (method == "post") - { - SETOPT (CURLOPT_URL, url.c_str ()); - SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ()); - } - else - SETOPT (CURLOPT_URL, url.c_str ()); + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; - retval = perform (false); - } + return retval; + } - ~curl_object_rep (void) - { - if (curl) - curl_easy_cleanup (curl); - } + std::string lasterror (void) const + { + return std::string (curl_easy_strerror (errnum)); + } - bool is_valid (void) const { return valid; } - - bool perform (bool curlerror = true) const - { - bool retval = false; - - BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + void set_ostream (std::ostream& os) const + { + SETOPT (CURLOPT_WRITEDATA, static_cast (&os)); + } - errnum = curl_easy_perform (curl); - if (errnum != CURLE_OK) - { - if (curlerror) - error ("%s", curl_easy_strerror (errnum)); - } - else - retval = true; - - END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; - - return retval; - } + void set_istream (std::istream& is) const + { + SETOPT (CURLOPT_READDATA, static_cast (&is)); + } - std::string lasterror (void) const - { - return std::string (curl_easy_strerror (errnum)); - } - - void set_ostream (std::ostream& os) const - { - SETOPT (CURLOPT_WRITEDATA, static_cast (&os)); - } + void ascii (void) + { + SETOPT (CURLOPT_TRANSFERTEXT, 1); + ascii_mode = true; + } - void set_istream (std::istream& is) const - { - SETOPT (CURLOPT_READDATA, static_cast (&is)); - } - - void ascii (void) - { - SETOPT (CURLOPT_TRANSFERTEXT, 1); - ascii_mode = true; - } + void binary (void) + { + SETOPT (CURLOPT_TRANSFERTEXT, 0); + ascii_mode = false; + } - void binary (void) - { - SETOPT (CURLOPT_TRANSFERTEXT, 0); - ascii_mode = false; - } - - bool is_ascii (void) const { return ascii_mode; } - - bool is_binary (void) const { return !ascii_mode; } - - void cwd (const std::string& path) const - { - struct curl_slist *slist = 0; - std::string cmd = "cwd " + path; - slist = curl_slist_append (slist, cmd.c_str ()); - SETOPT (CURLOPT_POSTQUOTE, slist); - perform (); - SETOPT (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - } + void cwd (const std::string& path) const + { + struct curl_slist *slist = 0; + std::string cmd = "cwd " + path; + slist = curl_slist_append (slist, cmd.c_str ()); + SETOPT (CURLOPT_POSTQUOTE, slist); + perform (); + SETOPT (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - void del (const std::string& file) const - { - struct curl_slist *slist = 0; - std::string cmd = "dele " + file; - slist = curl_slist_append (slist, cmd.c_str ()); - SETOPT (CURLOPT_POSTQUOTE, slist); - perform (); - SETOPT (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - } - - void rmdir (const std::string& path) const - { - struct curl_slist *slist = 0; - std::string cmd = "rmd " + path; - slist = curl_slist_append (slist, cmd.c_str ()); - SETOPT (CURLOPT_POSTQUOTE, slist); - perform (); - SETOPT (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - } + void del (const std::string& file) const + { + struct curl_slist *slist = 0; + std::string cmd = "dele " + file; + slist = curl_slist_append (slist, cmd.c_str ()); + SETOPT (CURLOPT_POSTQUOTE, slist); + perform (); + SETOPT (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - bool mkdir (const std::string& path, bool curlerror = true) const - { - bool retval = false; - struct curl_slist *slist = 0; - std::string cmd = "mkd " + path; - slist = curl_slist_append (slist, cmd.c_str ()); - SETOPTR (CURLOPT_POSTQUOTE, slist); - retval = perform (curlerror); - SETOPTR (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - return retval; - } - - void rename (const std::string& oldname, const std::string& newname) const - { - struct curl_slist *slist = 0; - std::string cmd = "rnfr " + oldname; - slist = curl_slist_append (slist, cmd.c_str ()); - cmd = "rnto " + newname; - slist = curl_slist_append (slist, cmd.c_str ()); - SETOPT (CURLOPT_POSTQUOTE, slist); - perform (); - SETOPT (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - } + void rmdir (const std::string& path) const + { + struct curl_slist *slist = 0; + std::string cmd = "rmd " + path; + slist = curl_slist_append (slist, cmd.c_str ()); + SETOPT (CURLOPT_POSTQUOTE, slist); + perform (); + SETOPT (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - void put (const std::string& file, std::istream& is) const - { - std::string url = "ftp://" + host + "/" + file; - SETOPT (CURLOPT_URL, url.c_str ()); - SETOPT (CURLOPT_UPLOAD, 1); - SETOPT (CURLOPT_NOBODY, 0); - set_istream (is); - perform (); - set_istream (std::cin); - SETOPT (CURLOPT_NOBODY, 1); - SETOPT (CURLOPT_UPLOAD, 0); - url = "ftp://" + host; - SETOPT (CURLOPT_URL, url.c_str ()); - } + bool mkdir (const std::string& path, bool silent) const + { + bool retval = false; + struct curl_slist *slist = 0; + std::string cmd = "mkd " + path; + slist = curl_slist_append (slist, cmd.c_str ()); + SETOPTR (CURLOPT_POSTQUOTE, slist); + retval = perform (silent); + SETOPTR (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + return retval; + } - void get (const std::string& file, std::ostream& os) const - { - std::string url = "ftp://" + host + "/" + file; - SETOPT (CURLOPT_URL, url.c_str ()); - SETOPT (CURLOPT_NOBODY, 0); - set_ostream (os); - perform (); - set_ostream (octave_stdout); - SETOPT (CURLOPT_NOBODY, 1); - url = "ftp://" + host; - SETOPT (CURLOPT_URL, url.c_str ()); - } - - void mget_directory (const std::string& directory, - const std::string& target) const - { - std::string sep = file_ops::dir_sep_str (); - file_stat fs (directory); - - if (!fs || !fs.is_dir ()) - { - std::string msg; - int status = octave_mkdir (directory, 0777, msg); + void rename (const std::string& oldname, const std::string& newname) const + { + struct curl_slist *slist = 0; + std::string cmd = "rnfr " + oldname; + slist = curl_slist_append (slist, cmd.c_str ()); + cmd = "rnto " + newname; + slist = curl_slist_append (slist, cmd.c_str ()); + SETOPT (CURLOPT_POSTQUOTE, slist); + perform (); + SETOPT (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - if (status < 0) - { - error ("__ftp_mget__: can't create directory %s%s%s. %s", - target.c_str (), sep.c_str (), directory.c_str (), - msg.c_str ()); - - return; - } - } - - cwd (directory); - - if (! error_state) - { - unwind_protect_safe frame; - - frame.add_fcn (reset_path, this); - - string_vector sv = list (); - - for (octave_idx_type i = 0; i < sv.length (); i++) - { - time_t ftime; - bool fisdir; - double fsize; - - get_fileinfo (sv(i), fsize, ftime, fisdir); - - if (fisdir) - mget_directory (sv(i), target + directory + sep); - else - { - std::string realfile = target + directory + sep + sv(i); - - std::ofstream ofile (realfile.c_str (), - std::ios::out | std::ios::binary); + void put (const std::string& file, std::istream& is) const + { + std::string url = "ftp://" + host + "/" + file; + SETOPT (CURLOPT_URL, url.c_str ()); + SETOPT (CURLOPT_UPLOAD, 1); + SETOPT (CURLOPT_NOBODY, 0); + set_istream (is); + perform (); + set_istream (std::cin); + SETOPT (CURLOPT_NOBODY, 1); + SETOPT (CURLOPT_UPLOAD, 0); + url = "ftp://" + host; + SETOPT (CURLOPT_URL, url.c_str ()); + } - if (! ofile.is_open ()) - { - error ("__ftp_mget__: unable to open file"); - break; - } - - unwind_protect_safe frame2; - - frame2.add_fcn (delete_file, realfile); - - get (sv(i), ofile); - - ofile.close (); + void get (const std::string& file, std::ostream& os) const + { + std::string url = "ftp://" + host + "/" + file; + SETOPT (CURLOPT_URL, url.c_str ()); + SETOPT (CURLOPT_NOBODY, 0); + set_ostream (os); + perform (); + set_ostream (octave_stdout); + SETOPT (CURLOPT_NOBODY, 1); + url = "ftp://" + host; + SETOPT (CURLOPT_URL, url.c_str ()); + } - if (!error_state) - frame2.discard (); - else - frame2.run (); - } - - if (error_state) - break; - } - } - } + void dir (void) const + { + std::string url = "ftp://" + host + "/"; + SETOPT (CURLOPT_URL, url.c_str ()); + SETOPT (CURLOPT_NOBODY, 0); + perform (); + SETOPT (CURLOPT_NOBODY, 1); + url = "ftp://" + host; + SETOPT (CURLOPT_URL, url.c_str ()); + } - string_vector mput_directory (const std::string& base, - const std::string& directory) const - { - string_vector retval; - - std::string realdir - = (base.length () == 0 - ? directory : base + file_ops::dir_sep_str () + directory); - - if (! mkdir (directory, false)) - warning ("__ftp_mput__: can not create the remote directory ""%s""", - realdir.c_str ()); + string_vector list (void) const + { + string_vector retval; + std::ostringstream buf; + std::string url = "ftp://" + host + "/"; + SETOPTR (CURLOPT_WRITEDATA, static_cast (&buf)); + SETOPTR (CURLOPT_URL, url.c_str ()); + SETOPTR (CURLOPT_DIRLISTONLY, 1); + SETOPTR (CURLOPT_NOBODY, 0); + perform (); + SETOPTR (CURLOPT_NOBODY, 1); + url = "ftp://" + host; + SETOPTR (CURLOPT_WRITEDATA, static_cast (&octave_stdout)); + SETOPTR (CURLOPT_DIRLISTONLY, 0); + SETOPTR (CURLOPT_URL, url.c_str ()); - cwd (directory); - - if (! error_state) - { - unwind_protect_safe frame; - - frame.add_fcn (reset_path, this); - - dir_entry dirlist (realdir); - - if (dirlist) - { - string_vector files = dirlist.read (); - - for (octave_idx_type i = 0; i < files.length (); i++) - { - std::string file = files (i); - - if (file == "." || file == "..") - continue; - - std::string realfile = realdir + file_ops::dir_sep_str () + file; - file_stat fs (realfile); - - if (! fs.exists ()) - { - error ("__ftp__mput: file ""%s"" does not exist", - realfile.c_str ()); - break; - } + // Count number of directory entries + std::string str = buf.str (); + octave_idx_type n = 0; + size_t pos = 0; + while (true) + { + pos = str.find_first_of ('\n', pos); + if (pos == std::string::npos) + break; + pos++; + n++; + } + retval.resize (n); + pos = 0; + for (octave_idx_type i = 0; i < n; i++) + { + size_t newpos = str.find_first_of ('\n', pos); + if (newpos == std::string::npos) + break; - if (fs.is_dir ()) - { - retval.append (mput_directory (realdir, file)); + retval(i) = str.substr(pos, newpos - pos); + pos = newpos + 1; + } + return retval; + } - if (error_state) - break; - } - else - { - // FIXME Does ascii mode need to be flagged here? - std::ifstream ifile (realfile.c_str (), std::ios::in | - std::ios::binary); + void get_fileinfo (const std::string& filename, double& filesize, + time_t& filetime, bool& fileisdir) const + { + std::string path = pwd (); - if (! ifile.is_open ()) - { - error ("__ftp_mput__: unable to open file ""%s""", - realfile.c_str ()); - break; - } - - put (file, ifile); - - ifile.close (); + std::string url = "ftp://" + host + "/" + path + "/" + filename; + SETOPT (CURLOPT_URL, url.c_str ()); + SETOPT (CURLOPT_FILETIME, 1); + SETOPT (CURLOPT_HEADERFUNCTION, throw_away); + SETOPT (CURLOPT_WRITEFUNCTION, throw_away); - if (error_state) - break; - - retval.append (realfile); - } - } - } - else - error ("__ftp_mput__: can not read the directory ""%s""", - realdir.c_str ()); - } + // FIXME + // The MDTM command fails for a directory on the servers I tested + // so this is a means of testing for directories. It also means + // I can't get the date of directories! - return retval; - } - - void dir (void) const - { - std::string url = "ftp://" + host + "/"; - SETOPT (CURLOPT_URL, url.c_str ()); - SETOPT (CURLOPT_NOBODY, 0); - perform (); - SETOPT (CURLOPT_NOBODY, 1); - url = "ftp://" + host; - SETOPT (CURLOPT_URL, url.c_str ()); - } - - string_vector list (void) const - { - string_vector retval; - std::ostringstream buf; - std::string url = "ftp://" + host + "/"; - SETOPTR (CURLOPT_WRITEDATA, static_cast (&buf)); - SETOPTR (CURLOPT_URL, url.c_str ()); - SETOPTR (CURLOPT_DIRLISTONLY, 1); - SETOPTR (CURLOPT_NOBODY, 0); - perform (); - SETOPTR (CURLOPT_NOBODY, 1); - url = "ftp://" + host; - SETOPTR (CURLOPT_WRITEDATA, static_cast (&octave_stdout)); - SETOPTR (CURLOPT_DIRLISTONLY, 0); - SETOPTR (CURLOPT_URL, url.c_str ()); + if (! perform (true)) + { + fileisdir = true; + filetime = -1; + filesize = 0; + } + else + { + fileisdir = false; + time_t ft; + curl_easy_getinfo (curl, CURLINFO_FILETIME, &ft); + filetime = ft; + double fs; + curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs); + filesize = fs; + } - // Count number of directory entries - std::string str = buf.str (); - octave_idx_type n = 0; - size_t pos = 0; - while (true) - { - pos = str.find_first_of ('\n', pos); - if (pos == std::string::npos) - break; - pos++; - n++; - } - retval.resize (n); - pos = 0; - for (octave_idx_type i = 0; i < n; i++) - { - size_t newpos = str.find_first_of ('\n', pos); - if (newpos == std::string::npos) - break; + SETOPT (CURLOPT_WRITEFUNCTION, write_data); + SETOPT (CURLOPT_HEADERFUNCTION, 0); + SETOPT (CURLOPT_FILETIME, 0); + url = "ftp://" + host; + SETOPT (CURLOPT_URL, url.c_str ()); + + // The MDTM command seems to reset the path to the root with the + // servers I tested with, so cd again into the correct path. Make + // the path absolute so that this will work even with servers that + // don't end up in the root after an MDTM command. + cwd ("/" + path); + } - retval(i) = str.substr(pos, newpos - pos); - pos = newpos + 1; - } - return retval; - } - - void get_fileinfo (const std::string& filename, double& filesize, - time_t& filetime, bool& fileisdir) const - { - std::string path = pwd (); + std::string pwd (void) const + { + struct curl_slist *slist = 0; + std::string retval; + std::ostringstream buf; - std::string url = "ftp://" + host + "/" + path + "/" + filename; - SETOPT (CURLOPT_URL, url.c_str ()); - SETOPT (CURLOPT_FILETIME, 1); - SETOPT (CURLOPT_HEADERFUNCTION, throw_away); - SETOPT (CURLOPT_WRITEFUNCTION, throw_away); + slist = curl_slist_append (slist, "pwd"); + SETOPTR (CURLOPT_POSTQUOTE, slist); + SETOPTR (CURLOPT_HEADERFUNCTION, write_data); + SETOPTR (CURLOPT_WRITEHEADER, static_cast(&buf)); - // FIXME - // The MDTM command fails for a directory on the servers I tested - // so this is a means of testing for directories. It also means - // I can't get the date of directories! + perform (); + retval = buf.str (); - if (! perform (false)) - { - fileisdir = true; - filetime = -1; - filesize = 0; - } - else - { - fileisdir = false; - time_t ft; - curl_easy_getinfo (curl, CURLINFO_FILETIME, &ft); - filetime = ft; - double fs; - curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs); - filesize = fs; - } + // Can I assume that the path is alway in "" on the last line + size_t pos2 = retval.rfind ('"'); + size_t pos1 = retval.rfind ('"', pos2 - 1); + retval = retval.substr (pos1 + 1, pos2 - pos1 - 1); + + SETOPTR (CURLOPT_HEADERFUNCTION, 0); + SETOPTR (CURLOPT_WRITEHEADER, 0); + SETOPTR (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); - SETOPT (CURLOPT_WRITEFUNCTION, write_data); - SETOPT (CURLOPT_HEADERFUNCTION, 0); - SETOPT (CURLOPT_FILETIME, 0); - url = "ftp://" + host; - SETOPT (CURLOPT_URL, url.c_str ()); + return retval; + } + +private: - // The MDTM command seems to reset the path to the root with the - // servers I tested with, so cd again into the correct path. Make - // the path absolute so that this will work even with servers that - // don't end up in the root after an MDTM command. - cwd ("/" + path); - } + CURL *curl; + mutable CURLcode errnum; + + // No copying! + + curl_transfer (const curl_transfer&); - std::string pwd (void) const - { - struct curl_slist *slist = 0; - std::string retval; - std::ostringstream buf; + curl_transfer& operator = (const curl_transfer&); - slist = curl_slist_append (slist, "pwd"); - SETOPTR (CURLOPT_POSTQUOTE, slist); - SETOPTR (CURLOPT_HEADERFUNCTION, write_data); - SETOPTR (CURLOPT_WRITEHEADER, static_cast(&buf)); - - perform (); - retval = buf.str (); - - // Can I assume that the path is alway in "" on the last line - size_t pos2 = retval.rfind ('"'); - size_t pos1 = retval.rfind ('"', pos2 - 1); - retval = retval.substr (pos1 + 1, pos2 - pos1 - 1); + void init (const std::string& user, const std::string& passwd, + std::istream& is, std::ostream& os) + { + // No data transfer by default + SETOPT (CURLOPT_NOBODY, 1); - SETOPTR (CURLOPT_HEADERFUNCTION, 0); - SETOPTR (CURLOPT_WRITEHEADER, 0); - SETOPTR (CURLOPT_POSTQUOTE, 0); - curl_slist_free_all (slist); - - return retval; - } - - octave_refcount count; + // Set the username and password + userpwd = user; + if (! passwd.empty ()) + userpwd += ":" + passwd; + if (! userpwd.empty ()) + SETOPT (CURLOPT_USERPWD, userpwd.c_str ()); - private: + // Define our callback to get called when there's data to be written. + SETOPT (CURLOPT_WRITEFUNCTION, write_data); - CURL *curl; - std::string host; - std::string userpwd; - bool valid; - bool ascii_mode; - mutable CURLcode errnum; + // Set a pointer to our struct to pass to the callback. + SETOPT (CURLOPT_WRITEDATA, static_cast (&os)); - // No copying! + // Define our callback to get called when there's data to be read + SETOPT (CURLOPT_READFUNCTION, read_data); - curl_object_rep (const curl_object_rep& ov); - - curl_object_rep& operator = (const curl_object_rep&); + // Set a pointer to our struct to pass to the callback. + SETOPT (CURLOPT_READDATA, static_cast (&is)); - void init (const std::string& user, const std::string& passwd, - std::istream& is, std::ostream& os) - { - // No data transfer by default - SETOPT (CURLOPT_NOBODY, 1); + // Follow redirects. + SETOPT (CURLOPT_FOLLOWLOCATION, true); + + // Don't use EPSV since connecting to sites that don't support it + // will hang for some time (3 minutes?) before moving on to try PASV + // instead. + SETOPT (CURLOPT_FTP_USE_EPSV, false); - // Set the username and password - userpwd = user; - if (! passwd.empty ()) - userpwd += ":" + passwd; - if (! userpwd.empty ()) - SETOPT (CURLOPT_USERPWD, userpwd.c_str ()); - - // Define our callback to get called when there's data to be written. - SETOPT (CURLOPT_WRITEFUNCTION, write_data); + SETOPT (CURLOPT_NOPROGRESS, true); + SETOPT (CURLOPT_FAILONERROR, true); - // Set a pointer to our struct to pass to the callback. - SETOPT (CURLOPT_WRITEDATA, static_cast (&os)); - - // Define our callback to get called when there's data to be read - SETOPT (CURLOPT_READFUNCTION, read_data); + SETOPT (CURLOPT_POSTQUOTE, 0); + SETOPT (CURLOPT_QUOTE, 0); + } - // Set a pointer to our struct to pass to the callback. - SETOPT (CURLOPT_READDATA, static_cast (&is)); - - // Follow redirects. - SETOPT (CURLOPT_FOLLOWLOCATION, true); + std::string form_query_string (const Cell& param) + { + std::ostringstream query; - // Don't use EPSV since connecting to sites that don't support it - // will hang for some time (3 minutes?) before moving on to try PASV - // instead. - SETOPT (CURLOPT_FTP_USE_EPSV, false); - - SETOPT (CURLOPT_NOPROGRESS, true); - SETOPT (CURLOPT_FAILONERROR, true); + for (int i = 0; i < param.numel (); i += 2) + { + std::string name = param(i).string_value (); + std::string text = param(i+1).string_value (); - SETOPT (CURLOPT_POSTQUOTE, 0); - SETOPT (CURLOPT_QUOTE, 0); - } + // Encode strings. + char *enc_name = curl_easy_escape (curl, name.c_str (), + name.length ()); + char *enc_text = curl_easy_escape (curl, text.c_str (), + text.length ()); - std::string form_query_string (const Cell& param) - { - std::ostringstream query; + query << enc_name << "=" << enc_text; - for (int i = 0; i < param.numel (); i += 2) - { - std::string name = param(i).string_value (); - std::string text = param(i+1).string_value (); + curl_free (enc_name); + curl_free (enc_text); - // Encode strings. - char *enc_name = curl_easy_escape (curl, name.c_str (), - name.length ()); - char *enc_text = curl_easy_escape (curl, text.c_str (), - text.length ()); + if (i < param.numel ()-1) + query << "&"; + } - query << enc_name << "=" << enc_text; - - curl_free (enc_name); - curl_free (enc_text); + query.flush (); - if (i < param.numel ()-1) - query << "&"; - } + return query.str (); + } +}; - query.flush (); - - return query.str (); - } #undef SETOPT #undef SETOPTR - }; + +#endif +static void +disabled_error (void) +{ + error ("support for url transfers was disabled when Octave was built"); +} + +class url_transfer +{ public: - curl_object (void) : rep (new curl_object_rep ()) { } +#if defined (HAVE_CURL) +# define REP_CLASS curl_transfer +#else +# define REP_CLASS base_url_transfer +#endif - curl_object (const std::string& host, const std::string& user, - const std::string& passwd) - : rep (new curl_object_rep (host, user, passwd)) { } + url_transfer (void) : rep (new REP_CLASS ()) + { +#if !defined (HAVE_CURL) + disabled_error (); +#endif + } - curl_object (const std::string& url, const std::string& method, - const Cell& param, std::ostream& os, bool& retval) - : rep (new curl_object_rep (url, method, param, os, retval)) { } + url_transfer (const std::string& host, const std::string& user, + const std::string& passwd) + : rep (new REP_CLASS (host, user, passwd)) + { +#if !defined (HAVE_CURL) + disabled_error (); +#endif + } - curl_object (const curl_object& h) : rep (h.rep) + url_transfer (const std::string& url, const std::string& method, + const Cell& param, std::ostream& os, bool& retval) + : rep (new REP_CLASS (url, method, param, os, retval)) + { +#if !defined (HAVE_CURL) + disabled_error (); +#endif + } + +#undef REP_CLASS + + url_transfer (const url_transfer& h) : rep (h.rep) { rep->count++; } - ~curl_object (void) + ~url_transfer (void) { if (--rep->count == 0) delete rep; } - curl_object& operator = (const curl_object& h) + url_transfer& operator = (const url_transfer& h) { if (this != &h) { @@ -780,9 +888,9 @@ void rmdir (const std::string& path) const { rep->rmdir (path); } - bool mkdir (const std::string& path, bool curlerror = true) const + bool mkdir (const std::string& path, bool silent = false) const { - return rep->mkdir (path, curlerror); + return rep->mkdir (path, silent); } void rename (const std::string& oldname, const std::string& newname) const @@ -826,7 +934,7 @@ private: - curl_object_rep *rep; + base_url_transfer *rep; }; typedef octave_handle curl_handle; @@ -885,19 +993,19 @@ ? lookup (val.double_value ()) : curl_handle (); } - static curl_object get_object (double val) + static url_transfer get_object (double val) { return get_object (lookup (val)); } - static curl_object get_object (const octave_value& val) + static url_transfer get_object (const octave_value& val) { return get_object (lookup (val)); } - static curl_object get_object (const curl_handle& h) + static url_transfer get_object (const curl_handle& h) { - return instance_ok () ? instance->do_get_object (h) : curl_object (); + return instance_ok () ? instance->do_get_object (h) : url_transfer (); } static curl_handle make_curl_handle (const std::string& host, @@ -917,14 +1025,14 @@ static ch_manager *instance; - typedef std::map::iterator iterator; - typedef std::map::const_iterator const_iterator; + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; typedef std::set::iterator free_list_iterator; typedef std::set::const_iterator const_free_list_iterator; // A map of handles to curl objects. - std::map handle_map; + std::map handle_map; // The available curl handles. std::set handle_free_list; @@ -943,11 +1051,11 @@ return (p != handle_map.end ()) ? p->first : curl_handle (); } - curl_object do_get_object (const curl_handle& h) + 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 : curl_object (); + return (p != handle_map.end ()) ? p->second : url_transfer (); } curl_handle do_make_curl_handle (const std::string& host, @@ -956,16 +1064,12 @@ { curl_handle h = get_handle (); - curl_object obj (host, user, passwd); + url_transfer obj (host, user, passwd); if (! error_state) handle_map[h] = obj; else - { - do_free (h); - - h = curl_handle (); - } + h = curl_handle (); return h; } @@ -1055,8 +1159,6 @@ ch_manager *ch_manager::instance = 0; -#endif - DEFUN_DLD (urlwrite, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Loadable Function} {} urlwrite (@var{url}, @var{localfile})\n\ @@ -1106,8 +1208,6 @@ { octave_value_list retval; -#ifdef HAVE_CURL - int nargin = args.length (); // verify arguments @@ -1188,38 +1288,34 @@ frame.add_fcn (delete_file, filename); bool ok; - curl_object curl = curl_object (url, method, param, ofile, ok); + url_transfer curl = url_transfer (url, method, param, ofile, ok); ofile.close (); - if (!error_state) - frame.discard (); - else - frame.run (); + if (! error_state) + { + frame.discard (); - if (nargout > 0) - { - if (ok) + if (nargout > 0) { - retval(2) = std::string (); - retval(1) = true; - retval(0) = octave_env::make_absolute (filename); + if (ok) + { + 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 (); + } } - else - { - retval(2) = curl.lasterror (); - retval(1) = false; - retval(0) = std::string (); - } + + if (nargout < 2 && ! ok) + error ("urlwrite: curl: %s", curl.lasterror ().c_str ()); } - if (nargout < 2 && ! ok) - error ("urlwrite: curl: %s", curl.lasterror ().c_str ()); - -#else - gripe_disabled_feature ("urlwrite", "urlwrite"); -#endif - return retval; } @@ -1266,8 +1362,6 @@ // Octave's return value octave_value_list retval; -#ifdef HAVE_CURL - int nargin = args.length (); // verify arguments @@ -1322,22 +1416,21 @@ std::ostringstream buf; bool ok; - curl_object curl = curl_object (url, method, param, buf, ok); + url_transfer curl = url_transfer (url, method, param, buf, ok); - if (nargout > 0) + if (! error_state) { - // Return empty string if no error occured. - retval(2) = ok ? "" : curl.lasterror (); - retval(1) = ok; - retval(0) = buf.str (); - } + if (nargout > 0) + { + // Return empty string if no error occured. + retval(2) = ok ? "" : curl.lasterror (); + retval(1) = ok; + retval(0) = buf.str (); + } - if (nargout < 2 && ! ok) - error ("urlread: curl: %s", curl.lasterror().c_str()); - -#else - gripe_disabled_feature ("urlread", "urlread"); -#endif + if (nargout < 2 && ! ok) + error ("urlread: curl: %s", curl.lasterror().c_str()); + } return retval; } @@ -1351,7 +1444,6 @@ { octave_value retval; -#ifdef HAVE_CURL int nargin = args.length (); std::string host; std::string user = "anonymous"; @@ -1380,9 +1472,6 @@ retval = ch.value (); } } -#else - gripe_disabled_feature ("__ftp__", "FTP"); -#endif return retval; } @@ -1394,23 +1483,23 @@ @end deftypefn") { octave_value retval; -#ifdef HAVE_CURL + int nargin = args.length (); if (nargin != 1) error ("__ftp_pwd__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const 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"); } -#else - gripe_disabled_feature ("__ftp_pwd__", "FTP"); -#endif return retval; } @@ -1421,14 +1510,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 1 && nargin != 2) error ("__ftp_cwd__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1445,11 +1538,8 @@ else error ("__ftp_cwd__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_cwd__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_dir__, args, nargout, @@ -1459,14 +1549,17 @@ @end deftypefn") { octave_value retval; -#ifdef HAVE_CURL + int nargin = args.length (); if (nargin != 1) error ("__ftp_dir__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1526,9 +1619,6 @@ else error ("__ftp_dir__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_dir__", "FTP"); -#endif return retval; } @@ -1539,25 +1629,26 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 1) error ("__ftp_ascii__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const 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"); } -#else - gripe_disabled_feature ("__ftp_ascii__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_binary__, args, , @@ -1566,25 +1657,26 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 1) error ("__ftp_binary__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const 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"); } -#else - gripe_disabled_feature ("__ftp_binary__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_close__, args, , @@ -1593,7 +1685,8 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 1) @@ -1602,16 +1695,16 @@ { 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"); } -#else - gripe_disabled_feature ("__ftp_close__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_mode__, args, , @@ -1621,23 +1714,23 @@ @end deftypefn") { octave_value retval; -#ifdef HAVE_CURL + int nargin = args.length (); if (nargin != 1) error ("__ftp_mode__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const 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"); } -#else - gripe_disabled_feature ("__ftp_mode__", "FTP"); -#endif return retval; } @@ -1648,14 +1741,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 2) error ("__ftp_delete__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1669,11 +1766,8 @@ else error ("__ftp_delete__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_delete__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_rmdir__, args, , @@ -1682,14 +1776,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 2) error ("__ftp_rmdir__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1703,11 +1801,8 @@ else error ("__ftp_rmdir__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_rmdir__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_mkdir__, args, , @@ -1716,14 +1811,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 2) error ("__ftp_mkdir__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1737,11 +1836,8 @@ else error ("__ftp_mkdir__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_mkdir__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_rename__, args, , @@ -1750,14 +1846,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 3) error ("__ftp_rename__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1772,11 +1872,8 @@ else error ("__ftp_rename__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_rename__", "FTP"); -#endif - return octave_value (); + return retval; } DEFUN_DLD (__ftp_mput__, args, nargout, @@ -1785,16 +1882,18 @@ Undocumented internal function\n\ @end deftypefn") { - string_vector retval; + octave_value retval; -#ifdef HAVE_CURL int nargin = args.length (); if (nargin != 2) error ("__ftp_mput__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1802,6 +1901,8 @@ if (! error_state) { + string_vector result; + glob_match pattern (file_ops::tilde_expand (pat)); string_vector files = pattern.glob (); @@ -1819,7 +1920,7 @@ if (fs.is_dir ()) { - retval.append (curl.mput_directory ("", file)); + result.append (curl.mput_directory ("", file)); if (error_state) break; } @@ -1842,9 +1943,12 @@ if (error_state) break; - retval.append (file); + result.append (file); } } + + if (nargout > 0) + retval = result; } else error ("__ftp_mput__: expecting file name patter as second argument"); @@ -1852,11 +1956,8 @@ else error ("__ftp_mput__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_mput__", "FTP"); -#endif - return (nargout > 0 ? octave_value (retval) : octave_value ()); + return retval; } DEFUN_DLD (__ftp_mget__, args, , @@ -1865,14 +1966,18 @@ Undocumented internal function\n\ @end deftypefn") { -#ifdef HAVE_CURL + octave_value retval; + int nargin = args.length (); if (nargin != 2 && nargin != 3) error ("__ftp_mget__: incorrect number of arguments"); else { - const curl_object curl = ch_manager::get_object (args(0)); + const url_transfer curl = ch_manager::get_object (args(0)); + + if (error_state) + return retval; if (curl.is_valid ()) { @@ -1941,9 +2046,6 @@ else error ("__ftp_mget__: invalid ftp handle"); } -#else - gripe_disabled_feature ("__ftp_mget__", "FTP"); -#endif - return octave_value (); + return retval; }