aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-12-19 21:24:53 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-12-19 22:31:37 +0100
commita06f4a258f618dbad2ce01feadab6908db00bda5 (patch)
tree71497daf091671b478abf90bda5a3a222c5e91eb /src
parent2aeb122fb2720fa6809b9429aca5154f8eec5e6d (diff)
downloadPROJ-a06f4a258f618dbad2ce01feadab6908db00bda5.tar.gz
PROJ-a06f4a258f618dbad2ce01feadab6908db00bda5.zip
Add proj_context_set_network_callbacks() with an empty implementation
Diffstat (limited to 'src')
-rw-r--r--src/ctx.cpp3
-rw-r--r--src/filemanager.cpp164
-rw-r--r--src/filemanager.hpp6
-rw-r--r--src/open_lib.cpp6
-rw-r--r--src/proj.h76
-rw-r--r--src/proj_internal.h13
6 files changed, 267 insertions, 1 deletions
diff --git a/src/ctx.cpp b/src/ctx.cpp
index da90d4d7..a7cf8cb3 100644
--- a/src/ctx.cpp
+++ b/src/ctx.cpp
@@ -33,6 +33,7 @@
#include "proj_experimental.h"
#include "proj_internal.h"
+#include "filemanager.hpp"
/************************************************************************/
/* pj_get_ctx() */
@@ -96,6 +97,7 @@ projCtx_t projCtx_t::createDefault()
ctx.debug_level = PJ_LOG_NONE;
ctx.logger = pj_stderr_logger;
ctx.fileapi_legacy = pj_get_default_fileapi();
+ NS_PROJ::FileManager::fillDefaultNetworkInterface(&ctx);
if( getenv("PROJ_DEBUG") != nullptr )
{
@@ -139,6 +141,7 @@ projCtx_t::projCtx_t(const projCtx_t& other)
file_finder = other.file_finder;
file_finder_legacy = other.file_finder_legacy;
file_finder_user_data = other.file_finder_user_data;
+ networking = other.networking;
}
/************************************************************************/
diff --git a/src/filemanager.cpp b/src/filemanager.cpp
index b6164519..97f6a4dc 100644
--- a/src/filemanager.cpp
+++ b/src/filemanager.cpp
@@ -25,11 +25,21 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+#ifndef FROM_PROJ_CPP
+#define FROM_PROJ_CPP
+#endif
+
#include "filemanager.hpp"
+#include "proj.h"
+#include "proj/internal/internal.hpp"
#include "proj_internal.h"
+//! @cond Doxygen_Suppress
+
NS_PROJ_START
+using namespace internal;
+
// ---------------------------------------------------------------------------
File::File() = default;
@@ -160,6 +170,79 @@ std::unique_ptr<File> FileLegacyAdapter::open(PJ_CONTEXT *ctx,
// ---------------------------------------------------------------------------
+class NetworkFile : public File {
+ PJ_CONTEXT *m_ctx;
+ PROJ_NETWORK_HANDLE *m_handle;
+ unsigned long long m_pos = 0;
+
+ NetworkFile(const NetworkFile &) = delete;
+ NetworkFile &operator=(const NetworkFile &) = delete;
+
+ protected:
+ NetworkFile(PJ_CONTEXT *ctx, PROJ_NETWORK_HANDLE *handle)
+ : m_ctx(ctx), m_handle(handle) {}
+
+ public:
+ ~NetworkFile() override;
+
+ size_t read(void *buffer, size_t sizeBytes) override;
+ bool seek(unsigned long long offset, int whence) override;
+ unsigned long long tell() override;
+
+ static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename);
+};
+
+// ---------------------------------------------------------------------------
+
+std::unique_ptr<File> NetworkFile::open(PJ_CONTEXT *ctx, const char *filename) {
+ std::vector<unsigned char> buffer(16 * 1024);
+ size_t size_read = 0;
+ auto handle = ctx->networking.open(ctx, filename, buffer.size(), &buffer[0],
+ &size_read, ctx->networking.user_data);
+ return std::unique_ptr<File>(handle ? new NetworkFile(ctx, handle)
+ : nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+size_t NetworkFile::read(void *buffer, size_t sizeBytes) {
+ size_t nRead = m_ctx->networking.read_range(
+ m_ctx, m_handle, m_pos, sizeBytes, buffer, m_ctx->networking.user_data);
+ m_pos += nRead;
+ return nRead;
+}
+
+// ---------------------------------------------------------------------------
+
+bool NetworkFile::seek(unsigned long long offset, int whence) {
+ if (whence == SEEK_SET) {
+ m_pos = offset;
+ } else if (whence == SEEK_CUR) {
+ m_pos += offset;
+ } else {
+ if (offset != 0)
+ return false;
+ const auto filesize = m_ctx->networking.get_file_size(
+ m_ctx, m_handle, m_ctx->networking.user_data);
+ if (filesize == 0)
+ return false;
+ m_pos = filesize;
+ }
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+
+unsigned long long NetworkFile::tell() { return m_pos; }
+
+// ---------------------------------------------------------------------------
+
+NetworkFile::~NetworkFile() {
+ m_ctx->networking.close(m_ctx, m_handle, m_ctx->networking.user_data);
+}
+
+// ---------------------------------------------------------------------------
+
std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) {
#ifndef REMOVE_LEGACY_SUPPORT
// If the user has specified a legacy fileapi, use it
@@ -167,7 +250,88 @@ std::unique_ptr<File> FileManager::open(PJ_CONTEXT *ctx, const char *filename) {
return FileLegacyAdapter::open(ctx, filename);
}
#endif
+ if (starts_with(filename, "http://") || starts_with(filename, "https://")) {
+ return NetworkFile::open(ctx, filename);
+ }
return FileStdio::open(ctx, filename);
}
+// ---------------------------------------------------------------------------
+
+static PROJ_NETWORK_HANDLE *
+no_op_network_open(PJ_CONTEXT *ctx, const char * /* url */,
+ size_t, /* size to read */
+ void *, /* buffer to update with bytes read*/
+ size_t *, /* output: size actually read */
+ void * /*user_data*/) {
+ pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Network functionality not available");
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+
+static void no_op_network_close(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *,
+ void * /*user_data*/) {}
+
+// ---------------------------------------------------------------------------
+
+static const char *no_op_network_get_last_error(PJ_CONTEXT *,
+ PROJ_NETWORK_HANDLE *,
+ void * /*user_data*/) {
+ return "Network functionality not available";
+}
+
+// ---------------------------------------------------------------------------
+
+void FileManager::fillDefaultNetworkInterface(PJ_CONTEXT *ctx) {
+ ctx->networking.open = no_op_network_open;
+ ctx->networking.close = no_op_network_close;
+ ctx->networking.get_last_error = no_op_network_get_last_error;
+}
+
+// ---------------------------------------------------------------------------
+
NS_PROJ_END
+
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+/** Define a custom set of callbacks for network access.
+ *
+ * All callbacks should be provided (non NULL pointers).
+ *
+ * @param ctx PROJ context, or NULL
+ * @param open_cbk Callback to open a remote file given its URL
+ * @param close_cbk Callback to close a remote file.
+ * @param get_header_value_cbk Callback to get HTTP headers
+ * @param get_file_size_cbk Callback to get the size of the remote file.
+ * @param read_range_cbk Callback to read a range of bytes inside a remote file.
+ * @param get_last_error_cbk Callback to get last error message.
+ * @param user_data Arbitrary pointer provided by the user, and passed to the
+ * above callbacks. May be NULL.
+ * @return TRUE in case of success.
+ */
+int proj_context_set_network_callbacks(
+ PJ_CONTEXT *ctx, proj_network_open_cbk_type open_cbk,
+ proj_network_close_cbk_type close_cbk,
+ proj_network_get_header_value_cbk_type get_header_value_cbk,
+ proj_network_get_file_size_cbk_type get_file_size_cbk,
+ proj_network_read_range_type read_range_cbk,
+ proj_network_get_last_error_type get_last_error_cbk, void *user_data) {
+ if (ctx == nullptr) {
+ ctx = pj_get_default_ctx();
+ }
+ if (!open_cbk || !close_cbk || !get_header_value_cbk ||
+ !get_file_size_cbk || !read_range_cbk || !get_last_error_cbk) {
+ return false;
+ }
+ ctx->networking.open = open_cbk;
+ ctx->networking.close = close_cbk;
+ ctx->networking.get_header_value = get_header_value_cbk;
+ ctx->networking.get_file_size = get_file_size_cbk;
+ ctx->networking.read_range = read_range_cbk;
+ ctx->networking.get_last_error = get_last_error_cbk;
+ ctx->networking.user_data = user_data;
+ return true;
+}
diff --git a/src/filemanager.hpp b/src/filemanager.hpp
index 19478c48..0e830450 100644
--- a/src/filemanager.hpp
+++ b/src/filemanager.hpp
@@ -35,6 +35,8 @@
NS_PROJ_START
+//! @cond Doxygen_Suppress
+
class File;
class FileManager {
@@ -48,6 +50,8 @@ class FileManager {
// "High-level" interface, honoring PROJ_LIB and the like.
static std::unique_ptr<File> open_resource_file(PJ_CONTEXT *ctx,
const char *name);
+
+ static void fillDefaultNetworkInterface(PJ_CONTEXT *ctx);
};
// ---------------------------------------------------------------------------
@@ -63,6 +67,8 @@ class File {
virtual unsigned long long tell() = 0;
};
+//! @endcond Doxygen_Suppress
+
NS_PROJ_END
#endif // FILEMANAGER_HPP_INCLUDED \ No newline at end of file
diff --git a/src/open_lib.cpp b/src/open_lib.cpp
index e1572754..6bdd5510 100644
--- a/src/open_lib.cpp
+++ b/src/open_lib.cpp
@@ -53,6 +53,8 @@ PROJ_LIB;
nullptr;
#endif
+using namespace NS_PROJ::internal;
+
/************************************************************************/
/* pj_set_finder() */
/************************************************************************/
@@ -238,7 +240,9 @@ pj_open_lib_internal(projCtx ctx, const char *name, const char *mode,
else if (strchr(dir_chars,*name)
|| (*name == '.' && strchr(dir_chars,name[1]))
|| (!strncmp(name, "..", 2) && strchr(dir_chars,name[2]))
- || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2])) )
+ || (name[0] != '\0' && name[1] == ':' && strchr(dir_chars,name[2]))
+ || starts_with(name, "http://")
+ || starts_with(name, "https://"))
sysname = name;
/* or try to use application provided file finder */
diff --git a/src/proj.h b/src/proj.h
index fc309542..ba6e7043 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -353,6 +353,82 @@ void PROJ_DLL proj_context_set_search_paths(PJ_CONTEXT *ctx, int count_paths, co
void PROJ_DLL proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable);
int PROJ_DLL proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path);
+/*! @endcond */
+
+/** Opaque structure for PROJ. Implementations might cast it to their
+ * structure/class of choice. */
+typedef struct PROJ_NETWORK_HANDLE PROJ_NETWORK_HANDLE;
+
+/** Network access: open callback
+ *
+ * Should try to read the size_to_read first bytes of the file of URL url,
+ * and write them to buffer. *out_size_read should be updated with the actual
+ * amount of bytes read (== size_to_read if the file is larger than size_to_read).
+ * During this read, the implementation should make sure to store the HTTP
+ * headers from the server response to be able to respond to
+ * proj_network_get_header_value_cbk_type and proj_network_get_file_size_cbk_type
+ * callbacks.
+ *
+ * @return a non-NULL opaque handle in case of success.
+ */
+typedef PROJ_NETWORK_HANDLE* (*proj_network_open_cbk_type)(
+ PJ_CONTEXT* ctx,
+ const char* url,
+ size_t size_to_read,
+ void* buffer,
+ size_t* out_size_read,
+ void* user_data);
+
+/** Network access: close callback */
+typedef void (*proj_network_close_cbk_type)(PJ_CONTEXT* ctx,
+ PROJ_NETWORK_HANDLE* handle,
+ void* user_data);
+
+/** Network access: get HTTP headers */
+typedef const char* (*proj_network_get_header_value_cbk_type)(
+ PJ_CONTEXT* ctx,
+ PROJ_NETWORK_HANDLE* handle,
+ const char* header_name,
+ void* user_data);
+
+/** Network access: get file size */
+typedef unsigned long long (*proj_network_get_file_size_cbk_type)(
+ PJ_CONTEXT* ctx,
+ PROJ_NETWORK_HANDLE* handle,
+ void* user_data);
+
+/** Network access: read range
+ *
+ * Read size_to_read bytes from handle, starting at offset, into
+ * buffer.
+ * @return the number of bytes actually read (0 in case of error)
+ */
+typedef size_t (*proj_network_read_range_type)(
+ PJ_CONTEXT* ctx,
+ PROJ_NETWORK_HANDLE* handle,
+ unsigned long long offset,
+ size_t size_to_read,
+ void* buffer,
+ void* user_data);
+
+/** Network access: get last error message */
+typedef const char* (*proj_network_get_last_error_type)(
+ PJ_CONTEXT* ctx,
+ PROJ_NETWORK_HANDLE*,
+ void* user_data);
+
+int PROJ_DLL proj_context_set_network_callbacks(
+ PJ_CONTEXT* ctx,
+ proj_network_open_cbk_type open_cbk,
+ proj_network_close_cbk_type close_cbk,
+ proj_network_get_header_value_cbk_type get_header_value_cbk,
+ proj_network_get_file_size_cbk_type get_file_size_cbk,
+ proj_network_read_range_type read_range_cbk,
+ proj_network_get_last_error_type get_last_error_cbk,
+ void* user_data);
+
+/*! @cond Doxygen_Suppress */
+
/* Manage the transformation definition object PJ */
PJ PROJ_DLL *proj_create (PJ_CONTEXT *ctx, const char *definition);
PJ PROJ_DLL *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv);
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 93134946..aefeea39 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -666,6 +666,17 @@ struct projFileAPI_t;
struct projCppContext;
+struct projNetworkCallbacksAndData
+{
+ proj_network_open_cbk_type open = nullptr;
+ proj_network_close_cbk_type close = nullptr;
+ proj_network_get_header_value_cbk_type get_header_value = nullptr;
+ proj_network_get_file_size_cbk_type get_file_size = nullptr;
+ proj_network_read_range_type read_range = nullptr;
+ proj_network_get_last_error_type get_last_error = nullptr;
+ void* user_data = nullptr;
+};
+
/* proj thread context */
struct projCtx_t {
int last_errno = 0;
@@ -684,6 +695,8 @@ struct projCtx_t {
const char* (*file_finder) (PJ_CONTEXT *, const char*, void* user_data) = nullptr;
void* file_finder_user_data = nullptr;
+ projNetworkCallbacksAndData networking{};
+
int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString()
projCtx_t() = default;