GME
13
|
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