/*

copyright (c) 1992  Microsoft Corporation

Module Name:

    Ole1Guid.cpp

Abstract:

    This module converts 1.0 class names to GUIDs (CLSIDs).
	See the ole2issues database (bug 318) for a description of this
	whole mechanism.

Author:

	Jason Fuller	(jasonful)	19-October-1992
*/

// ErikGav 31-Dec-93  Chicago port

#include <ole2int.h>

// CLSID_* declarations
#include <ole1cls.h>

#define STR_MAX 80
ASSERTDATA

typedef unsigned short HASH;

typedef struct ENTRY
{
	LPCWSTR  sz;		// class name
	const CLSID FAR* pclsid;	// corresponding CLSID
} ENTRY;


// This is the map.
// The +6 removes "CLSID_"
// It does unnatural things with the pre-processor, but it ensures that
// the map and ole1cls.h will always be in sync.

// REVIEW OPTIMIZATION
// If it is a problem that the strings in this map are in the data segment,
// we can use yet more pre-processor tricks to define a code-segment LPCSTR
// variable for each string, then put these variables in the map.

static ENTRY rgentMap[] =
{
	#define DEFINE_OLE1GUID(clsid,x,xx,xxx,sz) sz, &clsid, /##/
	#include "ole1cls.h"
	#undef DEFINE_OLE1GUID

	NULL, NULL

};



#pragma SEG(VerifyIs10Class)
// verify name is ole1class; errors: CO_E_CLASSSTRING
static INTERNAL VerifyIs10Class(LPCWSTR szOle1)
{
	HKEY hkey = NULL;	
	HKEY hkey2 = NULL;	
	HRESULT hresult = NOERROR;

    if (ERROR_SUCCESS != RegOpenKey (HKEY_CLASSES_ROOT, (LPWSTR) szOle1, &hkey) ||
		ERROR_SUCCESS != RegOpenKey (hkey, L"protocol\\StdFileEditing", &hkey2))
	{
		// This string is not an OLE 1 server, so we do not know
		// what it is.
		hresult = ReportResult (0, CO_E_CLASSSTRING, 0, 0);
	}
	if (hkey)
		RegCloseKey (hkey);
	if (hkey2)
		RegCloseKey (hkey2);
	return hresult;
}



#pragma SEG(Hash)
// hash ole1 class name; errors: none
static INTERNAL_(HASH) Hash(LPCWSTR sz)
{
	HASH hash = 0;
	Assert (sizeof (HASH) == 2);  // This is vital

	while (*sz)
		hash = 257 * hash + *sz++;

	return hash;
}



#define RegCall(f) if ((f) != ERROR_SUCCESS)	{ \
						if (hkey) RegCloseKey (hkey); \
						return ResultFromScode (REGDB_E_WRITEREGDB); \
					}
#pragma SEG(WriteToRegDb)
// errors: REGDB_E_WRITEREGDB
static INTERNAL WriteToRegDb
    (LPCWSTR szOle1,	  // OLE1 class name e.g. "ExcelWorksheet"
	LPCWSTR szClsid)	  // "{...0046}"
{
	WCHAR szKey [256];
	WCHAR szUserName [256];
	LONG cbUserName = 256;
	HKEY hkey=NULL;

	Assert (szClsid[0] == '{');
	Assert (szOle1 [0] != '{');

	// szOle1 = User type Name
	// 		clsid = {...0046}        <- write this
	RegCall (RegOpenKey (HKEY_CLASSES_ROOT, (LPWSTR) szOle1, &hkey))
	RegCall (RegQueryValue (hkey, NULL, szUserName, &cbUserName))
	RegCall (RegSetValue (hkey, L"Clsid", REG_SZ, (LPWSTR) szClsid, wcslen(szClsid)))

	if (0==wcscmp(szClsid, L"{00030003-0000-0000-C000-000000000046}"))
	{
		// Word
		RegSetValue (hkey, L"PackageOnFileDrop", REG_SZ, (LPWSTR)NULL, 0);
	}

	RegCall (RegCloseKey (hkey))
	hkey=NULL;

	// write this:
	// CLSID
	//		{...00046} = User Type Name
	//				Ole1Class = szOle1
	//				ProgID    = szOle1
	wsprintf (szKey, L"CLSID\\%ws", szClsid);
	RegCall (RegSetValue (HKEY_CLASSES_ROOT, (LPWSTR) szKey, REG_SZ, szUserName,
						cbUserName-1))
	RegCall (RegOpenKey (HKEY_CLASSES_ROOT, (LPWSTR) szKey, &hkey))
	RegCall (RegSetValue (hkey, L"Ole1Class", REG_SZ, (LPWSTR) szOle1, wcslen(szOle1)))
	RegCall (RegSetValue (hkey, L"ProgID", REG_SZ, (LPWSTR) szOle1, wcslen(szOle1)))
	RegCall (RegCloseKey (hkey))
	return NOERROR;
}

#undef RegCall

#pragma SEG(Ole10_CLSIDFromString)

// Ole10_CLSIDFromString
//
// This function must only be called when the CLSID is NOT in the reg db
// (under key "Clsid")
//
// errors: CO_E_CLASSSTRING (not ole1 class); REGDB_E_WRITEREGDB (errors with reg.dat)
//
INTERNAL 	Ole10_CLSIDFromString
	(LPCWSTR szOle1,
	CLSID FAR* pclsid,
	BOOL	fForceAssign) // always assign a CLSID, even if not in reg db
{
	WCHAR szClsid[STR_MAX];
	ENTRY FAR* pent;

	*pclsid = CLSID_NULL;

	#ifdef _DEBUG
	{
		// This function should not be called if the CLSID is in the reg db
		HKEY hkey, hkey2;
		WCHAR szKey[256];
		wsprintf (szKey, L"CLSID\\%ws", szOle1);
		if (ERROR_SUCCESS==RegOpenKey (HKEY_CLASSES_ROOT, szKey, &hkey))
		{
			Assert (ERROR_SUCCESS != RegOpenKey (hkey, L"Clsid", &hkey2));
			RegCloseKey (hkey);
		}
	}
	#endif

	// Look for class name in Map
	for (pent = (ENTRY FAR*) rgentMap; pent->sz != NULL; pent++)
	{
		// Because atoms and reg db keys are case-insensitive, OLE 1.0
		// was case insensitive.  So let's be case-insensitive also.
		if (0==wcsicmp(szOle1, pent->sz))
		{
			*pclsid = *pent->pclsid;
			Verify(StringFromCLSID2 (*pent->pclsid, szClsid, STR_MAX) != 0);
			break;
		}
	}
			
	if (IsEqualGUID(*pclsid, CLSID_NULL))
	{
		HRESULT hresult;

		if (!fForceAssign &&
			(NOERROR != (hresult = VerifyIs10Class (szOle1))))
		{
			// This happens when trying to get a CLSID for a packaged
			// object, e.g., trying to get a CLSID for "txtfile"
			// because someone called GetClassFile("foo.txt").
			// So do not assert
			return hresult;
		}

		
		// Class name is not in our table, so make up a GUID by hashing
		HASH hash = Hash (szOle1);
		wsprintf (szClsid, L"{0004%02X%02X-0000-0000-C000-000000000046}",
				  LOBYTE(hash), HIBYTE(hash));
		Verify(CLSIDFromString (szClsid, pclsid) == NOERROR);
	}

        Assert((!IsEqualGUID(*pclsid, CLSID_NULL)) &&
               "About to write NULL GUID into registry");

	HRESULT hresult = WriteToRegDb (szOle1, szClsid);
	// If forcing the assignment of a CLSID even if the string
	// is not in the reg db, then WriteToRegDb is allowed to fail.
	return fForceAssign ? NOERROR : hresult;
}

#pragma SEG(Ole10_StringFromCLSID)

// Ole10_StringFromCLSID
//
// Only call this function if the "Ole1Class" key is NOT yet in the reg db
//
// errors: E_UNEXPECTED (clsid not a known ole1 class)
//
INTERNAL Ole10_StringFromCLSID(
	REFCLSID clsid,
	LPWSTR szOut,
	int cbMax)
{
	ENTRY FAR* pent;
	LPWSTR szClsid = NULL;

	// Look it up in Map
	for (pent = (ENTRY FAR*) rgentMap; pent->sz != NULL; pent++)
	{
		if (IsEqualGUID(clsid, *pent->pclsid))
		{
			if (wcslen (pent->sz) +1 > (ULONG)cbMax)
			{
				// unlikely
				return E_OUTOFMEMORY;
			}
			wcscpy (szOut, pent->sz);
			
			if (NOERROR==StringFromCLSID (clsid, &szClsid))
			{
				// If the info had already been in the reg db, then
				// this function wouldn't have been called.  So write
				// the info now.  This happens when we a 1.0 object (and CLSID)
				// in a 2.0 file comes from a machine where the server is
				// registered to one where it is not.
				WriteToRegDb (pent->sz, szClsid);
				delete szClsid;
			}
			return NOERROR;
		}
	}

	// We could not find the CLSID.
	// We could iterate through the reg db and assign CLSIDs to all 1.0
	// servers that do not yet have a CLSID, until we find one that matches.
	// We decided it is not worth doing this because if the server
	// was in the reg db, it probably would have been assigned a CLSID
	// already.
	// If not found, it is a hashed CLSID for a server that
	// is not in our map and has not been assigned a CLSID (perhaps
	// because it does not exist on this machine).
	return E_UNEXPECTED;
}


