diff options
Diffstat (limited to 'subversion/bindings/javahl/native/OperationContext.cpp')
-rw-r--r-- | subversion/bindings/javahl/native/OperationContext.cpp | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/subversion/bindings/javahl/native/OperationContext.cpp b/subversion/bindings/javahl/native/OperationContext.cpp new file mode 100644 index 0000000..b455c36 --- /dev/null +++ b/subversion/bindings/javahl/native/OperationContext.cpp @@ -0,0 +1,657 @@ +/** + * @copyright + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * @endcopyright + * + * @file OperationContext.cpp + * @brief Implementation of the class OperationContext + */ + +#include <apr_file_io.h> + +#include "GlobalConfig.h" +#include "OperationContext.h" +#include "JNIUtil.h" +#include "JNICriticalSection.h" + +#include "Prompter.h" +#include "CreateJ.h" +#include "EnumMapper.h" +#include "CommitMessage.h" + +#include "svn_client.h" +#include "private/svn_wc_private.h" +#include "private/svn_dep_compat.h" +#include "svn_private_config.h" + +OperationContext::OperationContext(SVN::Pool &pool) + : m_config(NULL), + m_prompter(NULL), + m_cancelOperation(0), + m_pool(&pool), + m_jctx(NULL), + m_jcfgcb(NULL), + m_jtunnelcb(NULL) +{} + +void +OperationContext::attachJavaObject( + jobject contextHolder, const char *contextClassType, + const char *contextFieldName, jfieldID * ctxFieldID) +{ + JNIEnv *env = JNIUtil::getEnv(); + + /* Grab a global reference to the Java object embedded in the parent + Java object. */ + if ((*ctxFieldID) == 0) + { + jclass clazz = env->GetObjectClass(contextHolder); + if (JNIUtil::isJavaExceptionThrown()) + return; + + (*ctxFieldID) = env->GetFieldID(clazz, contextFieldName, contextClassType); + if (JNIUtil::isJavaExceptionThrown() || (*ctxFieldID) == 0) + return; + + env->DeleteLocalRef(clazz); + } + + jobject jctx = env->GetObjectField(contextHolder, (*ctxFieldID)); + if (JNIUtil::isJavaExceptionThrown()) + return; + + m_jctx = env->NewGlobalRef(jctx); + if (JNIUtil::isJavaExceptionThrown()) + return; + + env->DeleteLocalRef(jctx); +} + +OperationContext::~OperationContext() +{ + JNIEnv *env = JNIUtil::getEnv(); + env->DeleteGlobalRef(m_jctx); + if (m_jcfgcb) + env->DeleteGlobalRef(m_jcfgcb); + if (m_jtunnelcb) + env->DeleteGlobalRef(m_jtunnelcb); +} + +apr_hash_t * +OperationContext::getConfigData() +{ + if(m_pool->getPool() == NULL) + { + JNIUtil::throwNullPointerException("pool is null"); + } + + if (m_config == NULL) + { + const char *configDir = m_configDir.c_str(); + if (m_configDir.empty()) + configDir = NULL; + SVN_JNI_ERR( + svn_config_get_config(&m_config, configDir, m_pool->getPool()), NULL); + notifyConfigLoad(); + } + + return m_config; +} + +svn_auth_baton_t * +OperationContext::getAuthBaton(SVN::Pool &in_pool) +{ + svn_auth_baton_t *ab; + apr_pool_t *pool = in_pool.getPool(); + + apr_hash_t * configData = getConfigData(); + + if (configData == NULL) + { + return NULL; + } + + svn_config_t *config = static_cast<svn_config_t *>(apr_hash_get(configData, + SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING)); + + const bool use_native_store = GlobalConfig::useNativeCredentialsStore(); + + /* The whole list of registered providers */ + apr_array_header_t *providers; + svn_auth_provider_object_t *provider; + + if (use_native_store) + { + /* Populate the registered providers with the platform-specific providers */ + SVN_JNI_ERR( + svn_auth_get_platform_specific_client_providers( + &providers, config, pool), + NULL); + + /* Use the prompter (if available) to prompt for password and cert + * caching. */ + svn_auth_plaintext_prompt_func_t plaintext_prompt_func; + void *plaintext_prompt_baton; + svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func; + void *plaintext_passphrase_prompt_baton; + + if (m_prompter.get()) + { + plaintext_prompt_func = Prompter::plaintext_prompt; + plaintext_prompt_baton = m_prompter.get(); + plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt; + plaintext_passphrase_prompt_baton = m_prompter.get(); + } + else + { + plaintext_prompt_func = NULL; + plaintext_prompt_baton = NULL; + plaintext_passphrase_prompt_func = NULL; + plaintext_passphrase_prompt_baton = NULL; + } + + /* The main disk-caching auth providers, for both + * 'username/password' creds and 'username' creds. */ + + svn_auth_get_simple_provider2(&provider, plaintext_prompt_func, + plaintext_prompt_baton, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + svn_auth_get_username_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + svn_auth_get_ssl_server_trust_file_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_file_provider(&provider, pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + svn_auth_get_ssl_client_cert_pw_file_provider2( + &provider, + plaintext_passphrase_prompt_func, plaintext_passphrase_prompt_baton, + pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + } + else + { + // Not using hte native credentials store, start with an empty + // providers array. + providers = apr_array_make(pool, 0, sizeof(svn_auth_provider_object_t *)); + } + + if (m_prompter.get()) + { + /* Two basic prompt providers: username/password, and just username.*/ + provider = m_prompter->get_provider_simple(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_username(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + /* Three ssl prompt providers, for server-certs, client-certs, + * and client-cert-passphrases. */ + provider = m_prompter->get_provider_server_ssl_trust(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_client_ssl(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + + provider = m_prompter->get_provider_client_ssl_password(in_pool); + APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; + } + + /* Build an authentication baton to give to libsvn_client. */ + svn_auth_open(&ab, providers, pool); + + /* Place any default --username or --password credentials into the + * auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */ + if (!m_userName.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, + apr_pstrdup(in_pool.getPool(), m_userName.c_str())); + if (!m_passWord.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, + apr_pstrdup(in_pool.getPool(), m_passWord.c_str())); + /* Store where to retrieve authentication data? */ + if (!m_configDir.empty()) + svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR, + apr_pstrdup(in_pool.getPool(), m_configDir.c_str())); + return ab; +} + +jobject OperationContext::getSelf() const +{ + return m_jctx; +} + +void +OperationContext::username(const char *pi_username) +{ + m_userName = (pi_username == NULL ? "" : pi_username); +} + +void +OperationContext::password(const char *pi_password) +{ + m_passWord = (pi_password == NULL ? "" : pi_password); +} + +void +OperationContext::setPrompt(Prompter::UniquePtr prompter) +{ + m_prompter = prompter; +} + +void +OperationContext::setConfigDirectory(const char *configDir) +{ + // A change to the config directory may necessitate creation of + // the config templates. + SVN::Pool requestPool; + SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), ); + + m_configDir = (configDir == NULL ? "" : configDir); + + m_config = NULL; +} + +const char * +OperationContext::getConfigDirectory() const +{ + return (m_configDir.empty() ? NULL : m_configDir.c_str()); +} + +void OperationContext::setConfigEventHandler(jobject jcfgcb) +{ + JNIEnv *env = JNIUtil::getEnv(); + if (jcfgcb) + { + jcfgcb = env->NewGlobalRef(jcfgcb); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + if (m_jcfgcb) + env->DeleteGlobalRef(m_jcfgcb); + m_jcfgcb = jcfgcb; +} + +jobject OperationContext::getConfigEventHandler() const +{ + return m_jcfgcb; +} + +const char * +OperationContext::getUsername() const +{ + return (m_userName.empty() ? NULL : m_userName.c_str()); +} + +const char * +OperationContext::getPassword() const +{ + return (m_passWord.empty() ? NULL : m_passWord.c_str()); +} + +Prompter::UniquePtr OperationContext::clonePrompter() const +{ + if (m_prompter.get()) + return m_prompter->clone(); + return Prompter::UniquePtr(NULL); +} + +void OperationContext::setTunnelCallback(jobject jtunnelcb) +{ + JNIEnv *env = JNIUtil::getEnv(); + if (jtunnelcb) + { + jtunnelcb = env->NewGlobalRef(jtunnelcb); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + if (m_jtunnelcb) + env->DeleteGlobalRef(m_jtunnelcb); + m_jtunnelcb = jtunnelcb; +} + +jobject OperationContext::getTunnelCallback() const +{ + return m_jtunnelcb; +} + +void +OperationContext::cancelOperation() +{ + svn_atomic_set(&m_cancelOperation, 1); +} + +void +OperationContext::resetCancelRequest() +{ + svn_atomic_set(&m_cancelOperation, 0); +} + +bool +OperationContext::isCancelledOperation() +{ + return bool(svn_atomic_read(&m_cancelOperation)); +} + +svn_error_t * +OperationContext::checkCancel(void *cancelBaton) +{ + OperationContext *that = static_cast<OperationContext *>(cancelBaton); + if (that->isCancelledOperation()) + return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Operation cancelled")); + else if (JNIUtil::isJavaExceptionThrown()) + return svn_error_create(SVN_ERR_CANCELLED, JNIUtil::wrapJavaException(), + _("Operation cancelled")); + else + return SVN_NO_ERROR; +} + +void +OperationContext::progress(apr_off_t progressVal, apr_off_t total, void *baton, + apr_pool_t *pool) +{ + jobject jctx = (jobject) baton; + if (!jctx) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + // Create a local frame for our references + env->PushLocalFrame(LOCAL_FRAME_SIZE); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID mid = 0; + if (mid == 0) + { + jclass clazz = env->GetObjectClass(jctx); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + mid = env->GetMethodID(clazz, "onProgress", + "(" JAVAHL_ARG("/ProgressEvent;") ")V"); + if (JNIUtil::isJavaExceptionThrown() || mid == 0) + POP_AND_RETURN_NOTHING(); + } + + static jmethodID midCT = 0; + jclass clazz = env->FindClass(JAVAHL_CLASS("/ProgressEvent")); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + if (midCT == 0) + { + midCT = env->GetMethodID(clazz, "<init>", "(JJ)V"); + if (JNIUtil::isJavaExceptionThrown() || midCT == 0) + POP_AND_RETURN_NOTHING(); + } + + // Call the Java method. + jobject jevent = env->NewObject(clazz, midCT, (jlong) progressVal, + (jlong) total); + if (JNIUtil::isJavaExceptionThrown()) + POP_AND_RETURN_NOTHING(); + + env->CallVoidMethod(jctx, mid, jevent); + + POP_AND_RETURN_NOTHING(); +} + +const char * +OperationContext::getClientName() const +{ + return "javahl"; +} + +svn_error_t * +OperationContext::clientName(void *baton, const char **name, apr_pool_t *pool) +{ + OperationContext *that = (OperationContext *) baton; + + *name = that->getClientName(); + + return SVN_NO_ERROR; +} + +void +OperationContext::notifyConfigLoad() +{ + if (!m_jcfgcb) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + static jmethodID onload_mid = 0; + if (0 == onload_mid) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/callback/ConfigEvent")); + if (JNIUtil::isJavaExceptionThrown()) + return; + onload_mid = env->GetMethodID(cls, "onLoad", + "(" JAVAHL_ARG("/ISVNConfig;") ")V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jclass cfg_cls = env->FindClass(JAVAHL_CLASS("/util/ConfigImpl")); + if (JNIUtil::isJavaExceptionThrown()) + return; + + static jmethodID ctor_mid = 0; + if (0 == ctor_mid) + { + ctor_mid = env->GetMethodID(cfg_cls, "<init>", "(J)V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + static jmethodID dispose_mid = 0; + if (0 == dispose_mid) + { + dispose_mid = env->GetMethodID(cfg_cls, "dispose", "()V"); + if (JNIUtil::isJavaExceptionThrown()) + return; + } + + jobject jcbimpl = env->NewObject(cfg_cls, ctor_mid, + reinterpret_cast<jlong>(this)); + if (JNIUtil::isJavaExceptionThrown()) + return; + env->CallVoidMethod(m_jcfgcb, onload_mid, jcbimpl); + if (JNIUtil::isJavaExceptionThrown()) + return; + env->CallVoidMethod(jcbimpl, dispose_mid); + env->DeleteLocalRef(jcbimpl); +} + +namespace { +class TunnelContext +{ +public: + explicit TunnelContext(apr_pool_t *pool) + : request_in(NULL), + request_out(NULL), + response_in(NULL), + response_out(NULL), + jclosecb(NULL) + { + status = apr_file_pipe_create_ex(&request_in, &request_out, + APR_FULL_BLOCK, pool); + if (!status) + status = apr_file_pipe_create_ex(&response_in, &response_out, + APR_FULL_BLOCK, pool); + } + + ~TunnelContext() + { + apr_file_close(request_out); + apr_file_close(response_in); + } + + apr_file_t *request_in; + apr_file_t *request_out; + apr_file_t *response_in; + apr_file_t *response_out; + apr_status_t status; + jobject jclosecb; +}; + +jobject create_Channel(const char *class_name, JNIEnv *env, apr_file_t *fd) +{ + jclass cls = env->FindClass(class_name); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + jmethodID ctor = env->GetMethodID(cls, "<init>", "(J)V"); + if (JNIUtil::isJavaExceptionThrown()) + return NULL; + return env->NewObject(cls, ctor, reinterpret_cast<jlong>(fd)); +} + +jobject create_RequestChannel(JNIEnv *env, apr_file_t *fd) +{ + return create_Channel(JAVAHL_CLASS("/util/RequestChannel"), env, fd); +} +jobject create_ResponseChannel(JNIEnv *env, apr_file_t *fd) +{ + return create_Channel(JAVAHL_CLASS("/util/ResponseChannel"), env, fd); +} +} // anonymous namespace + +svn_boolean_t +OperationContext::checkTunnel(void *tunnel_baton, const char *tunnel_name) +{ + JNIEnv *env = JNIUtil::getEnv(); + + jstring jtunnel_name = JNIUtil::makeJString(tunnel_name); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + static jmethodID mid = 0; + if (0 == mid) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/callback/TunnelAgent")); + if (JNIUtil::isJavaExceptionThrown()) + return false; + mid = env->GetMethodID(cls, "checkTunnel", + "(Ljava/lang/String;)Z"); + if (JNIUtil::isJavaExceptionThrown()) + return false; + } + + jobject jtunnelcb = jobject(tunnel_baton); + jboolean check = env->CallBooleanMethod(jtunnelcb, mid, jtunnel_name); + if (JNIUtil::isJavaExceptionThrown()) + return false; + + return svn_boolean_t(check); +} + +svn_error_t * +OperationContext::openTunnel(svn_stream_t **request, svn_stream_t **response, + svn_ra_close_tunnel_func_t *close_func, + void **close_baton, + void *tunnel_baton, + const char *tunnel_name, const char *user, + const char *hostname, int port, + svn_cancel_func_t cancel_func, void *cancel_baton, + apr_pool_t *pool) +{ + TunnelContext *tc = new TunnelContext(pool); + if (tc->status) + { + delete tc; + return svn_error_trace( + svn_error_wrap_apr(tc->status, _("Could not open tunnel streams"))); + } + + *close_func = closeTunnel; + *close_baton = tc; + *request = svn_stream_from_aprfile2(tc->request_out, FALSE, pool); + *response = svn_stream_from_aprfile2(tc->response_in, FALSE, pool); + + JNIEnv *env = JNIUtil::getEnv(); + + jobject jrequest = create_RequestChannel(env, tc->request_in); + SVN_JNI_CATCH(, SVN_ERR_BASE); + + jobject jresponse = create_ResponseChannel(env, tc->response_out); + SVN_JNI_CATCH(, SVN_ERR_BASE); + + jstring jtunnel_name = JNIUtil::makeJString(tunnel_name); + SVN_JNI_CATCH(, SVN_ERR_BASE); + + jstring juser = JNIUtil::makeJString(user); + SVN_JNI_CATCH(, SVN_ERR_BASE); + + jstring jhostname = JNIUtil::makeJString(hostname); + SVN_JNI_CATCH(, SVN_ERR_BASE); + + static jmethodID mid = 0; + if (0 == mid) + { + jclass cls = env->FindClass(JAVAHL_CLASS("/callback/TunnelAgent")); + SVN_JNI_CATCH(, SVN_ERR_BASE); + SVN_JNI_CATCH( + mid = env->GetMethodID( + cls, "openTunnel", + "(Ljava/nio/channels/ReadableByteChannel;" + "Ljava/nio/channels/WritableByteChannel;" + "Ljava/lang/String;" + "Ljava/lang/String;" + "Ljava/lang/String;I)" + JAVAHL_ARG("/callback/TunnelAgent$CloseTunnelCallback;")), + SVN_ERR_BASE); + } + + jobject jtunnelcb = jobject(tunnel_baton); + SVN_JNI_CATCH( + tc->jclosecb = env->CallObjectMethod( + jtunnelcb, mid, jrequest, jresponse, + jtunnel_name, juser, jhostname, jint(port)), + SVN_ERR_BASE); + + return SVN_NO_ERROR; +} + +void +OperationContext::closeTunnel(void *tunnel_context, void *) +{ + TunnelContext* tc = static_cast<TunnelContext*>(tunnel_context); + jobject jclosecb = tc->jclosecb; + delete tc; + + if (!jclosecb) + return; + + JNIEnv *env = JNIUtil::getEnv(); + + static jmethodID mid = 0; + if (0 == mid) + { + jclass cls; + SVN_JNI_CATCH_VOID( + cls= env->FindClass( + JAVAHL_CLASS("/callback/TunnelAgent$CloseTunnelCallback"))); + SVN_JNI_CATCH_VOID(mid = env->GetMethodID(cls, "closeTunnel", "()V")); + } + SVN_JNI_CATCH_VOID(env->CallVoidMethod(jclosecb, mid)); +} |