/** * @copyright * ==================================================================== * Copyright (c) 2003-2008 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== * @endcopyright * * @file SVNClient.cpp * @brief: Implementation of the SVNClient class */ #include "SVNClient.h" #include "JNIUtil.h" #include "Notify.h" #include "Notify2.h" #include "CopySources.h" #include "DiffSummaryReceiver.h" #include "ConflictResolverCallback.h" #include "ProgressListener.h" #include "Prompter.h" #include "Pool.h" #include "Targets.h" #include "Revision.h" #include "RevisionRange.h" #include "BlameCallback.h" #include "ProplistCallback.h" #include "LogMessageCallback.h" #include "InfoCallback.h" #include "StatusCallback.h" #include "ChangelistCallback.h" #include "ListCallback.h" #include "JNIByteArray.h" #include "CommitMessage.h" #include "EnumMapper.h" #include "StringArray.h" #include "RevpropTable.h" #include "svn_auth.h" #include "svn_dso.h" #include "svn_types.h" #include "svn_client.h" #include "svn_sorts.h" #include "svn_time.h" #include "svn_diff.h" #include "svn_config.h" #include "svn_io.h" #include "svn_path.h" #include "svn_utf.h" #include "svn_private_config.h" #include "../include/org_tigris_subversion_javahl_Revision.h" #include "../include/org_tigris_subversion_javahl_NodeKind.h" #include "../include/org_tigris_subversion_javahl_StatusKind.h" #include "JNIStringHolder.h" #include #include #include struct log_msg_baton { const char *message; CommitMessage *messageHandler; }; SVNClient::SVNClient() { m_notify = NULL; m_notify2 = NULL; m_progressListener = NULL; m_prompter = NULL; m_commitMessage = NULL; m_conflictResolver = NULL; } SVNClient::~SVNClient() { delete m_notify; delete m_notify2; delete m_progressListener; delete m_prompter; delete m_conflictResolver; } SVNClient *SVNClient::getCppObject(jobject jthis) { static jfieldID fid = 0; jlong cppAddr = SVNBase::findCppAddrForJObject(jthis, &fid, JAVA_PACKAGE"/SVNClient"); return (cppAddr == 0 ? NULL : reinterpret_cast(cppAddr)); } void SVNClient::dispose(jobject jthis) { static jfieldID fid = 0; SVNBase::dispose(jthis, &fid, JAVA_PACKAGE"/SVNClient"); } jstring SVNClient::getAdminDirectoryName() { Pool requestPool; jstring name = JNIUtil::makeJString(svn_wc_get_adm_dir(requestPool.pool())); if (JNIUtil::isJavaExceptionThrown()) return NULL; return name; } jboolean SVNClient::isAdminDirectory(const char *name) { Pool requestPool; return svn_wc_is_adm_dir(name, requestPool.pool()) ? JNI_TRUE : JNI_FALSE; } const char *SVNClient::getLastPath() { return m_lastPath.c_str(); } /** * List directory entries of a URL. */ void SVNClient::list(const char *url, Revision &revision, Revision &pegRevision, svn_depth_t depth, int direntFields, bool fetchLocks, ListCallback *callback) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_NULL_PTR_EX(url, "path or url", ); Path urlPath(url); SVN_JNI_ERR(urlPath.error_occured(), ); SVN_JNI_ERR(svn_client_list2(urlPath.c_str(), pegRevision.revision(), revision.revision(), depth, direntFields, fetchLocks, ListCallback::callback, callback, ctx, requestPool.pool()), ); } void SVNClient::status(const char *path, svn_depth_t depth, bool onServer, bool getAll, bool noIgnore, bool ignoreExternals, StringArray &changelists, StatusCallback *callback) { Pool requestPool; svn_revnum_t youngest = SVN_INVALID_REVNUM; svn_opt_revision_t rev; SVN_JNI_NULL_PTR_EX(path, "path", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Path checkedPath(path); SVN_JNI_ERR(checkedPath.error_occured(), ); rev.kind = svn_opt_revision_unspecified; SVN_JNI_ERR(svn_client_status3(&youngest, checkedPath.c_str(), &rev, StatusCallback::callback, callback, depth, getAll, onServer, noIgnore, ignoreExternals, changelists.array(requestPool), ctx, requestPool.pool()), ); } void SVNClient::username(const char *pi_username) { m_userName = (pi_username == NULL ? "" : pi_username); } void SVNClient::password(const char *pi_password) { m_passWord = (pi_password == NULL ? "" : pi_password); } void SVNClient::setPrompt(Prompter *prompter) { delete m_prompter; m_prompter = prompter; } void SVNClient::logMessages(const char *path, Revision &pegRevision, Revision &revisionStart, Revision &revisionEnd, bool stopOnCopy, bool discoverPaths, bool includeMergedRevisions, StringArray &revProps, long limit, LogMessageCallback *callback) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Targets target(path); const apr_array_header_t *targets = target.array(requestPool); SVN_JNI_ERR(target.error_occured(), ); SVN_JNI_ERR(svn_client_log4(targets, pegRevision.revision(), revisionStart.revision(), revisionEnd.revision(), limit, discoverPaths, stopOnCopy, includeMergedRevisions, revProps.array(requestPool), LogMessageCallback::callback, callback, ctx, requestPool.pool()), ); } jlong SVNClient::checkout(const char *moduleName, const char *destPath, Revision &revision, Revision &pegRevision, svn_depth_t depth, bool ignoreExternals, bool allowUnverObstructions) { Pool requestPool; SVN_JNI_NULL_PTR_EX(moduleName, "moduleName", -1); SVN_JNI_NULL_PTR_EX(destPath, "destPath", -1); Path url(moduleName); Path path(destPath); SVN_JNI_ERR(url.error_occured(), -1); SVN_JNI_ERR(path.error_occured(), -1); svn_revnum_t rev; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return -1; SVN_JNI_ERR(svn_client_checkout3(&rev, url.c_str(), path.c_str(), pegRevision.revision(), revision.revision(), depth, ignoreExternals, allowUnverObstructions, ctx, requestPool.pool()), -1); return rev; } void SVNClient::notification(Notify *notify) { delete m_notify; m_notify = notify; } void SVNClient::notification2(Notify2 *notify2) { delete m_notify2; m_notify2 = notify2; } void SVNClient::setConflictResolver(ConflictResolverCallback *conflictResolver) { delete m_conflictResolver; m_conflictResolver = conflictResolver; } void SVNClient::setProgressListener(ProgressListener *listener) { delete m_progressListener; m_progressListener = listener; } void SVNClient::remove(Targets &targets, const char *message, bool force, bool keep_local, RevpropTable &revprops) { svn_commit_info_t *commit_info = NULL; Pool requestPool; svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return; const apr_array_header_t *targets2 = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), ); SVN_JNI_ERR(svn_client_delete3(&commit_info, targets2, force, keep_local, revprops.hash(requestPool), ctx, requestPool.pool()), ); } void SVNClient::revert(const char *path, svn_depth_t depth, StringArray &changelists) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Targets target(path); const apr_array_header_t *targets = target.array(requestPool); SVN_JNI_ERR(target.error_occured(), ); SVN_JNI_ERR(svn_client_revert2(targets, depth, changelists.array(requestPool), ctx, requestPool.pool()), ); } void SVNClient::add(const char *path, svn_depth_t depth, bool force, bool no_ignore, bool add_parents) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_add4(intPath.c_str(), depth, force, no_ignore, add_parents, ctx, requestPool.pool()), ); } jlongArray SVNClient::update(Targets &targets, Revision &revision, svn_depth_t depth, bool depthIsSticky, bool ignoreExternals, bool allowUnverObstructions) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); apr_array_header_t *revs; if (ctx == NULL) return NULL; const apr_array_header_t *array = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), NULL); SVN_JNI_ERR(svn_client_update3(&revs, array, revision.revision(), depth, depthIsSticky, ignoreExternals, allowUnverObstructions, ctx, requestPool.pool()), NULL); JNIEnv *env = JNIUtil::getEnv(); jlongArray jrevs = env->NewLongArray(revs->nelts); if (JNIUtil::isJavaExceptionThrown()) return NULL; jlong *jrevArray = env->GetLongArrayElements(jrevs, NULL); if (JNIUtil::isJavaExceptionThrown()) return NULL; for (int i = 0; i < revs->nelts; ++i) { jlong rev = APR_ARRAY_IDX(revs, i, svn_revnum_t); jrevArray[i] = rev; } env->ReleaseLongArrayElements(jrevs, jrevArray, 0); return jrevs; } jlong SVNClient::commit(Targets &targets, const char *message, svn_depth_t depth, bool noUnlock, bool keepChangelist, StringArray &changelists, RevpropTable &revprops) { Pool requestPool; svn_commit_info_t *commit_info = NULL; const apr_array_header_t *targets2 = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), -1); svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return SVN_INVALID_REVNUM; SVN_JNI_ERR(svn_client_commit4(&commit_info, targets2, depth, noUnlock, keepChangelist, changelists.array(requestPool), revprops.hash(requestPool), ctx, requestPool.pool()), SVN_INVALID_REVNUM); if (commit_info && SVN_IS_VALID_REVNUM(commit_info->revision)) return commit_info->revision; return SVN_INVALID_REVNUM; } void SVNClient::copy(CopySources ©Sources, const char *destPath, const char *message, bool copyAsChild, bool makeParents, RevpropTable &revprops) { Pool requestPool; apr_array_header_t *srcs = copySources.array(requestPool); if (srcs == NULL) { JNIUtil::throwNativeException(JAVA_PACKAGE "/ClientException", "Invalid copy sources"); return; } SVN_JNI_NULL_PTR_EX(destPath, "destPath", ); Path destinationPath(destPath); SVN_JNI_ERR(destinationPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return; svn_commit_info_t *commit_info; SVN_JNI_ERR(svn_client_copy4(&commit_info, srcs, destinationPath.c_str(), copyAsChild, makeParents, revprops.hash(requestPool), ctx, requestPool.pool()), ); } void SVNClient::move(Targets &srcPaths, const char *destPath, const char *message, bool force, bool moveAsChild, bool makeParents, RevpropTable &revprops) { Pool requestPool; const apr_array_header_t *srcs = srcPaths.array(requestPool); SVN_JNI_ERR(srcPaths.error_occured(), ); SVN_JNI_NULL_PTR_EX(destPath, "destPath", ); Path destinationPath(destPath); SVN_JNI_ERR(destinationPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return; svn_commit_info_t *commit_info; SVN_JNI_ERR(svn_client_move5(&commit_info, (apr_array_header_t *) srcs, destinationPath.c_str(), force, moveAsChild, makeParents, revprops.hash(requestPool), ctx, requestPool.pool()), ); } void SVNClient::mkdir(Targets &targets, const char *message, bool makeParents, RevpropTable &revprops) { Pool requestPool; svn_commit_info_t *commit_info = NULL; svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return; const apr_array_header_t *targets2 = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), ); SVN_JNI_ERR(svn_client_mkdir3(&commit_info, targets2, makeParents, revprops.hash(requestPool), ctx, requestPool.pool()), ); } void SVNClient::cleanup(const char *path) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_cleanup(intPath.c_str(), ctx, requestPool.pool()),); } void SVNClient::resolve(const char *path, svn_depth_t depth, svn_wc_conflict_choice_t choice) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_resolve(intPath.c_str(), depth, choice, ctx, requestPool.pool()), ); } jlong SVNClient::doExport(const char *srcPath, const char *destPath, Revision &revision, Revision &pegRevision, bool force, bool ignoreExternals, svn_depth_t depth, const char *nativeEOL) { Pool requestPool; SVN_JNI_NULL_PTR_EX(srcPath, "srcPath", -1); SVN_JNI_NULL_PTR_EX(destPath, "destPath", -1); Path sourcePath(srcPath); SVN_JNI_ERR(sourcePath.error_occured(), -1); Path destinationPath(destPath); SVN_JNI_ERR(destinationPath.error_occured(), -1); svn_revnum_t rev; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return -1; SVN_JNI_ERR(svn_client_export4(&rev, sourcePath.c_str(), destinationPath.c_str(), pegRevision.revision(), revision.revision(), force, ignoreExternals, depth, nativeEOL, ctx, requestPool.pool()), -1); return rev; } jlong SVNClient::doSwitch(const char *path, const char *url, Revision &revision, Revision &pegRevision, svn_depth_t depth, bool depthIsSticky, bool ignoreExternals, bool allowUnverObstructions) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", -1); SVN_JNI_NULL_PTR_EX(url, "url", -1); Path intUrl(url); SVN_JNI_ERR(intUrl.error_occured(), -1); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), -1); svn_revnum_t rev; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return -1; SVN_JNI_ERR(svn_client_switch2(&rev, intPath.c_str(), intUrl.c_str(), pegRevision.revision(), revision.revision(), depth, depthIsSticky, ignoreExternals, allowUnverObstructions, ctx, requestPool.pool()), -1); return rev; } void SVNClient::doImport(const char *path, const char *url, const char *message, svn_depth_t depth, bool noIgnore, bool ignoreUnknownNodeTypes, RevpropTable &revprops) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(url, "url", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); Path intUrl(url); SVN_JNI_ERR(intUrl.error_occured(), ); svn_commit_info_t *commit_info = NULL; svn_client_ctx_t *ctx = getContext(message); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_import3(&commit_info, intPath.c_str(), intUrl.c_str(), depth, noIgnore, ignoreUnknownNodeTypes, revprops.hash(requestPool), ctx, requestPool.pool()), ); } jobjectArray SVNClient::suggestMergeSources(const char *path, Revision &pegRevision) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; apr_array_header_t *sources; SVN_JNI_ERR(svn_client_suggest_merge_sources(&sources, path, pegRevision.revision(), ctx, requestPool.pool()), NULL); JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass("java/lang/String"); if (JNIUtil::isJavaExceptionThrown()) return NULL; jobjectArray jsuggestions = env->NewObjectArray(sources->nelts, clazz, NULL); for (int i = 0; i < sources->nelts; ++i) { const char *source = APR_ARRAY_IDX(sources, i, const char *); jstring jpath = JNIUtil::makeJString(source); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->SetObjectArrayElement(jsuggestions, i, jpath); if (JNIUtil::isJavaExceptionThrown()) return NULL; } return jsuggestions; } void SVNClient::merge(const char *path1, Revision &revision1, const char *path2, Revision &revision2, const char *localPath, bool force, svn_depth_t depth, bool ignoreAncestry, bool dryRun, bool recordOnly) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path1, "path1", ); SVN_JNI_NULL_PTR_EX(path2, "path2", ); SVN_JNI_NULL_PTR_EX(localPath, "localPath", ); Path intLocalPath(localPath); SVN_JNI_ERR(intLocalPath.error_occured(), ); Path srcPath1(path1); SVN_JNI_ERR(srcPath1.error_occured(), ); Path srcPath2 = path2; SVN_JNI_ERR(srcPath2.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_merge3(srcPath1.c_str(), revision1.revision(), srcPath2.c_str(), revision2.revision(), intLocalPath.c_str(), depth, ignoreAncestry, force, recordOnly, dryRun, NULL, ctx, requestPool.pool()), ); } void SVNClient::merge(const char *path, Revision &pegRevision, std::vector &rangesToMerge, const char *localPath, bool force, svn_depth_t depth, bool ignoreAncestry, bool dryRun, bool recordOnly) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(localPath, "localPath", ); Path intLocalPath(localPath); SVN_JNI_ERR(intLocalPath.error_occured(), ); Path srcPath(path); SVN_JNI_ERR(srcPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; apr_array_header_t *ranges = apr_array_make(requestPool.pool(), rangesToMerge.size(), sizeof(const svn_opt_revision_range_t *)); std::vector::const_iterator it; for (it = rangesToMerge.begin(); it != rangesToMerge.end(); ++it) { if (it->toRange(requestPool)->start.kind == svn_opt_revision_unspecified && it->toRange(requestPool)->end.kind == svn_opt_revision_unspecified) { svn_opt_revision_range_t *range = (svn_opt_revision_range_t *)apr_pcalloc(requestPool.pool(), sizeof(*range)); range->start.kind = svn_opt_revision_number; range->start.value.number = 1; range->end.kind = svn_opt_revision_head; APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = range; } else { APR_ARRAY_PUSH(ranges, const svn_opt_revision_range_t *) = it->toRange(requestPool); } if (JNIUtil::isExceptionThrown()) return; } SVN_JNI_ERR(svn_client_merge_peg3(srcPath.c_str(), ranges, pegRevision.revision(), intLocalPath.c_str(), depth, ignoreAncestry, force, recordOnly, dryRun, NULL, ctx, requestPool.pool()), ); } void SVNClient::mergeReintegrate(const char *path, Revision &pegRevision, const char *localPath, bool dryRun) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(localPath, "localPath", ); Path intLocalPath(localPath); SVN_JNI_ERR(intLocalPath.error_occured(), ); Path srcPath(path); SVN_JNI_ERR(srcPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_merge_reintegrate(srcPath.c_str(), pegRevision.revision(), intLocalPath.c_str(), dryRun, NULL, ctx, requestPool.pool()), ); } jobject SVNClient::getMergeinfo(const char *target, Revision &pegRevision) { Pool requestPool; JNIEnv *env = JNIUtil::getEnv(); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; svn_mergeinfo_t mergeinfo; Path intLocalTarget(target); SVN_JNI_ERR(intLocalTarget.error_occured(), NULL); SVN_JNI_ERR(svn_client_mergeinfo_get_merged(&mergeinfo, intLocalTarget.c_str(), pegRevision.revision(), ctx, requestPool.pool()), NULL); if (mergeinfo == NULL) return NULL; // Transform mergeinfo into Java Mergeinfo object. jclass clazz = env->FindClass(JAVA_PACKAGE "/Mergeinfo"); if (JNIUtil::isJavaExceptionThrown()) return NULL; static jmethodID ctor = 0; if (ctor == 0) { ctor = env->GetMethodID(clazz, "", "()V"); if (JNIUtil::isJavaExceptionThrown()) return NULL; } static jmethodID addRevisions = 0; if (addRevisions == 0) { addRevisions = env->GetMethodID(clazz, "addRevisions", "(Ljava/lang/String;" "[L"JAVA_PACKAGE"/RevisionRange;)V"); if (JNIUtil::isJavaExceptionThrown()) return NULL; } jobject jmergeinfo = env->NewObject(clazz, ctor); if (JNIUtil::isJavaExceptionThrown()) return NULL; apr_hash_index_t *hi; for (hi = apr_hash_first(requestPool.pool(), mergeinfo); hi; hi = apr_hash_next(hi)) { const void *path; void *val; apr_hash_this(hi, &path, NULL, &val); jstring jpath = JNIUtil::makeJString((const char *) path); jobjectArray jranges = makeJRevisionRangeArray((apr_array_header_t *) val); env->CallVoidMethod(jmergeinfo, addRevisions, jpath, jranges); env->DeleteLocalRef(jranges); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jpath); if (JNIUtil::isJavaExceptionThrown()) return NULL; } return jmergeinfo; } void SVNClient::getMergeinfoLog(int type, const char *pathOrURL, Revision &pegRevision, const char *mergeSourceURL, Revision &srcPegRevision, bool discoverChangedPaths, StringArray &revProps, LogMessageCallback *callback) { Pool requestPool; JNIEnv *env = JNIUtil::getEnv(); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_NULL_PTR_EX(pathOrURL, "path or url", ); Path urlPath(pathOrURL); SVN_JNI_ERR(urlPath.error_occured(), ); SVN_JNI_NULL_PTR_EX(mergeSourceURL, "merge source url", ); Path srcURL(mergeSourceURL); SVN_JNI_ERR(srcURL.error_occured(), ); switch (type) { case 0: SVN_JNI_ERR( svn_client_mergeinfo_log_eligible(urlPath.c_str(), pegRevision.revision(), srcURL.c_str(), srcPegRevision.revision(), LogMessageCallback::callback, callback, discoverChangedPaths, revProps.array(requestPool), ctx, requestPool.pool()), ); return; case 1: SVN_JNI_ERR( svn_client_mergeinfo_log_merged(urlPath.c_str(), pegRevision.revision(), srcURL.c_str(), srcPegRevision.revision(), LogMessageCallback::callback, callback, discoverChangedPaths, revProps.array(requestPool), ctx, requestPool.pool()), ); return; } return; } /** * Get a property. */ jobject SVNClient::propertyGet(jobject jthis, const char *path, const char *name, Revision &revision, Revision &pegRevision) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", NULL); SVN_JNI_NULL_PTR_EX(name, "name", NULL); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; apr_hash_t *props; SVN_JNI_ERR(svn_client_propget3(&props, name, intPath.c_str(), pegRevision.revision(), revision.revision(), NULL, svn_depth_empty, NULL, ctx, requestPool.pool()), NULL); apr_hash_index_t *hi; // only one element since we disabled recurse hi = apr_hash_first(requestPool.pool(), props); if (hi == NULL) return NULL; // no property with this name svn_string_t *propval; apr_hash_this(hi, NULL, NULL, (void**)&propval); if (propval == NULL) return NULL; return createJavaProperty(jthis, path, name, propval); } void SVNClient::properties(const char *path, Revision &revision, Revision &pegRevision, svn_depth_t depth, StringArray &changelists, ProplistCallback *callback) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_proplist3(intPath.c_str(), pegRevision.revision(), revision.revision(), depth, changelists.array(requestPool), ProplistCallback::callback, callback, ctx, requestPool.pool()), ); return; } void SVNClient::propertySet(const char *path, const char *name, const char *value, svn_depth_t depth, StringArray &changelists, bool force, RevpropTable &revprops) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(name, "name", ); svn_string_t *val; if (value == NULL) val = NULL; else val = svn_string_create(value, requestPool.pool()); svn_commit_info_t *commit_info = NULL; Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_propset3(&commit_info, name, val, intPath.c_str(), depth, force, SVN_INVALID_REVNUM, changelists.array(requestPool), revprops.hash(requestPool), ctx, requestPool.pool()), ); } void SVNClient::diff(const char *target1, Revision &revision1, const char *target2, Revision &revision2, Revision *pegRevision, const char *relativeToDir, const char *outfileName, svn_depth_t depth, StringArray &changelists, bool ignoreAncestry, bool noDiffDelete, bool force) { svn_error_t *err; Pool requestPool; const char *c_relToDir = relativeToDir ? svn_path_canonicalize(relativeToDir, requestPool.pool()) : relativeToDir; SVN_JNI_NULL_PTR_EX(target1, "target", ); // target2 is ignored when pegRevision is provided. if (pegRevision == NULL) SVN_JNI_NULL_PTR_EX(target2, "target2", ); SVN_JNI_NULL_PTR_EX(outfileName, "outfileName", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Path path1(target1); SVN_JNI_ERR(path1.error_occured(), ); apr_file_t *outfile = NULL; apr_status_t rv = apr_file_open(&outfile, svn_path_internal_style(outfileName, requestPool.pool()), APR_CREATE|APR_WRITE|APR_TRUNCATE , APR_OS_DEFAULT, requestPool.pool()); if (rv != APR_SUCCESS) { SVN_JNI_ERR(svn_error_createf(rv, NULL, _("Cannot open file '%s'"), outfileName), ); } // We don't use any options to diff. apr_array_header_t *diffOptions = apr_array_make(requestPool.pool(), 0, sizeof(char *)); if (pegRevision) { err = svn_client_diff_peg4(diffOptions, path1.c_str(), pegRevision->revision(), revision1.revision(), revision2.revision(), c_relToDir, depth, ignoreAncestry, noDiffDelete, force, SVN_APR_LOCALE_CHARSET, outfile, NULL /* error file */, changelists.array(requestPool), ctx, requestPool.pool()); } else { // "Regular" diff (without a peg revision). Path path2(target2); err = path2.error_occured(); if (err) { if (outfile) goto cleanup; SVN_JNI_ERR(err, ); } err = svn_client_diff4(diffOptions, path1.c_str(), revision1.revision(), path2.c_str(), revision2.revision(), c_relToDir, depth, ignoreAncestry, noDiffDelete, force, SVN_APR_LOCALE_CHARSET, outfile, NULL /* error file */, changelists.array(requestPool), ctx, requestPool.pool()); } cleanup: rv = apr_file_close(outfile); if (rv != APR_SUCCESS) { svn_error_clear(err); SVN_JNI_ERR(svn_error_createf(rv, NULL, _("Cannot close file '%s'"), outfileName), ); } SVN_JNI_ERR(err, ); } void SVNClient::diff(const char *target1, Revision &revision1, const char *target2, Revision &revision2, const char *relativeToDir, const char *outfileName, svn_depth_t depth, StringArray &changelists, bool ignoreAncestry, bool noDiffDelete, bool force) { diff(target1, revision1, target2, revision2, NULL, relativeToDir, outfileName, depth, changelists, ignoreAncestry, noDiffDelete, force); } void SVNClient::diff(const char *target, Revision &pegRevision, Revision &startRevision, Revision &endRevision, const char *relativeToDir, const char *outfileName, svn_depth_t depth, StringArray &changelists, bool ignoreAncestry, bool noDiffDelete, bool force) { diff(target, startRevision, NULL, endRevision, &pegRevision, relativeToDir, outfileName, depth, changelists, ignoreAncestry, noDiffDelete, force); } void SVNClient::diffSummarize(const char *target1, Revision &revision1, const char *target2, Revision &revision2, svn_depth_t depth, StringArray &changelists, bool ignoreAncestry, DiffSummaryReceiver &receiver) { Pool requestPool; SVN_JNI_NULL_PTR_EX(target1, "target1", ); SVN_JNI_NULL_PTR_EX(target2, "target2", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Path path1(target1); SVN_JNI_ERR(path1.error_occured(), ); Path path2(target2); SVN_JNI_ERR(path2.error_occured(), ); SVN_JNI_ERR(svn_client_diff_summarize2(path1.c_str(), revision1.revision(), path2.c_str(), revision2.revision(), depth, ignoreAncestry, changelists.array(requestPool), DiffSummaryReceiver::summarize, &receiver, ctx, requestPool.pool()), ); } void SVNClient::diffSummarize(const char *target, Revision &pegRevision, Revision &startRevision, Revision &endRevision, svn_depth_t depth, StringArray &changelists, bool ignoreAncestry, DiffSummaryReceiver &receiver) { Pool requestPool; SVN_JNI_NULL_PTR_EX(target, "target", ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Path path(target); SVN_JNI_ERR(path.error_occured(), ); SVN_JNI_ERR(svn_client_diff_summarize_peg2(path.c_str(), pegRevision.revision(), startRevision.revision(), endRevision.revision(), depth, ignoreAncestry, changelists.array(requestPool), DiffSummaryReceiver::summarize, &receiver, ctx, requestPool.pool()), ); } #if defined(SVN_HAVE_KWALLET) || defined(SVN_HAVE_GNOME_KEYRING) /* Set *PROVIDER according to PROVIDER_NAME and PROVIDER_TYPE, * allocating it in POOL. * * Valid PROVIDER_NAME values are: "gnome_keyring" and "kwallet" * (they correspond to the loadable libraries named, e.g., * "libsvn_auth_gnome_keyring-1.so.0", etc.) * * Valid PROVIDER_TYPE values are: "simple" and "ssl_client_cert_pw" * (they correspond to function names found in the loaded library, * such as "svn_auth_get_gnome_keyring_simple_provider", etc). * * What actually happens is we load the library and invoke the * appropriate provider function to supply *PROVIDER, like so: * * svn_auth_get___provider(PROVIDER, POOL); * * If the library load fails, return an error (with the effect on * *PROVIDER undefined). But if the symbol is simply not found in the * library, or if the PROVIDER_TYPE is unrecognized, set *PROVIDER to * NULL and return success. */ static svn_error_t * get_auth_provider(svn_auth_provider_object_t **provider, const char *provider_name, const char *provider_type, apr_pool_t *pool) { apr_dso_handle_t *dso; apr_dso_handle_sym_t symbol; const char *libname; const char *funcname; *provider = NULL; libname = apr_psprintf(pool, "libsvn_auth_%s-%d.so.0", provider_name, SVN_VER_MAJOR); funcname = apr_psprintf(pool, "svn_auth_get_%s_%s_provider", provider_name, provider_type); SVN_ERR(svn_dso_load(&dso, libname)); if (dso) { if (! apr_dso_sym(&symbol, dso, funcname)) { if (strcmp(provider_type, "simple") == 0) { svn_auth_simple_provider_func_t func; func = (svn_auth_simple_provider_func_t) symbol; func(provider, pool); } else if (strcmp(provider_type, "ssl_client_cert_pw") == 0) { svn_auth_ssl_client_cert_pw_provider_func_t func; func = (svn_auth_ssl_client_cert_pw_provider_func_t) symbol; func(provider, pool); } } } return SVN_NO_ERROR; } #endif svn_client_ctx_t *SVNClient::getContext(const char *message) { apr_pool_t *pool = JNIUtil::getRequestPool()->pool(); svn_auth_baton_t *ab; svn_client_ctx_t *ctx; SVN_JNI_ERR(svn_client_create_context(&ctx, pool), NULL); apr_array_header_t *providers = apr_array_make(pool, 10, sizeof(svn_auth_provider_object_t *)); /* The main disk-caching auth providers, for both * 'username/password' creds and 'username' creds. */ svn_auth_provider_object_t *provider; #if defined(WIN32) && !defined(__MINGW32__) svn_auth_get_windows_simple_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif #ifdef SVN_HAVE_KEYCHAIN_SERVICES svn_auth_get_keychain_simple_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_keychain_ssl_client_cert_pw_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif #ifdef SVN_HAVE_GNOME_KEYRING SVN_JNI_ERR(get_auth_provider(&provider, "gnome_keyring", "simple", pool), NULL); if (provider) { APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } SVN_JNI_ERR(get_auth_provider(&provider, "gnome_keyring", "ssl_client_cert_pw", pool), NULL); if (provider) { APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } #endif #ifdef SVN_HAVE_KWALLET SVN_JNI_ERR(get_auth_provider(&provider, "kwallet", "simple", pool), NULL); if (provider) { APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } SVN_JNI_ERR(get_auth_provider(&provider, "kwallet", "ssl_client_cert_pw", pool), NULL); if (provider) { APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } #endif svn_auth_get_simple_provider(&provider, 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; /* The server-cert, client-cert, and client-cert-password providers. */ #if defined(WIN32) && !defined(__MINGW32__) svn_auth_get_windows_ssl_server_trust_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif 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_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; if (m_prompter != NULL) { /* Two basic prompt providers: username/password, and just username.*/ provider = m_prompter->getProviderSimple(); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; provider = m_prompter->getProviderUsername(); 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->getProviderServerSSLTrust(); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; provider = m_prompter->getProviderClientSSL(); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; provider = m_prompter->getProviderClientSSLPassword(); 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, m_userName.c_str()); if (!m_passWord.empty()) svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, m_passWord.c_str()); ctx->auth_baton = ab; ctx->notify_func = Notify::notify; ctx->notify_baton = m_notify; ctx->log_msg_func3 = getCommitMessage; ctx->log_msg_baton3 = getCommitMessageBaton(message); ctx->cancel_func = checkCancel; m_cancelOperation = false; ctx->cancel_baton = this; const char *configDir = m_configDir.c_str(); if (m_configDir.length() == 0) configDir = NULL; SVN_JNI_ERR(svn_config_get_config(&(ctx->config), configDir, pool), NULL); ctx->notify_func2= Notify2::notify; ctx->notify_baton2 = m_notify2; ctx->progress_func = ProgressListener::progress; ctx->progress_baton = m_progressListener; if (m_conflictResolver) { ctx->conflict_func = ConflictResolverCallback::resolveConflict; ctx->conflict_baton = m_conflictResolver; } return ctx; } svn_error_t * SVNClient::getCommitMessage(const char **log_msg, const char **tmp_file, const apr_array_header_t *commit_items, void *baton, apr_pool_t *pool) { *log_msg = NULL; *tmp_file = NULL; log_msg_baton *lmb = (log_msg_baton *) baton; if (lmb && lmb->messageHandler) { jstring jmsg = lmb->messageHandler->getCommitMessage(commit_items); if (jmsg != NULL) { JNIStringHolder msg(jmsg); *log_msg = apr_pstrdup(pool, msg); } return SVN_NO_ERROR; } else if (lmb && lmb->message) { *log_msg = apr_pstrdup(pool, lmb->message); return SVN_NO_ERROR; } return SVN_NO_ERROR; } void *SVNClient::getCommitMessageBaton(const char *message) { if (message != NULL || m_commitMessage) { log_msg_baton *baton = (log_msg_baton *) apr_palloc(JNIUtil::getRequestPool()->pool(), sizeof(*baton)); baton->message = message; baton->messageHandler = m_commitMessage; return baton; } return NULL; } jobject SVNClient::createJavaProperty(jobject jthis, const char *path, const char *name, svn_string_t *value) { JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass(JAVA_PACKAGE"/PropertyData"); if (JNIUtil::isJavaExceptionThrown()) return NULL; static jmethodID mid = 0; if (mid == 0) { mid = env->GetMethodID(clazz, "", "(L"JAVA_PACKAGE"/SVNClient;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;[B)V"); if (JNIUtil::isJavaExceptionThrown()) return NULL; } jstring jPath = JNIUtil::makeJString(path); if (JNIUtil::isJavaExceptionThrown()) return NULL; jstring jName = JNIUtil::makeJString(name); if (JNIUtil::isJavaExceptionThrown()) return NULL; jstring jValue = JNIUtil::makeJString(value->data); if (JNIUtil::isJavaExceptionThrown()) return NULL; jbyteArray jData = JNIUtil::makeJByteArray((const signed char *)value->data, value->len); if (JNIUtil::isJavaExceptionThrown()) return NULL; jobject jprop = env->NewObject(clazz, mid, jthis, jPath, jName, jValue, jData); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(clazz); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jPath); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jName); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jValue); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jData); if (JNIUtil::isJavaExceptionThrown()) return NULL; return jprop; } jbyteArray SVNClient::fileContent(const char *path, Revision &revision, Revision &pegRevision) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", NULL); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); size_t size = 0; svn_stream_t *read_stream = createReadStream(requestPool.pool(), intPath.c_str(), revision, pegRevision, size); if (read_stream == NULL) return NULL; JNIEnv *env = JNIUtil::getEnv(); // size will be set to the number of bytes available. jbyteArray jcontent = env->NewByteArray(size); if (JNIUtil::isJavaExceptionThrown()) return NULL; jbyte *jbytes = env->GetByteArrayElements(jcontent, NULL); if (JNIUtil::isJavaExceptionThrown()) return NULL; svn_error_t *err = svn_stream_read(read_stream, (char *) jbytes, &size); env->ReleaseByteArrayElements(jcontent, jbytes, 0); SVN_JNI_ERR(err, NULL); if (JNIUtil::isJavaExceptionThrown()) return NULL; return jcontent; } void SVNClient::streamFileContent(const char *path, Revision &revision, Revision &pegRevision, jobject outputStream, size_t bufSize) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); JNIEnv *env = JNIUtil::getEnv(); jclass outputStreamClass = env->FindClass("java/io/OutputStream"); if (outputStreamClass == NULL) return; jmethodID writeMethod = env->GetMethodID(outputStreamClass, "write", "([BII)V"); if (writeMethod == NULL) return; // Create the buffer. jbyteArray buffer = env->NewByteArray(bufSize); if (JNIUtil::isJavaExceptionThrown()) return; jbyte *bufData = env->GetByteArrayElements(buffer, NULL); if (JNIUtil::isJavaExceptionThrown()) return; size_t contentSize = 0; svn_stream_t *read_stream = createReadStream(requestPool.pool(), path, revision, pegRevision, contentSize); if (read_stream == NULL) return; while (contentSize > 0) { size_t readSize = bufSize > contentSize ? contentSize : bufSize; svn_error_t *err; err = svn_stream_read(read_stream, (char *)bufData, &readSize); if (err != NULL) { env->ReleaseByteArrayElements(buffer, bufData, 0); svn_stream_close(read_stream); SVN_JNI_ERR(err, ); } env->ReleaseByteArrayElements(buffer, bufData, JNI_COMMIT); env->CallVoidMethod(outputStream, writeMethod, buffer, 0, readSize); if (JNIUtil::isJavaExceptionThrown()) { env->ReleaseByteArrayElements(buffer, bufData, 0); svn_stream_close(read_stream); return; } contentSize -= readSize; } env->ReleaseByteArrayElements(buffer, bufData, 0); return; } svn_stream_t *SVNClient::createReadStream(apr_pool_t *pool, const char *path, Revision &revision, Revision &pegRevision, size_t &size) { svn_stream_t *read_stream = NULL; if (revision.revision()->kind == svn_opt_revision_working) { // We want the working copy. Going back to the server returns // base instead (which is not what we want). apr_file_t *file = NULL; apr_finfo_t finfo; apr_status_t apr_err = apr_stat(&finfo, path, APR_FINFO_MIN, pool); if (apr_err) { JNIUtil::handleAPRError(apr_err, _("open file")); return NULL; } apr_err = apr_file_open(&file, path, APR_READ, 0, pool); if (apr_err) { JNIUtil::handleAPRError(apr_err, _("open file")); return NULL; } read_stream = svn_stream_from_aprfile2(file, TRUE, pool); size = finfo.size; } else { svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; svn_stringbuf_t *buf = svn_stringbuf_create("", pool); read_stream = svn_stream_from_stringbuf(buf, pool); SVN_JNI_ERR(svn_client_cat2(read_stream, path, pegRevision.revision(), revision.revision(), ctx, pool), NULL); size = buf->len; } return read_stream; } jobject SVNClient::revProperty(jobject jthis, const char *path, const char *name, Revision &rev) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", NULL); SVN_JNI_NULL_PTR_EX(name, "name", NULL); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; const char *URL; svn_string_t *propval; svn_revnum_t set_rev; SVN_JNI_ERR(svn_client_url_from_path(&URL, intPath.c_str(), requestPool.pool()), NULL); if (URL == NULL) { SVN_JNI_ERR(svn_error_create(SVN_ERR_UNVERSIONED_RESOURCE, NULL, _("Either a URL or versioned item is required.")), NULL); } SVN_JNI_ERR(svn_client_revprop_get(name, &propval, URL, rev.revision(), &set_rev, ctx, requestPool.pool()), NULL); if (propval == NULL) return NULL; return createJavaProperty(jthis, path, name, propval); } void SVNClient::relocate(const char *from, const char *to, const char *path, bool recurse) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(from, "from", ); SVN_JNI_NULL_PTR_EX(to, "to", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); Path intFrom(from); SVN_JNI_ERR(intFrom.error_occured(), ); Path intTo(to); SVN_JNI_ERR(intTo.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_relocate(intPath.c_str(), intFrom.c_str(), intTo.c_str(), recurse, ctx, requestPool.pool()), ); } void SVNClient::blame(const char *path, Revision &pegRevision, Revision &revisionStart, Revision &revisionEnd, bool ignoreMimeType, bool includeMergedRevisions, BlameCallback *callback) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); apr_pool_t *pool = requestPool.pool(); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; SVN_JNI_ERR(svn_client_blame4(intPath.c_str(), pegRevision.revision(), revisionStart.revision(), revisionEnd.revision(), svn_diff_file_options_create(pool), ignoreMimeType, includeMergedRevisions, BlameCallback::callback, callback, ctx, pool), ); } void SVNClient::setConfigDirectory(const char *configDir) { // A change to the config directory may necessitate creation of // the config templates. Pool requestPool; SVN_JNI_ERR(svn_config_ensure(configDir, requestPool.pool()), ); m_configDir = (configDir == NULL ? "" : configDir); } const char *SVNClient::getConfigDirectory() { return m_configDir.c_str(); } void SVNClient::commitMessageHandler(CommitMessage *commitMessage) { delete m_commitMessage; m_commitMessage = commitMessage; } void SVNClient::cancelOperation() { m_cancelOperation = true; } svn_error_t *SVNClient::checkCancel(void *cancelBaton) { SVNClient *that = (SVNClient*)cancelBaton; if (that->m_cancelOperation) return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Operation canceled")); else return SVN_NO_ERROR; } void SVNClient::addToChangelist(Targets &srcPaths, const char *changelist, svn_depth_t depth, StringArray &changelists) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); const apr_array_header_t *srcs = srcPaths.array(requestPool); SVN_JNI_ERR(srcPaths.error_occured(), ); SVN_JNI_ERR(svn_client_add_to_changelist(srcs, changelist, depth, changelists.array(requestPool), ctx, requestPool.pool()), ); } void SVNClient::removeFromChangelists(Targets &srcPaths, svn_depth_t depth, StringArray &changelists) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); const apr_array_header_t *srcs = srcPaths.array(requestPool); SVN_JNI_ERR(srcPaths.error_occured(), ); SVN_JNI_ERR(svn_client_remove_from_changelists(srcs, depth, changelists.array(requestPool), ctx, requestPool.pool()), ); } void SVNClient::getChangelists(const char *rootPath, StringArray &changelists, svn_depth_t depth, ChangelistCallback *callback) { Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); SVN_JNI_ERR(svn_client_get_changelists(rootPath, changelists.array(requestPool), depth, ChangelistCallback::callback, callback, ctx, requestPool.pool()), ); } jobject SVNClient::createJavaLock(const svn_lock_t *lock) { if (lock == NULL) return NULL; JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass(JAVA_PACKAGE"/Lock"); if (JNIUtil::isJavaExceptionThrown()) return NULL; static jmethodID mid = 0; if (mid == 0) { mid = env->GetMethodID(clazz, "", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;" "Ljava/lang/String;JJ)V"); if (JNIUtil::isJavaExceptionThrown()) return NULL; } jstring jOwner = JNIUtil::makeJString(lock->owner); if (JNIUtil::isJavaExceptionThrown()) return NULL; jstring jPath = JNIUtil::makeJString(lock->path); if (JNIUtil::isJavaExceptionThrown()) return NULL; jstring jToken = JNIUtil::makeJString(lock->token); if (JNIUtil::isJavaExceptionThrown()) return NULL; jstring jComment = JNIUtil::makeJString(lock->comment); if (JNIUtil::isJavaExceptionThrown()) return NULL; jlong jCreationDate = lock->creation_date; jlong jExpirationDate = lock->expiration_date; jobject jlock = env->NewObject(clazz, mid, jOwner, jPath, jToken, jComment, jCreationDate, jExpirationDate); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(clazz); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jOwner); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jPath); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jToken); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jComment); if (JNIUtil::isJavaExceptionThrown()) return NULL; return jlock; } void SVNClient::lock(Targets &targets, const char *comment, bool force) { Pool requestPool; const apr_array_header_t *targetsApr = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); SVN_JNI_ERR(svn_client_lock(targetsApr, comment, force, ctx, requestPool.pool()), ); } void SVNClient::unlock(Targets &targets, bool force) { Pool requestPool; const apr_array_header_t *targetsApr = targets.array(requestPool); SVN_JNI_ERR(targets.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); SVN_JNI_ERR(svn_client_unlock((apr_array_header_t*)targetsApr, force, ctx, requestPool.pool()), ); } void SVNClient::setRevProperty(jobject jthis, const char *path, const char *name, Revision &rev, const char *value, bool force) { Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", ); SVN_JNI_NULL_PTR_EX(name, "name", ); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), ); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; const char *URL; SVN_JNI_ERR(svn_client_url_from_path(&URL, intPath.c_str(), requestPool.pool()), ); if (URL == NULL) { SVN_JNI_ERR(svn_error_create(SVN_ERR_UNVERSIONED_RESOURCE, NULL, _("Either a URL or versioned item is required.")), ); } svn_string_t *val = svn_string_create(value, requestPool.pool()); svn_revnum_t set_revision; SVN_JNI_ERR(svn_client_revprop_set(name, val, URL, rev.revision(), &set_revision, force, ctx, requestPool.pool()), ); } struct version_status_baton { svn_revnum_t min_rev; /* lowest revision found. */ svn_revnum_t max_rev; /* highest revision found. */ svn_boolean_t switched; /* is anything switched? */ svn_boolean_t modified; /* is anything modified? */ svn_boolean_t committed; /* examine last committed revisions */ svn_boolean_t done; /* note completion of our task. */ const char *wc_path; /* path whose URL we're looking for. */ const char *wc_url; /* URL for the path whose URL we're looking for. */ apr_pool_t *pool; /* pool in which to store alloc-needy things. */ }; /* This implements `svn_cancel_func_t'. */ static svn_error_t * cancel(void *baton) { struct version_status_baton *sb = (version_status_baton *)baton; if (sb->done) return svn_error_create(SVN_ERR_CANCELLED, NULL, "Finished"); else return SVN_NO_ERROR; } /* An svn_wc_status_func2_t callback function for anaylyzing status * structures. */ static void analyze_status(void *baton, const char *path, svn_wc_status2_t *status) { struct version_status_baton *sb = (version_status_baton *)baton; if (sb->done) return; if (! status->entry) return; /* Added files have a revision of no interest */ if (status->text_status != svn_wc_status_added) { svn_revnum_t item_rev = (sb->committed ? status->entry->cmt_rev : status->entry->revision); if (sb->min_rev == SVN_INVALID_REVNUM || item_rev < sb->min_rev) sb->min_rev = item_rev; if (sb->max_rev == SVN_INVALID_REVNUM || item_rev > sb->max_rev) sb->max_rev = item_rev; } sb->switched |= status->switched; sb->modified |= (status->text_status != svn_wc_status_normal); sb->modified |= (status->prop_status != svn_wc_status_normal && status->prop_status != svn_wc_status_none); if (sb->wc_path && (! sb->wc_url) && (strcmp(path, sb->wc_path) == 0) && (status->entry)) sb->wc_url = apr_pstrdup(sb->pool, status->entry->url); } /* This implements `svn_wc_notify_func_t'. */ static void notify(void *baton, const char *path, svn_wc_notify_action_t action, svn_node_kind_t kind, const char *mime_type, svn_wc_notify_state_t content_state, svn_wc_notify_state_t prop_state, svn_revnum_t revision) { struct version_status_baton *sb = (version_status_baton *)baton; if ((action == svn_wc_notify_status_external) || (action == svn_wc_notify_status_completed)) sb->done = TRUE; } jstring SVNClient::getVersionInfo(const char *path, const char *trailUrl, bool lastChanged) { struct version_status_baton sb; Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", NULL); sb.switched = FALSE; sb.modified = FALSE; sb.committed = FALSE; sb.min_rev = SVN_INVALID_REVNUM; sb.max_rev = SVN_INVALID_REVNUM; sb.wc_path = NULL; sb.wc_url = NULL; sb.done = FALSE; sb.pool = requestPool.pool(); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); int wc_format; svn_client_ctx_t ctx = { 0 }; SVN_JNI_ERR(svn_wc_check_wc(intPath.c_str(), &wc_format, requestPool.pool()), NULL); if (! wc_format) { svn_node_kind_t kind; SVN_JNI_ERR(svn_io_check_path(intPath.c_str(), &kind, requestPool.pool()), NULL); if (kind == svn_node_dir) { return JNIUtil::makeJString("exported"); } else { char *message = JNIUtil::getFormatBuffer(); apr_snprintf(message, JNIUtil::formatBufferSize, _("'%s' not versioned, and not exported\n"), path); return JNIUtil::makeJString(message); } } sb.wc_path = path; svn_opt_revision_t rev; rev.kind = svn_opt_revision_unspecified; ctx.config = apr_hash_make(requestPool.pool()); /* Setup the notification and cancellation callbacks, and their * shared baton (which is also shared with the status function). */ ctx.notify_func = notify; ctx.notify_baton = &sb; ctx.cancel_func = cancel; ctx.cancel_baton = &sb; svn_error_t *err; err = svn_client_status3(NULL, intPath.c_str(), &rev, analyze_status, &sb, svn_depth_infinity, TRUE, FALSE, FALSE, FALSE, NULL, &ctx, requestPool.pool()); if (err && (err->apr_err == SVN_ERR_CANCELLED)) svn_error_clear(err); else SVN_JNI_ERR(err, NULL); if ((! sb.switched ) && (trailUrl)) { /* If the trailing part of the URL of the working copy directory * does not match the given trailing URL then the whole working * copy is switched. */ if (! sb.wc_url) { sb.switched = TRUE; } else { apr_size_t len1 = strlen(trailUrl); apr_size_t len2 = strlen(sb.wc_url); if ((len1 > len2) || strcmp(sb.wc_url + len2 - len1, trailUrl)) sb.switched = TRUE; } } std::ostringstream value; value << sb.min_rev; if (sb.min_rev != sb.max_rev) { value << ":"; value << sb.max_rev; } if (sb.modified) value << "M"; if (sb.switched) value << "S"; return JNIUtil::makeJString(value.str().c_str()); } jobjectArray SVNClient::revProperties(jobject jthis, const char *path, Revision &revision) { apr_hash_t *props; Pool requestPool; SVN_JNI_NULL_PTR_EX(path, "path", NULL); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); const char *URL; svn_revnum_t set_rev; SVN_JNI_ERR(svn_client_url_from_path(&URL, intPath.c_str(), requestPool.pool()), NULL); svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return NULL; SVN_JNI_ERR(svn_client_revprop_list(&props, URL, revision.revision(), &set_rev, ctx, requestPool.pool()), NULL); apr_hash_index_t *hi; int count = apr_hash_count(props); JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass(JAVA_PACKAGE"/PropertyData"); if (JNIUtil::isJavaExceptionThrown()) return NULL; jobjectArray jprops = env->NewObjectArray(count, clazz, NULL); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(clazz); if (JNIUtil::isJavaExceptionThrown()) return NULL; int i = 0; for (hi = apr_hash_first(requestPool.pool(), props); hi; hi = apr_hash_next(hi), ++i) { const char *key; svn_string_t *val; apr_hash_this(hi, (const void **)&key, NULL, (void**)&val); jobject object = createJavaProperty(jthis, path, key, val); env->SetObjectArrayElement(jprops, i, object); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(object); if (JNIUtil::isJavaExceptionThrown()) return NULL; } return jprops; } struct info_baton { std::vector infoVect; int info_ver; apr_pool_t *pool; }; /** * Get information about a file or directory. */ jobject SVNClient::info(const char *path) { Pool requestPool; svn_wc_adm_access_t *adm_access; const svn_wc_entry_t *entry; SVN_JNI_NULL_PTR_EX(path, "path", NULL); Path intPath(path); SVN_JNI_ERR(intPath.error_occured(), NULL); SVN_JNI_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, intPath.c_str(), FALSE, 0, NULL, NULL, requestPool.pool()), NULL); SVN_JNI_ERR(svn_wc_entry(&entry, intPath.c_str(), adm_access, FALSE, requestPool.pool()), NULL); return createJavaInfo(entry); } jobject SVNClient::createJavaInfo(const svn_wc_entry_t *entry) { if (entry == NULL) return NULL; JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass(JAVA_PACKAGE"/Info"); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } static jmethodID mid = 0; if (mid == 0) { mid = env->GetMethodID(clazz, "", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;" "IILjava/lang/String;JJLjava/util/Date;" "Ljava/util/Date;Ljava/util/Date;" "ZZZZJLjava/lang/String;)V"); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } } jstring jName = JNIUtil::makeJString(entry->name); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jstring jUrl = JNIUtil::makeJString(entry->url); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jstring jUuid = JNIUtil::makeJString(entry->uuid); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jstring jRepository = JNIUtil::makeJString(entry->repos); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jint jSchedule = EnumMapper::mapScheduleKind(entry->schedule); jint jNodeKind = EnumMapper::mapNodeKind(entry->kind); jstring jAuthor = JNIUtil::makeJString(entry->cmt_author); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jlong jRevision = entry->revision; jlong jLastChangedRevision = entry->cmt_rev; jobject jLastChangedDate = JNIUtil::createDate(entry->cmt_date); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jobject jLastDateTextUpdate = JNIUtil::createDate(entry->text_time); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jobject jLastDatePropsUpdate = JNIUtil::createDate(entry->prop_time); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jboolean jCopied = entry->copied ? JNI_TRUE : JNI_FALSE; jboolean jDeleted = entry->deleted ? JNI_TRUE : JNI_FALSE; jboolean jAbsent = entry->absent ? JNI_TRUE : JNI_FALSE; jboolean jIncomplete = entry->incomplete ? JNI_TRUE : JNI_FALSE; jlong jCopyRev = entry->copyfrom_rev; jstring jCopyUrl = JNIUtil::makeJString(entry->copyfrom_url); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } jobject jinfo = env->NewObject(clazz, mid, jName, jUrl, jUuid, jRepository, jSchedule, jNodeKind, jAuthor, jRevision, jLastChangedRevision, jLastChangedDate, jLastDateTextUpdate, jLastDatePropsUpdate, jCopied, jDeleted, jAbsent, jIncomplete, jCopyRev, jCopyUrl); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(clazz); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jName); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jUrl); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jUuid); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jRepository); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jAuthor); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jLastChangedDate); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jLastDateTextUpdate); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jLastDatePropsUpdate); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } env->DeleteLocalRef(jCopyUrl); if (JNIUtil::isJavaExceptionThrown()) { return NULL; } return jinfo; } void SVNClient::info2(const char *path, Revision &revision, Revision &pegRevision, svn_depth_t depth, StringArray &changelists, InfoCallback *callback) { SVN_JNI_NULL_PTR_EX(path, "path", ); Pool requestPool; svn_client_ctx_t *ctx = getContext(NULL); if (ctx == NULL) return; Path checkedPath(path); SVN_JNI_ERR(checkedPath.error_occured(), ); SVN_JNI_ERR(svn_client_info2(checkedPath.c_str(), pegRevision.revision(), revision.revision(), InfoCallback::callback, callback, depth, changelists.array(requestPool), ctx, requestPool.pool()), ); } jobjectArray SVNClient::makeJRevisionRangeArray(apr_array_header_t *ranges) { JNIEnv *env = JNIUtil::getEnv(); jclass clazz = env->FindClass(JAVA_PACKAGE "/RevisionRange"); if (JNIUtil::isJavaExceptionThrown()) return NULL; jobjectArray jranges = env->NewObjectArray(ranges->nelts, clazz, NULL); for (int i = 0; i < ranges->nelts; ++i) { // Convert svn_merge_range_t *'s to Java RevisionRange objects. svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i, svn_merge_range_t *); jobject jrange = RevisionRange::makeJRevisionRange(range); if (jrange == NULL) return NULL; env->SetObjectArrayElement(jranges, i, jrange); if (JNIUtil::isJavaExceptionThrown()) return NULL; env->DeleteLocalRef(jrange); if (JNIUtil::isJavaExceptionThrown()) return NULL; } return jranges; }