/**
 * 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, &current)  );
		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);
}
Example #4
0
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);
}