GME  13
SVNClient.cpp
Go to the documentation of this file.
00001 #include "StdAfx.h"
00002 
00003 #ifndef _WIN64
00004 
00005 #include "SVNClient.h"
00006 #include "svn_dso.h"
00007 #include "svn_utf.h"
00008 #include "svn_nls.h"
00009 #include "svn_fs.h"
00010 #include "svn_hash.h"
00011 #include "svn_props.h"
00012 
00013 #include "SVNDialogCommit.h"
00014 #include "SVNDialogPlaintext.h"
00015 #include "SVNDialogSSLServerTrust.h"
00016 #include "SVNDialogLogin.h"
00017 
00018 #pragma comment(lib, "advapi32.lib")
00019 #pragma comment(lib, "shfolder.lib")
00020 #pragma comment(lib, "ws2_32.lib")
00021 #pragma comment(lib, "secur32.lib")
00022 #pragma comment(lib, "crypt32.lib")
00023 #pragma comment(lib, "version.lib")
00024 #pragma comment(lib, "psapi.lib")
00025 
00026 
00027 // Subversion libraries and dependencies
00028 #pragma comment(lib, "libapr-1.lib")
00029 #pragma comment(lib, "libaprutil-1.lib")
00030 #pragma comment(lib, "libapriconv-1.lib")
00031 #pragma comment(lib, "xml.lib")
00032 
00033 #pragma comment(lib, "libeay32.lib")
00034 #pragma comment(lib, "ssleay32.lib")
00035 
00036 #pragma comment(lib, "svn_client-1.lib")
00037 #pragma comment(lib, "svn_delta-1.lib")
00038 #pragma comment(lib, "svn_diff-1.lib")
00039 #pragma comment(lib, "svn_fs-1.lib")
00040 #pragma comment(lib, "libsvn_fs_fs-1.lib")
00041 #pragma comment(lib, "libsvn_fs_util-1.lib")
00042 #pragma comment(lib, "svn_ra-1.lib")
00043 #pragma comment(lib, "libsvn_ra_local-1.lib")
00044 #pragma comment(lib, "libsvn_ra_svn-1.lib")
00045 #pragma comment(lib, "svn_repos-1.lib")
00046 #pragma comment(lib, "svn_subr-1.lib")
00047 #pragma comment(lib, "svn_wc-1.lib")
00048 #pragma comment(lib, "libsvn_ra_serf-1.lib")
00049 #pragma comment(lib, "serf-1.lib")
00050 
00051 // These are contained by serf-1.lib
00052 // NOTE: zlibstatD.lib includes /DEFAULTLIB:"MSVCRT" disrectives 
00053 // (instead of "MSVCRTD"), resulting in linker warnings.
00054 
00055 //#ifdef _DEBUG
00056 //      #pragma comment(lib, "zlibstatD.lib")
00057 //#else
00058 //      #pragma comment(lib, "zlibstat.lib")
00059 //#endif
00060 
00061 #define SVNTHROW(FUNC) \
00062         do { \
00063         svn_error_t* _err = (FUNC); \
00064         if( _err ) { \
00065         throw CSVNError(_err); \
00066         } \
00067         } while(false)
00068 
00069 
00071 // SVN Error
00073 CSVNError::CSVNError(svn_error_t* e) : svnError(e)
00074 {
00075 }
00076 
00077 CSVNError::~CSVNError()
00078 {
00079         svn_error_clear(svnError);
00080 }
00081 
00082 CString CSVNError::msg() const
00083 {
00084         char buf[SVN_ERROR_MSG_MAX];
00085 
00086         const char *ret = svn_err_best_message(svnError, buf, sizeof(buf));
00087         return CString(ret);
00088 }
00089 
00091 // SVN Pool
00093 CSVNPool::CSVNPool(apr_pool_t* parentPool)
00094 {
00095         pool = svn_pool_create(parentPool);
00096 }
00097 
00098 CSVNPool::~CSVNPool()
00099 {
00100         if (pool) {
00101                 svn_pool_destroy(pool);
00102                 pool = NULL;
00103         }
00104 }
00105 
00106 CSVNPool::operator apr_pool_ptr() const
00107 {
00108         return pool;
00109 }
00110 
00111 
00113 // SVN Client
00115 CSVNClient::CSVNClient() 
00116         : isInitialized(false), ctx(NULL), pool(NULL)
00117 {
00118 }
00119 
00120 
00121 CSVNClient::~CSVNClient(void)
00122 {
00123         
00124         POSITION p = svnFiles.GetHeadPosition();
00125         while (p) {
00126                 delete svnFiles.GetNext(p);
00127         }
00128         // NOTE: there is a memory allocation problem in serf 
00129         // it causes problems when SSL certificates are rejected during conn. setup
00130         // svn_pool_destroy mitigates this problem (memory leaks "only" instead of accessing freed memory)
00131         svn_pool_destroy(pool); 
00132         apr_terminate();
00133 }
00134 
00135 void CSVNClient::initialize(void)
00136 {
00137         apr_status_t status;
00138 
00139         // TODO: subversion/libsvn_subr/cmdline.c contains a lot of esoteric stuff
00140         // such as "setvbuf", input/output encodings, exception handlers, locale settings, etc.
00141 
00142         /* Initialize the APR subsystem, and register an atexit() function
00143         to Uninitialize that subsystem at program exit. */
00144         status = apr_initialize();
00145         if (status)
00146         { 
00147                 // this is not a real svn error, but coming from apr
00148                 throw CSVNError(svn_error_create(status, NULL, NULL));
00149         }
00150 
00151         /* DSO pool must be created before any other pools used by the
00152         application so that pool cleanup doesn't unload DSOs too
00153         early. See docstring of svn_dso_initialize2(). */
00154         SVNTHROW(svn_dso_initialize2());
00155 
00156         /* Create a pool for use by the UTF-8 routines.  It will be cleaned
00157         up by APR at exit time. */
00158         pool = svn_pool_create(NULL);
00159         svn_utf_initialize2(FALSE, pool);
00160         SVNTHROW(svn_nls_init());
00161 
00162         /* Create top-level memory pool. */
00163         pool = svn_pool_create(NULL);
00164 
00165         /* Initialize the FS library. */
00166         //SVNTHROW(svn_fs_initialize(pool));
00167 
00168         /* Initialize the RA library. */
00169         SVNTHROW(svn_ra_initialize(pool));
00170 
00171         /* Make sure the ~/.subversion run-time config files exist */
00172         SVNTHROW(svn_config_ensure(NULL, pool));
00173 
00174 
00175         /* All clients need to fill out a client_ctx object. */
00176         {
00177                 apr_hash_t *cfg_hash;
00178                 svn_config_t *cfg_config;
00179 
00180                 /* Load the run-time config file into a hash */
00181                 SVNTHROW(svn_config_get_config (&cfg_hash, NULL, pool));
00182 
00183                 cfg_config = (svn_config_t *)svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG);
00184 
00185                 /* Initialize and allocate the client_ctx object. */
00186                 SVNTHROW(svn_client_create_context2(&ctx, cfg_hash, pool));
00187 
00188 
00189                 /* Set the working copy administrative directory name. */
00190                 if (getenv ("SVN_ASP_DOT_NET_HACK"))
00191                 {
00192                         SVNTHROW(svn_wc_set_adm_dir ("_svn", pool));
00193                 }
00194 
00195                 /* Callbacks */
00196 
00197                 /* A func (& context) which receives event signals during checkouts, updates, commits, etc.  */
00198                 ctx->notify_func2 = cbNotify;
00199                 ctx->notify_baton2 = this;
00200 
00201                 /* A func (& context) which can receive log messages */
00202                 ctx->log_msg_func3 = cbLog;
00203                 ctx->log_msg_baton3 = this;
00204 
00205                 /* A func (& context) which checks whether the user cancelled */
00206                 ctx->cancel_func = cbCancel;
00207                 ctx->cancel_baton = this;
00208 
00209                 /* A func (& context) for network progress */
00210                 ctx->progress_func = cbProgress;
00211                 ctx->progress_baton = this;
00212 
00213                 /* A func (& context) for conflict resolution */
00214                 ctx->conflict_func2 = cbConflict;
00215                 ctx->conflict_baton2 = this;
00216 
00217                 /* Make the client_ctx capable of authenticating users */
00218                 {
00219                         svn_auth_provider_object_t *provider;
00220                         apr_array_header_t *providers;
00221 
00222                         /* For caching encrypted username/password and client cert passwords - no prompting */
00223                         SVNTHROW(svn_auth_get_platform_specific_client_providers(&providers, cfg_config, pool));
00224 
00225                         /* For caching unencrypted username/password (also from config file) - prompting only to confirm storing creds in cleartext */
00226                         svn_auth_get_simple_provider2(&provider, cbAuthPlaintextPrompt, this, pool);
00227                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00228 
00229                         /* For guessing and optionally saving username - no prompting */
00230                         svn_auth_get_username_provider(&provider, pool);
00231                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00232 
00233                         /* For validating server SSL certs from windows certificate store - no prompting (windows itself might show a dialog ?) */
00234                         SVNTHROW(svn_auth_get_platform_specific_provider(&provider, "windows", "ssl_server_trust", pool));
00235                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00236 
00237                         /* For validating and/or storing SSL server certs (in custom files) - no prompting */
00238                         svn_auth_get_ssl_server_trust_file_provider(&provider, pool);
00239                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00240 
00241                         /* For retrieving custom client certificate (file path) from server config - no prompting */
00242                         svn_auth_get_ssl_client_cert_file_provider(&provider, pool);
00243                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00244 
00245                         svn_auth_get_ssl_client_cert_pw_file_provider2(&provider, cbAuthPlaintextPassphrasePrompt, this, pool);
00246                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00247 
00248                         svn_auth_get_simple_prompt_provider(&provider, cbAuthSimplePrompt, this, 2 /* retry limit */, pool);
00249                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00250 
00251                         svn_auth_get_username_prompt_provider(&provider, cbAuthUsernamePrompt, this, 2 /* retry limit */, pool);
00252                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00253 
00254                         svn_auth_get_ssl_server_trust_prompt_provider(&provider, cbAuthSSLServerTrustPrompt, this, pool);
00255                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00256 
00257                         svn_auth_get_ssl_client_cert_pw_prompt_provider(&provider, cbAuthSSLClientCertPWPrompt, this, 2 /* retry limit */, pool);
00258                         APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00259 
00260                         /* If configuration allows, add a provider for client-cert path
00261                         prompting, too. */
00262                         svn_boolean_t ssl_client_cert_file_prompt;
00263                         SVNTHROW(svn_config_get_bool(cfg_config, &ssl_client_cert_file_prompt,
00264                                 SVN_CONFIG_SECTION_AUTH,
00265                                 SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
00266                                 FALSE));
00267                         if (ssl_client_cert_file_prompt)
00268                         {
00269                                 svn_auth_get_ssl_client_cert_prompt_provider(&provider, cbAuthSSLClientCertPrompt, this, 2 /* retry limit */, pool);
00270                                 APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
00271                         }
00272 
00273                         /* Register the auth-providers into the context's auth_baton. */
00274                         svn_auth_open (&ctx->auth_baton, providers, pool);
00275                 }
00276         }
00277 
00278         isInitialized = true;
00279 }
00280 
00281 CSVNFile* CSVNClient::embraceFile(const CString & filePath)
00282 {
00283         CSVNFile* svnFile = new CSVNFile(this, filePath);
00284         if (svnFile) {
00285                 svnFiles.AddTail(svnFile);
00286         }
00287         return svnFile;
00288 }
00289 
00290 void CSVNClient::forgetFile(CSVNFile* svnFile)
00291 {
00292         POSITION p = svnFiles.Find(svnFile);
00293         while (p) {
00294                 delete svnFiles.GetAt(p);
00295                 svnFiles.RemoveAt(p);
00296                 p = svnFiles.Find(svnFile);
00297         }
00298 }
00299 
00301 // SVN Client Context Callbacks
00303 void CSVNClient::cbNotify(
00304         void *baton, 
00305         const svn_wc_notify_t *notify, 
00306         apr_pool_t *pool)
00307 {
00308         CSVNClient *self = (CSVNClient*)baton;
00309         ASSERT(self);
00310         self->lastNotifyAction = notify->action;
00311 }
00312 
00313 svn_error_t* CSVNClient::cbLog(
00314         const char **log_msg, 
00315         const char **tmp_file, 
00316         const apr_array_header_t *commit_items, 
00317         void *baton, 
00318         apr_pool_t *pool)
00319 {
00320         CSVNClient *self = (CSVNClient*)baton;
00321         CSVNDialogCommit dlg;
00322         *log_msg = NULL;
00323 
00324         ASSERT(self);
00325 
00326         if (apr_is_empty_array(commit_items)) {
00327                 return SVN_NO_ERROR;
00328         }
00329         
00330         svn_client_commit_item3_t* item = APR_ARRAY_IDX(commit_items, 0, svn_client_commit_item3_t*);
00331         dlg.filename = item->path;
00332         dlg.repository = item->url;
00333         dlg.revision = item->revision;
00334         if (dlg.DoModal() == IDOK) {
00335                 svn_string_t* logMsg = svn_string_create(CStringA(dlg.logMessage), pool);
00336                 *log_msg = logMsg->data;
00337                 self->canceledOperation = false;
00338         }
00339         else {
00340                 self->canceledOperation = true;
00341         }
00342         
00343         return SVN_NO_ERROR;
00344 }
00345 
00346 svn_error_t* CSVNClient::cbCancel(void *cancel_baton)
00347 {
00348         // not needed now (cancel button on user interfaces ?)
00349         return SVN_NO_ERROR;
00350 }
00351 
00352 void CSVNClient::cbProgress(
00353         apr_off_t progress, 
00354         apr_off_t total, 
00355         void *baton, 
00356         apr_pool_t *pool)
00357 {
00358         //TODO: implement this
00359 }
00360 
00361 svn_error_t* CSVNClient::cbConflict(
00362         svn_wc_conflict_result_t **result, 
00363         const svn_wc_conflict_description2_t *description, 
00364         void *baton, apr_pool_t *result_pool, 
00365         apr_pool_t *scratch_pool)
00366 {
00367         // too advanced to handle in this client
00368         return SVN_NO_ERROR;
00369 }
00370 
00372 // SVN Client Auth Callbacks
00374 
00375 //
00376 // Auth callback function for asking whether storing a password to disk in plaintext is allowed.
00377 // 
00378 svn_error_t* CSVNClient::cbAuthPlaintextPrompt(
00379         svn_boolean_t *may_save_plaintext, 
00380         const char *realmstring, 
00381         void *baton, 
00382         apr_pool_t *pool)
00383 {
00384         CSVNDialogPlaintext dlg;
00385         dlg.realm = realmstring;
00386         *may_save_plaintext = (dlg.DoModal() == IDOK) ? TRUE : FALSE;
00387         return SVN_NO_ERROR;
00388 }
00389 
00390 svn_error_t* CSVNClient::cbAuthPlaintextPassphrasePrompt(
00391         svn_boolean_t *may_save_plaintext, 
00392         const char *realmstring, 
00393         void *baton, 
00394         apr_pool_t *pool)
00395 {
00396         CSVNDialogPlaintext dlg;
00397         dlg.realm = realmstring;
00398         *may_save_plaintext = (dlg.DoModal() == IDOK) ? TRUE : FALSE;
00399         return SVN_NO_ERROR;
00400 }
00401 
00402 svn_error_t* CSVNClient::cbAuthSimplePrompt(
00403         svn_auth_cred_simple_t **cred, 
00404         void *baton, 
00405         const char *realm, 
00406         const char *username, 
00407         svn_boolean_t may_save, 
00408         apr_pool_t *pool)
00409 {
00410         CSVNDialogLogin dlg;
00411         dlg.realm = realm;
00412         dlg.passwordEnabled = TRUE;
00413         dlg.permanentEnabled = may_save;
00414 
00415         if (dlg.DoModal() == IDOK) {
00416                 svn_auth_cred_simple_t *ret = (svn_auth_cred_simple_t *)apr_pcalloc(pool, sizeof(*ret));
00417                 svn_string_t* username_s = svn_string_create(CStringA(dlg.username), pool);
00418                 ret->username = username_s ? username_s->data : NULL;
00419                 svn_string_t* password_s = svn_string_create(CStringA(dlg.password), pool);
00420                 ret->password = password_s ? password_s->data : NULL;
00421                 ret->may_save = dlg.permanent;
00422                 *cred = ret;
00423         }
00424         else {
00425                 *cred = NULL;
00426         }
00427         return SVN_NO_ERROR;
00428 }
00429 
00430 svn_error_t* CSVNClient::cbAuthUsernamePrompt(
00431         svn_auth_cred_username_t **cred, 
00432         void *baton, 
00433         const char *realm, 
00434         svn_boolean_t may_save, 
00435         apr_pool_t *pool)
00436 {
00437         CSVNDialogLogin dlg;
00438         dlg.realm = realm;
00439         dlg.passwordEnabled = FALSE;
00440         dlg.permanentEnabled = may_save;
00441 
00442         if (dlg.DoModal() == IDOK) {
00443                 svn_auth_cred_username_t *ret = (svn_auth_cred_username_t *)apr_pcalloc(pool, sizeof(*ret));
00444                 svn_string_t* username_s = svn_string_create(CStringA(dlg.username), pool);
00445                 ret->username = username_s ? username_s->data : NULL;
00446                 ret->may_save = dlg.permanent;
00447                 *cred = ret;
00448         }
00449         else {
00450                 *cred = NULL;
00451         }
00452         return SVN_NO_ERROR;
00453 }
00454 
00455 svn_error_t* CSVNClient::cbAuthSSLServerTrustPrompt(
00456         svn_auth_cred_ssl_server_trust_t **cred, 
00457         void *baton, 
00458         const char *realm, 
00459         apr_uint32_t failures, 
00460         const svn_auth_ssl_server_cert_info_t *cert_info, 
00461         svn_boolean_t may_save, 
00462         apr_pool_t *pool)
00463 {
00464         CSVNDialogSSLServerTrust dlg;
00465         dlg.host = cert_info->hostname;
00466         dlg.fingerprint = cert_info->fingerprint;
00467         dlg.issuer = cert_info->issuer_dname;
00468         dlg.permanentEnabled = may_save;
00469 
00470         if (failures & SVN_AUTH_SSL_NOTYETVALID) {
00471                 dlg.problems += _T("Certificate is not yet valid.\r");
00472         }
00473         if (failures & SVN_AUTH_SSL_EXPIRED) {
00474                 dlg.problems += _T("Certificate has expired.\r");
00475         }
00476         if (failures & SVN_AUTH_SSL_CNMISMATCH) {
00477                 dlg.problems += _T("Certificate's CN does not match the remote hostname.\r");
00478         }
00479         if (failures & SVN_AUTH_SSL_UNKNOWNCA) {
00480                 dlg.problems += _T("Certificate authority is unknown or not trusted.\r");
00481         }
00482         if (failures & SVN_AUTH_SSL_OTHER) {
00483                 dlg.problems += _T("Other failure.\r");
00484         }
00485 
00486         if (dlg.DoModal() == IDOK) {
00487                 svn_auth_cred_ssl_server_trust_t *ret = (svn_auth_cred_ssl_server_trust_t *)apr_pcalloc(pool, sizeof(*ret));
00488                 ret->accepted_failures = failures; 
00489                 ret->may_save = dlg.permanent;
00490                 *cred = ret;
00491         }
00492         else {
00493                 *cred = NULL;
00494         }
00495 
00496         return SVN_NO_ERROR;
00497 }
00498 
00499 svn_error_t* CSVNClient::cbAuthSSLClientCertPWPrompt(
00500         svn_auth_cred_ssl_client_cert_pw_t **cred, 
00501         void *baton, 
00502         const char *realm, 
00503         svn_boolean_t may_save, 
00504         apr_pool_t *pool)
00505 {
00506         // Unsupported
00507         *cred = NULL;
00508         return SVN_NO_ERROR;
00509 }
00510 
00511 svn_error_t* CSVNClient::cbAuthSSLClientCertPrompt(
00512         svn_auth_cred_ssl_client_cert_t **cred, 
00513         void *baton, 
00514         const char *realm, 
00515         svn_boolean_t may_save, 
00516         apr_pool_t *pool)
00517 {
00518         // Unsupported
00519         *cred = NULL;
00520         return SVN_NO_ERROR;
00521 }
00522 
00523 
00525 // SVN File
00527 CSVNFile::CSVNFile(CSVNClient* client, const CString & filePath) 
00528         : client(client), filePath(filePath), versioned(false), tracked(false), owned(false), latest(false)
00529 {
00530 }
00531 
00532 CSVNFile::~CSVNFile()
00533 {
00534 }
00535 
00536 void CSVNFile::updateStatus(bool checkServer)
00537 {
00538         svn_error_t* e;
00539 
00540         if (client->isInitialized) {
00541                 CSVNPool scratch_pool(client->pool);
00542 
00543                 svn_opt_revision_t revision = {svn_opt_revision_head, {0}};
00544 
00545                 e = svn_client_status5(NULL, client->ctx, CStringA(filePath), 
00546                         &revision, svn_depth_immediates, TRUE, checkServer ? TRUE : FALSE, 
00547                         FALSE, FALSE, TRUE, NULL, cbStatus, this, scratch_pool);
00548 
00549                 if (e && e->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) {
00550                         versioned = tracked = owned = latest = false;
00551                         svn_error_clear(e);
00552                 }
00553                 else {
00554                         SVNTHROW(e);
00555                 }
00556         }
00557 }
00558 
00559 bool CSVNFile::isVersioned()
00560 {
00561         updateStatus();
00562         return versioned;
00563 }
00564 
00565 bool CSVNFile::isTracked()
00566 {
00567         updateStatus();
00568         return tracked;
00569 }
00570 
00571 bool CSVNFile::isOwned()
00572 {
00573         updateStatus();
00574         return owned;
00575 }
00576 
00577 bool CSVNFile::isLatest()
00578 {
00579         updateStatus(true);
00580         return latest;
00581 }
00582 
00583 bool CSVNFile::update()
00584 {
00585         CStringA filePathA(filePath);
00586         const char* target = filePathA;
00587         CSVNPool scratch_pool(client->pool);
00588         apr_array_header_t* targets = apr_array_make(scratch_pool, 1, sizeof(target));
00589         APR_ARRAY_PUSH(targets, const char*) = target;
00590 
00591         svn_opt_revision_t revision = {svn_opt_revision_head, {0}};
00592 
00593         SVNTHROW(svn_client_update4(NULL, targets, &revision, svn_depth_files, FALSE, FALSE, FALSE,
00594                                 TRUE, FALSE, client->ctx, scratch_pool)); 
00595 
00596         return (client->lastNotifyAction == svn_wc_notify_update_completed);
00597 }
00598 
00599 bool CSVNFile::takeOwnership()
00600 {
00601         CStringA filePathA(filePath);
00602         const char* target = filePathA;
00603         CSVNPool scratch_pool(client->pool);
00604         apr_array_header_t* targets = apr_array_make(scratch_pool, 1, sizeof(target));
00605         APR_ARRAY_PUSH(targets, const char*) = target;
00606         
00607         SVNTHROW(svn_client_lock(targets, "GME auto-locking", FALSE, client->ctx, scratch_pool));
00608 
00609         return (client->lastNotifyAction == svn_wc_notify_locked);
00610 } 
00611 
00612 bool CSVNFile::commit()
00613 {
00614         CStringA filePathA(filePath);
00615         const char* target = filePathA;
00616         CSVNPool scratch_pool(client->pool);
00617         apr_array_header_t* targets = apr_array_make(scratch_pool, 1, sizeof(target));
00618         APR_ARRAY_PUSH(targets, const char*) = target;
00619         
00620         SVNTHROW(svn_client_commit6(targets, svn_depth_immediates, FALSE, FALSE, FALSE, 
00621                 FALSE, FALSE, NULL, NULL, NULL, NULL, client->ctx, scratch_pool));
00622         
00623         // commit does not release the lock if the file was not changed (empty commit)
00624         if (!client->canceledOperation) {
00625                 updateStatus();
00626                 if (owned) {
00627                         SVNTHROW(svn_client_unlock(targets, FALSE, client->ctx, scratch_pool));
00628                         updateStatus();
00629                 }
00630         }
00631 
00632         return !client->canceledOperation;
00633 }
00634 
00636 // Operation Callbacks
00638 svn_error_t* CSVNFile::cbStatus(
00639         void *baton, 
00640         const char *path, 
00641         const svn_client_status_t *status, 
00642         apr_pool_t *scratch_pool)
00643 {
00644         CSVNFile* self = (CSVNFile*)baton;
00645 
00646         if (status->versioned) {
00647                 self->versioned = true;
00648                 self->latest = (status->ood_changed_rev == SVN_INVALID_REVNUM);
00649                 self->owned = (status->lock != NULL);
00650 
00651                 self->tracked = false;
00652                 apr_hash_t* props;
00653                 svn_opt_revision_t revision = {svn_opt_revision_base, {0}};
00654 
00655                 SVN_ERR(svn_client_propget(&props, SVN_PROP_NEEDS_LOCK, CStringA(self->filePath), 
00656                         &revision, FALSE, self->client->ctx, scratch_pool));
00657                 
00658                 if (apr_hash_count(props)) {
00659                         svn_string_t *hval;
00660                         apr_hash_index_t* hi = apr_hash_first(scratch_pool, props);
00661                         apr_hash_this(hi, NULL, 0, (void**)&hval);
00662                         if (hval && !svn_string_isempty(hval)) {
00663                                 self->tracked = true;
00664                         }
00665                 }
00666 
00667         }
00668 
00669         return SVN_NO_ERROR;
00670 }
00671 
00672 #endif // #ifndef _WIN64