Mercurial > hg > octave-nkf
diff src/DLD-FUNCTIONS/urlwrite.cc @ 9880:7f77e5081e83
Add ftp objects
author | David Bateman <dbateman@free.fr> |
---|---|
date | Sat, 28 Nov 2009 11:44:57 +0100 |
parents | 7c02ec148a3c |
children | dd3fc8ba4796 |
line wrap: on
line diff
--- a/src/DLD-FUNCTIONS/urlwrite.cc +++ b/src/DLD-FUNCTIONS/urlwrite.cc @@ -2,6 +2,7 @@ /* Copyright (C) 2006, 2007, 2008 Alexander Barth +Copyright (C) 2009 David Bateman This file is part of Octave. @@ -31,16 +32,20 @@ #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 "glob-match.h" #include "defun-dld.h" #include "error.h" #include "oct-obj.h" #include "ov-cell.h" #include "pager.h" +#include "oct-map.h" #include "unwind-prot.h" #if defined (HAVE_CURL) @@ -49,163 +54,636 @@ #include <curl/types.h> #include <curl/easy.h> -// Write callback function for curl. - -static int +static int write_data (void *buffer, size_t size, size_t nmemb, void *streamp) { - // *stream is actually an ostream object. std::ostream& stream = *(static_cast<std::ostream*> (streamp)); stream.write (static_cast<const char*> (buffer), size*nmemb); return (stream.fail () ? 0 : size * nmemb); } -// Form the query string based on param. - -static std::string -form_query_string (CURL *curl, const Cell& param) +static int +read_data (void *buffer, size_t size, size_t nmemb, void *streamp) { - std::ostringstream query; - - for (int i = 0; i < param.numel (); i += 2) - { - std::string name = param(i).string_value (); - std::string text = param(i+1).string_value (); - - // 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 ()); - - query << enc_name << "=" << enc_text; - - curl_free (enc_name); - curl_free (enc_text); - - if (i < param.numel()-1) - query << "&"; - } - - query.flush (); - - return query.str (); + std::istream& stream = *(static_cast<std::istream*> (streamp)); + stream.read (static_cast<char*> (buffer), size*nmemb); + if (stream.eof ()) + return stream.gcount (); + else + return (stream.fail () ? 0 : size * nmemb); } -// curl front-end - -static void -urlget_cleanup (CURL *curl) +static size_t +throw_away (void *, size_t size, size_t nmemb, void *) { - curl_easy_cleanup (curl); - curl_global_cleanup (); + return static_cast<size_t>(size * nmemb); } -static CURLcode -urlget (const std::string& url, const std::string& method, - const Cell& param, std::ostream& stream) +class +curl_handle { - CURL *curl; +private: + class + curl_handle_rep + { + public: + curl_handle_rep (void) : count (1), valid (true), ascii (false) + { + curl = curl_easy_init (); + if (!curl) + error ("can not create curl handle"); + } + + ~curl_handle_rep (void) + { + if (curl) + curl_easy_cleanup (curl); + } + + bool is_valid (void) const + { + return valid; + } + + bool perform (bool curlerror) const + { + bool retval = false; + if (!error_state) + { + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode res = curl_easy_perform (curl); + if (res != CURLE_OK) + { + if (curlerror) + error ("%s", curl_easy_strerror (res)); + } + else + retval = true; + + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + } + return retval; + } - curl = curl_easy_init(); + CURL* handle (void) const + { + return curl; + } + + bool is_ascii (void) const + { + return ascii; + } + + bool is_binary (void) const + { + return !ascii; + } + + size_t count; + std::string host; + bool valid; + bool ascii; + + private: + CURL *curl; - if (! curl) - return CURLE_FAILED_INIT; + // No copying! + + curl_handle_rep (const curl_handle_rep& ov); + + curl_handle_rep& operator = (const curl_handle_rep&); + }; + +public: - // handle paramters of GET or POST request +// I'd love to rewrite this as a private method of the curl_handle +// class, but you can't pass the va_list from the wrapper setopt to +// the curl_easy_setopt function. +#define setopt(option, parameter) \ + { \ + CURLcode res = curl_easy_setopt (rep->handle (), option, parameter); \ + if (res != CURLE_OK) \ + error ("%s", curl_easy_strerror (res)); \ + } + + curl_handle (void) : rep (new curl_handle_rep ()) + { + rep->valid = false; + } + + curl_handle (const std::string& _host, const std::string& user, + const std::string& passwd) : + rep (new curl_handle_rep ()) + { + rep->host = _host; + init (user, passwd, std::cin, octave_stdout); + + std::string url = "ftp://" + _host; + setopt (CURLOPT_URL, url.c_str()); - std::string query_string = form_query_string (curl,param); - //octave_stdout << "query_string " << query_string << std::endl; + // Setup the link, with no transfer + if (!error_state) + perform (); + } + + curl_handle (const std::string& url, const std::string& method, + const Cell& param, std::ostream& os, bool& retval) : + rep (new curl_handle_rep ()) + { + retval = false; + + init ("", "", std::cin, os); + + setopt (CURLOPT_NOBODY, 0); + + // Don't need to store the parameters here as we can't change + // the URL after the handle is created + std::string query_string = form_query_string (param); - if (method == "get") + 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()); + + if (!error_state) + retval = perform (false); + } + + curl_handle (const curl_handle& h) : rep (h.rep) { - query_string = url + "?" + query_string; - curl_easy_setopt (curl, CURLOPT_URL, query_string.c_str ()); + rep->count++; + } + + ~curl_handle (void) + { + if (--rep->count == 0) + delete rep; + } + + curl_handle& operator = (const curl_handle& h) + { + if (this != &h) + { + if (--rep->count == 0) + delete rep; + + rep = h.rep; + rep->count++; + } + return *this; + } + + bool is_valid (void) const + { + return rep->is_valid (); } - else if (method == "post") + + std::string lasterror (void) const + { + CURLcode errno; + + curl_easy_getinfo (rep->handle(), CURLINFO_OS_ERRNO, &errno); + + return std::string (curl_easy_strerror (errno)); + } + + void set_ostream (std::ostream& os) const + { + setopt (CURLOPT_WRITEDATA, static_cast<void*> (&os)); + } + + void set_istream (std::istream& is) const { - curl_easy_setopt (curl, CURLOPT_URL, url.c_str ()); - curl_easy_setopt (curl, CURLOPT_POSTFIELDS, query_string.c_str ()); + setopt (CURLOPT_READDATA, static_cast<void*> (&is)); + } + + void ascii (void) const + { + setopt (CURLOPT_TRANSFERTEXT, 1); + rep->ascii = true; + } + + void binary (void) const + { + setopt (CURLOPT_TRANSFERTEXT, 0); + rep->ascii = false; + } + + bool is_ascii (void) const + { + return rep->is_ascii (); + } + + bool is_binary (void) const + { + return rep->is_binary (); } - else - curl_easy_setopt (curl, CURLOPT_URL, url.c_str()); + + 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); + if (! error_state) + perform (); + setopt (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - // Define our callback to get called when there's data to be written. - curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data); + 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); + if (! error_state) + 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); + if (! error_state) + perform (); + setopt (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } - // Set a pointer to our struct to pass to the callback. - curl_easy_setopt (curl, CURLOPT_WRITEDATA, static_cast<void*> (&stream)); + 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()); + setopt (CURLOPT_POSTQUOTE, slist); + if (! error_state) + retval = perform (curlerror); + setopt (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + return retval; + } - // Follow redirects. - curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, true); + 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); + if (! error_state) + perform (); + setopt (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + } + + void put (const std::string& file, std::istream& is) const + { + std::string url = "ftp://" + rep->host + "/" + file; + setopt (CURLOPT_URL, url.c_str()); + setopt (CURLOPT_UPLOAD, 1); + setopt (CURLOPT_NOBODY, 0); + set_istream (is); + if (! error_state) + perform (); + set_istream (std::cin); + setopt (CURLOPT_NOBODY, 1); + setopt (CURLOPT_UPLOAD, 0); + url = "ftp://" + rep->host; + setopt (CURLOPT_URL, url.c_str()); + } - // 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. - curl_easy_setopt (curl, CURLOPT_FTP_USE_EPSV, false); + void get (const std::string& file, std::ostream& os) const + { + std::string url = "ftp://" + rep->host + "/" + file; + setopt (CURLOPT_URL, url.c_str()); + setopt (CURLOPT_NOBODY, 0); + set_ostream (os); + if (! error_state) + perform (); + set_ostream (octave_stdout); + setopt (CURLOPT_NOBODY, 1); + url = "ftp://" + rep->host; + setopt (CURLOPT_URL, url.c_str()); + } + + void dir (void) const + { + std::string url = "ftp://" + rep->host + "/"; + setopt (CURLOPT_URL, url.c_str()); + setopt (CURLOPT_NOBODY, 0); + if (! error_state) + perform (); + setopt (CURLOPT_NOBODY, 1); + url = "ftp://" + rep->host; + setopt (CURLOPT_URL, url.c_str()); + } - curl_easy_setopt (curl, CURLOPT_NOPROGRESS, true); - curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, url.c_str ()); - curl_easy_setopt (curl, CURLOPT_FAILONERROR, true); + string_vector list (void) const + { + std::ostringstream buf; + std::string url = "ftp://" + rep->host + "/"; + setopt (CURLOPT_WRITEDATA, static_cast<void*> (&buf)); + setopt (CURLOPT_URL, url.c_str()); + setopt (CURLOPT_DIRLISTONLY, 1); + setopt (CURLOPT_NOBODY, 0); + if (! error_state) + perform (); + setopt (CURLOPT_NOBODY, 1); + url = "ftp://" + rep->host; + setopt (CURLOPT_WRITEDATA, static_cast<void*> (&octave_stdout)); + setopt (CURLOPT_DIRLISTONLY, 0); + setopt (CURLOPT_URL, url.c_str()); + + // 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++; + } + string_vector retval (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; + + 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(); - // Switch on full protocol/debug output. - // curl_easy_setopt (curl, CURLOPT_VERBOSE, true); + std::string url = "ftp://" + rep->host + "/" + path + "/" + filename; + setopt (CURLOPT_URL, url.c_str()); + setopt (CURLOPT_FILETIME, 1); + setopt (CURLOPT_HEADERFUNCTION, throw_away); + setopt (CURLOPT_WRITEFUNCTION, throw_away); - CURLcode res = CURLE_OK; + // 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! + if (! error_state) + { + if (! perform (false)) + { + fileisdir = true; + filetime = -1; + filesize = 0; + } + else + { + fileisdir = false; + time_t ft; + curl_easy_getinfo(rep->handle (), CURLINFO_FILETIME, &ft); + filetime = ft; + double fs; + curl_easy_getinfo(rep->handle (), + CURLINFO_CONTENT_LENGTH_DOWNLOAD, &fs); + filesize = fs; + } + } - // To understand the following, see the definitions of these macros - // in libcruft/misc/quit.h. The idea is that we call sigsetjmp here - // then the signal handler calls siglongjmp to get back here - // immediately. Then we perform some cleanup and throw an interrupt - // exception which will get us back to the top level, cleaning up - // any local C++ objects on the stack as we go. + setopt (CURLOPT_WRITEFUNCTION, write_data); + setopt (CURLOPT_HEADERFUNCTION, 0); + setopt (CURLOPT_FILETIME, 0); + url = "ftp://" + rep->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); + } + + std::string pwd (void) const + { + struct curl_slist *slist = 0; + std::string retval; + std::ostringstream buf; - BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_1; + slist = curl_slist_append (slist, "pwd"); + setopt (CURLOPT_POSTQUOTE, slist); + setopt (CURLOPT_HEADERFUNCTION, write_data); + setopt (CURLOPT_WRITEHEADER, static_cast<void *>(&buf)); + + if (! error_state) + { + 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); + } + setopt (CURLOPT_HEADERFUNCTION, 0); + setopt (CURLOPT_WRITEHEADER, 0); + setopt (CURLOPT_POSTQUOTE, 0); + curl_slist_free_all (slist); + + return retval; + } - // We were interrupted (this code is inside a block that is only - // called when siglongjmp is called from a signal handler). + bool perform (bool curlerror = true) const + { + return rep->perform (curlerror); + } + +private: + curl_handle_rep *rep; + + std::string form_query_string (const Cell& param) + { + std::ostringstream query; + + for (int i = 0; i < param.numel (); i += 2) + { + std::string name = param(i).string_value (); + std::string text = param(i+1).string_value (); + + // Encode strings. + char *enc_name = curl_easy_escape (rep->handle(), name.c_str (), + name.length ()); + char *enc_text = curl_easy_escape (rep->handle(), text.c_str (), + text.length ()); + + query << enc_name << "=" << enc_text; + + curl_free (enc_name); + curl_free (enc_text); + + if (i < param.numel()-1) + query << "&"; + } + + query.flush (); - // Is there a better error code to use? Maybe it doesn't matter - // because we are about to throw an execption. + return query.str (); + } + + 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); + + // Set the username and password + if (user.length () != 0) + setopt (CURLOPT_USERNAME, user.c_str()); + if (passwd.length () != 0) + setopt (CURLOPT_PASSWORD, passwd.c_str()); + + // Define our callback to get called when there's data to be written. + setopt (CURLOPT_WRITEFUNCTION, write_data); - res = CURLE_ABORTED_BY_CALLBACK; - urlget_cleanup (curl); - octave_rethrow_exception (); + // Set a pointer to our struct to pass to the callback. + setopt (CURLOPT_WRITEDATA, static_cast<void*> (&os)); + + // Define our callback to get called when there's data to be read + setopt (CURLOPT_READFUNCTION, read_data); + + // Set a pointer to our struct to pass to the callback. + setopt (CURLOPT_READDATA, static_cast<void*> (&is)); + + // 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); + + setopt (CURLOPT_NOPROGRESS, true); + setopt (CURLOPT_FAILONERROR, true); - BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE_2; + setopt (CURLOPT_POSTQUOTE, 0); + setopt (CURLOPT_QUOTE, 0); + } + +#undef setopt +}; + +class +curl_handles +{ +public: + + typedef std::map<std::string, curl_handle>::iterator iterator; + typedef std::map<std::string, curl_handle>::const_iterator const_iterator; - res = curl_easy_perform (curl); + curl_handles (void) : map () + { + curl_global_init(CURL_GLOBAL_DEFAULT); + } + + ~curl_handles (void) + { + // Remove the elements of the map explicitly as they should + // be deleted before the call to curl_global_cleanup + for (iterator pa = begin (); pa != end (); pa++) + map.erase (pa); - END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + curl_global_cleanup (); + } + + iterator begin (void) { return iterator (map.begin ()); } + const_iterator begin (void) const { return const_iterator (map.begin ()); } + + iterator end (void) { return iterator (map.end ()); } + const_iterator end (void) const { return const_iterator (map.end ()); } - // If we are not interuppted, we will end up here, so we still need - // to clean up. + iterator seek (const std::string& k) { return map.find (k); } + const_iterator seek (const std::string& k) const { return map.find (k); } + + std::string key (const_iterator p) const { return p->first; } + + curl_handle& contents (const std::string& k) + { + return map[k]; + } + + curl_handle contents (const std::string& k) const + { + const_iterator p = seek (k); + return p != end () ? p->second : curl_handle (); + } + + curl_handle& contents (iterator p) + { return p->second; } - urlget_cleanup (curl); + curl_handle contents (const_iterator p) const + { return p->second; } + + void del (const std::string& k) + { + iterator p = map.find (k); + + if (p != map.end ()) + map.erase (p); + } - return res; +private: + std::map<std::string, curl_handle> map; +}; + +static curl_handles handles; + +static void +cleanup_urlwrite (std::string filename) +{ + file_ops::unlink (filename); } -#endif - -static bool urlwrite_delete_file; - -static std::string urlwrite_filename; +static void +reset_path (const curl_handle curl) +{ + curl.cwd (".."); +} -static void -urlwrite_cleanup_file (void *) +void delete_file (std::string file) { - if (urlwrite_delete_file) - file_ops::unlink (urlwrite_filename); + file_ops::unlink (file); } +#endif DEFUN_DLD (urlwrite, args, nargout, "-*- texinfo -*-\n\ -@deftypefn {Loadable Function} {} urlwrite (@var{URL}, @var{localfile})\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\ +Download a remote file specified by its @var{url} and save it as\n\ @var{localfile}. For example,\n\ \n\ @example\n\ @@ -270,8 +748,6 @@ // name to store the file if download is succesful std::string filename = args(1).string_value(); - urlwrite_filename = filename; - if (error_state) { error ("urlwrite: localfile must be a character string"); @@ -319,8 +795,6 @@ file_stat fs (filename); - urlwrite_delete_file = ! fs.exists (); - std::ofstream ofile (filename.c_str(), std::ios::out | std::ios::binary); if (! ofile.is_open ()) @@ -329,19 +803,23 @@ return retval; } - unwind_protect::add (urlwrite_cleanup_file); + unwind_protect::frame_id_t uwp_frame = unwind_protect::begin_frame (); - CURLcode res = urlget (url, method, param, ofile); + unwind_protect::add_fcn (cleanup_urlwrite, filename); + + bool res; + curl_handle curl = curl_handle (url, method, param, ofile, res); ofile.close (); - urlwrite_delete_file = (res != CURLE_OK); - - unwind_protect::run (); + if (!error_state) + unwind_protect::discard_frame (uwp_frame); + else + unwind_protect::run_frame (uwp_frame); if (nargout > 0) { - if (res == CURLE_OK) + if (res) { retval(2) = std::string (); retval(1) = true; @@ -349,14 +827,14 @@ } else { - retval(2) = std::string (curl_easy_strerror (res)); + retval(2) = curl.lasterror (); retval(1) = false; retval(0) = std::string (); } } - if (nargout < 2 && res != CURLE_OK) - error ("urlwrite: curl: %s", curl_easy_strerror (res)); + if (nargout < 2 && res) + error ("urlwrite: curl: %s", curl.lasterror ().c_str ()); #else error ("urlwrite: not available in this version of Octave"); @@ -371,7 +849,7 @@ @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\ +Download a remote file specified by its @var{url} and return its content\n\ in string @var{s}. For example,\n\ \n\ @example\n\ @@ -463,18 +941,19 @@ std::ostringstream buf; - CURLcode res = urlget (url, method, param, buf); + bool res; + curl_handle curl = curl_handle (url, method, param, buf, res); if (nargout > 0) { retval(0) = buf.str (); - retval(1) = res == CURLE_OK; + retval(1) = res; // Return empty string if no error occured. - retval(2) = std::string (res == CURLE_OK ? "" : curl_easy_strerror (res)); + retval(2) = res ? "" : curl.lasterror (); } - if (nargout < 2 && res != CURLE_OK) - error ("urlread: curl: %s", curl_easy_strerror (res)); + if (nargout < 2 && !res) + error ("urlread: curl: %s", curl.lasterror().c_str()); #else error ("urlread: not available in this version of Octave"); @@ -482,3 +961,782 @@ return retval; } + +DEFUN_DLD (__ftp__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp__ (@var{handle}, @var{host})\n\ +@deftypefnx {Loadable Function} {} __ftp__ (@var{handle}, @var{host}, @var{username}, @var{password})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + std::string handle; + std::string host; + std::string user = "anonymous"; + std::string passwd = ""; + + if (nargin < 2 || nargin > 4) + error ("incorrect number of arguments"); + else + { + handle = args(0).string_value (); + host = args(1).string_value (); + + if (nargin > 1) + user = args(2).string_value (); + + if (nargin > 2) + passwd = args(3).string_value (); + + if (!error_state) + { + handles.contents (handle) = curl_handle (host, user, passwd); + + if (error_state) + handles.del (handle); + } + } +#else + error ("__ftp__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_pwd__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_pwd__ (@var{handle})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ + octave_value retval; +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + retval = curl.pwd (); + else + error ("__ftp_pwd__: invalid ftp handle"); + } + } +#else + error ("__ftp_pwd__: not available in this version of Octave"); +#endif + + return retval; +} + +DEFUN_DLD (__ftp_cwd__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_cwd__ (@var{handle}, @var{path})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1 && nargin != 2) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string path = ""; + + if (nargin > 1) + path = args(1).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.cwd (path); + else + error ("__ftp_cwd__: invalid ftp handle"); + } + } +#else + error ("__ftp_cwd__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_dir__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_dir__ (@var{handle})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ + octave_value retval; +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + 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"); + } + } +#else + error ("__ftp_dir__: not available in this version of Octave"); +#endif + + return retval; +} + +DEFUN_DLD (__ftp_ascii__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_ascii__ (@var{handle})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.ascii (); + else + error ("__ftp_ascii__: invalid ftp handle"); + } + } +#else + error ("__ftp_ascii__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_binary__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_binary__ (@var{handle})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.binary (); + else + error ("__ftp_binary__: invalid ftp handle"); + } + } +#else + error ("__ftp_binary__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_close__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_close__ (@var{handle})\n\ + Undocumented internal function\n\ + @end deftypefn") + { + #ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + if (!error_state) + handles.del (handle); + } + #else + error ("__ftp_close__: not available in this version of Octave"); + #endif + + return octave_value (); + } + +DEFUN_DLD (__ftp_mode__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_mode__ (@var{handle})\n\ + Undocumented internal function\n\ + @end deftypefn") + { + octave_value retval; + #ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 1) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + retval = (curl.is_ascii() ? "ascii" : "binary"); + else + error ("__ftp_binary__: invalid ftp handle"); + } + } + #else + error ("__ftp_mode__: not available in this version of Octave"); + #endif + + return retval; + } + +DEFUN_DLD (__ftp_delete__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_delete__ (@var{handle}, @var{path})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 2) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string file = args(1).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.del (file); + else + error ("__ftp_delete__: invalid ftp handle"); + } + } +#else + error ("__ftp_delete__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_rmdir__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_rmdir__ (@var{handle}, @var{path})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 2) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string dir = args(1).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.rmdir (dir); + else + error ("__ftp_rmdir__: invalid ftp handle"); + } + } +#else + error ("__ftp_rmdir__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_mkdir__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_mkdir__ (@var{handle}, @var{path})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 2) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string dir = args(1).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.mkdir (dir); + else + error ("__ftp_mkdir__: invalid ftp handle"); + } + } +#else + error ("__ftp_mkdir__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +DEFUN_DLD (__ftp_rename__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_rename__ (@var{handle}, @var{path})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 3) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string oldname = args(1).string_value (); + std::string newname = args(2).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + curl.rename (oldname, newname); + else + error ("__ftp_rename__: invalid ftp handle"); + } + } +#else + error ("__ftp_rename__: not available in this version of Octave"); +#endif + + return octave_value (); +} + +static string_vector +mput_directory (const curl_handle& curl, const std::string& base, + const std::string& dir) +{ + string_vector retval; + + if (! curl.mkdir (dir, false)) + warning ("__ftp_mput__: can not create the remote directory ""%s""", + (base.length() == 0 ? dir : base + + file_ops::dir_sep_str () + dir).c_str ()); + + curl.cwd (dir); + + if (! error_state) + { + unwind_protect::frame_id_t uwp_frame = + unwind_protect::begin_frame (); + + unwind_protect::add_fcn (reset_path, curl); + + std::string realdir = base.length() == 0 ? dir : base + + file_ops::dir_sep_str () + dir; + + 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 (curl, 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; + } + + curl.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()); + + unwind_protect::run_frame (uwp_frame); + } + + return retval; +} + +DEFUN_DLD (__ftp_mput__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_mput__ (@var{handle}, @var{files})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ + string_vector retval; + +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 2) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + std::string pat = args(1).string_value (); + + if (!error_state) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + { + 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 ()) + { + retval.append (mput_directory (curl, "", file)); + if (error_state) + 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 (error_state) + break; + + retval.append (file); + } + } + } + else + error ("__ftp_mput__: invalid ftp handle"); + } + } +#else + error ("__ftp_mput__: not available in this version of Octave"); +#endif + + return (nargout > 0 ? octave_value (retval) : octave_value ()); +} + +#ifdef HAVE_CURL +static void +getallfiles (const curl_handle& curl, const std::string& dir, + const std::string& target) +{ + std::string sep = file_ops::dir_sep_str (); + file_stat fs (dir); + + if (!fs || !fs.is_dir ()) + { + std::string msg; + int status = file_ops::mkdir (dir, 0777, msg); + + if (status < 0) + error ("__ftp_mget__: can't create directory %s%s%s. %s", + target.c_str(), sep.c_str(), dir.c_str(), msg.c_str()); + } + + if (! error_state) + { + curl.cwd (dir); + + if (! error_state) + { + unwind_protect::frame_id_t uwp_frame = + unwind_protect::begin_frame (); + + unwind_protect::add_fcn (reset_path, curl); + + string_vector sv = curl.list (); + + for (octave_idx_type i = 0; i < sv.length (); i++) + { + time_t ftime; + bool fisdir; + double fsize; + + curl.get_fileinfo (sv(i), fsize, ftime, fisdir); + + if (fisdir) + getallfiles (curl, sv(i), target + dir + sep); + else + { + std::string realfile = target + dir + 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::frame_id_t uwp_frame2 = + unwind_protect::begin_frame (); + + unwind_protect::add_fcn (delete_file, realfile); + + curl.get (sv(i), ofile); + + ofile.close (); + + if (!error_state) + unwind_protect::discard_frame (uwp_frame2); + else + unwind_protect::run_frame (uwp_frame2); + } + + if (error_state) + break; + } + + unwind_protect::run_frame (uwp_frame); + } + } +} +#endif + +DEFUN_DLD (__ftp_mget__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {} __ftp_mget__ (@var{handle}, @var{files})\n\ +Undocumented internal function\n\ +@end deftypefn") +{ +#ifdef HAVE_CURL + int nargin = args.length (); + + if (nargin != 2 && nargin != 3) + error ("incorrect number of arguments"); + else + { + std::string handle = args(0).string_value (); + 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) + { + const curl_handle curl = handles.contents (handle); + + if (curl.is_valid ()) + { + 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) + getallfiles (curl, 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::frame_id_t uwp_frame = + unwind_protect::begin_frame (); + + unwind_protect::add_fcn (delete_file, target + sv(i)); + + curl.get (sv(i), ofile); + + ofile.close (); + + if (!error_state) + unwind_protect::discard_frame (uwp_frame); + else + unwind_protect::run_frame (uwp_frame); + } + + if (error_state) + break; + } + } + if (n == 0) + error ("__ftp_mget__: file not found"); + } + } + } +#else + error ("__ftp_mget__: not available in this version of Octave"); +#endif + + return octave_value (); +}