/** * This is a special case of __get_original_content_hid() that * basically doest that and then fetches the blob and returns * the actual symlink target. * * This is a convenience for STATUS that can report 'target' * fields rather than (or in addition to) 'hid' for symlinks. * */ void sg_wc_liveview_item__get_original_symlink_target(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_string ** ppStringTarget) { const char * pszHidContent; SG_string * pStringTarget = NULL; SG_byte* pBuffer = NULL; SG_uint64 uSize = 0u; if (pLVI->tneType != SG_TREENODEENTRY_TYPE_SYMLINK) SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "GetOriginalSymlinkTarget: '%s' is not a symlink.", SG_string__sz(pLVI->pStringEntryname)) ); SG_ERR_CHECK( sg_wc_liveview_item__get_original_content_hid(pCtx, pLVI, pWcTx, SG_FALSE, &pszHidContent) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pszHidContent, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &pStringTarget, pBuffer, (SG_uint32)uSize) ); *ppStringTarget = pStringTarget; pStringTarget = NULL; fail: SG_STRING_NULLFREE(pCtx, pStringTarget); SG_NULLFREE(pCtx, pBuffer); }
void SG_password__get( SG_context *pCtx, const char *szRepoSpec, const char *username, SG_string **pPassword) { SG_byte *pwdata = NULL; SG_uint32 pwlen; SG_string *password = NULL; SG_string *path = NULL; SG_string *server = NULL; SecProtocolType proto; SG_uint32 port; SG_bool isValid = SG_FALSE; OSStatus findRes; SG_NULLARGCHECK_RETURN(pPassword); *pPassword = NULL; SG_NULLARGCHECK(username); SG_NULLARGCHECK(szRepoSpec); SG_ERR_CHECK( _sg_password__parse_url(pCtx, szRepoSpec, &isValid, &proto, &server, &path, &port) ); if (! isValid) SG_ERR_THROW(SG_ERR_NOTIMPLEMENTED); findRes = SecKeychainFindInternetPassword( NULL, SG_STRLEN( SG_string__sz(server) ), SG_string__sz(server), 0, NULL, SG_STRLEN(username), username, SG_STRLEN( SG_string__sz(path) ), SG_string__sz(path), port, proto, kSecAuthenticationTypeDefault, (UInt32 *)&pwlen, (void **)&pwdata, NULL); if (findRes == errSecItemNotFound || findRes == errSecInteractionNotAllowed) goto fail; else if (findRes != errSecSuccess) _SG_THROW_MAC_SEC_ERROR(findRes); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &password, pwdata, pwlen) ); *pPassword = password; password = NULL; fail: if (pwdata) SecKeychainItemFreeContent(NULL, pwdata); SG_STRING_NULLFREE(pCtx, path); SG_STRING_NULLFREE(pCtx, server); SG_STRING_NULLFREE(pCtx, password); }
static void _format_comment(SG_context* pCtx, SG_bool onlyIncludeFirstLine, const char* szLinePrefix, const char* szComment, char** ppszReturn) { SG_bool bFoundLineBreak = SG_FALSE; SG_string* pstr = NULL; SG_uint32 lenPrefix = SG_STRLEN(szLinePrefix); SG_uint32 offset; { const char* pos; for (pos = szComment; *pos; pos++) { if (*pos == SG_CR || *pos == SG_LF) { bFoundLineBreak = SG_TRUE; break; } } if (!bFoundLineBreak) return; if(onlyIncludeFirstLine) { SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &pstr, (const SG_byte*)szComment, (SG_uint32)(pos-szComment)) ); SG_ERR_CHECK( SG_string__sizzle(pCtx, &pstr, (SG_byte**)ppszReturn, NULL) ); return; } offset = (SG_uint32)(pos-szComment); } SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstr, szComment) ); while (offset < SG_string__length_in_bytes(pstr)) { SG_byte current; SG_ERR_CHECK( SG_string__get_byte_l(pCtx, pstr, offset, ¤t) ); if (current == SG_CR) { SG_byte next; bFoundLineBreak = SG_TRUE; SG_ERR_CHECK( SG_string__get_byte_l(pCtx, pstr, offset+1, &next) ); if (next != SG_LF) { // Mac format, lines end with \r only. Consoles will not advance a line. SG_ERR_CHECK( SG_string__insert__sz(pCtx, pstr, offset+1, "\n") ); } offset++; } else if (current == SG_LF) { bFoundLineBreak = SG_TRUE; } offset++; if (bFoundLineBreak) { SG_ERR_CHECK( SG_string__insert__sz(pCtx, pstr, offset, szLinePrefix) ); offset += lenPrefix; bFoundLineBreak = SG_FALSE; } } SG_ERR_CHECK( SG_string__sizzle(pCtx, &pstr, (SG_byte**)ppszReturn, NULL) ); return; fail: SG_STRING_NULLFREE(pCtx, pstr); }
static void _sg_workingdir__get_entry2(SG_context * pCtx, SG_repo * pRepo, const SG_pathname * pPathSub, const char * pszGid, SG_treenode_entry_type type, const char * pszidHidContent, const char * pszidHidXattrs, SG_int64 iAttributeBits, SG_vhash * pvhTimestamps) { SG_file* pFile = NULL; SG_string* pstrLink = NULL; SG_byte* pBytes = NULL; SG_vhash * pvhGid = NULL; if (SG_TREENODEENTRY_TYPE_DIRECTORY == type) { /* create the directory and then recurse into it */ SG_ERR_CHECK( SG_fsobj__mkdir__pathname(pCtx, pPathSub) ); SG_ERR_CHECK( _sg_workingdir__get_dir(pCtx, pRepo, pPathSub, pszidHidContent, pvhTimestamps) ); } else if (SG_TREENODEENTRY_TYPE_REGULAR_FILE == type) { SG_ERR_CHECK( SG_file__open__pathname(pCtx, pPathSub, SG_FILE_RDWR | SG_FILE_CREATE_NEW, SG_FSOBJ_PERMS__MASK, &pFile) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_file(pCtx, pRepo, pszidHidContent, pFile, NULL) ); SG_ERR_CHECK( SG_file__close(pCtx, &pFile) ); } else if (SG_TREENODEENTRY_TYPE_SYMLINK == type) { SG_uint64 iLenBytes = 0; SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pRepo, pszidHidContent, &pBytes, &iLenBytes) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &pstrLink, pBytes, (SG_uint32) iLenBytes) ); SG_ERR_CHECK( SG_fsobj__symlink(pCtx, pstrLink, pPathSub) ); SG_NULLFREE(pCtx, pBytes); SG_STRING_NULLFREE(pCtx, pstrLink); } else { SG_ERR_THROW(SG_ERR_NOTIMPLEMENTED); } if (pszidHidXattrs) { #ifdef SG_BUILD_FLAG_FEATURE_XATTR SG_ERR_CHECK( _sg_workingdir__set_xattrs(pCtx, pRepo, pPathSub, pszidHidXattrs) ); #else // TODO do we need to stuff something into the pendingtree to remind us // TODO that the entry originally had an XAttr and we just didn't restore // TODO it when we populated the WD on this Windows system? #endif } SG_ERR_CHECK( SG_attributes__bits__apply(pCtx, pPathSub, iAttributeBits) ); if (pvhTimestamps && (SG_TREENODEENTRY_TYPE_REGULAR_FILE == type)) { SG_fsobj_stat stat; SG_int64 iTimeNow; SG_ERR_CHECK( SG_fsobj__stat__pathname(pCtx, pPathSub, &stat) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx, &iTimeNow) ); SG_ERR_CHECK( SG_VHASH__ALLOC(pCtx, &pvhGid) ); SG_ERR_CHECK( SG_vhash__add__int64(pCtx, pvhGid, "mtime_ms", stat.mtime_ms) ); SG_ERR_CHECK( SG_vhash__add__int64(pCtx, pvhGid, "clock_ms", iTimeNow) ); SG_ERR_CHECK( SG_vhash__add__vhash(pCtx, pvhTimestamps, pszGid, &pvhGid) ); // this steals our vhash } fail: SG_VHASH_NULLFREE(pCtx, pvhGid); }
/** * This is a special case of __get_current_content_hid() that * basically does that and then fetches the blob and returns * the actual target string contained within. * * Normally, we wouldn't need this specialization, but if we * have a QUEUED overwrite-symlink-target, THEN WE NEED TO LIE * and return the new/queued value rather than the actual * target value that the existing symlink has in the WD. * */ void sg_wc_liveview_item__get_current_symlink_target(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_string ** ppStringTarget) { SG_byte* pBuffer = NULL; SG_uint64 uSize = 0u; if (pLVI->tneType != SG_TREENODEENTRY_TYPE_SYMLINK) SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "GetCurrentSymlinkTarget: '%s' is not a symlink.", SG_string__sz(pLVI->pStringEntryname)) ); if (pLVI->queuedOverwrites.pvhContent) { // We have a QUEUED operation on this item that changed the // contents. Get the 'current' value from the journal. const char * psz = NULL; SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "target", &psz) ); if (psz) { // last overwrite-type operation gave us a SYMLINK-TARGET. #if TRACE_WC_LIE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentSymlinkTarget: using journal '%s' for: %s\n", psz, SG_string__sz(pLVI->pStringEntryname)) ); #endif SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, ppStringTarget, psz) ); return; } SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "file", &psz) ); if (psz) { // last overwrite-type operation used a TEMP file. // this cannot/should not happen. SG_ERR_THROW2( SG_ERR_INVALIDARG, (pCtx, "GetCurrentSymlinkTarget: journal contains temp file '%s' for: %s", psz, SG_string__sz(pLVI->pStringEntryname)) ); } SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "hid", &psz) ); if (psz) { // last overwrite-type operation used an HID. SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, psz, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } SG_ERR_THROW2( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: required field missing from vhash for: %s", SG_string__sz(pLVI->pStringEntryname)) ); } // Otherwise, return the current pre-tx value. SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow) ); if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live)) { if (pLVI->pPcRow_PC) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPcRow_PC->p_d_sparse) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPcRow_PC->p_d_sparse->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else if (pLVI->pPrescanRow->pPcRow_Ref) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else { // With the addition of {sparse_hid,sparse_attrbits} to tbl_PC, // we should not get here. SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: unhandled case when sparse for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } } else if (pLVI->pPrescanRow->pRD) { const SG_string * pStringTargetRef; SG_ERR_CHECK( sg_wc_readdir__row__get_content_symlink_target(pCtx, pWcTx, pLVI->pPrescanRow->pRD, &pStringTargetRef) ); SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, ppStringTarget, pStringTargetRef) ); return; } else if (pLVI->pPrescanRow->pTneRow) { SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPrescanRow->pTneRow->p_d->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else { // perhaps an ADD-SPECIAL + DELETE // or an ADDED+LOST in an UPDATE ? SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: unhandled case for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } fail: SG_NULLFREE(pCtx, pBuffer); }
static void _sg_password__parse_url( SG_context *pCtx, const char *url, SG_bool *pIsValid, SG_string **ppProto, SG_string **ppServer, SG_string **ppPath, SG_uint32 *pPort ) { const char *slash, *colon, *ss; SG_string *server = NULL; SG_string *path = NULL; SG_string *proto = NULL; SG_NULLARGCHECK(url); *pIsValid = SG_FALSE; if (strncmp("http://", url, 7) == 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &proto, "http") ); *pPort = 80; url += 7; } else if (strncmp("https://", url, 8) == 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &proto, "https") ); *pPort = 443; url += 8; } else goto fail; slash = strchr(url, '/'); if (! slash) goto fail; SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &server, (const SG_byte *)url, (SG_uint32)(slash - url)) ); ss = SG_string__sz(server); colon = strchr(ss, ':'); if (colon) { SG_uint32 port = atoi(colon + 1); SG_uint32 plen = colon - ss; if (port == 0) goto fail; *pPort = port; SG_ERR_CHECK( SG_string__remove(pCtx, server, plen, SG_STRLEN(ss) - plen) ); } SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &path, "/") ); *ppPath = path; path = NULL; *ppServer = server; server = NULL; *ppProto = proto; proto = NULL; *pIsValid = SG_TRUE; fail: SG_STRING_NULLFREE(pCtx, server); SG_STRING_NULLFREE(pCtx, path); SG_STRING_NULLFREE(pCtx, proto); }