zorba-coders team mailing list archive
-
zorba-coders team
-
Mailing list archive
-
Message #26516
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
Paul J. Lucas has proposed merging lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba.
Commit message:
1. New FTP client.
2. Moved curl_streambuf code to shared util-curl module.
Requested reviews:
Paul J. Lucas (paul-lucas)
For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/feature-ftp-client/+merge/201082
1. New FTP client.
2. Moved curl_streambuf code to shared util-curl module.
--
https://code.launchpad.net/~zorba-coders/zorba/feature-ftp-client/+merge/201082
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'CMakeCPack.cmake'
--- CMakeCPack.cmake 2013-08-06 16:51:59 +0000
+++ CMakeCPack.cmake 2014-01-09 19:12:12 +0000
@@ -128,7 +128,7 @@
SET(CPACK_POSTFLIGHT_SCRIPT "${CMAKE_BINARY_DIR}/osx_postflight.sh")
CONFIGURE_FILE("${CMAKE_SOURCE_DIR}/scripts/osx_postflight.sh.in"
"${CMAKE_BINARY_DIR}/osx_postflight.sh")
- MESSAGE ( STATUS "script = "${CPACK_POSTFLIGHT_SCRIPT} )
+ MESSAGE ( STATUS "script = ${CPACK_POSTFLIGHT_SCRIPT}" )
ENDIF ( APPLE )
INCLUDE(CPack)
INCLUDE(CPack.cmake)
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-11-04 22:29:39 +0000
+++ CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -235,6 +235,24 @@
ENDIF(_clock_gettime_in_rt)
#
+# cURL
+#
+#/*IF (ZORBA_SUPPRESS_CURL)*/
+#/* MESSAGE( STATUS "ZORBA_SUPPRESS_CURL is true - not searching for cURL library" )*/
+#/*ELSE (ZORBA_SUPPRESS_CURL)*/
+#/* MESSAGE( STATUS "Looking for cURL" )*/
+#/* FIND_PACKAGE( CURL )*/
+#/* IF (CURL_FOUND)*/
+#/* MESSAGE( STATUS "Found cURL library -- " ${CURL_LIBRARIES})*/
+#/* INCLUDE_DIRECTORIES( ${CURL_INCLUDE_DIR} )*/
+#/* SET(requiredlibs ${requiredlibs} ${CURL_LIBRARIES})*/
+#/* ELSE (CURL_FOUND)*/
+#/* MESSAGE( STATUS "The cURL library was not found - ftp-client and http-client will not be built" )*/
+#/* ENDIF (CURL_FOUND)*/
+#/*ENDIF (ZORBA_SUPPRESS_CURL)*/
+#/*SET( ZORBA_HAVE_CURL ${CURL_FOUND} CACHE BOOL "Whether Zorba found cURL" FORCE )*/
+
+#
# TcMalloc support
#
IF(ZORBA_USE_TCMALLOC)
@@ -591,7 +609,6 @@
# non-core modules are available.
ADD_SUBDIRECTORY(bin)
-
ADD_SUBDIRECTORY(test)
ADD_SUBDIRECTORY(config)
ADD_SUBDIRECTORY(schemas)
=== modified file 'include/zorba/item.h'
--- include/zorba/item.h 2013-09-26 07:38:44 +0000
+++ include/zorba/item.h 2014-01-09 19:12:12 +0000
@@ -418,7 +418,7 @@
* @throw ZorbaException if an error occured (e.g. the Item is not of type JSON Object).
*/
Item
- getObjectValue(String aName) const;
+ getObjectValue(String const &aName) const;
/**
* Checks whether the item's content is streamable.
=== modified file 'include/zorba/util/base64_stream.h'
--- include/zorba/util/base64_stream.h 2013-08-05 22:23:16 +0000
+++ include/zorba/util/base64_stream.h 2014-01-09 19:12:12 +0000
@@ -269,11 +269,10 @@
* @param stream The stream to attach the base64::streambuf to. If the
* stream already has a base64::streambuf attached to it, this contructor
* does nothing.
- * @param charset The name of the character encoding to convert from/to.
* @return \c true only if a base64::streambuf was attached.
*/
- bool attach( StreamType &stream, char const *charset ) {
- if ( base64::attach( stream, charset ) ) {
+ bool attach( StreamType &stream ) {
+ if ( base64::attach( stream ) ) {
stream_ = &stream;
return true;
}
=== renamed file 'src/util/mem_streambuf.h' => 'include/zorba/util/mem_streambuf.h'
--- src/util/mem_streambuf.h 2013-08-01 00:31:20 +0000
+++ include/zorba/util/mem_streambuf.h 2014-01-09 19:12:12 +0000
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ZORBA_MMAP_STREAMBUF_H
-#define ZORBA_MMAP_STREAMBUF_H
+#ifndef ZORBA_API_MEM_STREAMBUF_H
+#define ZORBA_API_MEM_STREAMBUF_H
#include <streambuf>
@@ -118,5 +118,5 @@
///////////////////////////////////////////////////////////////////////////////
} // namespace zorba
-#endif /* ZORBA_MMAP_STREAMBUF_H */
+#endif /* ZORBA_API_MEM_STREAMBUF_H */
/* vim:set et sw=2 ts=2: */
=== modified file 'modules/CMakeLists.txt'
--- modules/CMakeLists.txt 2013-10-16 22:04:45 +0000
+++ modules/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -12,11 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+ADD_SUBDIRECTORY(util-curl)
ADD_SUBDIRECTORY(atomic)
ADD_SUBDIRECTORY(com)
ADD_SUBDIRECTORY(full-text)
ADD_SUBDIRECTORY(functx)
ADD_SUBDIRECTORY(dateTime)
+ADD_SUBDIRECTORY(ftp-client)
ADD_SUBDIRECTORY(http-client)
ADD_SUBDIRECTORY(item)
ADD_SUBDIRECTORY(json)
=== added directory 'modules/ftp-client'
=== added file 'modules/ftp-client/CMakeLists.txt'
--- modules/ftp-client/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -0,0 +1,26 @@
+# Copyright 2006-2013 The FLWOR Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+IF (ZORBA_HAVE_CURL)
+ SET(ZorbaUtilCurlModule_DIR "../util-curl")
+ FIND_PACKAGE(ZorbaUtilCurlModule REQUIRED)
+ IF (ZorbaUtilCurlModule_FOUND)
+ INCLUDE_DIRECTORIES("${ZorbaUtilCurlModule_INCLUDE_DIRS}")
+ DECLARE_ZORBA_MODULE( FILE ftp-client.xq VERSION 1.0
+ URI "http://zorba.io/modules/ftp-client"
+ LINK_LIBRARIES ${CURL_LIBRARIES} ${ZorbaUtilCurlModule_LIBS})
+ ENDIF (ZorbaUtilCurlModule_FOUND)
+ENDIF (ZORBA_HAVE_CURL)
+
+# vim:set et sw=2 ts=2:
=== added file 'modules/ftp-client/ftp-client.xq'
--- modules/ftp-client/ftp-client.xq 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq 2014-01-09 19:12:12 +0000
@@ -0,0 +1,342 @@
+jsoniq version "1.0";
+
+(:
+ : Copyright 2006-2013 The FLWOR Foundation.
+ :
+ : Licensed under the Apache License, Version 2.0 (the "License");
+ : you may not use this file except in compliance with the License.
+ : You may obtain a copy of the License at
+ :
+ : http://www.apache.org/licenses/LICENSE-2.0
+ :
+ : Unless required by applicable law or agreed to in writing, software
+ : distributed under the License is distributed on an "AS IS" BASIS,
+ : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ : See the License for the specific language governing permissions and
+ : limitations under the License.
+ :)
+
+(:===========================================================================:)
+
+(:~
+ : This module provides functions for performing FTP commands.
+ : <p/>
+ : @author Paul J. Lucas
+ : @library <a href="http://curl.haxx.se/">cURL Library</a>
+ : @project Zorba/Input Output/FTP Client
+ :)
+module namespace ftp = "http://zorba.io/modules/ftp-client";
+
+declare namespace an = "http://zorba.io/annotations";
+declare namespace ver = "http://zorba.io/options/versioning";
+
+declare option ver:module-version "1.0";
+
+(:~
+ : Attempts to connect to an FTP server specified by the given URI.
+ :
+ : @param $uri The address of the FTP server to connect to.
+ : It may either be simple host-name
+ : (<code>ftp.example.com</code>)
+ : or a URI using the <code>ftp</code>
+ : or <code>ftps</code> (FTP over SSL/TLS)
+ : schemes
+ : (<code>ftp://ftp.example.com</code>).
+ : If the latter,
+ : the URI may also contain
+ : <code>username</code>,
+ : <code>password</code>,
+ : and
+ : <code>port</code>
+ : authority subcomponents
+ : per RFC 3986.
+ : @param $options The options to use:
+ : <dl>
+ : <dt><code>user</code></dt>
+ : <dd>
+ : The user to log in as;
+ : default: <code>"ftp"</code> (for anonymous FTP).
+ : If <code>$uri</code> is actually a URI instead of a simple host-name,
+ : the user, if specified, must be part of the URI
+ : and the value of this option is ignored.
+ : </dd>
+ : <dt><code>password</code></dt>
+ : <dd>
+ : The password to use to log in;
+ : default: <code>"ftp@xxxxxxxxxxx"</code> (for anonymous FTP).
+ : If <code>$uri</code> is actually a URI instead of a simple host-name,
+ : the password, if specified, must be part of the URI
+ : and the value of this option is ignored.
+ : </dd>
+ : <dt><code>port</code></dt>
+ : <dd>
+ : The port number to use;
+ : default: whatever the default for the protocol is.
+ : If <code>$uri</code> is actually a URI instead of a simple host-name,
+ : the port, if specified, must be part of the URI
+ : and the value of this option is ignored.
+ : </dd>
+ : <dt><code>protocol</code></dt>
+ : <dd>
+ : The protocol to use,
+ : either <code>"ftp'</code> or <code>"ftps"</code>;
+ : default: <code>"ftp"</code>.
+ : If <code>$uri</code> is actually a URI instead of a simple host-name,
+ : the protocol, if specified, must be part of the URI
+ : and the value of this option is ignored.
+ : </dd>
+ : <dt><code>SSL-communication</code></dt>
+ : <dd>
+ : Whether to use SSL/TLS, one of:
+ : <dl>
+ : <dt><code>"none"</code></dt>
+ : <dd>Don't use SSL.</dd>
+ : <dt><code>"try"</code></dt>
+ : <dd>Try using SSL, but, if unsuccessful, continue anyway.</dd>
+ : <dt><code>"control"</code></dt>
+ : <dd>Require SSL for the control connection.</dd>
+ : <dt><code>"all"</code></dt>
+ : <dd>Require SSL for all communication.</dd>
+ : </dl>
+ : default:
+ : <code>"none"</code> for protocol <code>"ftp"</code>
+ : and
+ : <code>"all"</code> for protocol <code>"ftps"</code>
+ : Note that any value other than <code>"none"</code>
+ : for <code>"ftp"</code> implies <em>explicit</em> SSL;
+ : use of <code>"ftps"</code> implies <em>implicit</em> SSL.
+ : See "<a href="http://en.wikipedia.org/wiki/FTPS#Methods_of_invoking_security">Methods of invoking security</a>."
+ : </dd>
+ : <dt><code>SSL-verify</code></dt>
+ : <dd>
+ : When doing FTP over SSL/TLS,
+ : whether to verify the authenticity of the server's certificate
+ : and that the certificate is for that server;
+ : default: <code>true</code>.
+ : (You should <em>never</em> set this to <code>false</code>
+ : unless you are testing your own FTP server
+ : with a self-signed certificate.)
+ : </dd>
+ : <dt><code>trace</code></dt>
+ : <dd>
+ : Whether to emit information to standard error
+ : tracing the communication between the FTP client and server;
+ : default: <code>false</code>.
+ : </dd>
+ : </dl>
+ : @return an opaque URI that serves as a connection handle to be used with
+ : other functions in this module.
+ : @error ftp:ALREADY_CONNECTED if <code>$uri</code> is already connected to.
+ : @error ftp:INVALID_ARGUMENT if any option is invalid.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:connect( $uri as string, $options as object )
+ as anyURI external;
+
+(:~
+ : Deletes a file from the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the file to delete.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:delete( $conn as string, $remote-path as string )
+ external;
+
+(:~
+ : Disconnects from an FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : After successful completion of of this function, the handle is no longer
+ : valid.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:disconnect( $conn as anyURI )
+ external;
+
+(:~
+ : Gets a binary file from the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the file to get.
+ : @return the binary content of <code>$remote-path</code>.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:get-binary( $conn as anyURI, $remote-path as string )
+ as base64Binary external;
+
+(:~
+ : Gets a text file from the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the file to get. It must not be empty.
+ : @param $encoding The character encoding of the file.
+ : @return the text content of <code>$remote-path</code>.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty
+ : or <code>$encoding</code> is either an invalid or unsupported encoding.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:get-text( $conn as anyURI, $remote-path as string, $encoding as string )
+ as string external;
+
+(:~
+ : Gets a text file, presumed to be encoded in UTF-8, from the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the file to get. It must not be empty.
+ : @return the text content of <code>$remote-path</code>.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:get-text( $conn as anyURI, $remote-path as string )
+ as string
+{
+ ftp:get-text( $conn, $remote-path, "UTF-8" )
+};
+
+(:~
+ : Gets a listing for a directory on an FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The full path of the directory on the FTP server to get
+ : the directory listing for.
+ : An empty path is equivalent to <code>/</code>.
+ : @return Returns a sequence of JSON objects, one per file or subdirectory in
+ : the listing.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:list( $conn as anyURI, $remote-path as string )
+ as object* external;
+
+(:~
+ : Creates a directory on the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the new directory to create.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:mkdir( $conn as string, $remote-path as string )
+ external;
+
+(:~
+ : Uploads binary data to a file to the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $binary The binary data to upload.
+ : @param $remote-path The path of the file to upload to. It must not be empty.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:put-binary( $conn as anyURI, $binary as base64Binary,
+ $remote-path as string )
+ external;
+
+(:~
+ : Uploads text to a file to the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $text The text to upload.
+ : @param $remote-path The path of the file to upload to. It must not be empty.
+ : @param $encoding The character encoding of the file.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty
+ : or <code>$encoding</code> is either an invalid or unsupported encoding.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:put-text( $conn as anyURI, $text as string, $remote-path as string,
+ $encoding as string )
+ external;
+
+(:~
+ : Uploads text to a UTF-8 encoded file on the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $text The text to upload.
+ : @param $remote-path The path of the file to upload to. It must not be empty.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:put-text( $conn as anyURI, $text as string, $remote-path as string )
+{
+ ftp:put-text( $conn, $text, $remote-path, "UTF-8" )
+};
+
+(:~
+ : Renames a file or directory on an FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-from-path The path of the file or directory to rename.
+ : @param $remote-to-path The new name.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-from-path</code> or
+ : <code>$remote-to-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:rename( $conn as string, $remote-from-path as string,
+ $remote-to-path as string )
+ external;
+
+(:~
+ : Removes a directory from the FTP server.
+ :
+ : @param $conn The opaque URI connection handle previously returned by
+ : <code>ftp:connect()</code>.
+ : @param $remote-path The path of the directory to remove.
+ : @error ftp:INVALID_ARGUMENT if <code>$remote-path</code> is empty.
+ : @error ftp:NOT_CONNECTED if <code>$conn</code> is either an invalid handle
+ : or is no longer a valid handle.
+ : @error ftp:FTP_ERROR if there was some other FTP error.
+ :)
+declare %an:sequential function
+ftp:rmdir( $conn as string, $remote-path as string )
+ external;
+
+(:===========================================================================:)
+
+(: vim:set syntax=xquery et sw=2 ts=2: :)
=== added directory 'modules/ftp-client/ftp-client.xq.src'
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_connections.cpp'
--- modules/ftp-client/ftp-client.xq.src/ftp_connections.cpp 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_connections.cpp 2014-01-09 19:12:12 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// standard
+#include <cassert>
+
+// local
+#include "ftp_connections.h"
+
+namespace zorba {
+namespace ftp_client {
+
+///////////////////////////////////////////////////////////////////////////////
+
+connections::~connections() {
+ for ( key_buf_map::const_iterator i = key_buf_.begin();
+ i != key_buf_.end(); ++i ) {
+ delete i->second;
+ }
+}
+
+bool connections::delete_buf( String const &key ) {
+ key_buf_map::iterator const i( key_buf_.find( key ) );
+ if ( i != key_buf_.end() ) {
+ delete i->second;
+ key_buf_.erase( i );
+ return true;
+ }
+ return false;
+}
+
+void connections::destroy() throw() {
+ delete this;
+}
+
+curl::streambuf* connections::get_buf( String const &key ) const {
+ key_buf_map::const_iterator const i( key_buf_.find( key ) );
+ if ( i != key_buf_.end() )
+ return i->second;
+ return 0;
+}
+
+curl::streambuf* connections::new_buf( String const &key ) {
+ curl::streambuf *&buf = key_buf_[ key ];
+ assert( !buf );
+ buf = new curl::streambuf();
+ return buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_connections.h'
--- modules/ftp-client/ftp-client.xq.src/ftp_connections.h 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_connections.h 2014-01-09 19:12:12 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#ifndef ZORBA_FTP_CLIENT_FTP_CONNECTIONS_H
+#define ZORBA_FTP_CLIENT_FTP_CONNECTIONS_H
+
+// standard
+#include <map>
+
+// Zorba
+#include <zorba/external_function_parameter.h>
+#include <zorba/zorba_string.h>
+
+// util-curl module
+#include "curl_streambuf.h"
+
+namespace zorba {
+namespace ftp_client {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * An ftp::connections is-an ExternalFunctionParameter (that's added to a
+ * query's dynamic context) for keeping a mapping between keys (representing
+ * URIs of FTP servers) and their associated curl::streambufs (the active
+ * connections to said servers).
+ */
+class connections : public ExternalFunctionParameter {
+public:
+ ~connections();
+
+ /**
+ * Deletes a curl::streambuf associatied with the given \a key.
+ *
+ * @param key The key associated with the curl::streambuf to delete.
+ * @return Returns \a true only if \a key was associated with a
+ * curl::streambuf and therefore deleted; \c false otherwise.
+ */
+ bool delete_buf( String const &key );
+
+ /**
+ * Gets an existing curl::streambuf associatied with the given \a key.
+ *
+ * @param key The key to get the associated curl::streambuf for.
+ * @return Returns said curl::streambuf or \c nullptr if \a key has no
+ * associated curl::streambuf.
+ */
+ curl::streambuf* get_buf( String const &key ) const;
+
+ /**
+ * Creates a new curl::streambuf and associates it with the given \a key.
+ *
+ * @param key The key to associate the newly created curl::streambuf to.
+ * The key must not alraedy be associated with any curl::streambuf.
+ * @return Returns the newly created curl::streambuf.
+ */
+ curl::streambuf* new_buf( String const &key );
+
+ // inherited
+ virtual void destroy() throw();
+
+private:
+ // map keys -> cURL::streambuf*
+ typedef std::map<String,curl::streambuf*> key_buf_map;
+ key_buf_map key_buf_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+#endif /* ZORBA_FTP_CLIENT_FTP_CONNECTIONS_H */
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_functions.cpp'
--- modules/ftp-client/ftp-client.xq.src/ftp_functions.cpp 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_functions.cpp 2014-01-09 19:12:12 +0000
@@ -0,0 +1,932 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// standard
+#include <cctype>
+#include <ctime>
+#include <istream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility> /* for pair */
+#include <vector>
+
+// Zorba
+#include <zorba/dynamic_context.h>
+#include <zorba/empty_sequence.h>
+#include <zorba/item_factory.h>
+#include <zorba/item_sequence.h>
+#include <zorba/iterator.h>
+#include <zorba/singleton_item_sequence.h>
+#include <zorba/store_consts.h>
+#include <zorba/user_exception.h>
+#include <zorba/util/base64_stream.h>
+#include <zorba/util/mem_streambuf.h>
+#include <zorba/util/transcode_stream.h>
+
+// local
+#include "ftp_connections.h"
+#include "ftp_functions.h"
+#include "ftp_module.h"
+#include "ftpparse.c"
+
+// FTP reply codes; see RFC 959.
+#define FTP_REPLY_ACTION_COMPLETED 250
+#define FTP_REPLY_PATH_CREATED 257
+#define FTP_REPLY_ACTION_ABORTED 451
+#define FTP_REPLY_ACTION_NOT_TAKEN 550
+
+#define IS_ATOMIC_TYPE(ITEM,TYPE) \
+ ( (ITEM).isAtomic() && (ITEM).getTypeCode() == store::TYPE )
+
+// Eliminates "control may reach end of non-void function" warning.
+#define THROW_EXCEPTION(...) while (1) throw_exception( __VA_ARGS__ )
+
+/**
+ * This is our "external function parameter" name in the dynamic context.
+ */
+#define ZORBA_FTP_CONNECTIONS "http://zorba.io/modules/ftp-client/connections"
+
+using namespace std;
+
+namespace zorba {
+namespace ftp_client {
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct curl_helper {
+ curl_helper( curl::streambuf*, curl_slist* = 0 );
+ ~curl_helper();
+private:
+ curl::streambuf *const cbuf_;
+ curl_slist *const slist_;
+};
+
+curl_helper::curl_helper( curl::streambuf *cbuf, curl_slist *slist ) :
+ cbuf_( cbuf ),
+ slist_( slist )
+{
+ ZORBA_CURLM_ASSERT( curl_multi_remove_handle( cbuf_->curlm(), cbuf_->curl() ) );
+}
+
+curl_helper::~curl_helper() {
+ if ( slist_ )
+ curl_slist_free_all( slist_ );
+ CURL *const cobj = cbuf_->curl();
+ curl_easy_setopt( cobj, CURLOPT_CUSTOMREQUEST, NULL );
+ curl_easy_setopt( cobj, CURLOPT_HEADERDATA, NULL );
+ curl_easy_setopt( cobj, CURLOPT_HEADERFUNCTION, NULL );
+ curl_easy_setopt( cobj, CURLOPT_UPLOAD, 0L );
+ curl_multi_add_handle( cbuf_->curlm(), cobj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static connections& get_connections( DynamicContext const *dctx ) {
+ connections *conns = static_cast<connections*>(
+ dctx->getExternalFunctionParameter( ZORBA_FTP_CONNECTIONS )
+ );
+ if ( !conns ) {
+ conns = new connections();
+ dctx->addExternalFunctionParameter( ZORBA_FTP_CONNECTIONS, conns );
+ }
+ return *conns;
+}
+
+static int get_ftp_reply_code( CURL *cobj ) {
+ long code;
+ ZORBA_CURL_ASSERT( curl_easy_getinfo( cobj, CURLINFO_RESPONSE_CODE, &code ) );
+ return static_cast<int>( code );
+}
+
+inline int get_scheme_len( String const &uri ) {
+ if ( uri.compare( 0, 6, "ftp://" ) == 0 )
+ return 6;
+ if ( uri.compare( 0, 6, "ftps://" ) == 0 )
+ return 7;
+ return 0;
+}
+
+static String host_part( String /* intentionally not const& */ uri ) {
+ if ( int const scheme_len = get_scheme_len( uri ) ) {
+ uri.erase( 0, scheme_len );
+ String::size_type const at_pos = uri.find( '@' );
+ if ( at_pos != String::npos )
+ uri.erase( 0, at_pos + 1 ); // erase user:password@
+ String::size_type const colon_pos = uri.find( ':' );
+ if ( colon_pos != String::npos )
+ uri.erase( colon_pos ); // erase :port
+ }
+ return uri;
+}
+
+static String make_uri( String const &uri,
+ String path, // intentionally not const&
+ bool path_is_dir = false ) {
+ if ( path.empty() )
+ path = '/';
+ else {
+ if ( path_is_dir && path[ path.size() - 1 ] != '/' )
+ path += '/';
+ if ( path[0] != '/' )
+ path.insert( (String::size_type)0, 1, '/' );
+ }
+ String result( uri );
+ result += path;
+ return result;
+}
+
+static void stream_releaser( istream *is ) {
+ delete is;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+function::function( module const *m, char const *local_name ) :
+ module_( m ),
+ local_name_( local_name )
+{
+}
+
+bool function::get_bool_opt( Item const &options, char const *key,
+ bool default_value ) const {
+ Item const item( options.getObjectValue( key ) );
+ if ( item.isNull() )
+ return default_value;
+ if ( !IS_ATOMIC_TYPE( item, XS_BOOLEAN ) )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", key, "value must be boolean" );
+ return item.getBooleanValue();
+}
+
+int function::get_integer_opt( Item const &options, char const *key,
+ int default_value ) const {
+ Item const item( options.getObjectValue( key ) );
+ if ( item.isNull() )
+ return default_value;
+ if ( !IS_ATOMIC_TYPE( item, XS_INTEGER ) )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", key, "value must be integer" );
+ return item.getIntValue();
+}
+
+Item function::get_item_arg( ExternalFunction::Arguments_t const &args,
+ unsigned pos ) const {
+ Item result;
+ if ( pos < args.size() ) {
+ Iterator_t it( args[ pos ]->getIterator() );
+ it->open();
+ it->next( result );
+ it->close();
+ }
+ return result;
+}
+
+String function::getLocalName() const {
+ return local_name_;
+}
+
+String function::get_string_arg( ExternalFunction::Arguments_t const &args,
+ unsigned pos ) const {
+ String s;
+ Item const item( get_item_arg( args, pos ) );
+ if ( !item.isNull() )
+ s = item.getStringValue();
+ return s;
+}
+
+String function::get_string_opt( Item const &options, char const *key,
+ char const *default_value ) const {
+ Item const item( options.getObjectValue( key ) );
+ if ( item.isNull() )
+ return default_value;
+ if ( !IS_ATOMIC_TYPE( item, XS_STRING ) )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", key, "value must be string" );
+ return item.getStringValue();
+}
+
+String function::getURI() const {
+ return module_->getURI();
+}
+
+curl::streambuf* function::require_connection( DynamicContext const *dctx,
+ String const &conn ) const {
+ connections &conns = get_connections( dctx );
+ if ( curl::streambuf *const cbuf = conns.get_buf( conn.c_str() ) )
+ return cbuf;
+ THROW_EXCEPTION( "NOT_CONNECTED", host_part( conn ), "not connnected" );
+}
+
+void function::throw_exception( char const *error_code, char const *object,
+ char const *message, int ftp_code ) const {
+ string s;
+
+ if ( object && *object ) {
+ ostringstream oss;
+ oss << '"' << object << "\": " << message;
+ s = oss.str();
+ } else
+ s = message;
+
+ if ( ftp_code ) {
+ ostringstream oss;
+ oss << " (FTP code " << ftp_code << ')';
+ s += oss.str();
+ }
+
+ throw USER_EXCEPTION(
+ module_->getItemFactory()->createQName( getURI(), error_code ), s
+ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+get_function::get_function( module const *m, char const *local_name,
+ bool text ) :
+ function( m, local_name ),
+ text_( text )
+{
+}
+
+ItemSequence_t
+get_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+ String const encoding( text_ ? get_string_arg( args, 2 ) : "" );
+ if ( !encoding.empty() && transcode::is_necessary( encoding.c_str() ) &&
+ !transcode::is_supported( encoding.c_str() ) ) {
+ THROW_EXCEPTION( "INVALID_ARGUMENT", encoding, "unsupported encoding" );
+ }
+ String const uri( make_uri( conn, path ) );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ curl_easy_setopt( cobj, CURLOPT_TRANSFERTEXT, text_ ? 1 : 0 );
+ curl_easy_setopt( cobj, CURLOPT_URL, uri.c_str() );
+
+ istream *const is = new istream( cbuf );
+ if ( transcode::is_necessary( encoding.c_str() ) )
+ transcode::attach( *is, encoding.c_str() );
+
+ ItemFactory *const f = module_->getItemFactory();
+ Item result(
+ text_ ?
+ f->createStreamableString( *is, &stream_releaser )
+ :
+ f->createStreamableBase64Binary( *is, &stream_releaser, false )
+ );
+ return ItemSequence_t( new SingletonItemSequence( result ) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static size_t curl_read_callback( void *ptr, size_t size, size_t nmemb,
+ void *data ) {
+ size *= nmemb;
+ istream *const is = static_cast<istream*>( data );
+ is->read( static_cast<char*>( ptr ), static_cast<streamsize>( size ) );
+ return is->gcount();
+}
+
+put_function::put_function( module const *m, char const *local_name,
+ bool text ) :
+ function( m, local_name ),
+ text_( text )
+{
+}
+
+ItemSequence_t
+put_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ Item put_item( get_item_arg( args, 1 ) );
+ String const path( get_string_arg( args, 2 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+ String const encoding( text_ ? get_string_arg( args, 2 ) : "" );
+ if ( !encoding.empty() && transcode::is_necessary( encoding.c_str() ) &&
+ !transcode::is_supported( encoding.c_str() ) ) {
+ THROW_EXCEPTION( "INVALID_ARGUMENT", encoding, "unsupported encoding" );
+ }
+ String const uri( make_uri( conn, path ) );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_TRANSFERTEXT, text_ ? 1 : 0 ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_URL, uri.c_str() ) );
+
+ istream *is;
+ mem_streambuf mbuf;
+ auto_ptr<istream> raii_is;
+ String text;
+
+ if ( put_item.isStreamable() ) {
+ is = &put_item.getStream();
+ } else {
+ if ( text_ ) {
+ text = put_item.getStringValue();
+ mbuf.set( const_cast<char*>( text.data() ), text.size() );
+ } else {
+ size_t size;
+ char const *const data = put_item.getBase64BinaryValue( size );
+ mbuf.set( const_cast<char*>( data ), size );
+ }
+ is = new istream( &mbuf );
+ raii_is.reset( is );
+ }
+
+ //
+ // Thest raii_* objects must be constructed after raii_is so their destructor
+ // is called before that of raii_is (reverse order of construction) so either
+ // the base64_streambuf or transcode_streambuf is destroyed before the stream
+ // it's attached to is.
+ //
+ base64::auto_attach<istream> raii_b64;
+ transcode::auto_attach<istream> raii_xcode;
+ if ( text_ ) {
+ if ( !encoding.empty() && transcode::is_necessary( encoding.c_str() ) )
+ raii_xcode.attach( *is, encoding.c_str() );
+ } else {
+ if ( put_item.isEncoded() )
+ raii_b64.attach( *is );
+ }
+
+ try {
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_READDATA, is ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_READFUNCTION, curl_read_callback ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_UPLOAD, 1L ) );
+ curl_helper helper( cbuf );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ return ItemSequence_t( new EmptySequence() );
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+connect_function::connect_function( module const *m ) :
+ function( m, "connect" )
+{
+}
+
+ItemSequence_t
+connect_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const uri( get_string_arg( args, 0 ) );
+ String conn( uri );
+ //
+ // Even though we allow ftp URIs ("ftp://host"), we limit them to refer only
+ // to the host and not either a file or subdirectory; hence chop off
+ // everything starting at the first '/'.
+ //
+ int const scheme_len = get_scheme_len( conn );
+ String::size_type const slash_pos = conn.find( scheme_len, '/' );
+ if ( slash_pos != String::npos )
+ conn.erase( slash_pos );
+
+ Item const options( get_item_arg( args, 1 ) );
+ String const password( get_string_opt( options, "password" ) );
+ int const port( get_integer_opt( options, "port" ) );
+ String const protocol( get_string_opt( options, "protocol", "ftp" ) );
+ String const ssl_comm( get_string_opt( options, "SSL-communication" ) );
+ bool const ssl_verify( get_bool_opt( options, "SSL-verify", true ) );
+ bool const trace( get_bool_opt( options, "trace", false ) );
+ String const user( get_string_opt( options, "user" ) );
+
+ if ( !(protocol == "ftp" || protocol == "ftps") )
+ THROW_EXCEPTION(
+ "INVALID_ARGUMENT", "protcol", "must be either ftp or ftps"
+ );
+
+ long use_ssl;
+ if ( !ssl_comm.empty() ) {
+ if ( ssl_comm == "all" )
+ use_ssl = CURLUSESSL_ALL;
+ else if ( ssl_comm == "control" )
+ use_ssl = CURLUSESSL_CONTROL;
+ else if ( ssl_comm == "none" )
+ use_ssl = CURLUSESSL_NONE;
+ else if ( ssl_comm == "try" )
+ use_ssl = CURLUSESSL_TRY;
+ else
+ THROW_EXCEPTION(
+ "INVALID_ARGUMENT", "SSL-communication",
+ "must be one of: none, try, control, or all"
+ );
+ } else if ( protocol == "ftps" )
+ use_ssl = CURLUSESSL_ALL;
+ else
+ use_ssl = CURLUSESSL_NONE;
+
+ if ( !scheme_len ) {
+ if ( user.empty() && !password.empty() )
+ THROW_EXCEPTION(
+ "INVALID_ARGUMENT", "", "empty user and non-empty password"
+ );
+ if ( !user.empty() ) {
+ conn.insert( (String::size_type)0, 1, '@' );
+ if ( !password.empty() ) {
+ char *const esc_password =
+ curl_escape( const_cast<char*>( password.data() ), password.size() );
+ conn.insert( 0, esc_password );
+ curl_free( esc_password );
+ conn.insert( (String::size_type)0, 1, ':' );
+ }
+ conn.insert( 0, user );
+ }
+ conn.insert( 0, "://" );
+ conn.insert( 0, protocol );
+ if ( port ) {
+ conn.append( 1, ':' );
+ ostringstream oss;
+ oss << port;
+ conn.append( oss.str() );
+ }
+ }
+
+ connections &conns = get_connections( dctx );
+ curl::streambuf *cbuf = conns.get_buf( conn );
+ if ( cbuf )
+ THROW_EXCEPTION(
+ "ALREADY_CONNECTED", uri, "connection previously established"
+ );
+ cbuf = conns.new_buf( conn );
+
+ try {
+ cbuf->open( conn.c_str() );
+ CURL *const cobj = cbuf->curl();
+
+ if ( trace )
+ cbuf->curl_verbose( true );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_USE_SSL, use_ssl ) );
+ if ( !ssl_verify ) {
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_SSL_VERIFYHOST, 0L ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_SSL_VERIFYPEER, 0L ) );
+ }
+
+ curl_helper helper( cbuf );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ Item result( module_->getItemFactory()->createAnyURI( conn ) );
+ return ItemSequence_t( new SingletonItemSequence( result ) );
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", uri, e.what() );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if ZORBA_FTP_CWD_IMPLEMENTED
+ItemSequence_t
+cwd_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+
+ String req( "CWD " );
+ req += path;
+ curl_easy_setopt( cobj, CURLOPT_CUSTOMREQUEST, req.c_str() );
+ curl_easy_perform( cobj );
+
+ return ItemSequence_t( new EmptySequence() );
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+delete_function::delete_function( module const *m ) :
+ function( m, "delete" )
+{
+}
+
+ItemSequence_t
+delete_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+ String const command( "DELE " + path );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ curl_easy_setopt( cobj, CURLOPT_CUSTOMREQUEST, command.c_str() );
+
+ try {
+ curl_helper helper( cbuf );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ }
+ catch ( curl::exception const &e ) {
+ int const ftp_code = get_ftp_reply_code( cobj );
+ switch ( ftp_code ) {
+ case FTP_REPLY_ACTION_COMPLETED:
+ if ( e.curl_code() == CURLE_FTP_COULDNT_RETR_FILE ) {
+ //
+ // After the file is deleted, CURL tries to RETR it which fails, so
+ // ignore that error.
+ // (Also see <http://stackoverflow.com/a/13515807/99089>.)
+ //
+ break;
+ }
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ case FTP_REPLY_ACTION_NOT_TAKEN:
+ THROW_EXCEPTION( "FTP_ERROR", path, "file not found", ftp_code );
+ default:
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what(), ftp_code );
+ } // switch
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ }
+ return ItemSequence_t( new EmptySequence() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+disconnect_function::disconnect_function( module const *m ) :
+ function( m, "disconnect" )
+{
+}
+
+ItemSequence_t
+disconnect_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ connections &conns = get_connections( dctx );
+ if ( !conns.delete_buf( conn ) )
+ THROW_EXCEPTION( "NOT_CONNECTED", host_part( conn ), "not connected" );
+ return ItemSequence_t( new EmptySequence() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+get_binary_function::get_binary_function( module const *m ) :
+ get_function( m, "get-binary", false )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+get_text_function::get_text_function( module const *m ) :
+ get_function( m, "get-text", true )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class list_iterator : public ItemSequence, public Iterator {
+public:
+ list_iterator( curl::streambuf*, ItemFactory* );
+
+ // inherited from ItemSequence
+ Iterator_t getIterator();
+
+ // inherited from Iterator
+ void close();
+ bool isOpen() const;
+ bool next( Item& );
+ void open();
+
+private:
+ ItemFactory *const factory_;
+ istream is_;
+ bool open_;
+};
+
+list_iterator::list_iterator( curl::streambuf *cbuf, ItemFactory *factory ) :
+ factory_( factory ),
+ is_( cbuf ),
+ open_( false )
+{
+}
+
+void list_iterator::close() {
+ open_ = false;
+}
+
+Iterator_t list_iterator::getIterator() {
+ return this;
+}
+
+bool list_iterator::isOpen() const {
+ return open_;
+}
+
+bool list_iterator::next( Item &result ) {
+ static Item const mtime_key( factory_->createString( "mtime" ) );
+ static Item const name_key( factory_->createString( "name" ) );
+ static Item const size_key( factory_->createString( "size" ) );
+
+ string line;
+ while ( getline( is_, line ) ) {
+ if ( line.empty() )
+ continue;
+ if ( line[ line.size() - 1 ] == '\r' )
+ line.erase( line.size() - 1 );
+ struct ftpparse ftp_file;
+ if ( ftpparse( &ftp_file, line.data(), line.size() ) ) {
+ vector<pair<Item,Item> > kv;
+
+ String name( ftp_file.name, ftp_file.namelen );
+ Item const name_value( factory_->createString( name ) );
+ kv.push_back( make_pair( name_key, name_value ) );
+
+ switch ( ftp_file.sizetype ) {
+ case FTPPARSE_SIZE_ASCII:
+ case FTPPARSE_SIZE_BINARY: {
+ Item const size_value( factory_->createLong( ftp_file.size ) );
+ kv.push_back( make_pair( size_key, size_value ) );
+ break;
+ }
+ case FTPPARSE_SIZE_UNKNOWN:
+ // do nothing
+ break;
+ } // switch
+
+ struct tm tm;
+ gmtime_r( &ftp_file.mtime, &tm );
+ int const year = tm.tm_year + 1900;
+ switch ( ftp_file.mtimetype ) {
+ case FTPPARSE_MTIME_REMOTEDAY:
+ tm.tm_hour = tm.tm_min = 0;
+ // no break;
+ case FTPPARSE_MTIME_REMOTEMINUTE:
+ tm.tm_sec = 0;
+ tm.tm_gmtoff = 0;
+ // no break;
+ case FTPPARSE_MTIME_LOCAL: {
+ Item const mtime_value (
+ factory_->createDateTime(
+ year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, (int)tm.tm_gmtoff
+ )
+ );
+ kv.push_back( make_pair( mtime_key, mtime_value ) );
+ // no break;
+ }
+ case FTPPARSE_MTIME_UNKNOWN:
+ // do nothing
+ break;
+ } // switch
+
+ result = factory_->createJSONObject( kv );
+ return true;
+ } // if
+ } // while
+ return false;
+}
+
+void list_iterator::open() {
+ open_ = true;
+}
+
+list_function::list_function( module const *m ) :
+ function( m, "list" )
+{
+}
+
+ItemSequence_t
+list_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ String const uri( make_uri( conn, path, true ) );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ ZORBA_CURL_ASSERT( curl_easy_setopt( cobj, CURLOPT_URL, uri.c_str() ) );
+ try {
+ return ItemSequence_t(
+ new list_iterator( cbuf, module_->getItemFactory() )
+ );
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+mkdir_function::mkdir_function( module const *m ) :
+ function( m, "mkdir" )
+{
+}
+
+ItemSequence_t
+mkdir_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+ String const command( "MKD " + path );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ curl_easy_setopt( cobj, CURLOPT_CUSTOMREQUEST, command.c_str() );
+
+ try {
+ curl_helper helper( cbuf );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ }
+ catch ( curl::exception const &e ) {
+ int const ftp_code = get_ftp_reply_code( cobj );
+ switch ( ftp_code ) {
+ case FTP_REPLY_ACTION_NOT_TAKEN:
+ THROW_EXCEPTION( "FTP_ERROR", path, "directory exists", ftp_code );
+ case FTP_REPLY_PATH_CREATED:
+ if ( e.curl_code() == CURLE_FTP_COULDNT_RETR_FILE ) {
+ //
+ // After the directory is created, CURL tries to RETR it which fails,
+ // so ignore that error.
+ // (Also see <http://stackoverflow.com/a/13515807/99089>.)
+ //
+ break;
+ }
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ default:
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what(), ftp_code );
+ } // switch
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ }
+ return ItemSequence_t( new EmptySequence() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+put_binary_function::put_binary_function( module const *m ) :
+ put_function( m, "put-binary", false )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+put_text_function::put_text_function( module const *m ) :
+ put_function( m, "put-text", true )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static size_t curl_header_callback( void *ptr, size_t size, size_t nmemb,
+ void *data ) {
+ size *= nmemb;
+ String *const ftp_reply_msg = static_cast<String*>( data );
+
+ // skip 3-digit reply code and 1 space
+ char const *s = static_cast<char*>( ptr ) + 4;
+ size_t len = size - 4;
+
+ // trim trailing whitespace
+ while ( len && isspace( s[ len - 1 ] ) )
+ --len;
+
+ ftp_reply_msg->assign( s, len );
+ return size; // must always return original size
+}
+
+rename_function::rename_function( module const *m ) :
+ function( m, "rename" )
+{
+}
+
+ItemSequence_t
+rename_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const from_path( get_string_arg( args, 1 ) );
+ if ( from_path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "\"from\" path empty" );
+ String const to_path( get_string_arg( args, 2 ) );
+ if ( to_path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "\to\" path empty" );
+ String const command1( "RNFR " + from_path );
+ String const command2( "RNTO " + to_path );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+
+ curl_slist *slist = 0;
+ slist = curl_slist_append( slist, command1.c_str() );
+ slist = curl_slist_append( slist, command2.c_str() );
+ curl_easy_setopt( cobj, CURLOPT_QUOTE, slist );
+
+ //
+ // When doing a rename, there isn't a 1-to-1 mapping between an FTP reply
+ // code and a specific error; hence we can't use our own error message.
+ // Instead, we capture the FTP replies and parse the messages out of them.
+ // We actually only care about the last one since that's the one for the
+ // error. We then use that for the error message.
+ //
+ curl_easy_setopt( cobj, CURLOPT_HEADERFUNCTION, curl_header_callback );
+ String ftp_reply_msg;
+ curl_easy_setopt( cobj, CURLOPT_HEADERDATA, &ftp_reply_msg );
+
+ try {
+ curl_helper helper( cbuf, slist );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ }
+ catch ( curl::exception const &e ) {
+ int const ftp_code = get_ftp_reply_code( cobj );
+ switch ( ftp_code ) {
+ case FTP_REPLY_ACTION_ABORTED:
+ THROW_EXCEPTION( "FTP_ERROR", to_path, ftp_reply_msg, ftp_code );
+ case FTP_REPLY_ACTION_NOT_TAKEN:
+ THROW_EXCEPTION( "FTP_ERROR", from_path, ftp_reply_msg, ftp_code );
+ default:
+ THROW_EXCEPTION( "FTP_ERROR", from_path, e.what(), ftp_code );
+ } // switch
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", from_path, e.what() );
+ }
+ return ItemSequence_t( new EmptySequence() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+rmdir_function::rmdir_function( module const *m ) :
+ function( m, "rmdir" )
+{
+}
+
+ItemSequence_t
+rmdir_function::evaluate( ExternalFunction::Arguments_t const &args,
+ StaticContext const*,
+ DynamicContext const *dctx ) const {
+ String const conn( get_string_arg( args, 0 ) );
+ String const path( get_string_arg( args, 1 ) );
+ if ( path.empty() )
+ THROW_EXCEPTION( "INVALID_ARGUMENT", "", "empty path" );
+ String const command( "RMD " + path );
+
+ curl::streambuf *const cbuf = require_connection( dctx, conn );
+ CURL *const cobj = cbuf->curl();
+ curl_easy_setopt( cobj, CURLOPT_CUSTOMREQUEST, command.c_str() );
+
+ try {
+ curl_helper helper( cbuf );
+ ZORBA_CURL_ASSERT( curl_easy_perform( cobj ) );
+ }
+ catch ( curl::exception const &e ) {
+ int const ftp_code = get_ftp_reply_code( cobj );
+ switch ( ftp_code ) {
+ case FTP_REPLY_ACTION_COMPLETED:
+ if ( e.curl_code() == CURLE_FTP_COULDNT_RETR_FILE ) {
+ //
+ // After the directory is deleted, CURL tries to RETR it which fails,
+ // so ignore that error.
+ // (Also see <http://stackoverflow.com/a/13515807/99089>.)
+ //
+ break;
+ }
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ case FTP_REPLY_ACTION_NOT_TAKEN:
+ THROW_EXCEPTION( "FTP_ERROR", path, "directory not found", ftp_code );
+ default:
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what(), ftp_code );
+ } // switch
+ }
+ catch ( std::exception const &e ) {
+ THROW_EXCEPTION( "FTP_ERROR", path, e.what() );
+ }
+ return ItemSequence_t( new EmptySequence() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_functions.h'
--- modules/ftp-client/ftp-client.xq.src/ftp_functions.h 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_functions.h 2014-01-09 19:12:12 +0000
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ZORBA_FTP_CLIENT_MODULE_FUNCTIONS_H
+#define ZORBA_FTP_CLIENT_MODULE_FUNCTIONS_H
+
+// Zorba
+#include <zorba/function.h>
+#include <zorba/item.h>
+#include <zorba/zorba_string.h>
+
+#include "curl_streambuf.h"
+
+namespace zorba {
+namespace ftp_client {
+
+class module;
+
+///////////////////////////////////////////////////////////////////////////////
+
+class function : public ContextualExternalFunction {
+public:
+ // inherited
+ virtual String getLocalName() const;
+ virtual String getURI() const;
+
+protected:
+ function( module const *m, char const *local_name );
+
+ bool get_bool_opt( Item const&, char const*, bool = false ) const;
+ int get_integer_opt( Item const&, char const*, int = 0 ) const;
+ Item get_item_arg( ExternalFunction::Arguments_t const&, unsigned ) const;
+ String get_string_arg( ExternalFunction::Arguments_t const&, unsigned ) const;
+ String get_string_opt( Item const&, char const*, char const* = "" ) const;
+
+ curl::streambuf* require_connection( DynamicContext const*,
+ String const& ) const;
+
+ void throw_exception( char const*, char const*, char const*, int = 0 ) const;
+
+ void throw_exception( char const *error_code, String const &s,
+ char const *message, int ftp_code = 0 ) const {
+ throw_exception( error_code, s.c_str(), message, ftp_code );
+ }
+
+ void throw_exception( char const *error_code, String const &s,
+ String const &message, int ftp_code = 0 ) const {
+ throw_exception( error_code, s.c_str(), message.c_str(), ftp_code );
+ }
+
+ module const *const module_;
+ char const *const local_name_; // points to C string literal
+};
+
+class get_function : public function {
+public:
+ // inherited
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+
+protected:
+ get_function( module const*, char const *local_name, bool text );
+
+ bool const text_;
+};
+
+class put_function : public function {
+public:
+ // inherited
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+
+protected:
+ put_function( module const*, char const *local_name, bool text );
+
+ bool const text_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct connect_function : function {
+ connect_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct disconnect_function : function {
+ disconnect_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct delete_function : function {
+ delete_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct get_binary_function : get_function {
+ get_binary_function( module const* );
+};
+
+struct get_text_function : get_function {
+ get_text_function( module const* );
+};
+
+struct list_function : function {
+ list_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct mkdir_function : function {
+ mkdir_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct put_binary_function : put_function {
+ put_binary_function( module const* );
+};
+
+struct put_text_function : put_function {
+ put_text_function( module const* );
+};
+
+struct rename_function : function {
+ rename_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+struct rmdir_function : function {
+ rmdir_function( module const* );
+ ItemSequence_t evaluate( ExternalFunction::Arguments_t const&,
+ StaticContext const*, DynamicContext const* ) const;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+#endif /* ZORBA_FTP_CLIENT_MODULE_FUNCTIONS_H */
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_module.cpp'
--- modules/ftp-client/ftp-client.xq.src/ftp_module.cpp 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_module.cpp 2014-01-09 19:12:12 +0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Zorba
+#include <zorba/zorba.h>
+
+// local
+#include "ftp_functions.h"
+#include "ftp_module.h"
+
+namespace zorba {
+namespace ftp_client {
+
+///////////////////////////////////////////////////////////////////////////////
+
+module::module() {
+ item_factory_ = 0;
+}
+
+module::~module() {
+ for ( func_map_type::const_iterator i = func_map_.begin();
+ i != func_map_.end(); ++i ) {
+ delete i->second;
+ }
+}
+
+void module::destroy() {
+ delete this;
+}
+
+ExternalFunction* module::getExternalFunction( String const &local_name ) {
+ ExternalFunction *&f = func_map_[ local_name ];
+ if ( !f ) {
+ if ( local_name == "connect" )
+ f = new connect_function( this );
+ else if ( local_name == "delete" )
+ f = new delete_function( this );
+ else if ( local_name == "disconnect" )
+ f = new disconnect_function( this );
+ else if ( local_name == "get-binary" )
+ f = new get_binary_function( this );
+ else if ( local_name == "get-text" )
+ f = new get_text_function( this );
+ else if ( local_name == "list" )
+ f = new list_function( this );
+ else if ( local_name == "mkdir" )
+ f = new mkdir_function( this );
+ else if ( local_name == "put-binary" )
+ f = new put_binary_function( this );
+ else if ( local_name == "put-text" )
+ f = new put_text_function( this );
+ else if ( local_name == "rename" )
+ f = new rename_function( this );
+ else if ( local_name == "rmdir" )
+ f = new rmdir_function( this );
+ }
+ return f;
+}
+
+ItemFactory* module::getItemFactory() const {
+ if ( !item_factory_ )
+ item_factory_ = Zorba::getInstance(0)->getItemFactory();
+ return item_factory_;
+}
+
+String module::getURI() const {
+ static String const uri( "http://zorba.io/modules/ftp-client" );
+ return uri;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+
+#ifdef WIN32
+# define DLL_EXPORT __declspec(dllexport)
+#else
+# define DLL_EXPORT __attribute__ ((visibility("default")))
+#endif /* WIN32 */
+
+extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
+ return new zorba::ftp_client::module();
+}
+
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftp_module.h'
--- modules/ftp-client/ftp-client.xq.src/ftp_module.h 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftp_module.h 2014-01-09 19:12:12 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ZORBA_FTP_CLIENT_FTP_MODULE_H
+#define ZORBA_FTP_CLIENT_FTP_MODULE_H
+
+// standard
+#include <map>
+
+// Zorba
+#include <zorba/external_module.h>
+
+namespace zorba {
+namespace ftp_client {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class module : public ExternalModule {
+public:
+ module();
+ ~module();
+
+ ItemFactory* getItemFactory() const;
+
+ // inherited
+ virtual void destroy();
+ virtual ExternalFunction* getExternalFunction( String const& );
+ virtual String getURI() const;
+
+private:
+ // map function names -> ExternalFunction*
+ typedef std::map<String,ExternalFunction*> func_map_type;
+ mutable func_map_type func_map_;
+ mutable ItemFactory *item_factory_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace ftp_client
+} // namespace zorba
+
+#endif /* ZORBA_FTP_CLIENT_FTP_MODULE_H */
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/ftp-client/ftp-client.xq.src/ftpparse.c'
--- modules/ftp-client/ftp-client.xq.src/ftpparse.c 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftpparse.c 2014-01-09 19:12:12 +0000
@@ -0,0 +1,448 @@
+/* ftpparse.c, ftpparse.h: library for parsing FTP LIST responses
+20001223
+D. J. Bernstein, djb@xxxxxxxx
+http://cr.yp.to/ftpparse.html
+
+Commercial use is fine, if you let me know what programs you're using this in.
+
+Currently covered formats:
+EPLF.
+UNIX ls, with or without gid.
+Microsoft FTP Service.
+Windows NT FTP Server.
+VMS.
+WFTPD.
+NetPresenz (Mac).
+NetWare.
+MSDOS.
+
+Definitely not covered:
+Long VMS filenames, with information split across two lines.
+NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
+*/
+
+#include <time.h>
+#include "ftpparse.h"
+
+static long totai(long year,long month,long mday)
+{
+ long result;
+ if (month >= 2) month -= 2;
+ else { month += 10; --year; }
+ result = (mday - 1) * 10 + 5 + 306 * month;
+ result /= 10;
+ if (result == 365) { year -= 3; result = 1460; }
+ else result += 365 * (year % 4);
+ year /= 4;
+ result += 1461 * (year % 25);
+ year /= 25;
+ if (result == 36524) { year -= 3; result = 146096; }
+ else { result += 36524 * (year % 4); }
+ year /= 4;
+ result += 146097 * (year - 5);
+ result += 11017;
+ return result * 86400;
+}
+
+static int flagneedbase = 1;
+static time_t base; /* time() value on this OS at the beginning of 1970 TAI */
+static long now; /* current time */
+static int flagneedcurrentyear = 1;
+static long currentyear; /* approximation to current year */
+
+static void initbase(void)
+{
+ struct tm *t;
+ if (!flagneedbase) return;
+
+ base = 0;
+ t = gmtime(&base);
+ base = -(totai(t->tm_year + 1900,t->tm_mon,t->tm_mday) + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec);
+ /* assumes the right time_t, counting seconds. */
+ /* base may be slightly off if time_t counts non-leap seconds. */
+ flagneedbase = 0;
+}
+
+static void initnow(void)
+{
+ long day;
+ long year;
+
+ initbase();
+ now = time((time_t *) 0) - base;
+
+ if (flagneedcurrentyear) {
+ day = now / 86400;
+ if ((now % 86400) < 0) --day;
+ day -= 11017;
+ year = 5 + day / 146097;
+ day = day % 146097;
+ if (day < 0) { day += 146097; --year; }
+ year *= 4;
+ if (day == 146096) { year += 3; day = 36524; }
+ else { year += day / 36524; day %= 36524; }
+ year *= 25;
+ year += day / 1461;
+ day %= 1461;
+ year *= 4;
+ if (day == 1460) { year += 3; day = 365; }
+ else { year += day / 365; day %= 365; }
+ day *= 10;
+ if ((day + 5) / 306 >= 10) ++year;
+ currentyear = year;
+ flagneedcurrentyear = 0;
+ }
+}
+
+/* UNIX ls does not show the year for dates in the last six months. */
+/* So we have to guess the year. */
+/* Apparently NetWare uses ``twelve months'' instead of ``six months''; ugh. */
+/* Some versions of ls also fail to show the year for future dates. */
+static long guesstai(long month,long mday)
+{
+ long year;
+ long t;
+
+ initnow();
+
+ for (year = currentyear - 1;year < currentyear + 100;++year) {
+ t = totai(year,month,mday);
+ if (now - t < 350 * 86400)
+ return t;
+ }
+ return 0;
+}
+
+static int check(const char *buf,const char *monthname)
+{
+ if ((buf[0] != monthname[0]) && (buf[0] != monthname[0] - 32)) return 0;
+ if ((buf[1] != monthname[1]) && (buf[1] != monthname[1] - 32)) return 0;
+ if ((buf[2] != monthname[2]) && (buf[2] != monthname[2] - 32)) return 0;
+ return 1;
+}
+
+static const char *months[12] = {
+ "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"
+} ;
+
+static int getmonth(const char *buf,int len)
+{
+ int i;
+ if (len == 3)
+ for (i = 0;i < 12;++i)
+ if (check(buf,months[i])) return i;
+ return -1;
+}
+
+static long getlong(const char *buf,int len)
+{
+ long u = 0;
+ while (len-- > 0)
+ u = u * 10 + (*buf++ - '0');
+ return u;
+}
+
+int ftpparse(struct ftpparse *fp,const char *buf,int len)
+{
+ int i;
+ int j;
+ int state;
+ long size;
+ long year;
+ long month;
+ long mday;
+ long hour;
+ long minute;
+
+ fp->name = 0;
+ fp->namelen = 0;
+ fp->flagtrycwd = 0;
+ fp->flagtryretr = 0;
+ fp->sizetype = FTPPARSE_SIZE_UNKNOWN;
+ fp->size = 0;
+ fp->mtimetype = FTPPARSE_MTIME_UNKNOWN;
+ fp->mtime = 0;
+ fp->idtype = FTPPARSE_ID_UNKNOWN;
+ fp->id = 0;
+ fp->idlen = 0;
+
+ if (len < 2) /* an empty name in EPLF, with no info, could be 2 chars */
+ return 0;
+
+ switch(*buf) {
+ /* see http://pobox.com/~djb/proto/eplf.txt */
+ /* "+i8388621.29609,m824255902,/,\tdev" */
+ /* "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF" */
+ case '+':
+ i = 1;
+ for (j = 1;j < len;++j) {
+ if (buf[j] == 9) {
+ fp->name = buf + j + 1;
+ fp->namelen = len - j - 1;
+ return 1;
+ }
+ if (buf[j] == ',') {
+ switch(buf[i]) {
+ case '/':
+ fp->flagtrycwd = 1;
+ break;
+ case 'r':
+ fp->flagtryretr = 1;
+ break;
+ case 's':
+ fp->sizetype = FTPPARSE_SIZE_BINARY;
+ fp->size = getlong(buf + i + 1,j - i - 1);
+ break;
+ case 'm':
+ fp->mtimetype = FTPPARSE_MTIME_LOCAL;
+ initbase();
+ fp->mtime = base + getlong(buf + i + 1,j - i - 1);
+ break;
+ case 'i':
+ fp->idtype = FTPPARSE_ID_FULL;
+ fp->id = buf + i + 1;
+ fp->idlen = j - i - 1;
+ }
+ i = j + 1;
+ }
+ }
+ return 0;
+
+ /* UNIX-style listing, without inum and without blocks */
+ /* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */
+ /* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */
+ /* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */
+ /* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */
+ /* Also produced by Microsoft's FTP servers for Windows: */
+ /* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */
+ /* "d--------- 1 owner group 0 May 9 19:45 Softlib" */
+ /* Also WFTPD for MSDOS: */
+ /* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
+ /* Also NetWare: */
+ /* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
+ /* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */
+ /* Also NetPresenz for the Mac: */
+ /* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
+ /* "drwxrwxr-x folder 2 May 10 1996 network" */
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'l':
+ case 'p':
+ case 's':
+ case '-':
+
+ if (*buf == 'd') fp->flagtrycwd = 1;
+ if (*buf == '-') fp->flagtryretr = 1;
+ if (*buf == 'l') fp->flagtrycwd = fp->flagtryretr = 1;
+
+ state = 1;
+ i = 0;
+ for (j = 1;j < len;++j)
+ if ((buf[j] == ' ') && (buf[j - 1] != ' ')) {
+ switch(state) {
+ case 1: /* skipping perm */
+ state = 2;
+ break;
+ case 2: /* skipping nlink */
+ state = 3;
+ if ((j - i == 6) && (buf[i] == 'f')) /* for NetPresenz */
+ state = 4;
+ break;
+ case 3: /* skipping uid */
+ state = 4;
+ break;
+ case 4: /* getting tentative size */
+ size = getlong(buf + i,j - i);
+ state = 5;
+ break;
+ case 5: /* searching for month, otherwise getting tentative size */
+ month = getmonth(buf + i,j - i);
+ if (month >= 0)
+ state = 6;
+ else
+ size = getlong(buf + i,j - i);
+ break;
+ case 6: /* have size and month */
+ mday = getlong(buf + i,j - i);
+ state = 7;
+ break;
+ case 7: /* have size, month, mday */
+ if ((j - i == 4) && (buf[i + 1] == ':')) {
+ hour = getlong(buf + i,1);
+ minute = getlong(buf + i + 2,2);
+ fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
+ initbase();
+ fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
+ } else if ((j - i == 5) && (buf[i + 2] == ':')) {
+ hour = getlong(buf + i,2);
+ minute = getlong(buf + i + 3,2);
+ fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
+ initbase();
+ fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
+ }
+ else if (j - i >= 4) {
+ year = getlong(buf + i,j - i);
+ fp->mtimetype = FTPPARSE_MTIME_REMOTEDAY;
+ initbase();
+ fp->mtime = base + totai(year,month,mday);
+ }
+ else
+ return 0;
+ fp->name = buf + j + 1;
+ fp->namelen = len - j - 1;
+ state = 8;
+ break;
+ case 8: /* twiddling thumbs */
+ break;
+ }
+ i = j + 1;
+ while ((i < len) && (buf[i] == ' ')) ++i;
+ }
+
+ if (state != 8)
+ return 0;
+
+ fp->size = size;
+ fp->sizetype = FTPPARSE_SIZE_BINARY;
+
+ if (*buf == 'l')
+ for (i = 0;i + 3 < fp->namelen;++i)
+ if (fp->name[i] == ' ')
+ if (fp->name[i + 1] == '-')
+ if (fp->name[i + 2] == '>')
+ if (fp->name[i + 3] == ' ') {
+ fp->namelen = i;
+ break;
+ }
+
+ /* eliminate extra NetWare spaces */
+ if ((buf[1] == ' ') || (buf[1] == '['))
+ if (fp->namelen > 3)
+ if (fp->name[0] == ' ')
+ if (fp->name[1] == ' ')
+ if (fp->name[2] == ' ') {
+ fp->name += 3;
+ fp->namelen -= 3;
+ }
+
+ return 1;
+ }
+
+ /* MultiNet (some spaces removed from examples) */
+ /* "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)" */
+ /* "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)" */
+ /* and non-MutliNet VMS: */
+ /* "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)" */
+ for (i = 0;i < len;++i)
+ if (buf[i] == ';')
+ break;
+ if (i < len) {
+ fp->name = buf;
+ fp->namelen = i;
+ if (i > 4)
+ if (buf[i - 4] == '.')
+ if (buf[i - 3] == 'D')
+ if (buf[i - 2] == 'I')
+ if (buf[i - 1] == 'R') {
+ fp->namelen -= 4;
+ fp->flagtrycwd = 1;
+ }
+ if (!fp->flagtrycwd)
+ fp->flagtryretr = 1;
+ while (buf[i] != ' ') if (++i == len) return 0;
+ while (buf[i] == ' ') if (++i == len) return 0;
+ while (buf[i] != ' ') if (++i == len) return 0;
+ while (buf[i] == ' ') if (++i == len) return 0;
+ j = i;
+ while (buf[j] != '-') if (++j == len) return 0;
+ mday = getlong(buf + i,j - i);
+ while (buf[j] == '-') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != '-') if (++j == len) return 0;
+ month = getmonth(buf + i,j - i);
+ if (month < 0) return 0;
+ while (buf[j] == '-') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != ' ') if (++j == len) return 0;
+ year = getlong(buf + i,j - i);
+ while (buf[j] == ' ') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != ':') if (++j == len) return 0;
+ hour = getlong(buf + i,j - i);
+ while (buf[j] == ':') if (++j == len) return 0;
+ i = j;
+ while ((buf[j] != ':') && (buf[j] != ' ')) if (++j == len) return 0;
+ minute = getlong(buf + i,j - i);
+
+ fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
+ initbase();
+ fp->mtime = base + totai(year,month,mday) + hour * 3600 + minute * 60;
+
+ return 1;
+ }
+
+ /* MSDOS format */
+ /* 04-27-00 09:09PM <DIR> licensed */
+ /* 07-18-00 10:16AM <DIR> pub */
+ /* 04-14-00 03:47PM 589 readme.htm */
+ if ((*buf >= '0') && (*buf <= '9')) {
+ i = 0;
+ j = 0;
+ while (buf[j] != '-') if (++j == len) return 0;
+ month = getlong(buf + i,j - i) - 1;
+ while (buf[j] == '-') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != '-') if (++j == len) return 0;
+ mday = getlong(buf + i,j - i);
+ while (buf[j] == '-') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != ' ') if (++j == len) return 0;
+ year = getlong(buf + i,j - i);
+ if (year < 50) year += 2000;
+ if (year < 1000) year += 1900;
+ while (buf[j] == ' ') if (++j == len) return 0;
+ i = j;
+ while (buf[j] != ':') if (++j == len) return 0;
+ hour = getlong(buf + i,j - i);
+ while (buf[j] == ':') if (++j == len) return 0;
+ i = j;
+ while ((buf[j] != 'A') && (buf[j] != 'P')) if (++j == len) return 0;
+ minute = getlong(buf + i,j - i);
+ if (hour == 12) hour = 0;
+ if (buf[j] == 'A') if (++j == len) return 0;
+ if (buf[j] == 'P') { hour += 12; if (++j == len) return 0; }
+ if (buf[j] == 'M') if (++j == len) return 0;
+
+ while (buf[j] == ' ') if (++j == len) return 0;
+ if (buf[j] == '<') {
+ fp->flagtrycwd = 1;
+ while (buf[j] != ' ') if (++j == len) return 0;
+ }
+ else {
+ i = j;
+ while (buf[j] != ' ') if (++j == len) return 0;
+ fp->size = getlong(buf + i,j - i);
+ fp->sizetype = FTPPARSE_SIZE_BINARY;
+ fp->flagtryretr = 1;
+ }
+ while (buf[j] == ' ') if (++j == len) return 0;
+
+ fp->name = buf + j;
+ fp->namelen = len - j;
+
+ fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
+ initbase();
+ fp->mtime = base + totai(year,month,mday) + hour * 3600 + minute * 60;
+
+ return 1;
+ }
+
+ /* Some useless lines, safely ignored: */
+ /* "Total of 11 Files, 10966 Blocks." (VMS) */
+ /* "total 14786" (UNIX) */
+ /* "DISK$ANONFTP:[ANONYMOUS]" (VMS) */
+ /* "Directory DISK$PCSA:[ANONYM]" (VMS) */
+
+ return 0;
+}
=== added file 'modules/ftp-client/ftp-client.xq.src/ftpparse.h'
--- modules/ftp-client/ftp-client.xq.src/ftpparse.h 1970-01-01 00:00:00 +0000
+++ modules/ftp-client/ftp-client.xq.src/ftpparse.h 2014-01-09 19:12:12 +0000
@@ -0,0 +1,51 @@
+#ifndef FTPPARSE_H
+#define FTPPARSE_H
+
+/*
+ftpparse(&fp,buf,len) tries to parse one line of LIST output.
+
+The line is an array of len characters stored in buf.
+It should not include the terminating CR LF; so buf[len] is typically CR.
+
+If ftpparse() can't find a filename, it returns 0.
+
+If ftpparse() can find a filename, it fills in fp and returns 1.
+fp is a struct ftpparse, defined below.
+The name is an array of fp.namelen characters stored in fp.name;
+fp.name points somewhere within buf.
+*/
+
+struct ftpparse {
+ const char *name; /* not necessarily 0-terminated */
+ int namelen;
+ int flagtrycwd; /* 0 if cwd is definitely pointless, 1 otherwise */
+ int flagtryretr; /* 0 if retr is definitely pointless, 1 otherwise */
+ int sizetype;
+ long size; /* number of octets */
+ int mtimetype;
+ time_t mtime; /* modification time */
+ int idtype;
+ const char *id; /* not necessarily 0-terminated */
+ int idlen;
+} ;
+
+#define FTPPARSE_SIZE_UNKNOWN 0
+#define FTPPARSE_SIZE_BINARY 1 /* size is the number of octets in TYPE I */
+#define FTPPARSE_SIZE_ASCII 2 /* size is the number of octets in TYPE A */
+
+#define FTPPARSE_MTIME_UNKNOWN 0
+#define FTPPARSE_MTIME_LOCAL 1 /* time is correct */
+#define FTPPARSE_MTIME_REMOTEMINUTE 2 /* time zone and secs are unknown */
+#define FTPPARSE_MTIME_REMOTEDAY 3 /* time zone and time of day are unknown */
+/*
+When a time zone is unknown, it is assumed to be GMT. You may want
+to use localtime() for LOCAL times, along with an indication that the
+time is correct in the local time zone, and gmtime() for REMOTE* times.
+*/
+
+#define FTPPARSE_ID_UNKNOWN 0
+#define FTPPARSE_ID_FULL 1 /* unique identifier for files on this FTP server */
+
+extern int ftpparse(struct ftpparse *,const char *,int);
+
+#endif
=== modified file 'modules/http-client/CMakeLists.txt'
--- modules/http-client/CMakeLists.txt 2013-07-25 14:47:04 +0000
+++ modules/http-client/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -1,64 +1,52 @@
# Copyright 2006-2012 The FLWOR Foundation.
-#
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-#
-# cURL
-#
-SET (CURL_FOUND)
-IF(ZORBA_SUPPRESS_CURL)
- MESSAGE(STATUS "ZORBA_SUPPRESS_CURL is true - not searching for cURL library")
-ELSE(ZORBA_SUPPRESS_CURL)
- MESSAGE(STATUS "Looking for cURL")
- FIND_PACKAGE(CURL)
-
- IF(CURL_FOUND)
- MESSAGE(STATUS "Found cURL library -- " ${CURL_LIBRARIES})
- INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
+IF (ZORBA_HAVE_CURL)
+ SET(ZorbaUtilCurlModule_DIR "../util-curl")
+ FIND_PACKAGE(ZorbaUtilCurlModule REQUIRED)
+ IF (ZorbaUtilCurlModule_FOUND)
+ INCLUDE_DIRECTORIES("${ZorbaUtilCurlModule_INCLUDE_DIRS}")
DECLARE_ZORBA_MODULE(FILE xml/http-client-error.xq
URI "http://expath.org/ns/error")
-
+
DECLARE_ZORBA_SCHEMA(FILE xml/http-client.xsd
URI "http://expath.org/ns/http-client")
-
+
DECLARE_ZORBA_MODULE(FILE xml/http-client.xq VERSION 2.0
URI "http://www.zorba-xquery.com/modules/http-client"
- LINK_LIBRARIES ${CURL_LIBRARIES})
-
+ LINK_LIBRARIES ${CURL_LIBRARIES} ${ZorbaUtilCurlModule_LIBS})
+
DECLARE_ZORBA_MODULE(FILE json/http-client.xq VERSION 1.0
URI "http://zorba.io/modules/http-client"
- LINK_LIBRARIES ${CURL_LIBRARIES})
-
+ LINK_LIBRARIES ${CURL_LIBRARIES} ${ZorbaUtilCurlModule_LIBS})
+
DECLARE_ZORBA_MODULE(FILE conv/http-client-wrapper.xq VERSION 1.0
URI "http://zorba.io/modules/http-client-wrapper"
- LINK_LIBRARIES ${CURL_LIBRARIES})
-
+ LINK_LIBRARIES ${CURL_LIBRARIES} ${ZorbaUtilCurlModule_LIBS})
+
IF (WIN32) # Copy certificates for windows only
IF (MSVC_IDE)
SET(CACERT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../../bin/${CMAKE_BUILD_TYPE}/cacert.pem")
ELSE (MSVC_IDE)
- SET(CACERT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../../bin/cacert.pem")
+ SET(CACERT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../../bin/cacert.pem")
ENDIF (MSVC_IDE)
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cacert.pem" ${CACERT_DESTINATION} COPYONLY)
INSTALL(FILES ${CACERT_DESTINATION} DESTINATION bin)
- ENDIF (WIN32)
-
- ELSE(CURL_FOUND)
- MESSAGE(STATUS "The cURL library was not found - http-client will not be built")
- ENDIF(CURL_FOUND)
-ENDIF(ZORBA_SUPPRESS_CURL)
-SET(ZORBA_HAVE_CURL ${CURL_FOUND} CACHE BOOL "Whether Zorba found cURL" FORCE)
-MARK_AS_ADVANCED(ZORBA_HAVE_CURL)
+ ENDIF (WIN32)
+
+ ENDIF (ZorbaUtilCurlModule_FOUND)
+ENDIF (ZORBA_HAVE_CURL)
# vim:set et sw=2 ts=2:
=== modified file 'modules/http-client/json/http-client.xq'
--- modules/http-client/json/http-client.xq 2013-09-26 23:15:11 +0000
+++ modules/http-client/json/http-client.xq 2014-01-09 19:12:12 +0000
@@ -186,6 +186,7 @@
module namespace http = "http://zorba.io/modules/http-client";
import module namespace libjn = "http://jsoniq.org/function-library";
+import module namespace util-curl = "http://zorba.io/modules/util-curl";
declare namespace an = "http://zorba.io/annotations";
declare namespace ver = "http://zorba.io/options/versioning";
=== removed file 'modules/http-client/json/http-client.xq.src/curl_stream_buffer.cpp'
--- modules/http-client/json/http-client.xq.src/curl_stream_buffer.cpp 2013-06-14 16:32:01 +0000
+++ modules/http-client/json/http-client.xq.src/curl_stream_buffer.cpp 1970-01-01 00:00:00 +0000
@@ -1,379 +0,0 @@
-/*
- * Copyright 2006-2013 The FLWOR Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <zorba/config.h>
-
-#include <cstdlib>
-#include <cstring> /* for memcpy(3) */
-#include <iostream>
-#include <cassert>
-#ifndef WIN32
-#include <cerrno>
-#include <sys/time.h>
-#endif /* WIN32 */
-
-#include <curl/multi.h>
-
-#include "curl_stream_buffer.h"
-#include "inform_data_read.h"
-
-using namespace std;
-
-namespace zorba {
-namespace curl {
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define ZORBA_CURL_ASSERT(expr) \
- do { \
- if ( CURLcode const code##__LINE__ = (expr) ) \
- throw exception( #expr, "", code##__LINE__ ); \
- } while (0)
-
-#define ZORBA_CURLM_ASSERT(expr) \
- do { \
- if ( CURLMcode const code##__LINE__ = (expr) ) \
- if ( code##__LINE__ != CURLM_CALL_MULTI_PERFORM ) \
- throw exception( #expr, "", code##__LINE__ ); \
- } while (0)
-
-exception::exception( char const *function, char const *uri, char const *msg ) :
- std::exception(), msg_( msg )
-{
-}
-
-exception::exception( char const *function, char const *uri, CURLcode code ) :
- std::exception(),
- msg_( curl_easy_strerror( code ) )
-{
-}
-
-exception::exception( char const *function, char const *uri, CURLMcode code ) :
- std::exception(),
- msg_( curl_multi_strerror( code ) )
-{
-}
-
-exception::~exception() throw() {
- // out-of-line since it's virtual
-}
-
-const char* exception::what() const throw() {
- return msg_.c_str();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-CURL* create( char const *uri, write_fn_t fn, void *data ) {
- //
- // Having cURL initialization wrapped by a class and using a singleton static
- // instance guarantees that cURL is initialized exactly once before use and
- // and also is cleaned-up at program termination (when destructors for static
- // objects are called).
- //
- struct curl_initializer {
- curl_initializer() {
- ZORBA_CURL_ASSERT( curl_global_init( CURL_GLOBAL_ALL ) );
- }
- ~curl_initializer() {
- curl_global_cleanup();
- }
- };
- static curl_initializer initializer;
-
- CURL *const curl = curl_easy_init();
- if ( !curl )
- throw exception( "curl_easy_init()", uri, "" );
-
- try {
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_URL, uri ) );
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_WRITEDATA, data ) );
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, fn ) );
-
- // Tells cURL to follow redirects. CURLOPT_MAXREDIRS is by default set to -1
- // thus cURL will do an infinite number of redirects.
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1 ) );
-
-#ifndef ZORBA_VERIFY_PEER_SSL_CERTIFICATE
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 ) );
- //
- // CURLOPT_SSL_VERIFYHOST is left default, value 2, meaning verify that the
- // Common Name or Subject Alternate Name field in the certificate matches
- // the name of the server.
- //
- // Tested with https://www.npr.org/rss/rss.php?id=1001
- // About using SSL certs in curl: http://curl.haxx.se/docs/sslcerts.html
-#else
-# ifdef WIN32
- // set the root CA certificates file path
- if ( GENV.g_curl_root_CA_certificates_path[0] )
- ZORBA_CURL_ASSERT(
- curl_easy_setopt(
- curl, CURLOPT_CAINFO, GENV.g_curl_root_CA_certificates_path
- )
- );
-# endif /* WIN32 */
-#endif /* ZORBA_VERIFY_PEER_SSL_CERTIFICATE */
-
- //
- // Some servers don't like requests that are made without a user-agent
- // field, so we provide one.
- //
- //ZORBA_CURL_ASSERT(
- //curl_easy_setopt( curl, CURLOPT_USERAGENT, "libcurl-agent/1.0" )
- //);
-
- return curl;
- }
- catch ( ... ) {
- destroy( curl );
- throw;
- }
-}
-
-void destroy( CURL *curl ) {
- if ( curl ) {
- curl_easy_reset( curl );
- curl_easy_cleanup( curl );
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-streambuf::streambuf() {
- init();
-}
-
-streambuf::streambuf( char const *uri ) {
- init();
- open( uri );
-}
-
-streambuf::streambuf( CURL *curl ) {
- init();
- curl_ = curl;
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_WRITEDATA, this ) );
- ZORBA_CURL_ASSERT( curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_write_callback ) );
- init_curlm();
-}
-
-streambuf::~streambuf() {
- free( buf_ );
- close();
-#ifdef WIN32
- closesocket( dummy_socket_ );
-#endif
- // If we have been assigned memory ownership of theInformer, delete it now.
- if ( theOwnInformer )
- delete theInformer;
-}
-
-void streambuf::close() {
- if ( curl_ ) {
- if ( curlm_ ) {
- curl_multi_remove_handle( curlm_, curl_ );
- curl_multi_cleanup( curlm_ );
- curlm_ = 0;
- }
- destroy( curl_ );
- curl_ = 0;
- }
-}
-
-void streambuf::curl_read() {
- buf_len_ = 0;
- while ( curl_running_ && !buf_len_ ) {
- fd_set fd_read, fd_write, fd_except;
- FD_ZERO( &fd_read );
- FD_ZERO( &fd_write );
- FD_ZERO( &fd_except );
- int max_fd = -1;
-#ifdef WIN32
- //
- // Windows does not like a call to select where all arguments are 0, so we
- // just add a dummy socket to make the call to select happy.
- //
- FD_SET( dummy_socket_, &fd_read );
-#endif /* WIN32 */
- ZORBA_CURLM_ASSERT(
- curl_multi_fdset( curlm_, &fd_read, &fd_write, &fd_except, &max_fd )
- );
-
- //
- // Note that the fopen.c sample code is unnecessary at best or wrong at
- // worst; see: http://curl.haxx.se/mail/lib-2011-05/0011.html
- //
- timeval timeout;
- long curl_timeout_ms;
- ZORBA_CURLM_ASSERT( curl_multi_timeout( curlm_, &curl_timeout_ms ) );
- if ( curl_timeout_ms > 0 ) {
- timeout.tv_sec = curl_timeout_ms / 1000;
- timeout.tv_usec = curl_timeout_ms % 1000 * 1000;
- } else {
- //
- // From curl_multi_timeout(3):
- //
- // Note: if libcurl returns a -1 timeout here, it just means that
- // libcurl currently has no stored timeout value. You must not wait
- // too long (more than a few seconds perhaps) before you call
- // curl_multi_perform() again.
- //
- // So we just pick some not-too-long default.
- //
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
- }
-
- switch ( select( max_fd + 1, &fd_read, &fd_write, &fd_except, &timeout ) ) {
- case -1: // select error
-#ifdef WIN32
- char err_buf[8];
- sprintf( err_buf, "%d", WSAGetLastError() );
- throw exception( "select()", "", err_buf );
-#else
- throw exception( "select()", "", strerror( errno ) );
-#endif
- case 0: // timeout
- // no break;
- default:
- CURLMcode code;
- do {
- code = curl_multi_perform( curlm_, &curl_running_ );
- } while ( code == CURLM_CALL_MULTI_PERFORM );
- ZORBA_CURLM_ASSERT( code );
- }
- }
- if ( theInformer )
- theInformer->afterRead();
-}
-
-size_t streambuf::curl_write_callback( void *ptr, size_t size, size_t nmemb,
- void *data ) {
- size *= nmemb;
- streambuf *const that = static_cast<streambuf*>( data );
-
- if ( that->theInformer )
- that->theInformer->beforeRead();
-
- size_t const buf_free = that->buf_capacity_ - that->buf_len_;
- if ( size > buf_free ) {
- streamoff new_capacity = that->buf_capacity_ + size - buf_free;
- if ( void *const new_buf =
- realloc( that->buf_, static_cast<size_t>( new_capacity ) ) ) {
- that->buf_ = static_cast<char*>( new_buf );
- that->buf_capacity_ = new_capacity;
- } else
- throw exception( "realloc()", "" );
- }
- ::memcpy( that->buf_ + that->buf_len_, ptr, size );
- that->buf_len_ += size;
- return size;
-}
-
-void streambuf::init() {
- buf_ = 0;
- buf_capacity_ = 0;
- buf_len_ = 0;
- curl_ = 0;
- curlm_ = 0;
- curl_running_ = 0;
- theInformer = 0;
- theOwnInformer = false;
-#ifdef WIN32
- dummy_socket_ = socket( AF_INET, SOCK_DGRAM, 0 );
- if ( dummy_socket_ == CURL_SOCKET_BAD || dummy_socket_ == INVALID_SOCKET )
- throw exception( "socket()", "" );
-#endif /* WIN32 */
-}
-
-void streambuf::init_curlm() {
- //
- // Lie about cURL running initially so the while-loop in curl_read() will run
- // at least once.
- //
- curl_running_ = 1;
-
- //
- // Set the "get" pointer to the end (gptr() == egptr()) so a call to
- // underflow() and initial data read will be triggered.
- //
- buf_len_ = buf_capacity_;
- setg( buf_, buf_ + buf_len_, buf_ + buf_capacity_ );
-
- //
- // Clean-up has to be done here with try/catch (as opposed to relying on the
- // destructor) because open() can be called from the constructor. If an
- // exception is thrown, the constructor will not have completed, hence the
- // object will not have been fully constructed; therefore the destructor will
- // not be called.
- //
- try {
- if ( !(curlm_ = curl_multi_init()) )
- throw exception( "curl_multi_init()", "" );
- try {
- ZORBA_CURLM_ASSERT( curl_multi_add_handle( curlm_, curl_ ) );
- }
- catch ( ... ) {
- curl_multi_cleanup( curlm_ );
- curlm_ = 0;
- throw;
- }
- }
- catch ( ... ) {
- destroy( curl_ );
- curl_ = 0;
- throw;
- }
-}
-
-int streambuf::multi_perform() {
- underflow();
- CURLMsg *msg;
- int msgInQueue;
- int error = 0;
- while ( (msg = curl_multi_info_read( curlm_, &msgInQueue )) ) {
- if ( msg->msg == CURLMSG_DONE )
- error = msg->data.result;
- }
- return error;
-}
-
-void streambuf::open( char const *uri ) {
- curl_ = create( uri, curl_write_callback, this );
-
- init_curlm();
-}
-
-streamsize streambuf::showmanyc() {
- return egptr() - gptr();
-}
-
-streambuf::int_type streambuf::underflow() {
- while ( true ) {
- if ( gptr() < egptr() )
- return traits_type::to_int_type( *gptr() );
- curl_read();
- if ( !buf_len_ )
- return traits_type::eof();
- setg( buf_, buf_, buf_ + buf_len_ );
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-} // namespace curl
-} // namespace zorba
-/* vim:set et sw=2 ts=2: */
=== removed file 'modules/http-client/json/http-client.xq.src/curl_stream_buffer.h'
--- modules/http-client/json/http-client.xq.src/curl_stream_buffer.h 2013-06-14 16:32:01 +0000
+++ modules/http-client/json/http-client.xq.src/curl_stream_buffer.h 1970-01-01 00:00:00 +0000
@@ -1,193 +0,0 @@
-/*
- * Copyright 2006-2013 The FLWOR Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-#ifndef ZORBA_CURL_UTIL_H
-#define ZORBA_CURL_UTIL_H
-
-#include <zorba/config.h>
-
-#include <exception>
-#include <istream>
-#include <streambuf>
-#include <string>
-#include <curl/curl.h>
-
-namespace zorba {
-
-namespace http_client {
- class InformDataRead;
-}
-
-namespace curl {
-
-///////////////////////////////////////////////////////////////////////////////
-
-class exception : public std::exception {
-public:
- exception( char const *function, char const *uri, char const *msg = 0 );
- exception( char const *function, char const *uri, CURLcode code );
- exception( char const *function, char const *uri, CURLMcode code );
- ~exception() throw();
-
- virtual const char* what() const throw();
-
-private:
- std::string msg_;
-};
-
-////////// create & destroy ///////////////////////////////////////////////////
-
-/**
- * The signature type of cURL's write function callback.
- */
-typedef size_t (*write_fn_t)( void*, size_t, size_t, void* );
-
-/**
- * Creates a new, initialized cURL instance.
- *
- * @throws exception upon failure.
- */
-CURL* create( char const *uri, write_fn_t fn, void *data );
-
-/**
- * Destroys a cURL instance.
- *
- * @param instance A cURL instance. If \c NULL, does nothing.
- */
-void destroy( CURL *instance );
-
-////////// streambuf //////////////////////////////////////////////////////////
-
-/**
- * A curl::streambuf is-a std::streambuf for streaming the contents of URI
- * using cURL. However, do not use this class directly. Use uri::streambuf
- * instead.
- */
-class streambuf : public std::streambuf {
-public:
- /**
- * Constructs a %streambuf.
- */
- streambuf();
-
- /**
- * Constructs a %streambuf and opens a connection to the server hosting the
- * given URI for subsequent streaming.
- *
- * @param uri The URI to stream.
- */
- streambuf( char const *uri );
-
- /**
- * Constructs a %streambuf using an existing CURL object.
- *
- * @param curl The CURL object to use. This %streambuf takes ownership of
- * it.
- */
- streambuf( CURL *curl );
-
- /**
- * Destroys a %streambuf.
- */
- ~streambuf();
-
- /**
- * Opens a connection to the server hosting the given URI for subsequent
- * streaming.
- *
- * @param uri The URI to stream.
- * @throws exception upon failure.
- */
- void open( char const *uri );
-
- /**
- * Tests whether the buffer is open.
- *
- * @return Returns \c true only if the buffer is open.
- */
- bool is_open() const {
- return !!curl_;
- }
-
- /**
- * Closes this %streambuf.
- */
- void close();
-
- /**
- * Gets the CURL object in use.
- *
- * @return Return said CURL object.
- */
- CURL* curl() const {
- return curl_;
- }
-
- /**
- * Provide a InformDataRead that will get callbacks about read events.
- */
- void setInformer( http_client::InformDataRead *aInformer ) {
- theInformer = aInformer;
- }
-
- /**
- * Specify whether this streambuf has memory ownership over the
- * InformDataRead it has been passed. You can use this if, for example,
- * the lifetime of the streambuf will extend past the lifetime of the
- * object which created the InformDataRead.
- */
- void setOwnInformer( bool aOwnInformer ) {
- theOwnInformer = aOwnInformer;
- }
-
- int multi_perform();
-
-protected:
- // inherited
- std::streamsize showmanyc();
- int_type underflow();
-
-private:
- void curl_read();
- static size_t curl_write_callback( void*, size_t, size_t, void* );
-
- void init();
- void init_curlm();
-
- char *buf_;
- std::streamsize buf_capacity_;
- std::streamoff buf_len_;
-
- CURL *curl_;
- CURLM *curlm_;
- int curl_running_;
- http_client::InformDataRead *theInformer;
- bool theOwnInformer;
-
- // forbid
- streambuf( streambuf const& );
- streambuf& operator=( streambuf const& );
-#ifdef WIN32
- SOCKET dummy_socket_;
-#endif /* WIN32 */
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-} // namespace curl
-} // namespace zorba
-#endif /* ZORBA_CURL_UTIL_H */
-/* vim:set et sw=2 ts=2: */
=== modified file 'modules/http-client/json/http-client.xq.src/http_response_parser.cpp'
--- modules/http-client/json/http-client.xq.src/http_response_parser.cpp 2013-09-23 09:11:02 +0000
+++ modules/http-client/json/http-client.xq.src/http_response_parser.cpp 2014-01-09 19:12:12 +0000
@@ -36,9 +36,10 @@
#include <zorba/xquery_functions.h>
#include <zorba/internal/unique_ptr.h>
+#include "curl_streambuf.h"
+
#include "http_response_parser.h"
#include "http_request_handler.h"
-#include "curl_stream_buffer.h"
namespace zorba {
@@ -94,10 +95,10 @@
if (!t.empty())
{
if (t[0] == '"' && t[t.length()-1] == '"')
- {
+ {
t.erase( 0, 1 );
- t.erase(t.length() -1, 1);
- }
+ t.erase(t.length() -1, 1);
+ }
*charset = t;
}
}
@@ -129,11 +130,11 @@
int HttpResponseParser::parse()
{
- theStreamBuffer->setInformer(this);
+ theStreamBuffer->set_listener(this);
theHandler.begin();
bool lStatusAndMesssageParsed = false;
int lCode = 0;
- lCode = theStreamBuffer->multi_perform();
+ lCode = theStreamBuffer->curl_multi_info_read(false);
if (lCode)
return lCode;
if (!theStatusOnly) {
@@ -203,7 +204,7 @@
return lCode;
}
- void HttpResponseParser::beforeRead()
+ void HttpResponseParser::curl_read(void*,size_t)
{
if (theInsideRead) {
return;
@@ -218,10 +219,6 @@
theHandler.beginBody(theCurrentContentType, "", NULL);
}
- void HttpResponseParser::afterRead()
- {
- }
-
void HttpResponseParser::registerHandler()
{
curl_easy_setopt(theCurl, CURLOPT_HEADERFUNCTION, &curl_headerfunction);
@@ -343,7 +340,7 @@
// theStreamBuffer. Therefore, this HttpResponseParser object is no longer
// "self-contained". We delegate ownership of ourself to theStreamBuffer
// and mark ourselves as no longer being self-contained.
- theStreamBuffer->setOwnInformer(true);
+ theStreamBuffer->set_listener(this, true);
theSelfContained = false;
// The ownership of theStreamBuffer, in turn, is delegated to the
=== modified file 'modules/http-client/json/http-client.xq.src/http_response_parser.h'
--- modules/http-client/json/http-client.xq.src/http_response_parser.h 2013-07-19 17:36:52 +0000
+++ modules/http-client/json/http-client.xq.src/http_response_parser.h 2014-01-09 19:12:12 +0000
@@ -19,26 +19,21 @@
#include <string>
#include <map>
-#include <curl/curl.h>
+#include "curl_streambuf.h"
-#include "inform_data_read.h"
#include "error_thrower.h"
#include "http_response_handler.h"
namespace zorba {
class Item;
-namespace curl {
- class streambuf;
-}
-
namespace http_client
{
void parse_content_type( std::string const &s, std::string *mime_type, std::string *charset );
class HttpResponseHandler;
- class HttpResponseParser : public InformDataRead {
+ class HttpResponseParser : public curl::listener {
private:
HttpResponseHandler& theHandler;
CURL* theCurl;
@@ -49,7 +44,7 @@
headers_type theHeaders;
int theStatus;
std::string theMessage;
- zorba::curl::streambuf* theStreamBuffer;
+ curl::streambuf* theStreamBuffer;
std::string theId;
std::string theDescription;
bool theInsideRead;
@@ -76,8 +71,7 @@
* will return false.
*/
bool selfContained() { return theSelfContained; }
- virtual void beforeRead();
- virtual void afterRead();
+ virtual void curl_read(void*,size_t);
private:
void registerHandler();
void parseStatusAndMessage(std::string const &aHeader);
=== removed file 'modules/http-client/json/http-client.xq.src/inform_data_read.cpp'
--- modules/http-client/json/http-client.xq.src/inform_data_read.cpp 2013-06-14 16:32:01 +0000
+++ modules/http-client/json/http-client.xq.src/inform_data_read.cpp 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
-/*
- * Copyright 2006-2013 The FLWOR Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "inform_data_read.h"
-
-namespace zorba { namespace http_client {
- InformDataRead::~InformDataRead()
- {
- }
-}} //namespace zorba, http_client
=== removed file 'modules/http-client/json/http-client.xq.src/inform_data_read.h'
--- modules/http-client/json/http-client.xq.src/inform_data_read.h 2013-06-14 16:32:01 +0000
+++ modules/http-client/json/http-client.xq.src/inform_data_read.h 1970-01-01 00:00:00 +0000
@@ -1,27 +0,0 @@
-/*
- * Copyright 2006-2013 The FLWOR Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef INFORM_DATA_READ_H
-#define INFORM_DATA_READ_H
-namespace zorba { namespace http_client {
- class InformDataRead {
- public:
- virtual ~InformDataRead();
- public:
- virtual void beforeRead() = 0;
- virtual void afterRead() = 0;
- };
-}} //namespace zorba, http_client
-#endif //INFORM_DATA_READ_H
=== added directory 'modules/util-curl'
=== added file 'modules/util-curl/CMakeLists.txt'
--- modules/util-curl/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ modules/util-curl/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -0,0 +1,50 @@
+# Copyright 2006-2012 The FLWOR Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+PROJECT(ZorbaUtilCurlModule)
+
+IF (ZORBA_SUPPRESS_CURL)
+ MESSAGE(STATUS "ZORBA_SUPPRESS_CURL is true - not searching for cURL library")
+ELSE (ZORBA_SUPPRESS_CURL)
+ MESSAGE(STATUS "Looking for cURL")
+ FIND_PACKAGE(CURL)
+
+ IF (CURL_FOUND)
+ MESSAGE(STATUS "Found cURL library -- " ${CURL_LIBRARIES})
+ INCLUDE_DIRECTORIES(BEFORE SYSTEM "${CURL_INCLUDE_DIR}")
+ SET(requiredlibs ${requiredlibs} "${CURL_LIBRARIES}")
+
+ SET(ZORBA_PROJECT_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include")
+ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/include")
+ INSTALL(FILES include/curl_streambuf.h DESTINATION include)
+
+ ADD_SUBDIRECTORY("src")
+
+ IF (WIN32) # Copy certificates for windows only
+ IF (MSVC_IDE)
+ SET(CACERT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../../bin/${CMAKE_BUILD_TYPE}/cacert.pem")
+ ELSE (MSVC_IDE)
+ SET(CACERT_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../../bin/cacert.pem")
+ ENDIF (MSVC_IDE)
+ CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/cacert.pem" ${CACERT_DESTINATION} COPYONLY)
+ INSTALL(FILES ${CACERT_DESTINATION} DESTINATION bin)
+ ENDIF (WIN32)
+
+ ELSE (CURL_FOUND)
+ MESSAGE(STATUS "The cURL library was not found")
+ ENDIF (CURL_FOUND)
+ENDIF (ZORBA_SUPPRESS_CURL)
+SET(ZORBA_HAVE_CURL ${CURL_FOUND} CACHE BOOL "Whether Zorba found cURL" FORCE)
+
+# vim:set et sw=2 ts=2:
=== added file 'modules/util-curl/ZorbaUtilCurlModuleConfig.cmake'
--- modules/util-curl/ZorbaUtilCurlModuleConfig.cmake 1970-01-01 00:00:00 +0000
+++ modules/util-curl/ZorbaUtilCurlModuleConfig.cmake 2014-01-09 19:12:12 +0000
@@ -0,0 +1,19 @@
+# Copyright 2006-2013 The FLWOR Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET(ZorbaUtilCurlModule_FOUND TRUE)
+SET(ZorbaUtilCurlModule_INCLUDES "../util-curl/include")
+SET(ZorbaUtilCurlModule_INCLUDE_DIRS "${ZorbaUtilCurlModule_INCLUDES}")
+SET(ZorbaUtilCurlModule_LIBS util-curl)
+SET(ZorbaUtilCurlModule_LIBRARIES "${ZorbaUtilCurlModule_LIBS}")
=== added directory 'modules/util-curl/include'
=== added file 'modules/util-curl/include/curl_streambuf.h'
--- modules/util-curl/include/curl_streambuf.h 1970-01-01 00:00:00 +0000
+++ modules/util-curl/include/curl_streambuf.h 2014-01-09 19:12:12 +0000
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+#ifndef ZORBA_CURL_STREAMBUF_API_H
+#define ZORBA_CURL_STREAMBUF_API_H
+
+// standard
+#include <cstdlib>
+#include <exception>
+#include <streambuf>
+#include <string>
+#ifdef WIN32
+#include <windows.h>
+#endif /* WIN32 */
+
+// libcurl
+#include <curl/curl.h>
+
+// Zorba
+#include <zorba/util/fs_util.h>
+
+namespace zorba {
+namespace curl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * If defined, all calls to cURL wrapped by either ZORBA_CURL_ASSERT() or
+ * ZORBA_CURLM_ASSERT() will be printed to standard error.
+ */
+//#define ZORBA_TRACE_LIBCURL_CALLS 1
+
+#ifdef ZORBA_TRACE_LIBCURL_CALLS
+# define ZORBA_CURL_ECHO(CURL_FN) \
+ std::cerr << zorba::fs::base_name( __FILE__ ) << ':' << __LINE__ << ": " << #CURL_FN << std::endl
+#else
+# define ZORBA_CURL_ECHO(CURL_FN) (void)0
+#endif /* ZORBA_TRACE_LIBCURL_CALLS */
+
+#define ZORBA_CURL_ASSERT(EXPR) \
+ do { \
+ ZORBA_CURL_ECHO( EXPR ); \
+ if ( CURLcode const code##__LINE__ = (EXPR) ) \
+ throw zorba::curl::exception( #EXPR, "", code##__LINE__ ); \
+ } while (0)
+
+#define ZORBA_CURLM_ASSERT(EXPR) \
+ do { \
+ ZORBA_CURL_ECHO( EXPR ); \
+ if ( CURLMcode const code##__LINE__ = (EXPR) ) \
+ if ( code##__LINE__ != CURLM_CALL_MULTI_PERFORM ) \
+ throw zorba::curl::exception( #EXPR, "", code##__LINE__ ); \
+ } while (0)
+
+////////// exception //////////////////////////////////////////////////////////
+
+/**
+ * A curl::exception is-an exception for cURL errors. These are thrown instead
+ * of simply returning error codes (that are often ignored).
+ */
+class exception : public std::exception {
+public:
+ exception( char const *function, char const *uri, char const *msg = 0 );
+ exception( char const *function, char const *uri, CURLcode code );
+ exception( char const *function, char const *uri, CURLMcode code );
+ ~exception() throw();
+
+ /**
+ * Gets the cURL error code (returned from the "easy" interface).
+ *
+ * @return Returns said error code or 0 if none.
+ */
+ CURLcode curl_code() const {
+ return curl_code_;
+ }
+
+ /**
+ * Gets the cURL error code (returned from the "multi" interface).
+ *
+ * @return Returns said error code or 0 if none.
+ */
+ CURLMcode curlm_code() const {
+ return curlm_code_;
+ }
+
+ // inherited
+ virtual const char* what() const throw();
+
+private:
+ CURLcode curl_code_;
+ CURLMcode curlm_code_;
+ std::string msg_;
+};
+
+////////// streambuf //////////////////////////////////////////////////////////
+
+/**
+ * A listener can be used to "listen" to the raw data that cURL reads at the
+ * time it reads it.
+ */
+struct listener {
+ virtual ~listener();
+
+ /**
+ * This is called whenever cURL reads data and gives it to the streambuf.
+ *
+ * @param data A pointer to the data read.
+ * @param size The number of bytes read.
+ */
+ virtual void curl_read( void *data, size_t size ) = 0;
+};
+
+////////// streambuf //////////////////////////////////////////////////////////
+
+/**
+ * A curl::streambuf is-a std::streambuf for streaming the contents of URI
+ * using cURL. Note that this streambuf can be used only for reading
+ * (downloading) and not writing (uploading) via, say, FTP or HTTP.
+ */
+class streambuf : public std::streambuf {
+public:
+ /**
+ * Constructs a %streambuf.
+ */
+ streambuf();
+
+ /**
+ * Constructs a %streambuf and opens a connection to the server hosting the
+ * given URI for subsequent streaming.
+ *
+ * @param uri The URI to stream.
+ */
+ streambuf( char const *uri );
+
+ /**
+ * Constructs a %streambuf using an existing CURL object.
+ *
+ * @param curl The CURL object to use. This %streambuf takes ownership of
+ * it.
+ */
+ streambuf( CURL *curl );
+
+ /**
+ * Destroys a %streambuf.
+ */
+ ~streambuf();
+
+ /**
+ * Opens a connection to the server hosting the given URI for subsequent
+ * streaming.
+ *
+ * @param uri The URI to stream.
+ * @throws exception upon failure.
+ */
+ void open( char const *uri );
+
+ /**
+ * Tests whether the buffer is open.
+ *
+ * @return Returns \c true only if the buffer is open.
+ */
+ bool is_open() const {
+ return !!curl_;
+ }
+
+ /**
+ * Closes this %streambuf.
+ */
+ void close();
+
+ /**
+ * Gets the CURL ("easy") object in use.
+ *
+ * @return Return said CURL object.
+ */
+ CURL* curl() const {
+ return curl_;
+ }
+
+ /**
+ * Gets the CURLM ("multi") object in use.
+ *
+ * @return Return said CURLM object.
+ */
+ CURLM* curlm() const {
+ return curlm_;
+ }
+
+ /**
+ * Resets all options on the CURL object in use.
+ */
+ void curl_reset() {
+ curl_init();
+ }
+
+ /**
+ * Sets/clears CURL verbose mode.
+ *
+ * @param verbose If \c true, sets verbose mode; otherwise clears it.
+ */
+ void curl_verbose( bool verbose );
+
+public:
+ CURLcode curl_multi_info_read( bool throw_on_error = true );
+
+ void set_listener( listener *l, bool take_ownership = false );
+
+protected:
+ // inherited
+ std::streamsize showmanyc();
+ int_type underflow();
+ std::streamsize xsgetn( char_type*, std::streamsize );
+
+private:
+ void curl_create();
+ void curl_destroy();
+ void curl_init();
+ void curlm_init();
+ void curl_io( size_t* );
+ void curl_write();
+ static size_t curl_write_callback( void*, size_t, size_t, void* );
+ void init();
+
+ struct gbuf {
+ char *ptr_;
+ size_t capacity_, len_;
+ gbuf() : ptr_( 0 ), capacity_( 0 ), len_( 0 ) { }
+ ~gbuf() { free( ptr_ ); }
+ };
+
+ CURL *curl_;
+ CURLM *curlm_;
+ int curl_running_;
+ gbuf gbuf_;
+ listener *listener_;
+ bool listener_owner_;
+ bool verbose_;
+
+ // forbid
+ streambuf( streambuf const& );
+ streambuf& operator=( streambuf const& );
+#ifdef WIN32
+ SOCKET dummy_socket_;
+#endif /* WIN32 */
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace curl
+} // namespace zorba
+#endif /* ZORBA_CURL_STREAMBUF_API_H */
+/* vim:set et sw=2 ts=2: */
=== added directory 'modules/util-curl/src'
=== added file 'modules/util-curl/src/CMakeLists.txt'
--- modules/util-curl/src/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ modules/util-curl/src/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -0,0 +1,36 @@
+# Copyright 2006-2010 The FLWOR Foundation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DECLARE_ZORBA_MODULE(FILE "util-curl.xq" VERSION 1.0
+ URI "http://zorba.io/modules/util-curl")
+
+# The important stuff is the library that we install in Zorba's default lib
+# directory.
+ADD_LIBRARY(util-curl SHARED curl_streambuf.cpp util-curl.cpp)
+TARGET_LINK_LIBRARIES(util-curl zorba_${ZORBA_STORE_NAME} ${CURL_LIBRARIES})
+INSTALL(TARGETS util-curl
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib${LIB_SUFFIX}
+ ARCHIVE DESTINATION lib)
+
+# MAC OS X only property
+# This is required to make sure that the library has the correct install name
+# after installation such that dependent modules always find it.
+SET_TARGET_PROPERTIES(util-curl PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+# Set this in the parent scope so it will be put into our Config.cmake file.
+# As with many other things, this doesn't work when the module is installed.
+SET(ZORBA_PROJECT_LIBRARIES util-curl PARENT_SCOPE)
+
+# vim:set et sw=2 ts=2:
=== added file 'modules/util-curl/src/curl_streambuf.cpp'
--- modules/util-curl/src/curl_streambuf.cpp 1970-01-01 00:00:00 +0000
+++ modules/util-curl/src/curl_streambuf.cpp 2014-01-09 19:12:12 +0000
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// standard
+#include <algorithm>
+#include <cstdlib>
+#include <cstring> /* for memcpy(3) */
+#include <iostream>
+#ifndef WIN32
+#include <cerrno>
+#include <sys/time.h>
+#endif /* WIN32 */
+
+// libcurl
+#include <curl/multi.h>
+
+// Zorba
+#include "curl_streambuf.h"
+
+using namespace std;
+
+namespace zorba {
+namespace curl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+exception::exception( char const *function, char const *uri, char const *msg ) :
+ std::exception(),
+ curl_code_( CURLE_OK ), curlm_code_( CURLM_OK ), msg_( msg )
+{
+}
+
+exception::exception( char const *function, char const *uri, CURLcode code ) :
+ std::exception(),
+ curl_code_( code ), curlm_code_( CURLM_OK ),
+ msg_( curl_easy_strerror( code ) )
+{
+ ostringstream oss;
+ oss << " (CURLcode " << (int)code << ')';
+ msg_ += oss.str();
+}
+
+exception::exception( char const *function, char const *uri, CURLMcode code ) :
+ std::exception(),
+ curl_code_( CURLE_OK ), curlm_code_( code ),
+ msg_( curl_multi_strerror( code ) )
+{
+ ostringstream oss;
+ oss << " (CURLMcode " << (int)code << ')';
+ msg_ += oss.str();
+}
+
+exception::~exception() throw() {
+ // out-of-line since it's virtual
+}
+
+const char* exception::what() const throw() {
+ return msg_.c_str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+listener::~listener() {
+ // out-of-line since it's virtual
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+streambuf::streambuf() {
+ init();
+}
+
+streambuf::streambuf( char const *uri ) {
+ init();
+ open( uri );
+}
+
+streambuf::streambuf( CURL *curl ) {
+ init();
+ curl_ = curl;
+ curl_init();
+ curlm_init();
+}
+
+streambuf::~streambuf() {
+ close();
+#ifdef WIN32
+ closesocket( dummy_socket_ );
+#endif /* WIN32 */
+ if ( listener_owner_ )
+ delete listener_;
+}
+
+void streambuf::close() {
+ if ( curl_ ) {
+ if ( curlm_ ) {
+ curl_multi_remove_handle( curlm_, curl_ );
+ curl_multi_cleanup( curlm_ );
+ curlm_ = 0;
+ }
+ curl_destroy();
+ }
+}
+
+void streambuf::curl_create() {
+ //
+ // Having cURL initialization wrapped by a class and using a singleton static
+ // instance guarantees that cURL is initialized exactly once before use and
+ // and also is cleaned-up at program termination (when destructors for static
+ // objects are called).
+ //
+ struct curl_initializer {
+ curl_initializer() {
+ ZORBA_CURL_ASSERT( curl_global_init( CURL_GLOBAL_ALL ) );
+ }
+ ~curl_initializer() {
+ curl_global_cleanup();
+ }
+ };
+ static curl_initializer initializer;
+
+ curl_ = curl_easy_init();
+ if ( !curl_ )
+ throw exception( "curl_easy_init()", "", "" );
+ try {
+ curl_init();
+ }
+ catch ( ... ) {
+ curl_destroy();
+ throw;
+ }
+}
+
+void streambuf::curl_destroy() {
+ if ( curl_ ) {
+ curl_easy_reset( curl_ );
+ curl_easy_cleanup( curl_ );
+ curl_ = 0;
+ }
+}
+
+void streambuf::curl_verbose( bool verbose ) {
+ verbose_ = verbose;
+ if ( curl_ )
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_VERBOSE, verbose_ ? 1 : 0 ) );
+}
+
+void streambuf::curl_init() {
+ curl_easy_reset( curl_ );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_FOLLOWLOCATION, 1 ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_MAXREDIRS, 50L ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_NOPROGRESS, 1L ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_TCP_KEEPALIVE, 1L ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_WRITEDATA, this ) );
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_WRITEFUNCTION, curl_write_callback ) );
+
+ if ( verbose_ )
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_VERBOSE, 1 ) );
+}
+
+void streambuf::curl_io( size_t *len_ptr ) {
+ *len_ptr = 0;
+ while ( curl_running_ && !*len_ptr ) {
+ fd_set fd_read, fd_write, fd_except;
+ FD_ZERO( &fd_read );
+ FD_ZERO( &fd_write );
+ FD_ZERO( &fd_except );
+ int max_fd = -1;
+#ifdef WIN32
+ //
+ // Windows does not like a call to select where all arguments are 0, so we
+ // just add a dummy socket to make the call to select happy.
+ //
+ FD_SET( dummy_socket_, &fd_read );
+#endif /* WIN32 */
+ ZORBA_CURLM_ASSERT(
+ curl_multi_fdset( curlm_, &fd_read, &fd_write, &fd_except, &max_fd )
+ );
+
+ //
+ // Note that the fopen.c sample code is unnecessary at best or wrong at
+ // worst; see: http://curl.haxx.se/mail/lib-2011-05/0011.html
+ //
+ timeval timeout;
+ long curl_timeout_ms;
+ ZORBA_CURLM_ASSERT( curl_multi_timeout( curlm_, &curl_timeout_ms ) );
+ if ( curl_timeout_ms > 0 ) {
+ timeout.tv_sec = curl_timeout_ms / 1000;
+ timeout.tv_usec = curl_timeout_ms % 1000 * 1000;
+ } else {
+ //
+ // From curl_multi_timeout(3):
+ //
+ // Note: if libcurl returns a -1 timeout here, it just means that
+ // libcurl currently has no stored timeout value. You must not wait
+ // too long (more than a few seconds perhaps) before you call
+ // curl_multi_perform() again.
+ //
+ // So we just pick some not-too-long default.
+ //
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ }
+
+ switch ( select( max_fd + 1, &fd_read, &fd_write, &fd_except, &timeout ) ) {
+ case -1: // select error
+#ifdef WIN32
+ char err_buf[8];
+ sprintf( err_buf, "%d", WSAGetLastError() );
+ throw exception( "select()", "", err_buf );
+#else
+ throw exception( "select()", "", strerror( errno ) );
+#endif /* WIN32 */
+ case 0: // timeout
+ // no break;
+ default:
+ CURLMcode code;
+ do {
+ code = curl_multi_perform( curlm_, &curl_running_ );
+ } while ( code == CURLM_CALL_MULTI_PERFORM );
+ ZORBA_CURLM_ASSERT( code );
+ } // switch
+ } // while
+}
+
+CURLcode streambuf::curl_multi_info_read( bool throw_on_error ) {
+ underflow();
+ CURLMsg *msg;
+ int msgs_in_q;
+ CURLcode code = CURLE_OK;
+ while ( (msg = ::curl_multi_info_read( curlm_, &msgs_in_q )) )
+ if ( msg->msg == CURLMSG_DONE )
+ code = msg->data.result;
+ if ( code && throw_on_error )
+ throw exception( "curl_multi_info_read()", "", code );
+ return code;
+}
+
+size_t streambuf::curl_write_callback( void *ptr, size_t size, size_t nmemb,
+ void *data ) {
+ size *= nmemb;
+ streambuf *const that = static_cast<streambuf*>( data );
+
+ if ( that->listener_ )
+ that->listener_->curl_read( ptr, size );
+
+ size_t const gbuf_free = that->gbuf_.capacity_ - that->gbuf_.len_;
+ if ( size > gbuf_free ) {
+ size_t new_capacity = that->gbuf_.capacity_ + size - gbuf_free;
+ if ( void *const new_buf = realloc( that->gbuf_.ptr_, new_capacity ) ) {
+ that->gbuf_.ptr_ = static_cast<char*>( new_buf );
+ that->gbuf_.capacity_ = new_capacity;
+ } else
+ throw exception( "realloc()", "" );
+ }
+ ::memcpy( that->gbuf_.ptr_ + that->gbuf_.len_, ptr, size );
+ that->gbuf_.len_ += size;
+ return size;
+}
+
+void streambuf::init() {
+ curl_ = 0;
+ curlm_ = 0;
+ curl_running_ = 0;
+#ifdef WIN32
+ dummy_socket_ = socket( AF_INET, SOCK_DGRAM, 0 );
+ if ( dummy_socket_ == CURL_SOCKET_BAD || dummy_socket_ == INVALID_SOCKET )
+ throw exception( "socket()", "" );
+#endif /* WIN32 */
+ listener_ = 0;
+ listener_owner_ = false;
+}
+
+void streambuf::curlm_init() {
+ //
+ // Lie about cURL running initially so the while-loop in curl_io() will run
+ // at least once.
+ //
+ curl_running_ = 1;
+
+ //
+ // Set the "get" pointer to the end (gptr() == egptr()) so a call to
+ // underflow() and initial data read will be triggered.
+ //
+ gbuf_.len_ = gbuf_.capacity_;
+ setg( gbuf_.ptr_, gbuf_.ptr_ + gbuf_.len_, gbuf_.ptr_ + gbuf_.len_ );
+
+ //
+ // Clean-up has to be done here with try/catch (as opposed to relying on the
+ // destructor) because open() can be called from the constructor. If an
+ // exception is thrown, the constructor will not have completed, hence the
+ // object will not have been fully constructed; therefore the destructor will
+ // not be called.
+ //
+ try {
+ if ( !(curlm_ = curl_multi_init()) )
+ throw exception( "curl_multi_init()", "" );
+ try {
+ ZORBA_CURLM_ASSERT( curl_multi_add_handle( curlm_, curl_ ) );
+ }
+ catch ( ... ) {
+ curl_multi_cleanup( curlm_ );
+ curlm_ = 0;
+ throw;
+ }
+ }
+ catch ( ... ) {
+ curl_destroy();
+ throw;
+ }
+}
+
+void streambuf::open( char const *uri ) {
+ if ( !curl_ ) {
+ curl_create();
+ curlm_init();
+ }
+ ZORBA_CURL_ASSERT( curl_easy_setopt( curl_, CURLOPT_URL, uri ) );
+}
+
+void streambuf::set_listener( listener *l, bool take_ownership ) {
+ if ( listener_owner_ )
+ delete listener_;
+ listener_ = l;
+ listener_owner_ = take_ownership;
+}
+
+streamsize streambuf::showmanyc() {
+ return egptr() - gptr();
+}
+
+streambuf::int_type streambuf::underflow() {
+ while ( true ) {
+ if ( gptr() < egptr() )
+ return traits_type::to_int_type( *gptr() );
+ curl_io( &gbuf_.len_ );
+ if ( !gbuf_.len_ )
+ return traits_type::eof();
+ setg( gbuf_.ptr_, gbuf_.ptr_, gbuf_.ptr_ + gbuf_.len_ );
+ }
+}
+
+streamsize streambuf::xsgetn( char_type *to, streamsize size ) {
+ streamsize return_size = 0;
+
+ if ( streamsize const gsize = egptr() - gptr() ) {
+ streamsize const n = min( gsize, size );
+ traits_type::copy( to, gptr(), static_cast<size_t>( n ) );
+ gbump( static_cast<int>( n ) );
+ to += n;
+ size -= n, return_size += n;
+ }
+
+ while ( size > 0 ) {
+ streamsize const get = min( (streamsize)gbuf_.capacity_, size );
+ curl_io( &gbuf_.len_ );
+ if ( !gbuf_.len_ )
+ break;
+ setg( gbuf_.ptr_, gbuf_.ptr_, gbuf_.ptr_ + gbuf_.len_ );
+ streamsize const n = min( (streamsize)gbuf_.len_, size );
+ traits_type::copy( to, gptr(), n );
+ gbump( static_cast<int>( n ) );
+ to += n;
+ size -= n, return_size += n;
+ }
+ return return_size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace curl
+} // namespace zorba
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/util-curl/src/util-curl.cpp'
--- modules/util-curl/src/util-curl.cpp 1970-01-01 00:00:00 +0000
+++ modules/util-curl/src/util-curl.cpp 2014-01-09 19:12:12 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <zorba/external_module.h>
+
+namespace zorba {
+namespace util_curl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class module : public ExternalModule {
+public:
+ // inherited
+ virtual void destroy();
+ virtual ExternalFunction* getExternalFunction( String const& );
+ virtual String getURI() const;
+};
+
+void module::destroy() {
+ delete this;
+}
+
+ExternalFunction* module::getExternalFunction( String const& ) {
+ return 0;
+}
+
+String module::getURI() const {
+ return "http://zorba.io/modules/util-curl";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace util_curl
+} // namespace zorba
+
+#ifdef WIN32
+# define DLL_EXPORT __declspec(dllexport)
+#else
+# define DLL_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
+ return new zorba::util_curl::module();
+}
+
+/* vim:set et sw=2 ts=2: */
=== added file 'modules/util-curl/src/util-curl.xq'
--- modules/util-curl/src/util-curl.xq 1970-01-01 00:00:00 +0000
+++ modules/util-curl/src/util-curl.xq 2014-01-09 19:12:12 +0000
@@ -0,0 +1,34 @@
+xquery version "1.0";
+
+(:
+ : Copyright 2006-2013 The FLWOR Foundation.
+ :
+ : Licensed under the Apache License, Version 2.0 (the "License");
+ : you may not use this file except in compliance with the License.
+ : You may obtain a copy of the License at
+ :
+ : http://www.apache.org/licenses/LICENSE-2.0
+ :
+ : Unless required by applicable law or agreed to in writing, software
+ : distributed under the License is distributed on an "AS IS" BASIS,
+ : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ : See the License for the specific language governing permissions and
+ : limitations under the License.
+ :)
+
+(:~
+ : This module provides common functionality for modules that use libcurl.
+ : <p/>
+ : Modules using libcurl implementations must import this module
+ : to specify the dependency.
+ :
+ : @author Paul J. Lucas
+ : @library <a href="http://curl.haxx.se/download.html">libcurl</a>
+ : @project Zorba/cURL Utility
+ :)
+module namespace util-curl = "http://zorba.io/modules/util-curl";
+
+declare namespace ver = "http://zorba.io/options/versioning";
+declare option ver:module-version "1.0";
+
+(: vim:set et sw=2 ts=2: :)
=== modified file 'src/api/CMakeLists.txt'
--- src/api/CMakeLists.txt 2013-09-16 09:08:27 +0000
+++ src/api/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -59,6 +59,7 @@
auditimpl.cpp
store_consts.cpp
streambuf.cpp
+ mem_streambuf.cpp
transcode_streambuf.cpp
uuid.cpp
module_info_impl.cpp
=== modified file 'src/api/base64_util.cpp'
--- src/api/base64_util.cpp 2013-07-30 18:35:31 +0000
+++ src/api/base64_util.cpp 2014-01-09 19:12:12 +0000
@@ -22,8 +22,8 @@
// Zorba
#include <zorba/util/base64_util.h>
+#include <zorba/util/mem_streambuf.h>
#include "util/ascii_util.h"
-#include "util/mem_streambuf.h"
#include "util/string_util.h"
using namespace std;
=== modified file 'src/api/hexbinary_util.cpp'
--- src/api/hexbinary_util.cpp 2013-08-02 14:55:29 +0000
+++ src/api/hexbinary_util.cpp 2014-01-09 19:12:12 +0000
@@ -22,7 +22,7 @@
// Zorba
#include <zorba/util/hexbinary_util.h>
-#include "util/mem_streambuf.h"
+#include <zorba/util/mem_streambuf.h>
#include "util/string_util.h"
using namespace std;
=== modified file 'src/api/item.cpp'
--- src/api/item.cpp 2013-08-01 17:38:35 +0000
+++ src/api/item.cpp 2014-01-09 19:12:12 +0000
@@ -534,7 +534,7 @@
Item
-Item::getObjectValue(String aName) const
+Item::getObjectValue(String const &aName) const
{
ITEM_TRY
SYNC_CODE(AutoLock lock(GENV_STORE.getGlobalLock(), Lock::READ);)
=== renamed file 'src/util/mem_streambuf.cpp' => 'src/api/mem_streambuf.cpp'
--- src/util/mem_streambuf.cpp 2013-08-01 00:31:20 +0000
+++ src/api/mem_streambuf.cpp 2014-01-09 19:12:12 +0000
@@ -18,11 +18,10 @@
#include <cstring> /* for memcpy(3) */
#include <zorba/internal/cxx_util.h>
+#include <zorba/util/mem_streambuf.h>
#include "diagnostics/assert.h"
-#include "mem_streambuf.h"
-
using namespace std;
namespace zorba {
=== modified file 'src/diagnostics/dict.cpp'
--- src/diagnostics/dict.cpp 2013-08-02 18:34:59 +0000
+++ src/diagnostics/dict.cpp 2014-01-09 19:12:12 +0000
@@ -68,9 +68,15 @@
char const* lookup( char const *key ) {
typedef pair<entry const*,entry const*> range_type;
+ //
+ // It's possible an exception was thrown after shutdown has already started
+ // in which case the GlobalEnvironment no longer exists.
+ //
+ GlobalEnvironment const *const genv = GENV_PTR;
+ iso639_1::type lang = genv ? genv->get_host_lang() : locale::get_host_lang();
static entry const *begin, *end;
- if ( !begin && !get_dict( GENV.get_host_lang(), &begin, &end ) )
+ if ( !begin && !get_dict( lang, &begin, &end ) )
SET_DICT( en, begin, end );
entry entry_to_find;
=== modified file 'src/runtime/csv/pregenerated/csv.h'
--- src/runtime/csv/pregenerated/csv.h 2013-08-27 23:56:50 +0000
+++ src/runtime/csv/pregenerated/csv.h 2014-01-09 19:12:12 +0000
@@ -31,9 +31,9 @@
#include "runtime/base/narybase.h"
#include <sstream>
#include <vector>
+#include <zorba/util/mem_streambuf.h>
#include "runtime/csv/csv_util.h"
#include "util/csv_parser.h"
-#include "util/mem_streambuf.h"
#include "zorbatypes/zstring.h"
=== modified file 'src/runtime/json/json_impl.cpp'
--- src/runtime/json/json_impl.cpp 2013-12-12 18:41:52 +0000
+++ src/runtime/json/json_impl.cpp 2014-01-09 19:12:12 +0000
@@ -20,12 +20,12 @@
#include <zorba/diagnostic_list.h>
#include <zorba/internal/cxx_util.h>
+#include <zorba/util/mem_streambuf.h>
#include "runtime/json/json.h"
#include "store/api/item_factory.h"
#include "system/globalenv.h"
#include "util/ascii_util.h"
-#include "util/mem_streambuf.h"
#include "util/stream_util.h"
#include "jsonml_array.h"
=== modified file 'src/runtime/json/snelson.cpp'
--- src/runtime/json/snelson.cpp 2013-08-25 14:32:02 +0000
+++ src/runtime/json/snelson.cpp 2014-01-09 19:12:12 +0000
@@ -21,6 +21,7 @@
#include <zorba/diagnostic_list.h>
#include <zorba/internal/cxx_util.h>
#include <zorba/store_consts.h>
+#include <zorba/util/mem_streambuf.h>
#include "runtime/json/json.h"
#include "store/api/item_factory.h"
@@ -28,7 +29,6 @@
#include "types/root_typemanager.h"
#include "types/typeops.h"
#include "util/json_parser.h"
-#include "util/mem_streambuf.h"
#include "util/stl_util.h"
#include "zorbatypes/decimal.h"
#include "zorbatypes/float.h"
=== modified file 'src/runtime/spec/csv/csv.xml'
--- src/runtime/spec/csv/csv.xml 2013-08-27 23:56:50 +0000
+++ src/runtime/spec/csv/csv.xml 2014-01-09 19:12:12 +0000
@@ -8,9 +8,9 @@
<zorba:header>
<zorba:include form="Angle-bracket">sstream</zorba:include>
<zorba:include form="Angle-bracket">vector</zorba:include>
+ <zorba:include form="Angle-bracket">zorba/util/mem_streambuf.h</zorba:include>
<zorba:include form="Quoted">runtime/csv/csv_util.h</zorba:include>
<zorba:include form="Quoted">util/csv_parser.h</zorba:include>
- <zorba:include form="Quoted">util/mem_streambuf.h</zorba:include>
<zorba:include form="Quoted">zorbatypes/zstring.h</zorba:include>
</zorba:header>
=== modified file 'src/system/globalenv.h'
--- src/system/globalenv.h 2013-09-24 09:48:50 +0000
+++ src/system/globalenv.h 2014-01-09 19:12:12 +0000
@@ -102,6 +102,11 @@
return *m_globalEnv;
}
+ static GlobalEnvironment* getInstancePtr()
+ {
+ return m_globalEnv;
+ }
+
public:
~GlobalEnvironment();
@@ -151,6 +156,7 @@
#define GENV GlobalEnvironment::getInstance()
+#define GENV_PTR GlobalEnvironment::getInstancePtr()
#define GENV_TYPESYSTEM GlobalEnvironment::getInstance().getRootTypeManager()
=== modified file 'src/unit_tests/test_base64_streambuf.cpp'
--- src/unit_tests/test_base64_streambuf.cpp 2013-06-11 23:38:49 +0000
+++ src/unit_tests/test_base64_streambuf.cpp 2014-01-09 19:12:12 +0000
@@ -100,6 +100,13 @@
return b64_str == expected_b64_str;
}
+static void test_instantiate() {
+ base64::auto_attach<ostream> aao;
+ (void)aao;
+ base64::stream<ostream> b64os;
+ (void)b64os;
+}
+
static bool test_put( test const *t ) {
ostringstream oss;
{ // local scope
@@ -128,6 +135,8 @@
namespace UnitTests {
int test_base64_streambuf( int, char*[] ) {
+ test_instantiate();
+
int test_no = 0;
for ( test const *t = tests; t->raw_str; ++t, ++test_no ) {
ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_getline( t ) );
=== modified file 'src/util/CMakeLists.txt'
--- src/util/CMakeLists.txt 2013-08-21 23:52:57 +0000
+++ src/util/CMakeLists.txt 2014-01-09 19:12:12 +0000
@@ -22,7 +22,6 @@
json_parser.cpp
json_util.cpp
locale.cpp
- mem_streambuf.cpp
stream_util.cpp
string_util.cpp
time_util.cpp
Follow ups
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: noreply, 2014-01-13
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Zorba Build Bot, 2014-01-13
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Zorba Build Bot, 2014-01-13
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Zorba Build Bot, 2014-01-11
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Zorba Build Bot, 2014-01-11
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Zorba Build Bot, 2014-01-11
-
Re: [Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Matthias Brantner, 2014-01-11
-
Re: [Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Matthias Brantner, 2014-01-11
-
Re: [Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Matthias Brantner, 2014-01-11
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Paul J. Lucas, 2014-01-10
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Paul J. Lucas, 2014-01-10
-
[Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Paul J. Lucas, 2014-01-10
-
Re: [Merge] lp:~zorba-coders/zorba/feature-ftp-client into lp:zorba
From: Paul J. Lucas, 2014-01-10