diff --git a/CMakeLists.txt b/CMakeLists.txt index d8e9343f..fbe441df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,6 +271,18 @@ endif() set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) + +pkg_check_modules(libpcrecpp libpcrecpp) +if( libpcrecpp_FOUND ) + set(CMAKE_REQUIRED_INCLUDES ${libpcrecpp_INCLUDE_DIRS} ${REQUIRED_INCLUDES}) + check_include_file_cxx(pcrecpp.h HAVE_PCRECPP_H) + set(CMAKE_REQUIRED_INCLUDES) + if( NOT HAVE_PCRECPP_H ) + set(libpcrecpp_FOUND 0) + endif() + link_libraries(pcrecpp) +endif() + find_package(ZeroMQ QUIET) if( ZeroMQ_FOUND ) include_directories(${ZeroMQ_INCLUDE_DIR}) diff --git a/include/mujincontrollerclient/mujincontrollerclient.h b/include/mujincontrollerclient/mujincontrollerclient.h index 2cf4a7e9..45ce1b57 100644 --- a/include/mujincontrollerclient/mujincontrollerclient.h +++ b/include/mujincontrollerclient/mujincontrollerclient.h @@ -519,6 +519,9 @@ class MUJINCLIENT_API ControllerClient /// \param vdata filled with the contents of the file on the controller filesystem virtual void DownloadFileFromControllerIfModifiedSince_UTF16(const std::wstring& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout = 5.0) = 0; + + virtual std::string GetUnicodeFromPrimaryKey(const std::string& pk) const = 0; + /// \brief Deletes a file on the controller network filesystem. /// /// \param uri UTF-8 encoded file in the network filesystem to delete. @@ -602,6 +605,11 @@ class MUJINCLIENT_API ControllerClient /// virtual std::string SetObjectGeometryMesh(const std::string& objectPk, const std::string& geometryPk, const std::vector& data, const std::string& unit = "mm", double timeout = 5) = 0; +protected: + virtual std::string _Quote(const std::string& value) const = 0; + virtual std::string _Unquote(const std::string& value) const = 0; + virtual bool _ParseURI(const std::string& uri, std::string& scheme, std::string& authority, std::string& path, std::string& query, std::string& fragment) const = 0; + virtual std::string _AssembleURI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment) = 0; }; class MUJINCLIENT_API WebResource diff --git a/src/controllerclientimpl.cpp b/src/controllerclientimpl.cpp index 3cb87a6b..c5202d38 100644 --- a/src/controllerclientimpl.cpp +++ b/src/controllerclientimpl.cpp @@ -1363,6 +1363,90 @@ void ControllerClientImpl::_DownloadFileFromController(const std::string& destur } } + +std::string ControllerClientImpl::_Quote(const std::string& value) const { + if(_curl){ + char* output = curl_easy_escape(_curl, value.c_str(), value.length()); + if(output){ + return std::string(output); + } + } + return ""; +} + +std::string ControllerClientImpl::_Unquote(const std::string& value) const{ + if(_curl){ + int* outlength = NULL; + char* result = NULL; + result = curl_easy_unescape(_curl, value.c_str(), value.length(), outlength); + if(result){ + return string(result); + } + } + return ""; +} + +bool ControllerClientImpl::_ParseURI(const std::string& uri, std::string& scheme, std::string& authority, std::string& path, std::string& query, std::string& fragment) const{ + static pcrecpp::RE re("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); + std::string s1, s3, s6, s8; + if (re.FullMatch(uri, &s1, &scheme, &s3, &authority, &path, &s6, &query, &s8, &fragment)) + { + if(scheme == "mujin"){ + // need to do special check since may container fragment in path; + if(!boost::ends_with(path, "mujin.dae")){ + // try only to get last fragment part; + size_t fragmentIndex = fragment.rfind('#'); + path += "#" + fragment.substr(0, fragmentIndex); + fragment = fragment.substr(fragmentIndex+1); + } + } + path = _Unquote(path); + return true; + } + return false; +} + +std::string ControllerClientImpl::_AssembleURI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment){ + std::string uri = ""; + + uri = scheme + ":"; + if(!authority.empty()){ + uri = uri + "//" + authority; + } + if(scheme == "mujin"){ + if(!path.empty()){ + if(path[0] != '/'){ + uri = uri + '/' + path; + } + else{ + uri = uri + path; + } + } + } + else{ + if(!path.empty()){ + if(path[0] != '/'){ + uri += '/' + _Quote(path); + } + else{ + uri += _Quote(path); + } + } + } + + if(!query.empty()){ + uri += "?" + query; + } + if(!fragment.empty()){ + uri += '#' + fragment; + } + return uri; +} + +std::string ControllerClientImpl::GetUnicodeFromPrimaryKey(const std::string& pk) const{ + return _Unquote(pk); +} + void ControllerClientImpl::DeleteFileOnController_UTF8(const std::string& desturi) { boost::mutex::scoped_lock lock(_mutex); diff --git a/src/controllerclientimpl.h b/src/controllerclientimpl.h index 4fef3aee..3c82378d 100644 --- a/src/controllerclientimpl.h +++ b/src/controllerclientimpl.h @@ -20,7 +20,7 @@ #include #include - +#include namespace mujinclient { class ControllerClientImpl : public ControllerClient, public boost::enable_shared_from_this @@ -151,7 +151,7 @@ class ControllerClientImpl : public ControllerClient, public boost::enable_share { return _baseuri; } - + std::string GetUnicodeFromPrimaryKey(const std::string& pk) const; protected: int _CallPut(const std::string& relativeuri, const void* pdata, size_t nDataSize, rapidjson::Document& pt, curl_slist* headers, int expectedhttpcode=202, double timeout = 5.0); @@ -190,6 +190,13 @@ class ControllerClientImpl : public ControllerClient, public boost::enable_share int _CallGet(const std::string& desturi, rapidjson::Document& pt, int expectedhttpcode=200, double timeout = 5.0); int _CallGet(const std::string& desturi, std::string& outputdata, int expectedhttpcode=200, double timeout = 5.0); int _CallGet(const std::string& desturi, std::vector& outputdata, int expectedhttpcode=200, double timeout = 5.0); + + + std::string _Quote(const std::string& value) const; + std::string _Unquote(const std::string& value) const; + bool _ParseURI(const std::string& uri, std::string& scheme, std::string& authority, std::string& path, std::string& query, std::string& fragment) const; + std::string _AssembleURI(const std::string& scheme, const std::string& authority, const std::string& path, const std::string& query, const std::string& fragment); + /// \brief desturi is URL-encoded. Also assume _mutex is locked. virtual void _UploadFileToController_UTF8(const std::string& filename, const std::string& desturi); @@ -226,7 +233,7 @@ class ControllerClientImpl : public ControllerClient, public boost::enable_share /// \param stream is std::pair::const_iterator, size_t>*, which gets incremented everytime this function is called. static size_t _ReadInMemoryUploadCallback(void *ptr, size_t size, size_t nmemb, void *stream); - + int _lastmode; CURL *_curl; boost::mutex _mutex;