//
// Sharing.cpp
//
//		Code to install, enable, disable, and bind File and Printer sharing
//		for Microsoft networks (VSERVER).
//
// History:
//
//		 2/02/1999  KenSh     Created for JetNet
//		 9/29/1999  KenSh     Repurposed for Home Networking Wizard
//

#include "stdafx.h"
#include "NetConn.h"
#include "nconnwrap.h"
#include "Registry.h"
#include "TheApp.h"
#include "ParseInf.h"
#include "HookUI.h"


// Local functions
//
BOOL WINAPI FindValidSharingEnumKey(LPSTR pszBuf, int cchBuf);


// Check the registry to see if file/printer sharing is installed.
BOOL WINAPI IsSharingInstalled(BOOL bExhaustive)
{
	if (!FindValidSharingEnumKey(NULL, 0))
		return FALSE;

	if (bExhaustive)
	{
		if (!CheckInfSectionInstallation("netservr.inf", "VSERVER.Install"))
			return FALSE;
	}

	return TRUE;
}

BOOL WINAPI FindValidSharingEnumKey(LPSTR pszBuf, int cchBuf)
{
	return FindValidNetEnumKey(SZ_CLASS_SERVICE, SZ_SERVICE_VSERVER, pszBuf, cchBuf);
}

// Enables file/printer sharing on local NICs, disables it on Internet connections
HRESULT WINAPI EnableProtocolSharingAppropriately(LPCTSTR pszProtocolDeviceID)
{
	HRESULT hr = EnableDisableProtocolSharing(pszProtocolDeviceID, TRUE, FALSE);
	HRESULT hr2 = EnableDisableProtocolSharing(pszProtocolDeviceID, FALSE, TRUE);
	if (hr2 != NETCONN_SUCCESS)
		hr = hr2;
	return hr;
}

// Enables or disables file/printer sharing for either Dial-Up or non-Dial-Up connections,
// but not both.
HRESULT WINAPI EnableDisableProtocolSharing(LPCTSTR pszProtocolDeviceID, BOOL bEnable, BOOL bDialUp)
{
	HRESULT hr = NETCONN_SUCCESS;

	NETADAPTER* prgAdapters;
	int cAdapters = EnumNetAdapters(&prgAdapters);
	for (int iAdapter = 0; iAdapter < cAdapters; iAdapter++)
	{
		NETADAPTER* pAdapter = &prgAdapters[iAdapter];

		BOOL bExternalNic = IsAdapterBroadband(pAdapter);

		if (bDialUp)
		{
			if (!bExternalNic && pAdapter->bNetType != NETTYPE_DIALUP)
				continue;
		}
		else
		{
			if (bExternalNic || pAdapter->bNetType == NETTYPE_DIALUP)
				continue;
		}

		LPTSTR* prgBindings;
		int cBindings = EnumMatchingNetBindings(pAdapter->szEnumKey, pszProtocolDeviceID, &prgBindings);
		for (int iBinding = 0; iBinding < cBindings; iBinding++)
		{
			HRESULT hr2;
			if (bEnable)
			{
				hr2 = EnableSharingOnNetBinding(prgBindings[iBinding]);
			}
			else
			{
				hr2 = DisableSharingOnNetBinding(prgBindings[iBinding]);
			}

			if (hr2 != NETCONN_SUCCESS)
				hr = hr2;
		}
		NetConnFree(prgBindings);
	}
	NetConnFree(prgAdapters);

	return hr;
}

// pszNetBinding is of the form "MSTCP\0000"
HRESULT WINAPI DisableSharingOnNetBinding(LPCSTR pszNetBinding)
{
	HRESULT hr = NETCONN_SUCCESS;

	CRegistry regBindings;
	TCHAR szRegKey[MAX_PATH];
	wsprintf(szRegKey, "Enum\\Network\\%s\\Bindings", pszNetBinding);
	if (regBindings.OpenKey(HKEY_LOCAL_MACHINE, szRegKey, KEY_ALL_ACCESS))
	{
		for (DWORD iBinding = 0; ; )
		{
			CHAR szBinding[64];
			DWORD cchBinding = _countof(szBinding);
			if (ERROR_SUCCESS != RegEnumValue(regBindings.m_hKey, iBinding, szBinding, &cchBinding, NULL, NULL, NULL, NULL))
				break;

			CHAR chSave = szBinding[8];
			szBinding[8] = '\0';
			BOOL bSharing = !lstrcmpi(szBinding, "VSERVER\\");
			szBinding[8] = chSave;

			if (bSharing)
			{
				RemoveBindingFromParent(regBindings.m_hKey, szBinding);
				hr = NETCONN_NEED_RESTART;

				// Restart the enumeration, since we've changed the key
				iBinding = 0;
				continue;
			}
			else
			{
				// Note: Sharing may still be bound to the client (VREDIR), but we
				// don't remove that binding because Network Control Panel doesn't.
				// (Verified on Win98 gold.)
				//	HRESULT hr2 = DisableSharingOnNetBinding(szBinding);
				//	if (hr2 != NETCONN_SUCCESS)
				//		hr = hr2;
			}

			iBinding += 1; // advance to next binding
		}
	}

	return hr;
}

// pszNetBinding is of the form "MSTCP\0000"
HRESULT WINAPI EnableSharingOnNetBinding(LPCSTR pszNetBinding)
{
	HRESULT hr = NETCONN_SUCCESS;
	BOOL bFoundSharing = FALSE;

	CRegistry regBindings;
	TCHAR szRegKey[MAX_PATH];
	wsprintf(szRegKey, "Enum\\Network\\%s\\Bindings", pszNetBinding);
	if (!regBindings.CreateKey(HKEY_LOCAL_MACHINE, szRegKey, KEY_ALL_ACCESS))
	{
		ASSERT(FALSE);
		return NETCONN_UNKNOWN_ERROR;
	}

	DWORD iBinding = 0;
	for (;;)
	{
		CHAR szBinding[64];
		DWORD cchBinding = _countof(szBinding);
		if (ERROR_SUCCESS != RegEnumValue(regBindings.m_hKey, iBinding, szBinding, &cchBinding, NULL, NULL, NULL, NULL))
			break;

		CHAR chSave = szBinding[8];
		szBinding[8] = '\0';
		BOOL bSharing = !lstrcmpi(szBinding, "VSERVER\\");
		szBinding[8] = chSave;

		chSave = szBinding[7];
		szBinding[7] = '\0';
		BOOL bClient = !lstrcmpi(szBinding, "VREDIR\\");
		szBinding[7] = chSave;

		if (bSharing)
		{
			if (!IsValidNetEnumKey(SZ_CLASS_SERVICE, SZ_SERVICE_VSERVER, szBinding + _lengthof("VSERVER\\")))
			{
				// Found a dead link to nonexistent Enum item; delete it and restart search
				regBindings.DeleteValue(szBinding);
				iBinding = 0;
				continue;
			}
			else
			{
				bFoundSharing = TRUE;
			}
		}
		else if (bClient)
		{
			HRESULT hr2 = EnableSharingOnNetBinding(szBinding);
			if (hr2 != NETCONN_SUCCESS)
				hr = hr2;
		}

		iBinding++;
	}

	if (!bFoundSharing)
	{
		CHAR szBinding[64];
		HRESULT hr2 = CreateNewFilePrintSharing(szBinding, _countof(szBinding));
		if (hr2 != NETCONN_SUCCESS)
			hr = hr2;

		if (SUCCEEDED(hr2))
		{
			regBindings.SetStringValue(szBinding, "");
			hr = NETCONN_NEED_RESTART;
		}
	}

	return hr;
}

HRESULT WINAPI EnableSharingAppropriately()
{
	HRESULT hr = NETCONN_SUCCESS;

	NETADAPTER* prgAdapters;
	int cAdapters = EnumNetAdapters(&prgAdapters);
	for (int iAdapter = 0; iAdapter < cAdapters; iAdapter++)
	{
		NETADAPTER* pAdapter = &prgAdapters[iAdapter];

		// Walk through each protocol bound to the adapter
		LPTSTR* prgBindings;
		int cBindings = EnumNetBindings(pAdapter->szEnumKey, &prgBindings);
		for (int iBinding = 0; iBinding < cBindings; iBinding++)
		{
			LPTSTR pszBinding = prgBindings[iBinding];
			HRESULT hr2 = NETCONN_SUCCESS;

			BOOL bExternalNic = IsAdapterBroadband(pAdapter);

			// Disable file/printer sharing on:
			//	 * Dial-Up Adapters
			//	 * PPTP connections
			//	 * NIC used by ICS to connect to the Internet
			if (pAdapter->bNetType == NETTYPE_DIALUP ||
				pAdapter->bNetType == NETTYPE_PPTP ||
				bExternalNic)
			{
				hr2 = DisableSharingOnNetBinding(pszBinding);
			}
			// Enable file/printer sharing on:
			//	 * Ethernet adapters
			//   * IRDA adapters
			else if (pAdapter->bNetType == NETTYPE_LAN || 
					 pAdapter->bNetType == NETTYPE_IRDA)
			{
				hr2 = EnableSharingOnNetBinding(pszBinding);
			}

			if (hr2 != NETCONN_SUCCESS)
				hr = hr2;
		}
		NetConnFree(prgBindings);

	}
	NetConnFree(prgAdapters);

	return hr;
}

// InstallSharing (public)
//
//		Installs VSERVER, a.k.a. File and Printer sharing for Microsoft networks.
//		The standard progress UI is (mostly) suppressed, and instead the given
//		callback function is called so a custom progress UI can be implemented.
//
//		Returns a NETCONN_xxx result, defined in NetConn.h
//
// History:
//
//		 4/09/1999  KenSh     Created
//		 4/22/1999  KenSh     Remove file-sharing on Dial-Up connections
//		
HRESULT WINAPI InstallSharing(HWND hwndParent, PROGRESS_CALLBACK pfnProgress, LPVOID pvProgressParam)
{
	HRESULT hr = NETCONN_SUCCESS;
	BOOL bInstall = FALSE;

	// Remove any broken bindings
	RemoveBrokenNetItems(SZ_CLASS_SERVICE, SZ_SERVICE_VSERVER);

	if (IsSharingInstalled(FALSE))
	{
		// Sharing is set up in registry, but check for missing files
		if (!CheckInfSectionInstallation("netservr.inf", "VSERVER.Install"))
		{
			if (InstallInfSection("netservr.inf", "VSERVER.Install", TRUE))
			{
				hr = NETCONN_NEED_RESTART;
			}
		}
	}
	else
	{
		BeginSuppressNetdiUI(hwndParent, pfnProgress, pvProgressParam);
		DWORD dwResult = CallClassInstaller16(hwndParent, SZ_CLASS_SERVICE, SZ_SERVICE_VSERVER);
		EndSuppressNetdiUI();

		hr = HresultFromCCI(dwResult);
		if (g_bUserAbort)
		{
			hr = NETCONN_USER_ABORT;
		}
		else if (SUCCEEDED(hr))
		{
			hr = NETCONN_NEED_RESTART;
		}

		// Total hack to work around JetNet bug 1193
//		DoDummyDialog(hwndParent);
	}

//	if (SUCCEEDED(hr))
//	{
//		HRESULT hr2 = EnableSharingAppropriately();
//		if (hr2 != NETCONN_SUCCESS)
//			hr = hr2;
//	}

	HRESULT hr2 = EnableFileSharing();
	if (hr2 != NETCONN_SUCCESS)
		hr = hr2;

	hr2 = EnablePrinterSharing();
	if (hr2 != NETCONN_SUCCESS)
		hr = hr2;

	return hr;
}

// pConflict may be NULL if you don't need the details
BOOL WINAPI FindConflictingService(LPCSTR pszWantService, NETSERVICE* pConflict)
{
	CRegistry reg;
	if (reg.OpenKey(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\Class\\NetService", KEY_READ))
	{
		TCHAR szSubKey[80];

		for (DWORD iService = 0; ; iService++)
		{
			DWORD cchSubKey = _countof(szSubKey);
			if (ERROR_SUCCESS != RegEnumKeyEx(reg.m_hKey, iService, szSubKey, &cchSubKey, NULL, NULL, NULL, NULL))
				break;

			CRegistry regSubKey;
			if (regSubKey.OpenKey(reg.m_hKey, szSubKey, KEY_READ))
			{
				CRegistry regNdi;
				if (regNdi.OpenKey(regSubKey.m_hKey, "Ndi", KEY_READ))
				{
					CRegistry regCompat;
					if (regCompat.OpenKey(regNdi.m_hKey, "Compatibility", KEY_READ))
					{
						CString strExclude;
						if (regCompat.QueryStringValue("ExcludeAll", strExclude))
						{
							if (CheckMatchingInterface(pszWantService, strExclude))
							{
								if (pConflict != NULL)
								{
									regNdi.QueryStringValue("DeviceID", pConflict->szDeviceID, _countof(pConflict->szDeviceID));
									regSubKey.QueryStringValue("DriverDesc", pConflict->szDisplayName, _countof(pConflict->szDisplayName));
									wsprintf(pConflict->szClassKey, "NetService\\%s", szSubKey);
								}
								return TRUE;
							}
						}
					}
				}
			}
		}
	}

	return FALSE;
}

// pszBuf is filled with the new binding's enum key, e.g. "VSERVER\0001"
HRESULT CreateNewFilePrintSharing(LPSTR pszBuf, int cchBuf)
{
	HRESULT hr;

	if (FAILED(hr = FindAndCloneNetEnumKey(SZ_CLASS_SERVICE, SZ_SERVICE_VSERVER, pszBuf, cchBuf)))
	{
		ASSERT(FALSE);
		return hr;
	}

	// Now pszBuf contains a string of the form "VSERVER\0001"

	CHAR szBindings[60];
	CRegistry regBindings;
	lstrcpy(szBindings, pszBuf);		// "VSERVER\0001"
	lstrcat(szBindings, "\\Bindings");	// "VSERVER\0001\Bindings"


	if (FAILED(hr = OpenNetEnumKey(regBindings, szBindings, KEY_ALL_ACCESS)))
	{
		ASSERT(FALSE);
		return hr;
	}

	// Delete existing bindings (shouldn't be any, right?)
	regBindings.DeleteAllValues();

	return NETCONN_SUCCESS;
}

BOOL IsSharingEnabledHelper(LPCTSTR pszThis)
{
	CRegistry reg;
	if (reg.OpenKey(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\VxD\\VNETSUP", KEY_READ))
	{
		// If the value is missing or says "Yes", then sharing is enabled
		char szBuf[10];
		return (!reg.QueryStringValue(pszThis, szBuf, _countof(szBuf)) || 0 == lstrcmpi(szBuf, "Yes"));
	}

	return FALSE;
}

BOOL WINAPI IsFileSharingEnabled()
{
	return IsSharingEnabledHelper("FileSharing");
}

BOOL WINAPI IsPrinterSharingEnabled()
{
	return IsSharingEnabledHelper("PrintSharing");
}

HRESULT WINAPI EnableSharingHelper(LPCTSTR pszThis, LPCTSTR pszOther)
{
	HRESULT hr = NETCONN_SUCCESS;

	CRegistry reg;
	if (reg.OpenKey(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\VxD\\VNETSUP"))
	{
		// If Printer sharing is "No", then set File sharing to "Yes".
		// If Printer sharing is missing or is "Yes", delete both values (enables both).

		char szBuf[10];
		if (reg.QueryStringValue(pszOther, szBuf, _countof(szBuf)) &&
			0 != lstrcmpi(szBuf, "Yes"))
		{
			// Set file sharing value to "Yes" (if it's not already set)
			if (!reg.QueryStringValue(pszThis, szBuf, _countof(szBuf)) ||
				0 != lstrcmpi(szBuf, "Yes"))
			{
				reg.SetStringValue(pszThis, "Yes");
				hr = NETCONN_NEED_RESTART;
			}
		}
		else
		{
			// Delete file-sharing and printer-sharing entries (enables both).
			if (reg.QueryStringValue(pszThis, szBuf, _countof(szBuf)) &&
				0 != lstrcmpi(szBuf, "Yes"))
			{
				reg.DeleteValue(pszThis);
				reg.DeleteValue(pszOther);
				hr = NETCONN_NEED_RESTART;
			}
		}
	}

	return hr;
}

HRESULT WINAPI EnableFileSharing()
{
	return EnableSharingHelper("FileSharing", "PrintSharing");
}

HRESULT WINAPI EnablePrinterSharing()
{
	return EnableSharingHelper("PrintSharing", "FileSharing");
}