3
* ====================================================================
4
* Licensed to the Apache Software Foundation (ASF) under one
5
* or more contributor license agreements. See the NOTICE file
6
* distributed with this work for additional information
7
* regarding copyright ownership. The ASF licenses this file
8
* to you under the Apache License, Version 2.0 (the
9
* "License"); you may not use this file except in compliance
10
* with the License. You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* Unless required by applicable law or agreed to in writing,
15
* software distributed under the License is distributed on an
16
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
* KIND, either express or implied. See the License for the
18
* specific language governing permissions and limitations
20
* ====================================================================
23
* @file ClientContext.cpp
24
* @brief Implementation of the class ClientContext
27
#include "svn_client.h"
28
#include "private/svn_wc_private.h"
29
#include "svn_private_config.h"
31
#include "ClientContext.h"
33
#include "JNICriticalSection.h"
37
#include "EnumMapper.h"
38
#include "CommitMessage.h"
41
ClientContext::ClientContext(jobject jsvnclient, SVN::Pool &pool)
43
m_cancelOperation(false)
45
JNIEnv *env = JNIUtil::getEnv();
47
/* Grab a global reference to the Java object embedded in the parent Java
49
static jfieldID ctxFieldID = 0;
52
jclass clazz = env->GetObjectClass(jsvnclient);
53
if (JNIUtil::isJavaExceptionThrown())
56
ctxFieldID = env->GetFieldID(clazz, "clientContext",
57
"L"JAVA_PACKAGE"/SVNClient$ClientContext;");
58
if (JNIUtil::isJavaExceptionThrown() || ctxFieldID == 0)
61
env->DeleteLocalRef(clazz);
64
jobject jctx = env->GetObjectField(jsvnclient, ctxFieldID);
65
if (JNIUtil::isJavaExceptionThrown())
68
m_jctx = env->NewGlobalRef(jctx);
69
if (JNIUtil::isJavaExceptionThrown())
72
env->DeleteLocalRef(jctx);
74
SVN_JNI_ERR(svn_client_create_context(&m_context, pool.getPool()),
77
/* Clear the wc_ctx as we don't want to maintain this unconditionally
78
for compatibility reasons */
79
SVN_JNI_ERR(svn_wc_context_destroy(m_context->wc_ctx),
81
m_context->wc_ctx = NULL;
83
/* None of the following members change during the lifetime of
85
m_context->notify_func = NULL;
86
m_context->notify_baton = NULL;
87
m_context->log_msg_func3 = CommitMessage::callback;
88
m_context->log_msg_baton3 = NULL;
89
m_context->cancel_func = checkCancel;
90
m_context->cancel_baton = this;
91
m_context->notify_func2= notify;
92
m_context->notify_baton2 = m_jctx;
93
m_context->progress_func = progress;
94
m_context->progress_baton = m_jctx;
95
m_context->conflict_func2 = resolve;
96
m_context->conflict_baton2 = m_jctx;
98
m_context->client_name = "javahl";
102
ClientContext::~ClientContext()
106
JNIEnv *env = JNIUtil::getEnv();
107
env->DeleteGlobalRef(m_jctx);
111
/* Helper function to make sure that we don't keep dangling pointers in ctx.
112
Note that this function might be called multiple times if getContext()
113
is called on the same pool.
115
The use of this function assumes a proper subpool behavior by its user,
116
(read: SVNClient) usually per request.
120
struct clearctx_baton_t
122
svn_client_ctx_t *ctx;
123
svn_client_ctx_t *backup;
126
static apr_status_t clear_ctx_ptrs(void *ptr)
128
clearctx_baton_t *bt = (clearctx_baton_t*)ptr;
130
/* Reset all values to those before overwriting by getContext. */
131
*bt->ctx = *bt->backup;
139
ClientContext::getContext(CommitMessage *message, SVN::Pool &in_pool)
141
apr_pool_t *pool = in_pool.getPool();
142
svn_auth_baton_t *ab;
143
svn_client_ctx_t *ctx = m_context;
145
/* Make a temporary copy of ctx to restore at pool cleanup to avoid
146
leaving references to dangling pointers.
148
Note that this allows creating a stack of context changes if
149
the function is invoked multiple times with different pools.
151
clearctx_baton_t *bt = (clearctx_baton_t *)apr_pcalloc(pool, sizeof(*bt));
153
bt->backup = (svn_client_ctx_t*)apr_pmemdup(pool, ctx, sizeof(*ctx));
154
apr_pool_cleanup_register(in_pool.getPool(), bt, clear_ctx_ptrs,
160
const char *configDir = m_configDir.c_str();
161
if (m_configDir.empty())
163
SVN_JNI_ERR(svn_config_get_config(&(ctx->config), configDir,
167
bt->backup->config = ctx->config;
169
svn_config_t *config = (svn_config_t *) apr_hash_get(ctx->config,
170
SVN_CONFIG_CATEGORY_CONFIG,
171
APR_HASH_KEY_STRING);
174
/* The whole list of registered providers */
175
apr_array_header_t *providers;
177
/* Populate the registered providers with the platform-specific providers */
178
SVN_JNI_ERR(svn_auth_get_platform_specific_client_providers(&providers,
183
/* Use the prompter (if available) to prompt for password and cert
185
svn_auth_plaintext_prompt_func_t plaintext_prompt_func = NULL;
186
void *plaintext_prompt_baton = NULL;
187
svn_auth_plaintext_passphrase_prompt_func_t plaintext_passphrase_prompt_func;
188
void *plaintext_passphrase_prompt_baton = NULL;
190
if (m_prompter != NULL)
192
plaintext_prompt_func = Prompter::plaintext_prompt;
193
plaintext_prompt_baton = m_prompter;
194
plaintext_passphrase_prompt_func = Prompter::plaintext_passphrase_prompt;
195
plaintext_passphrase_prompt_baton = m_prompter;
198
/* The main disk-caching auth providers, for both
199
* 'username/password' creds and 'username' creds. */
200
svn_auth_provider_object_t *provider;
202
svn_auth_get_simple_provider2(&provider, plaintext_prompt_func,
203
plaintext_prompt_baton, pool);
204
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
206
svn_auth_get_username_provider(&provider, pool);
207
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
209
/* The server-cert, client-cert, and client-cert-password providers. */
210
SVN_JNI_ERR(svn_auth_get_platform_specific_provider(&provider,
217
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
219
svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
220
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
221
svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
222
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
223
svn_auth_get_ssl_client_cert_pw_file_provider2(&provider,
224
plaintext_passphrase_prompt_func,
225
plaintext_passphrase_prompt_baton, pool);
226
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
228
if (m_prompter != NULL)
230
/* Two basic prompt providers: username/password, and just username.*/
231
provider = m_prompter->getProviderSimple(in_pool);
233
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
235
provider = m_prompter->getProviderUsername(in_pool);
236
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
238
/* Three ssl prompt providers, for server-certs, client-certs,
239
* and client-cert-passphrases. */
240
provider = m_prompter->getProviderServerSSLTrust(in_pool);
241
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
243
provider = m_prompter->getProviderClientSSL(in_pool);
244
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
246
provider = m_prompter->getProviderClientSSLPassword(in_pool);
247
APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
250
/* Build an authentication baton to give to libsvn_client. */
251
svn_auth_open(&ab, providers, pool);
253
/* Place any default --username or --password credentials into the
254
* auth_baton's run-time parameter hash. ### Same with --no-auth-cache? */
255
if (!m_userName.empty())
256
svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_USERNAME,
257
apr_pstrdup(in_pool.getPool(),
258
m_userName.c_str()));
259
if (!m_passWord.empty())
260
svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD,
261
apr_pstrdup(in_pool.getPool(),
262
m_passWord.c_str()));
263
/* Store where to retrieve authentication data? */
264
if (!m_configDir.empty())
265
svn_auth_set_parameter(ab, SVN_AUTH_PARAM_CONFIG_DIR,
266
apr_pstrdup(in_pool.getPool(),
267
m_configDir.c_str()));
269
ctx->auth_baton = ab;
270
ctx->log_msg_baton3 = message;
271
m_cancelOperation = false;
273
SVN_JNI_ERR(svn_wc_context_create(&ctx->wc_ctx, NULL,
274
in_pool.getPool(), in_pool.getPool()),
281
ClientContext::username(const char *pi_username)
283
m_userName = (pi_username == NULL ? "" : pi_username);
287
ClientContext::password(const char *pi_password)
289
m_passWord = (pi_password == NULL ? "" : pi_password);
293
ClientContext::setPrompt(Prompter *prompter)
296
m_prompter = prompter;
300
ClientContext::setConfigDirectory(const char *configDir)
302
// A change to the config directory may necessitate creation of
303
// the config templates.
304
SVN::Pool requestPool;
305
SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.getPool()), );
307
m_configDir = (configDir == NULL ? "" : configDir);
308
m_context->config = NULL;
312
ClientContext::getConfigDirectory() const
314
return m_configDir.c_str();
318
ClientContext::cancelOperation()
320
m_cancelOperation = true;
324
ClientContext::checkCancel(void *cancelBaton)
326
ClientContext *that = (ClientContext *)cancelBaton;
327
if (that->m_cancelOperation)
328
return svn_error_create(SVN_ERR_CANCELLED, NULL,
329
_("Operation cancelled"));
335
ClientContext::notify(void *baton,
336
const svn_wc_notify_t *notify,
339
jobject jctx = (jobject) baton;
340
JNIEnv *env = JNIUtil::getEnv();
342
static jmethodID mid = 0;
345
jclass clazz = env->GetObjectClass(jctx);
346
if (JNIUtil::isJavaExceptionThrown())
349
mid = env->GetMethodID(clazz, "onNotify",
350
"(L"JAVA_PACKAGE"/ClientNotifyInformation;)V");
351
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
354
env->DeleteLocalRef(clazz);
357
jobject jInfo = CreateJ::ClientNotifyInformation(notify);
358
if (JNIUtil::isJavaExceptionThrown())
361
env->CallVoidMethod(jctx, mid, jInfo);
362
if (JNIUtil::isJavaExceptionThrown())
365
env->DeleteLocalRef(jInfo);
369
ClientContext::progress(apr_off_t progressVal, apr_off_t total,
370
void *baton, apr_pool_t *pool)
372
jobject jctx = (jobject) baton;
373
JNIEnv *env = JNIUtil::getEnv();
375
// Create a local frame for our references
376
env->PushLocalFrame(LOCAL_FRAME_SIZE);
377
if (JNIUtil::isJavaExceptionThrown())
380
static jmethodID mid = 0;
383
jclass clazz = env->GetObjectClass(jctx);
384
if (JNIUtil::isJavaExceptionThrown())
385
POP_AND_RETURN_NOTHING();
387
mid = env->GetMethodID(clazz, "onProgress",
388
"(L"JAVA_PACKAGE"/ProgressEvent;)V");
389
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
390
POP_AND_RETURN_NOTHING();
393
static jmethodID midCT = 0;
394
jclass clazz = env->FindClass(JAVA_PACKAGE"/ProgressEvent");
395
if (JNIUtil::isJavaExceptionThrown())
396
POP_AND_RETURN_NOTHING();
400
midCT = env->GetMethodID(clazz, "<init>", "(JJ)V");
401
if (JNIUtil::isJavaExceptionThrown() || midCT == 0)
402
POP_AND_RETURN_NOTHING();
405
// Call the Java method.
406
jobject jevent = env->NewObject(clazz, midCT,
407
(jlong) progressVal, (jlong) total);
408
if (JNIUtil::isJavaExceptionThrown())
409
POP_AND_RETURN_NOTHING();
411
env->CallVoidMethod(jctx, mid, jevent);
413
POP_AND_RETURN_NOTHING();
417
ClientContext::resolve(svn_wc_conflict_result_t **result,
418
const svn_wc_conflict_description2_t *desc,
420
apr_pool_t *result_pool,
421
apr_pool_t *scratch_pool)
423
jobject jctx = (jobject) baton;
424
JNIEnv *env = JNIUtil::getEnv();
426
// Create a local frame for our references
427
env->PushLocalFrame(LOCAL_FRAME_SIZE);
428
if (JNIUtil::isJavaExceptionThrown())
431
static jmethodID mid = 0;
434
jclass clazz = env->GetObjectClass(jctx);
435
if (JNIUtil::isJavaExceptionThrown())
436
POP_AND_RETURN(SVN_NO_ERROR);
438
mid = env->GetMethodID(clazz, "resolve",
439
"(L"JAVA_PACKAGE"/ConflictDescriptor;)"
440
"L"JAVA_PACKAGE"/ConflictResult;");
441
if (JNIUtil::isJavaExceptionThrown() || mid == 0)
442
POP_AND_RETURN(SVN_NO_ERROR);
445
// Create an instance of the conflict descriptor.
446
jobject jdesc = CreateJ::ConflictDescriptor(desc);
447
if (JNIUtil::isJavaExceptionThrown())
448
POP_AND_RETURN(SVN_NO_ERROR);
450
// Invoke the Java conflict resolver callback method using the descriptor.
451
jobject jresult = env->CallObjectMethod(jctx, mid, jdesc);
452
if (JNIUtil::isJavaExceptionThrown())
454
// If an exception is thrown by our conflict resolver, remove it
455
// from the JNI env, and convert it into a Subversion error.
456
SVN::Pool tmpPool(scratch_pool);
457
const char *msg = JNIUtil::thrownExceptionToCString(tmpPool);
458
svn_error_t *err = svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
460
env->PopLocalFrame(NULL);
464
*result = javaResultToC(jresult, result_pool);
467
// Unable to convert the result into a C representation.
468
env->PopLocalFrame(NULL);
469
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, NULL);
472
env->PopLocalFrame(NULL);
476
svn_wc_conflict_result_t *
477
ClientContext::javaResultToC(jobject jresult, apr_pool_t *pool)
479
JNIEnv *env = JNIUtil::getEnv();
481
// Create a local frame for our references
482
env->PushLocalFrame(LOCAL_FRAME_SIZE);
483
if (JNIUtil::isJavaExceptionThrown())
486
static jmethodID getChoice = 0;
487
static jmethodID getMergedPath = 0;
490
if (getChoice == 0 || getMergedPath == 0)
492
clazz = env->FindClass(JAVA_PACKAGE "/ConflictResult");
493
if (JNIUtil::isJavaExceptionThrown())
499
getChoice = env->GetMethodID(clazz, "getChoice",
500
"()L"JAVA_PACKAGE"/ConflictResult$Choice;");
501
if (JNIUtil::isJavaExceptionThrown() || getChoice == 0)
504
if (getMergedPath == 0)
506
getMergedPath = env->GetMethodID(clazz, "getMergedPath",
507
"()Ljava/lang/String;");
508
if (JNIUtil::isJavaExceptionThrown() || getMergedPath == 0)
512
jobject jchoice = env->CallObjectMethod(jresult, getChoice);
513
if (JNIUtil::isJavaExceptionThrown())
516
jstring jmergedPath = (jstring) env->CallObjectMethod(jresult, getMergedPath);
517
if (JNIUtil::isJavaExceptionThrown())
520
JNIStringHolder mergedPath(jmergedPath);
521
if (JNIUtil::isJavaExceptionThrown())
524
svn_wc_conflict_result_t *result =
525
svn_wc_create_conflict_result(EnumMapper::toConflictChoice(jchoice),
526
mergedPath.pstrdup(pool),
529
env->PopLocalFrame(NULL);