windows-updates-20010819
[openafs.git] / src / WINNT / afs_setup_utils / afs_setup_utils.cpp
index 363adc7..c391342 100644 (file)
  */
 
 /*
- * INCLUDES _________________________________________________________________\r
- *\r
- */\r
-\r
-extern "C" {\r
-#include <afs/param.h>\r
-#include <afs/stds.h>\r
-#include <afs/fileutil.h>\r
-}\r
-\r
-#include <windows.h>\r
-#include <stdio.h>\r
-#include <time.h>\r
-#include <assert.h>\r
-#include <stdlib.h>\r
-#include <io.h>\r
-#include <string.h>\r
-#include <SYS\STAT.H>\r
-#include <shellapi.h>\r
-\r
-#include <WINNT/afsreg.h>\r
-#include <WINNT/afssw.h>\r
-#include <WINNT/talocale.h>\r
-\r
-#include "resource.h"\r
-#include "progress_dlg.h"\r
-#include "sutil.h"\r
-#include "forceremove.h"\r
-\r
-\r
-/*\r
- * PROTOTYPES _________________________________________________________________\r
- *\r
- */\r
-static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered);\r
-BOOL UninstallCredsTool();\r
-BOOL ServerSpecificUninstall();\r
-BOOL ClientSpecificUninstall();\r
-\r
-\r
-\r
-/*\r
- * DEFINITIONS _________________________________________________________________\r
- *\r
- */\r
-#define SUCALLCONV  WINAPI\r
-\r
-#define UNINSTALL_TEMP_INFO_KEY     "HKEY_LOCAL_MACHINE\\Software\\AfsUninstallTempInfo"\r
-#define INSTALL_DIR_VALUE_NAME      "InstallDir"\r
-\r
-#define AFS_PRESERVED_CFG_INFO_KEY  "HKEY_LOCAL_MACHINE\\Software\\AfsPreservedConfigInfo"\r
-\r
-#define MS_SHARED_FILES_KEY         "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"\r
-\r
-// Log file to use when running in silent mode\r
-#define UNINSTALL_ERROR_LOG_NAME    "\\AfsUninstallErrorLog.txt"\r
-#define INSTALL_ERROR_LOG_NAME      "\\AfsInstallErrorLog.txt"\r
-\r
-#define TARGETDIR                   "<TARGETDIR>"\r
-#define WINDIR                      "<WINDIR>"\r
-#define WINSYSDIR                   "<WINSYSDIR>"\r
-\r
-#define WIN9X_START_MENU_REG_KEY    "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"\r
-#define WIN9X_START_MENU_REG_VALUE  "Programs"\r
-    \r
-#define WINNT_START_MENU_REG_KEY    "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"\r
-#define WINNT_START_MENU_REG_VALUE  "Common Programs"\r
-\r
-#define LOCALE_ID_LEN               4\r
-\r
-struct REGVALUE {\r
-    char *pszKey;\r
-    char *pszValue;\r
-};\r
-\r
-\r
-typedef BOOL (APP_UNINSTALL_FUNC)();\r
-\r
-\r
-\r
-struct APPINFO {\r
-    char *pszAppName;\r
-\r
-    // Service Info\r
-    char *pszSvcName;\r
-    char *pszSvcKey;\r
-    char *pszSvcDependOn;\r
-    char *pszSvcDisplayName;\r
-\r
-    char *pszNetworkProviderOrder;\r
-\r
-    // Message to use to tell the user that we have to stop the service\r
-    int nServiceShutdownMsgID;\r
-\r
-    // Message to use for the progress dialog that is shown while\r
-    // waiting for the service to stop.\r
-    int nServiceShutdownProgressMsgID;\r
-\r
-    // Location in registry of a key we can use to know that the app is installed\r
-    char *pszAppKey;\r
-\r
-    // Location in registry of this app's install dir\r
-    struct REGVALUE regInstallDir;\r
-\r
-    // Path Info\r
-    char *pszLocalRoot;     // The root dir below the install dir\r
-    char *pszBinPath;       // Path to remove from the system path\r
-\r
-    // Generated files and directories to delete.  These are both multistring lists.\r
-    char *pszDirsToDel;     // All files in these dirs will be deleted\r
-    char *pszFilesToDel;    // Use this if you want to delete files but leave the dir.  Wildcards can be used.\r
-\r
-    // Registry values to remove\r
-    struct REGVALUE *pRegValues;\r
-    struct REGVALUE *pWinNTRegValues;   // Only remove these if running WinNT\r
-    struct REGVALUE *pWin9XRegValues;   // Only remove these if running Win9X\r
-\r
-    // Start menu entries to delete\r
-    char *pszStartMenuEntries;\r
-\r
-    // Registry keys to save if a user wants to preserve config info during uninstall\r
-    char *pszRegKeysToPreserve;\r
-    int nPreserveConfigInfoMsgID;\r
-\r
-    // Uninstall func - used for things specific to this app\r
-    APP_UNINSTALL_FUNC *pUninstallFunc;\r
-};\r
-\r
-\r
-/*\r
- * App info structure for the Server product\r
- */\r
-struct APPINFO appServer = {\r
-    "AFS Server",\r
-    \r
-    AFSREG_SVR_SVC_NAME,\r
-    AFSREG_SVR_SVC_KEY,\r
-    0,  // No depend on\r
-    AFSREG_SVR_SVC_DISPLAYNAME_DATA,\r
-\r
-    0,  // No network provider order\r
-\r
-    IDS_MUST_STOP_SERVER,\r
-    IDS_WAITING_FOR_SERVER_TO_STOP,\r
-\r
-    AFSREG_SVR_SW_VERSION_KEY,\r
-    \r
-    { AFSREG_SVR_SW_VERSION_KEY, AFSREG_SVR_SW_VERSION_DIR_VALUE },\r
-\r
-    "\\Server",\r
-    "\\usr\\afs\\bin",\r
-\r
-    // Dirs to delete\r
-    TARGETDIR"\\Server\\usr\\afs\\bin\\backup\0"\r
-    TARGETDIR"\\Server\\usr\\afs\\bin\0"\r
-    TARGETDIR"\\Server\\usr\\afs\\db\0"\r
-    TARGETDIR"\\Server\\usr\\afs\\logs\0"\r
-    TARGETDIR"\\Server\\usr\\afs\\etc\0"\r
-    TARGETDIR"\\Server\\usr\\afs\\local\0"\r
-    TARGETDIR"\\Server\\usr\\afs\0"\r
-    TARGETDIR"\\Server\\usr\0",\r
-    \r
-    // Files to delete\r
-    TARGETDIR"\\Common\\*.gid\0"\r
-    TARGETDIR"\\Common\\*.fts\0",\r
-\r
-    0,  // No reg values\r
-    0,  // No NT only reg values\r
-    0,  // No 9x only reg values\r
-\r
-    "Server\0",\r
-\r
-    // Config info to preserve\r
-    AFSREG_SVR_SVC_KEY"\0", \r
-    IDS_PRESERVE_SERVER_CONFIG_INFO,\r
-\r
-    0   // No special uninstall function\r
-};\r
-\r
-// Registry values to remove for the Client\r
-struct REGVALUE clientRegValues[] = {\r
-    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", "{DC515C27-6CAC-11D1-BAE7-00C04FD140D2}" },\r
-    { 0, 0 }    // This indicates there are no more entries\r
-};\r
-\r
-struct REGVALUE clientWinNTRegValues[] = {\r
-    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\File Manager\\AddOns", "AFS Client FME" },\r
-    { 0, 0 }\r
-};\r
-\r
-struct REGVALUE clientWin9XRegValues[] = {\r
-    { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\NetworkProvider\\Order", "TransarcAFSDaemon" },\r
-    { 0, 0 }\r
-};\r
-\r
-/*\r
- * App info structure for the Client product\r
- */\r
-struct APPINFO appClient = {\r
-    "AFS Client",\r
-    \r
-    AFSREG_CLT_SVC_NAME,\r
-    AFSREG_CLT_SVC_KEY,\r
-    "5250435353004E657462696F730000",\r
-    AFSREG_CLT_SVC_DISPLAYNAME_DATA,\r
-\r
-    AFSREG_CLT_SVC_NAME,\r
-\r
-    IDS_MUST_STOP_CLIENT,\r
-    IDS_WAITING_FOR_CLIENT_TO_STOP,\r
-\r
-    AFSREG_CLT_SW_VERSION_KEY,\r
-\r
-    { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },\r
-\r
-    "\\Client",\r
-    "\\Program",\r
-\r
-    // No dirs to delete\r
-    0,\r
-    \r
-    // Files to delete\r
-    TARGETDIR"\\Common\\*.gid\0"\r
-    TARGETDIR"\\Common\\*.fts\0"\r
-    WINDIR"\\..\\AFSCache\0"\r
-    WINDIR"\\afsd.log\0"\r
-    WINDIR"\\afsd.ini\0"\r
-    WINDIR"\\afsdsbmt.ini\0"\r
-    WINDIR"\\afsd_init.log\0",\r
-    \r
-    clientRegValues,\r
-    clientWinNTRegValues,\r
-    clientWin9XRegValues,\r
-\r
-    // Start menu entries to remove\r
-    "Client\0",\r
-\r
-    // Config info to preserve\r
-    AFSREG_CLT_SVC_KEY"\0",\r
-    IDS_PRESERVE_CLIENT_CONFIG_INFO,\r
-\r
-    ClientSpecificUninstall\r
-};\r
-\r
-\r
-/*\r
- * App info structure for the Light Client product\r
- */\r
-struct APPINFO appLightClient = {\r
-    "AFS Light",\r
-    \r
-    // No service info \r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-\r
-    AFSREG_CLT_SVC_NAME,\r
-\r
-    // No service shutdown messages\r
-    0,\r
-    0,\r
-\r
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Light Client",\r
-\r
-    { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },\r
-\r
-    "\\Client",\r
-    "\\Program",\r
-\r
-    // No dirs to delete\r
-    0,\r
-    \r
-    // Files to delete\r
-    TARGETDIR"\\Common\\*.gid\0"\r
-    TARGETDIR"\\Common\\*.fts\0",\r
-\r
-    clientRegValues,\r
-    clientWinNTRegValues,\r
-    clientWin9XRegValues,\r
-\r
-    // Start menu entries to remove\r
-    "Light\0",\r
-\r
-    // Config info to preserve\r
-    AFSREG_CLT_SVC_KEY"\0",\r
-    IDS_PRESERVE_LIGHT_CLIENT_CONFIG_INFO,\r
-\r
-    UninstallCredsTool\r
-};\r
-\r
-\r
-/*\r
- * App info structure for the Control Center product\r
- */\r
-struct APPINFO appControlCenter = {\r
-    "AFS Control Center",\r
-    \r
-    // No service info\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-\r
-    // No network provider order\r
-    0,\r
-\r
-    // No service shutdown messages    \r
-    0,\r
-    0,\r
-\r
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center",\r
-    \r
-    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center\\CurrentVersion", "PathName" },\r
-\r
-    "\\Control Center",\r
-    "",\r
-\r
-    // No dirs to delete\r
-    0,\r
-    \r
-    // Files to delete\r
-    TARGETDIR"\\Common\\*.gid\0"\r
-    TARGETDIR"\\Common\\*.fts\0",\r
-    \r
-    0,  // No reg values\r
-    0,  // No NT only reg values\r
-    0,  // No 9x only reg values\r
-\r
-    // Start menu entries to remove\r
-    "Control Center\0",\r
-\r
-    // Config info to preserve\r
-    AFSREG_CLT_SVC_KEY"\0",\r
-    IDS_PRESERVE_CC_CONFIG_INFO,\r
-\r
-    0   // No uninstall function\r
-};\r
-\r
-\r
-/*\r
- * App info structure for the Sys Admin Doc files\r
- */\r
-struct APPINFO appDocs = {\r
-    "AFS Supplemental Documentation",\r
-\r
-    // No service info\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-\r
-    // No network provider order\r
-    0,\r
-\r
-    // No service shutdown messages    \r
-    0,\r
-    0,\r
-\r
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation",\r
-    \r
-    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation\\CurrentVersion", "PathName" },\r
-\r
-    "\\Documentation",\r
-    "",\r
-\r
-    // No dirs to delete\r
-    0,\r
-    \r
-    // Files to delete\r
-    TARGETDIR"\\Common\\*.gid\0"\r
-    TARGETDIR"\\Common\\*.fts\0",\r
-\r
-    0,  // No reg values\r
-    0,  // No NT only reg values\r
-    0,  // No 9x only reg values\r
-\r
-    // Start menu entries to remove\r
-    "Documentation\\AFS for Windows Backup Command Reference.lnk\0Documentation\\AFS Command Reference Manual.lnk\0Documentation\\AFS System Administrator's Guide.lnk\0",\r
-\r
-    0,  // No config info to preserve\r
-\r
-    0   // No uninstall function\r
-};\r
-\r
-\r
-// Shared and in-use files\r
-struct FILEINFO {\r
-    char *pszName;\r
-    int nUsedBy;\r
-};\r
-\r
-#define SERVER  1\r
-#define CLIENT  2\r
-#define LCLIENT 4\r
-#define CC      8\r
-#define DOCS    16\r
-\r
-\r
-struct FILEINFO fileInfo[] = {\r
-    { TARGETDIR"\\Common\\afsbosadmin.dll",             SERVER | CC },\r
-    { TARGETDIR"\\Common\\afscfgadmin.dll",             SERVER | CC },\r
-    { TARGETDIR"\\Common\\afsclientadmin.dll",          SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afskasadmin.dll",             SERVER | CC },\r
-    { TARGETDIR"\\Common\\afsptsadmin.dll",             SERVER | CC },\r
-    { TARGETDIR"\\Common\\afsvosadmin.dll",             SERVER | CC },\r
-    { TARGETDIR"\\Common\\afsadminutil.dll",            SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afsrpc.dll",                  SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afsauthent.dll",              SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\pthread.dll",                 SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\TaAfsAppLib.dll",             SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afsprocmgmt.dll",             SERVER | CLIENT | LCLIENT },\r
-    { TARGETDIR"\\Common\\afs_config.exe",              CLIENT | LCLIENT| CC },\r
-    { TARGETDIR"\\Common\\afseventmsg_????.dll",        SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afslegal_????.dll",           SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afsserver_????.dll",          SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afssvrcfg_????.dll",          SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\TaAfsAccountManager_????.dll",SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\TaAfsAppLib_????.dll",        SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\TaAfsServerManager_????.dll", SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afscreds_????.dll",           SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs_config_????.dll",         SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs_cpa_????.dll",            SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs_shl_ext_????.dll",        SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-nt.hlp",                  SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-nt.cnt",                  SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafssvrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafssvrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafsusrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafsusrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-cc.cnt",                  SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-cc.hlp",                  SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-light.cnt",               SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\afs-light.hlp",               SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafscfg.cnt",                SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Common\\taafscfg.hlp",                SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Client\\PROGRAM\\afs_shl_ext.dll",    CLIENT | LCLIENT },\r
-    { TARGETDIR"\\Client\\PROGRAM\\libafsconf.dll",     CLIENT | LCLIENT },\r
-    { TARGETDIR"\\Client\\PROGRAM\\afslogon.dll",       CLIENT },\r
-    { TARGETDIR"\\Client\\PROGRAM\\afslog95.dll",       LCLIENT },\r
-    { TARGETDIR"\\Control Center\\TaAfsAdmSvr.exe",     CC },\r
-    { WINSYSDIR"\\afs_cpa.cpl",                         CLIENT | LCLIENT | CC },\r
-    { WINSYSDIR"\\afsserver.cpl",                       SERVER },\r
-    { TARGETDIR"\\Common\\afsdcell.ini",                CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Documentation\\Html\\banner.gif",     SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\books.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\bot.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\index.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\index.htm",      SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\next.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\prev.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\toc.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\top.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },\r
-    { TARGETDIR"\\Documentation\\Html\\ReleaseNotes\\relnotes.htm",\r
-                                                        SERVER | CLIENT | LCLIENT | CC },\r
-    { TARGETDIR"\\Documentation\\Html\\InstallGd\\afsnt35i.htm",\r
-                                                        SERVER | CLIENT | LCLIENT | CC },\r
-    { 0,                                                0 }     // End of list\r
-};\r
-\r
-\r
-/*\r
- * VARIABLES _________________________________________________________________\r
- *\r
- */\r
-HINSTANCE hinst;\r
-\r
-static HWND hDlg;\r
-static BOOL bPreserveConfigInfo;\r
-static BOOL bSilentMode;\r
-static char *pszInstallDir;\r
-\r
-\r
-/*\r
- * FUNCTIONS _________________________________________________________________\r
- *\r
- */\r
-\r
-static BOOL UpgradeClientIntParm(HKEY hKey, char *pszOldParm, char *pszNewParm)\r
-{\r
-    int nData;\r
-    LONG result = ERROR_SUCCESS;\r
-\r
-    nData = GetPrivateProfileInt("AFS Client", pszOldParm, -1, "afsd.ini");\r
-    if (nData > -1)\r
-        result = RegSetValueEx(hKey, pszNewParm, 0, REG_DWORD, (BYTE *)&nData, sizeof(nData));\r
-\r
-    return (result == ERROR_SUCCESS);\r
-}\r
-\r
-static BOOL UpgradeClientStringParm(HKEY hKey, char *pszOldParm, char *pszNewParm)\r
-{\r
-    char szData[1024];\r
-    LONG result = ERROR_SUCCESS;\r
-\r
-    GetPrivateProfileString("AFS Client", pszOldParm, "", szData, sizeof(szData), "afsd.ini");\r
-    if (szData[0])\r
-        result = RegSetValueEx(hKey, pszNewParm, 0, REG_SZ, (PBYTE)szData, strlen(szData) + 1);\r
-\r
-    return (result == ERROR_SUCCESS);\r
-}\r
-\r
-int SUCALLCONV Upgrade34ClientConfigInfo()\r
-{\r
-    HKEY hKey;\r
-    LONG result;\r
-    int nData;\r
-    \r
-    result = RegOpenKeyAlt(AFSREG_NULL_KEY, AFSREG_CLT_SVC_PARAM_KEY, KEY_WRITE, TRUE, &hKey, 0);\r
-    if (result != ERROR_SUCCESS)\r
-        return -1;\r
-\r
-    UpgradeClientIntParm(hKey, "CacheSize", "CacheSize");\r
-    UpgradeClientIntParm(hKey, "Stats", "Stats");\r
-    UpgradeClientIntParm(hKey, "LogoffTokenTransfer", "LogoffTokenTransfer");\r
-    UpgradeClientIntParm(hKey, "LogoffTokenTransferTimeout", "LogoffTokenTransferTimeout");\r
-    UpgradeClientIntParm(hKey, "TrapOnPanic", "TrapOnPanic");\r
-    UpgradeClientIntParm(hKey, "TraceBufferSize", "TraceBufferSize");\r
-    UpgradeClientIntParm(hKey, "TraceOnShutdown", "TraceOnShutdown");\r
-    UpgradeClientIntParm(hKey, "ReportSessionStartups", "ReportSessionStartups");\r
-    \r
-    UpgradeClientStringParm(hKey, "MountRoot", "MountRoot");\r
-    UpgradeClientStringParm(hKey, "Cell", "Cell");\r
-\r
-    /* BlockSize to ChunkSize requires convertion */\r
-    nData = GetPrivateProfileInt("AFS Client", "BlockSize", -1, "afsd.ini");\r
-    if (nData > -1) {\r
-       DWORD chunkSize;\r
-       for (chunkSize = 0; (1 << chunkSize) < nData; chunkSize++);\r
-        (void) RegSetValueEx(hKey, "ChunkSize", 0, REG_DWORD, (BYTE *)&chunkSize, sizeof(chunkSize));\r
-    }\r
-\r
-    RegCloseKey(hKey);\r
-\r
-    return 0;\r
-}\r
-\r
-int SUCALLCONV Eradicate34Client()\r
-{\r
-    if (Client34Eradicate(TRUE) != ERROR_SUCCESS)\r
-        return -1;\r
-\r
-    return 0;\r
-}\r
-\r
-// This function was written a long time ago by Mike Comer for use by the \r
-// original DFS Client for NT install program.\r
-int SUCALLCONV CheckIfAdmin(void)\r
-{\r
-    HANDLE                  token = INVALID_HANDLE_VALUE;\r
-    PVOID                   buffer = 0;\r
-    DWORD                   bufLength;\r
-    DWORD                   realBufLength;\r
-    TOKEN_PRIMARY_GROUP     *pgroup;\r
-    TOKEN_GROUPS            *groups;\r
-    int                     result = -1;\r
-    DWORD                   groupCount;\r
-    LONG                    status;\r
-    PSID                    AdministratorSID = NULL;\r
-    SID_IDENTIFIER_AUTHORITY    authority = SECURITY_NT_AUTHORITY;\r
-\r
-    // allocate the SID for the Administrators group\r
-    if (!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorSID)) {\r
-        status = GetLastError();\r
-        goto getout;\r
-    }\r
-\r
-    // open the process token\r
-    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token)) {\r
-        status = GetLastError();\r
-        token = INVALID_HANDLE_VALUE;\r
-        goto getout;\r
-    }\r
-\r
-    // check primary group first\r
-    buffer = GlobalAlloc(GMEM_FIXED, sizeof(TOKEN_PRIMARY_GROUP));\r
-    if (!buffer) {\r
-        goto getout;\r
-    }\r
-\r
-    bufLength = sizeof(TOKEN_PRIMARY_GROUP);\r
-    while(1) {\r
-        if (!GetTokenInformation(token, TokenPrimaryGroup, buffer, bufLength, &realBufLength)) {\r
-            if (realBufLength > bufLength) {\r
-                // not enough space\r
-                GlobalFree(buffer);\r
-                bufLength = realBufLength;\r
-                buffer = GlobalAlloc(GMEM_FIXED, realBufLength);\r
-                if (!buffer) {\r
-                    goto getout;\r
-                }\r
-                continue;\r
-            }\r
-\r
-            goto getout;\r
-        }\r
-        break;\r
-    }\r
-\r
-    pgroup = (TOKEN_PRIMARY_GROUP *)buffer;\r
-    if (EqualSid(pgroup->PrimaryGroup, AdministratorSID)) {\r
-        result = 0;\r
-    } else {\r
-        // okay, try the secondary groups\r
-        while(1) {\r
-            if (!GetTokenInformation(token, TokenGroups, buffer, bufLength, &realBufLength)) {\r
-                if (realBufLength > bufLength) {\r
-                    // not enough space\r
-                    GlobalFree(buffer);\r
-                    bufLength = realBufLength;\r
-                    buffer = GlobalAlloc(GMEM_FIXED, realBufLength);\r
-                    if (!buffer) {\r
-                        goto getout;\r
-                    }\r
-                    continue;\r
-                }\r
-\r
-                // a real error\r
-                goto getout;\r
-            }\r
-            break;\r
-        }\r
-\r
-        // we have the list of groups here.  Process them:\r
-        groups = (TOKEN_GROUPS *)buffer;\r
-        for(groupCount = 0; groupCount < groups->GroupCount; groupCount++) {\r
-            if (EqualSid(groups->Groups[groupCount].Sid, AdministratorSID)) {\r
-                result = 0;\r
-                break;\r
-            }\r
-        }\r
-    }\r
-\r
-getout:\r
-\r
-    if (token != INVALID_HANDLE_VALUE) {\r
-        CloseHandle(token);\r
-    }\r
-\r
-    if (buffer) {\r
-        GlobalFree(buffer);\r
-    }\r
-\r
-    if (AdministratorSID) {\r
-        FreeSid(AdministratorSID);\r
-    }\r
-\r
-    return result;\r
-}\r
-\r
-static void SetSharedFileRefCount(char *pszFile, int nRefCount)\r
-{\r
-    LONG result;\r
-    HKEY hKey;\r
-    \r
-    result = RegOpenKeyAlt(AFSREG_NULL_KEY, MS_SHARED_FILES_KEY, KEY_WRITE, FALSE, &hKey, 0);\r
-    if (result != ERROR_SUCCESS)\r
-        return;\r
-    \r
-    if (nRefCount <= 0)\r
-        RegDeleteValue(hKey, pszFile);\r
-    else\r
-        RegSetValueEx(hKey, pszFile, 0, REG_DWORD, (BYTE *)&nRefCount, sizeof(int));    \r
-    \r
-    RegCloseKey(hKey);\r
-}\r
-\r
-static char *GetTimeStamp()\r
-{\r
-    char szTime[64], szDate[64];\r
-    static char szTimeDate[128];\r
-\r
-    _strtime(szTime);\r
-    _strdate(szDate);\r
-\r
-    sprintf(szTimeDate, "[%s %s] ", szTime, szDate);\r
-    \r
-    return szTimeDate;\r
-}\r
-\r
-int SUCALLCONV WriteToInstallErrorLog(char *pszMsg)\r
-{\r
-    static BOOL bWritten = FALSE;\r
-    FILE *fp;\r
-\r
-    // On the first write, recreate the file    \r
-    fp = fopen(INSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");\r
-    if (!fp)\r
-        return -1;\r
-        \r
-    fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);\r
-    \r
-    fclose(fp);\r
-    \r
-    bWritten = TRUE;\r
-    \r
-    return 0;\r
-}\r
-\r
-static void WriteToUninstallErrorLog(char *pszMsg)\r
-{\r
-    static BOOL bWritten = FALSE;\r
-    FILE *fp;\r
-\r
-    // On the first write, recreate the file    \r
-    fp = fopen(UNINSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");\r
-    if (!fp)\r
-        return;\r
-        \r
-    fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);\r
-    \r
-    fclose(fp);\r
-    \r
-    bWritten = TRUE;\r
-}\r
-\r
-static char *LoadResString(UINT uID)\r
-{\r
-    static char str[256];\r
-    GetString (str, uID);\r
-    return str;\r
-}\r
-\r
-static void ShowError(UINT nResID, LONG nError)\r
-{\r
-    char szErr[256];\r
-    char szPrompt[256];\r
-    char szMsg[256];\r
-    char szTitle[256];\r
-    char *psz;\r
-\r
-    psz = LoadResString(nResID);\r
-    if (psz)\r
-        strcpy(szErr, psz);\r
-    else\r
-        sprintf(szErr, "unknown error msg (Msg ID = %d)", nResID);\r
-    \r
-    psz = LoadResString(IDS_INSTALLATION_FAILURE);\r
-    strcpy(szPrompt, psz ? psz : "An error has occurred:  %s (Last Error = %ld).");\r
-\r
-    sprintf(szMsg, szPrompt, szErr, nError);\r
-\r
-    psz = LoadResString(IDS_TITLE);\r
-    strcpy(szTitle, psz ? psz : "AFS");\r
-\r
-    if (bSilentMode)\r
-        WriteToUninstallErrorLog(szMsg);\r
-    else\r
-        MessageBox(hDlg, szMsg, szTitle, MB_OK);\r
-}\r
-\r
-static int ShowMsg(UINT nResID, int nType)\r
-{\r
-    char szTitle[256];\r
-    char *psz;\r
-\r
-    psz = LoadResString(IDS_TITLE);\r
-    strcpy(szTitle, psz ? psz : "AFS");\r
-\r
-    return MessageBox(hDlg, LoadResString(nResID), szTitle, nType);\r
-}\r
-\r
-static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered)\r
-{\r
-    HKEY hKey;\r
-    LONG nResult;\r
-    DWORD dwType;\r
-    static char szInstallDir[256];\r
-    DWORD dwSize;\r
-    char *pszKey;\r
-    char *pszValue;\r
-\r
-    pszKey = bRemembered ? UNINSTALL_TEMP_INFO_KEY : pApp->regInstallDir.pszKey;\r
-    pszValue = bRemembered ? INSTALL_DIR_VALUE_NAME : pApp->regInstallDir.pszValue;\r
-\r
-    dwSize = sizeof(szInstallDir);\r
-\r
-    nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);\r
-    if (nResult == ERROR_SUCCESS) {\r
-        nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szInstallDir, &dwSize);\r
-        RegCloseKey(hKey);\r
-    }\r
-\r
-    if (nResult != ERROR_SUCCESS) {\r
-        ShowError(IDS_CANT_DETERMINE_APP_PATH, nResult);\r
-        return 0;\r
-    }\r
-\r
-    FilepathNormalizeEx(szInstallDir, FPN_BACK_SLASHES);\r
-\r
-    return szInstallDir;\r
-}\r
-\r
-static BOOL DoesRegKeyExist(char *pszKey)\r
-{\r
-    HKEY hKey;\r
-    LONG nResult;\r
-\r
-    nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);\r
-    if (nResult == ERROR_SUCCESS) {\r
-        RegCloseKey(hKey);\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-static BOOL IsAppInstalled(struct APPINFO *pApp)\r
-{\r
-    return DoesRegKeyExist(pApp->pszAppKey);\r
-}\r
-\r
-static void BuildShortPath(char *pszShortPath, UINT nShortPathLen, char *pszInstallDir, char *pszPath)\r
-{\r
-    strncpy(pszShortPath, pszInstallDir, nShortPathLen);\r
-    strncat(pszShortPath, pszPath, nShortPathLen);\r
-    \r
-    GetShortPathName(pszShortPath, pszShortPath, nShortPathLen);\r
-}\r
-\r
-static BOOL IsWin95()\r
-{\r
-    OSVERSIONINFO versionInformation;\r
-\r
-    versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
-    \r
-    GetVersionEx(&versionInformation);\r
-    \r
-    if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&\r
-        (versionInformation.dwMinorVersion == 0))\r
-        return TRUE;\r
-        \r
-    return FALSE;\r
-}\r
-\r
-int IsWin98()\r
-{\r
-    OSVERSIONINFO versionInformation;\r
-\r
-    versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
-    \r
-    GetVersionEx(&versionInformation);\r
-    \r
-    if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&\r
-        (versionInformation.dwMinorVersion == 10))\r
-        return 0;\r
-        \r
-    return -1;\r
-}\r
-\r
-static BOOL IsServiceInstalled(char *pszServiceKey)\r
-{\r
-    HKEY hKey;\r
-\r
-    if (RegOpenKeyAlt(0, pszServiceKey, KEY_READ, FALSE, &hKey, 0) == ERROR_SUCCESS) {\r
-        RegCloseKey(hKey);\r
-        return TRUE;\r
-    }\r
-\r
-    return FALSE;\r
-}\r
-\r
-// If this fails in anyway we just return.  No error is displayed.\r
-static void MakeSureServiceDoesNotExist(char *pszName)\r
-{\r
-    SC_HANDLE hServer = 0, hSCM = 0;\r
-    SERVICE_STATUS status;\r
-\r
-    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);\r
-    if (hSCM) {\r
-        hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS | DELETE);\r
-        if (hServer) {\r
-            if (QueryServiceStatus(hServer, &status)) {\r
-                if (status.dwCurrentState != SERVICE_STOPPED) {\r
-                    if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {\r
-                        CloseServiceHandle(hServer);\r
-                        CloseServiceHandle(hSCM);\r
-                        return;\r
-                    }\r
-                }\r
-            }\r
-            \r
-            // Try to delete even if status query fails\r
-            DeleteService(hServer);\r
-        }\r
-    }\r
-\r
-    if (hServer)\r
-        CloseServiceHandle(hServer);\r
-    if (hSCM)\r
-        CloseServiceHandle(hSCM);\r
-}\r
-\r
-static int InstallService(char *pszName, char *pszDependOn, char *pszDisplayName, char *pszServicePath, BOOL bInteractive)\r
-{\r
-    SC_HANDLE hServer = 0, hSCM;\r
-    BOOL bRestoreOldConfig = FALSE;\r
-\r
-    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);\r
-    if (!hSCM) {\r
-        ShowError(IDS_SCM_OPEN_FAILED, GetLastError());\r
-        return -1;\r
-    }\r
-\r
-/*  This code is not used, but it could be handy in the future so I am keeping it here.\r
-\r
-    // If the service exists, then we (most probably) are in the middle of an upgrade or reinstall.\r
-    bRestoreOldConfig = IsServiceInstalled(pszName);\r
-\r
-    if (bRestoreOldConfig) {\r
-        hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS);\r
-        if (!hServer || !ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
-            ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, GetLastError());\r
-            bRestoreOldConfig = FALSE;\r
-            // Fall through to service creation below\r
-        }\r
-    } \r
-*/\r
-    \r
-    if (!bRestoreOldConfig) {\r
-        DWORD dwServiceType;\r
-\r
-        // If the service already exists, the create call will fail.  This can\r
-        // happen if uninstall failed (which is not infrequent).  Making sure the\r
-        // service does not exist makes it easier for a user to install over top of\r
-        // a previously failed uninstall.\r
-        MakeSureServiceDoesNotExist(pszName);\r
-\r
-        dwServiceType = SERVICE_WIN32_OWN_PROCESS;\r
-        if (bInteractive)\r
-            dwServiceType |= SERVICE_INTERACTIVE_PROCESS;\r
-    \r
-        hServer = CreateService(hSCM, pszName, pszDisplayName,\r
-            SERVICE_ALL_ACCESS, dwServiceType, SERVICE_AUTO_START, \r
-            SERVICE_ERROR_NORMAL, pszServicePath, 0, 0, "RPCSS\0Netbios\0\0", 0, 0);\r
-    \r
-        if (!hServer)\r
-            ShowError(IDS_SERVICE_CREATE_FAILED, GetLastError());\r
-    }\r
-\r
-    if (hServer)\r
-        CloseServiceHandle(hServer);\r
-\r
-    CloseServiceHandle(hSCM);\r
-\r
-    return 0;\r
-}\r
-\r
-int SUCALLCONV InstallServerService(char *pszServicePath)\r
-{\r
-    return InstallService(appServer.pszSvcName, 0, appServer.pszSvcDisplayName, pszServicePath, TRUE);\r
-}\r
-\r
-int SUCALLCONV InstallClientService(char *pszServicePath)\r
-{\r
-    return InstallService(appClient.pszSvcName, appClient.pszSvcDependOn, appClient.pszSvcDisplayName, pszServicePath, FALSE);\r
-}\r
-\r
-static int UninstallService(struct APPINFO *pAppInfo)\r
-{\r
-    SC_HANDLE hServer, hSCM;\r
-    SERVICE_STATUS status;\r
-    BOOL bOk;\r
-    BOOL bServer = FALSE;\r
-    BOOL bShowingProgressDlg = FALSE;\r
-\r
-    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);\r
-    if (!hSCM) {\r
-        ShowError(IDS_SCM_OPEN_FAILED, GetLastError());\r
-        return -1;\r
-    }\r
-    \r
-    hServer = OpenService(hSCM, pAppInfo->pszSvcName, SERVICE_ALL_ACCESS | DELETE);\r
-    if (!hServer) {\r
-        ShowError(IDS_SERVICE_OPEN_FAILED, GetLastError());\r
-        CloseServiceHandle(hSCM);\r
-        return -1;\r
-    }\r
-\r
-    if (!QueryServiceStatus(hServer, &status)) {\r
-        ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());\r
-        CloseServiceHandle(hServer);\r
-        CloseServiceHandle(hSCM);\r
-        return -1;\r
-    }\r
-\r
-    if (status.dwCurrentState != SERVICE_STOPPED) {\r
-        if (pAppInfo->nServiceShutdownMsgID) {\r
-            if (!bSilentMode && (ShowMsg(pAppInfo->nServiceShutdownMsgID, MB_YESNO | MB_ICONQUESTION) == IDNO)) {\r
-                CloseServiceHandle(hServer);\r
-                CloseServiceHandle(hSCM);\r
-                return 1;\r
-            }\r
-        }\r
-\r
-        if (!bSilentMode)\r
-            bShowingProgressDlg = ShowProgressDialog(LoadResString(pAppInfo->nServiceShutdownProgressMsgID));\r
-\r
-        if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {\r
-            if (bShowingProgressDlg)\r
-                HideProgressDialog();\r
-            ShowError(IDS_SERVICE_STOP_FAILED, GetLastError());\r
-            CloseServiceHandle(hServer);\r
-            CloseServiceHandle(hSCM);\r
-            return -1;\r
-        }\r
-    }\r
-\r
-    // Wait for the service to stop\r
-    while (status.dwCurrentState != SERVICE_STOPPED) {\r
-        // I stopped waiting on dwWaitHint because it seemed the wait hint was too long.\r
-        // The service would be stopped but we'd still be asleep for a long time yet.\r
-        Sleep(5000);    //status.dwWaitHint);\r
-\r
-        if (!QueryServiceStatus(hServer, &status)) {\r
-            if (bShowingProgressDlg)\r
-                HideProgressDialog();\r
-            ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());\r
-            CloseServiceHandle(hServer);\r
-            CloseServiceHandle(hSCM);\r
-            return -1;\r
-        }\r
-    }\r
-\r
-    // The service has been stopped\r
-    if (bShowingProgressDlg)\r
-        HideProgressDialog();\r
-\r
-    // This code to disable the service may be of use some day so I am keeping it here.\r
-    // bOk = ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0); \r
-\r
-    bOk = DeleteService(hServer);\r
-\r
-    if (!bOk)\r
-        ShowError(IDS_SERVICE_DELETE_FAILED, GetLastError());\r
-\r
-    CloseServiceHandle(hServer);\r
-    CloseServiceHandle(hSCM);\r
-\r
-    return (bOk ? 0 : -1);\r
-}\r
-\r
-int SUCALLCONV AddToNetworkProviderOrder(char *pszWhatToAdd)\r
-{\r
-    return AddToProviderOrder(pszWhatToAdd) ? 0 : -1;\r
-}\r
-\r
-static int RemoveFromNetworkProviderOrder(char *pszWhatToDel)\r
-{\r
-    return RemoveFromProviderOrder(pszWhatToDel) ? 0 : -1;\r
-}\r
-\r
-int SUCALLCONV AddToPath(char *pszPath)\r
-{\r
-    return AddToSystemPath(pszPath) ? 0 : -1;\r
-}\r
-\r
-static int RemoveFromPath(char *pszPath)\r
-{\r
-    return RemoveFromSystemPath(pszPath) ? 0 : -1;\r
-}\r
-\r
-static void RemoveFiles(char *pszFileSpec)\r
-{\r
-    struct _finddata_t fileinfo;\r
-    long hSearch;\r
-    char szDel[MAX_PATH];\r
-    char szDir[MAX_PATH];\r
-    char *p;\r
-\r
-    strcpy(szDir, pszFileSpec);\r
-    p = strrchr(szDir, '\\');\r
-    if (p)\r
-        *p = 0;\r
-    \r
-    hSearch = _findfirst(pszFileSpec, &fileinfo);\r
-    if (hSearch == -1)\r
-        return;\r
-\r
-    while (1) {\r
-        if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {\r
-            sprintf(szDel, "%s\\%s", szDir, fileinfo.name);\r
-            DeleteFile(szDel);\r
-        }\r
-\r
-        if (_findnext(hSearch, &fileinfo) == -1)\r
-            break;\r
-    }\r
-\r
-    _findclose(hSearch);\r
-}\r
-\r
-static void RemoveDir(char *pszDir)\r
-{\r
-    char szFileSpec[MAX_PATH];\r
-\r
-    sprintf(szFileSpec, "%s\\*.*", pszDir);\r
-\r
-    RemoveFiles(szFileSpec);\r
-    RemoveDirectory(pszDir);\r
-}\r
-\r
-static void RemoveRegValues(struct REGVALUE *pRegValues)\r
-{\r
-    struct REGVALUE *pCurValue;\r
-    HKEY hKey;\r
-    LONG nResult;\r
-\r
-    if (!pRegValues)\r
-        return;\r
-\r
-    for (pCurValue = pRegValues; pCurValue->pszKey; pCurValue++) {\r
-        nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pCurValue->pszKey, KEY_ALL_ACCESS, FALSE, &hKey, 0);\r
-\r
-        if (nResult == ERROR_SUCCESS) {\r
-            nResult = RegDeleteValue(hKey, pCurValue->pszValue);\r
-            RegCloseKey(hKey);\r
-        }\r
-\r
-        if (nResult != ERROR_SUCCESS)\r
-            ShowError(IDS_REG_DELETE_VALUE_ERROR, nResult);\r
-    }\r
-}\r
-\r
-static BOOL UninstallCredsTool()\r
-{\r
-    int nResult = WinExec("afscreds /uninstall", SW_HIDE);\r
-\r
-    if (nResult <= 31) {\r
-        if (nResult != ERROR_FILE_NOT_FOUND)\r
-            ShowError(IDS_CANT_UNINSTALL_AFSCREDS, nResult);\r
-    }\r
-\r
-    // Always return true.  We don't want the uninstall to completely fail just\r
-    // because the creds tool didn't uninstall.\r
-    return TRUE;\r
-}\r
-\r
-\r
-static char *GetTempDir()\r
-{\r
-    DWORD result;\r
-    static char szTempDir[MAX_PATH];\r
-\r
-    result = GetTempPath(sizeof(szTempDir) - 1, szTempDir);\r
-    if (result == 0)\r
-        return "\\";\r
-        \r
-    return szTempDir;\r
-}\r
-\r
-static char *GetRootInstallDir()\r
-{\r
-    char *psz;\r
-    static char szRootInstallDir[MAX_PATH] = "";\r
-\r
-    if (szRootInstallDir[0] == 0) {\r
-        strcpy(szRootInstallDir, pszInstallDir);\r
-    \r
-        // Strip off the app specific part of the install dir\r
-        psz = strrchr(szRootInstallDir, '\\');\r
-        if (psz)\r
-            *psz = 0;\r
-    }\r
-\r
-    return szRootInstallDir;\r
-}\r
-\r
-static BOOL ClientSpecificUninstall()\r
-{\r
-    int nChoice;\r
-\r
-    // This function needs to do two things.  First it needs to see if the server is\r
-    // installed, and if it is, ask the user if they really want to uninstall the\r
-    // client given that the server needs the client.  Second, if we are uninstalling\r
-    // the client, we need to uninstall the creds tool.\r
-\r
-    if (!bSilentMode) {\r
-        if (IsAppInstalled(&appServer)) {\r
-            nChoice = ShowMsg(IDS_CLIENT_NEEDED_BY_SERVER, MB_ICONQUESTION | MB_YESNO);\r
-            if (nChoice == IDNO)\r
-                return FALSE;       // Cancel the uninstall\r
-        }\r
-    }\r
-    \r
-    UninstallCredsTool();\r
-\r
-    return TRUE;\r
-}\r
-\r
-static struct APPINFO *GetApp()\r
-{\r
-#ifdef SERVER_UNINST\r
-    return &appServer;\r
-#elif CLIENT_UNINST\r
-    return &appClient;\r
-#elif CC_UNINST\r
-    return &appControlCenter;\r
-#elif LIGHT_CLIENT_UNINST\r
-    return &appLightClient;\r
-#elif DOCS_UNINST\r
-    return &appDocs;\r
-#else\r
-    return 0;\r
-#endif;\r
-}\r
-\r
-static void RememberInstallDir(char *pszInstallDir)\r
-{\r
-    HKEY hKey;\r
-\r
-    // We remember the install dir so that when the UninstUninitialize function is called\r
-    // by the InstallShield uninstaller, we can find out where we were installed to.  We\r
-    // have to do this because by the time that function is called, the registry values\r
-    // created at install time are already gone.  We need to be able to find out where we\r
-    // were installed so we can clean up anything IS couldn't uninstall.  If this fails in \r
-    // any way then we don't care.  The only consequence is that some junk might be left on\r
-    // the users' system after an uninstall.\r
-    \r
-    LONG result = RegOpenKeyAlt(AFSREG_NULL_KEY, UNINSTALL_TEMP_INFO_KEY, KEY_WRITE, TRUE, &hKey, 0);\r
-    if (result != ERROR_SUCCESS)\r
-        return;\r
-\r
-    RegSetValueEx(hKey, INSTALL_DIR_VALUE_NAME, 0, REG_SZ, (PBYTE)pszInstallDir, strlen(pszInstallDir) + 1);    \r
-\r
-    RegCloseKey(hKey);\r
-}\r
-\r
-int SUCALLCONV SetSilentMode()\r
-{\r
-    bSilentMode = TRUE;\r
-\r
-    return 0;\r
-}\r
-\r
-static char *GetWinDir()\r
-{\r
-    static char szWinDir[MAX_PATH] = "";\r
-\r
-    if (!szWinDir[0]) \r
-        GetWindowsDirectory(szWinDir, sizeof(szWinDir));\r
-    \r
-    return szWinDir;\r
-}\r
-\r
-static char *GetWinSysDir()\r
-{\r
-    static char szWinSysDir[MAX_PATH] = "";\r
-\r
-    if (!szWinSysDir[0])\r
-        GetSystemDirectory(szWinSysDir, sizeof(szWinSysDir));\r
-    \r
-    return szWinSysDir;\r
-} \r
-\r
-static char *GetLocaleID()\r
-{\r
-    static char szID[25] = "";\r
-\r
-    if (szID[0] == 0) {\r
-        LCID dwID = GetSystemDefaultLCID();\r
-        \r
-         // Nuke the high word.  It contains a sort ID.\r
-        dwID &= 0x0000FFFF;\r
-        \r
-        // Convert locale ID to a string\r
-        itoa(dwID, szID, 10);\r
-\r
-        // This thing should never be more than LOCALE_ID_LEN characters long.\r
-        szID[LOCALE_ID_LEN] = 0;\r
-    }\r
-\r
-    return szID;\r
-}\r
-\r
-static char *ExpandPath(char *pszFile)\r
-{\r
-    static char szPath[MAX_PATH];\r
-    char *psz;\r
-\r
-    szPath[0] = 0;\r
-\r
-    // Convert a path containing TARGETDIR, WINDIR, or WINSYSDIR to a \r
-    // real path in the file system.  One of these MUST be the start of\r
-    // the file path passed in.  Also convert the string ???? to an\r
-    // actual locale number.\r
-    if (strncmp(pszFile, TARGETDIR, strlen(TARGETDIR)) == 0)\r
-        strcpy(szPath, GetRootInstallDir());\r
-    else if (strncmp(pszFile, WINDIR, strlen(WINDIR)) == 0)\r
-        strcpy(szPath, GetWinDir());\r
-    else if (strncmp(pszFile, WINSYSDIR, strlen(WINSYSDIR)) == 0)\r
-        strcpy(szPath, GetWinSysDir());\r
-    \r
-    if (szPath[0]) {    \r
-        psz = strchr(pszFile, '\\');\r
-        if (psz)\r
-            strcat(szPath, psz);\r
-    } else\r
-        strcpy(szPath, pszFile);\r
-\r
-    // Is this a language dll?\r
-    psz = strstr(szPath, "????.");\r
-    \r
-    // If it is, replace ???? with the locale number\r
-    if (psz)\r
-        strncpy(psz, GetLocaleID(), LOCALE_ID_LEN);\r
-\r
-    return szPath;\r
-}\r
-\r
-static BOOL FileNeededByOtherApp(struct APPINFO *pApp, struct FILEINFO *pFileInfo)\r
-{\r
-    // If the file is used by the server, the app being uninstalled is not the server, and\r
-    // the server is installed, then this file is used by another app.\r
-    if (!IsWinNT()) {\r
-        if ((pFileInfo->nUsedBy & LCLIENT) && (pApp != &appLightClient) && IsAppInstalled(&appLightClient))\r
-            return TRUE;\r
-        return FALSE;\r
-    }\r
-\r
-    if ((pFileInfo->nUsedBy & SERVER) && (pApp != &appServer) && IsAppInstalled(&appServer))\r
-        return TRUE;\r
-\r
-    if ((pFileInfo->nUsedBy & CLIENT) && (pApp != &appClient) && IsAppInstalled(&appClient))\r
-        return TRUE;\r
-\r
-    if ((pFileInfo->nUsedBy & CC) && (pApp != &appControlCenter) && IsAppInstalled(&appControlCenter))\r
-        return TRUE;\r
-    \r
-    return FALSE;\r
-}\r
-\r
-static void DeleteInUseFiles(struct APPINFO *pAppInfo, struct FILEINFO *pFileInfo)\r
-{\r
-    char szSrcPath[MAX_PATH];\r
-    char szDestPath[MAX_PATH];\r
-    char szTempDir[MAX_PATH];\r
-    int ii;\r
-\r
-    // If some app's file has been loaded before the app is uninstalled, then\r
-    // when an uninstall is attempted, the application and all of the dlls that\r
-    // its uses will be in use and IS will not be able to delete them.  Normally this\r
-    // is not a problem because IS will tell the user to reboot to finish the uninstall.\r
-    // However, we must support the ability to perform a silent uninstall followed\r
-    // immediatly by an install of the same product to the same directories.  If we let\r
-    // IS handle the uninstall of these files, this is not possible.  The reason is that\r
-    // when IS fails to remove these in use files, it marks them for deletion after the\r
-    // next reboot, which is fine.  Unfortunately, it leaves them in the dirs they were\r
-    // installed to.  So if we don't immediately reboot and perform an install to the\r
-    // same dirs, once a reboot is performed, those files get deleted and we have a \r
-    // broken installation.\r
-\r
-    // What we will do to fix all of this, is when the client is uninstalled, but\r
-    // before IS does anything, we will move the in use files and associated dlls\r
-    // into the temp dir and mark them for delete after a reboot.  Then an install\r
-    // that follows will succeed.\r
-\r
-    // Delete the files that may be in use.  If they are we actually move\r
-    // them to the temp dir and mark them for deletion after the next reboot.\r
-    for (ii = 0; pFileInfo[ii].pszName != 0; ii++) {\r
-        // Get the source path\r
-        strcpy(szSrcPath, ExpandPath(pFileInfo[ii].pszName));\r
-\r
-        // Only delete the file if it is not used by some other app\r
-        if (FileNeededByOtherApp(pAppInfo, &pFileInfo[ii]))\r
-            continue;\r
-\r
-        // If the file doesn't exist then go on to the next file.\r
-        if (_access(szSrcPath, 0) != 0)\r
-            continue;\r
-            \r
-        // See if we can do a regular delete of the file\r
-        if (DeleteFile(szSrcPath)) {\r
-            SetSharedFileRefCount(szSrcPath, 0);\r
-            continue;\r
-        }\r
-\r
-        // Get a temp dir that is on the same drive as the src path.\r
-        // We can't move an in use file to a different drive.\r
-        strcpy(szTempDir, GetTempDir());\r
-        if (szTempDir[0] != szSrcPath[0]) {\r
-            // Get the drive, colon, and slash of the src path\r
-            strncpy(szTempDir, szSrcPath, 3);\r
-            szTempDir[3] = 0;\r
-        }\r
-        \r
-        // Get the dest path - we will rename the file during the move\r
-        GetTempFileName(szTempDir, "AFS", 0, szDestPath);\r
-\r
-        // Move from source to dest, marking the file for deletion after a reboot\r
-        if (IsWin95()) {\r
-            if (MoveFile(szSrcPath, szDestPath)) {            \r
-                WritePrivateProfileString("rename", szSrcPath, szDestPath, "wininit.ini");\r
-                SetSharedFileRefCount(szSrcPath, 0);\r
-            }\r
-        } else {    // WinNT or Win98\r
-            if (MoveFileEx(szSrcPath, szDestPath, MOVEFILE_REPLACE_EXISTING)) {\r
-                SetFileAttributes(szDestPath, FILE_ATTRIBUTE_NORMAL);\r
-                MoveFileEx(szDestPath, 0, MOVEFILE_DELAY_UNTIL_REBOOT);\r
-                SetSharedFileRefCount(szSrcPath, 0);\r
-            }\r
-        }\r
-    }\r
-}\r
-\r
-// Delete a directory and all its files and subdirectories - Yee haaa!\r
-static void RemoveDirectoryTree(char *pszDir)\r
-{\r
-    HANDLE hFind;\r
-    WIN32_FIND_DATA findFileData;\r
-    char szSpec[MAX_PATH];\r
-    char szSubFileOrDir[MAX_PATH];\r
-    BOOL bContinue;\r
-\r
-    sprintf(szSpec, "%s\\*.*", pszDir);\r
-    \r
-    // First delete the contents of the dir\r
-    hFind = FindFirstFile(szSpec, &findFileData);\r
-    bContinue = (hFind != INVALID_HANDLE_VALUE);\r
-    \r
-    while (bContinue) {\r
-        if ((strcmp(findFileData.cFileName, ".") != 0) && (strcmp(findFileData.cFileName, "..") != 0)) {\r
-            sprintf(szSubFileOrDir, "%s\\%s", pszDir, findFileData.cFileName);\r
-            \r
-            if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)\r
-                RemoveDirectoryTree(szSubFileOrDir);\r
-            else\r
-                DeleteFile(szSubFileOrDir);\r
-        }\r
-\r
-        bContinue = FindNextFile(hFind, &findFileData);\r
-    }\r
-\r
-    FindClose(hFind);\r
-        \r
-    // Now remove the dir\r
-    RemoveDirectory(pszDir);\r
-} \r
-\r
-static char *GetStartMenuRoot()\r
-{\r
-    HKEY hKey;\r
-    LONG nResult;\r
-    DWORD dwType;\r
-    DWORD dwSize;\r
-    char *pszKey;\r
-    char *pszValue;\r
-\r
-    static char szStartMenuRoot[MAX_PATH] = "";\r
-\r
-    if (szStartMenuRoot[0] == 0) {\r
-        dwSize = sizeof(szStartMenuRoot);\r
-    \r
-        if (IsWinNT()) {\r
-            pszKey = WINNT_START_MENU_REG_KEY;\r
-            pszValue = WINNT_START_MENU_REG_VALUE;\r
-        } else {\r
-            pszKey = WIN9X_START_MENU_REG_KEY;\r
-            pszValue = WIN9X_START_MENU_REG_VALUE;\r
-        }\r
-        \r
-        nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);\r
-        if (nResult == ERROR_SUCCESS) {\r
-            nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szStartMenuRoot, &dwSize);\r
-            RegCloseKey(hKey);\r
-        }\r
-\r
-        if (nResult != ERROR_SUCCESS)\r
-            return 0;\r
-    }\r
-\r
-    FilepathNormalizeEx(szStartMenuRoot, FPN_BACK_SLASHES);\r
-\r
-    return szStartMenuRoot;\r
-}\r
-\r
-static char *GetAfsStartMenuRoot()\r
-{\r
-    static char szAfsStartMenuRoot[MAX_PATH] = "";\r
-    char *pszStartMenuRoot;\r
-    \r
-    if (szAfsStartMenuRoot[0] == 0) {    \r
-        pszStartMenuRoot = GetStartMenuRoot();\r
-        if (!pszStartMenuRoot)\r
-            return 0;\r
-\r
-        if (bSilentMode)\r
-            sprintf(szAfsStartMenuRoot, "%s\\IBM WebSphere\\Performance Pack\\AFS", pszStartMenuRoot );\r
-        else\r
-            sprintf(szAfsStartMenuRoot, "%s\\IBM AFS", pszStartMenuRoot );\r
-    }\r
-\r
-    return szAfsStartMenuRoot;\r
-}\r
-\r
-static BOOL IsADir(char *pszName)\r
-{\r
-    struct _stat statbuf;\r
-\r
-    if (_stat(pszName, &statbuf) < 0)\r
-        return FALSE;\r
-\r
-    return statbuf.st_mode & _S_IFDIR;\r
-}\r
-\r
-static void DeleteStartMenuEntries(char *pszEntries)\r
-{\r
-    char szStartMenuPath[MAX_PATH];\r
-    char *pszAfsStartMenuRoot;\r
-    char *pszCurEntry;\r
-\r
-    pszAfsStartMenuRoot = GetAfsStartMenuRoot();\r
-\r
-    if (!pszAfsStartMenuRoot)\r
-        return;\r
-        \r
-    for (pszCurEntry = pszEntries; *pszCurEntry; pszCurEntry += strlen(pszCurEntry) + 1) {\r
-        sprintf(szStartMenuPath, "%s\\%s", pszAfsStartMenuRoot, pszCurEntry);\r
-        if (IsADir(szStartMenuPath))\r
-            RemoveDirectoryTree(szStartMenuPath);\r
-        else\r
-            DeleteFile(szStartMenuPath);\r
-    }\r
-}\r
-\r
-static void RefreshStartMenu()\r
-{\r
-    char *pszAfsStartMenuRoot;\r
-    char szTemp[MAX_PATH];\r
-    \r
-    pszAfsStartMenuRoot = GetAfsStartMenuRoot();\r
-    if (!pszAfsStartMenuRoot)\r
-        return;\r
-\r
-    sprintf(szTemp, "%s - Refresh Attempt", pszAfsStartMenuRoot);\r
-        \r
-    // Deleting items from below the root level of the start menu does not \r
-    // cause it to refresh.  In order that users can see changes without\r
-    // rebooting we will temporarily rename our root most entry, which \r
-    // does cause a refresh of the start menu.\r
-    MoveFileEx(pszAfsStartMenuRoot, szTemp, MOVEFILE_REPLACE_EXISTING);\r
-    MoveFileEx(szTemp, pszAfsStartMenuRoot, MOVEFILE_REPLACE_EXISTING);\r
-}\r
-\r
-static BOOL PreserveConfigInfo(struct APPINFO *pApp)\r
-{\r
-    char *pszRegKey;\r
-    char szDestKey[256];\r
-    LONG result;\r
-\r
-    bPreserveConfigInfo = TRUE;\r
-\r
-    // If not in silent mode, ask user if they want to preserve the cfg info\r
-    if (!bSilentMode) {\r
-        int nChoice = ShowMsg(pApp->nPreserveConfigInfoMsgID, MB_ICONQUESTION | MB_YESNOCANCEL);\r
-        if (nChoice == IDCANCEL)\r
-            return FALSE;                   // Cancel the uninstall\r
-        else if (nChoice == IDNO) {     \r
-            bPreserveConfigInfo = FALSE;    // User doesn't want to preserve the config info\r
-            return TRUE;\r
-        }\r
-    }\r
-\r
-    // Copy each reg key (and all of its subkeys and values) to another place in the registry.\r
-    for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {\r
-        if (!DoesRegKeyExist(pszRegKey))\r
-            continue;\r
-\r
-        // Create the destination path for the copy\r
-        sprintf(szDestKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);\r
-\r
-        // Try to copy it\r
-        result = RegDupKeyAlt(pszRegKey, szDestKey);\r
-\r
-        if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND)) {\r
-            // If the copy failed, then delete any copies that succeeded\r
-            sprintf(szDestKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);\r
-            RegDeleteEntryAlt(szDestKey, REGENTRY_KEY);\r
-               goto done;\r
-        }\r
-    }\r
-\r
-       // Remember the integrated login setting if this app supports that and it was turned on\r
-       if (pApp->pszNetworkProviderOrder) {\r
-               // Was integerated login turned on?\r
-               BOOL bOn, bOk;\r
-               bOk = InNetworkProviderOrder(pApp->pszNetworkProviderOrder, &bOn);\r
-               if (bOk && bOn) {\r
-                       HKEY hKey;\r
-                       sprintf(szDestKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);\r
-                       result = RegOpenKeyAlt(AFSREG_NULL_KEY, szDestKey, KEY_WRITE, TRUE, &hKey, 0);\r
-                       // The existance of the key is a flag indicating that integrated login was turned on\r
-                       RegCloseKey(hKey);\r
-               }\r
-       }\r
-       \r
-done:\r
-       if ((result == ERROR_SUCCESS) || bSilentMode)\r
-           return TRUE;    // Continue with uninstall\r
-\r
-    // Report the error and ask the user if they want to continue the uninstall\r
-    return (ShowMsg(IDS_SAVE_OF_CONFIG_INFO_FAILED, MB_ICONEXCLAMATION | MB_YESNO) == IDYES);                  \r
-}\r
-\r
-int SUCALLCONV RestoreConfigInfo(int nApp)\r
-{\r
-    char *pszRegKey;\r
-    char szSrcKey[256];\r
-    struct APPINFO *pApp = 0;\r
-    BOOL bError = FALSE;\r
-    LONG result;\r
-\r
-    switch (nApp) {\r
-        case SERVER:    pApp = &appServer;          break;\r
-        case CLIENT:    pApp = &appClient;          break;\r
-        case LCLIENT:   pApp = &appLightClient;     break;\r
-        case CC:        pApp = &appControlCenter;   break;\r
-    }\r
-    \r
-    if (!pApp)\r
-        return -1;\r
-        \r
-    // Copy each reg key (and all of its subkeys and values) back to its original place in the registry.\r
-    for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {\r
-        // Create the source path for the copy\r
-        sprintf(szSrcKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);\r
-\r
-        if (!DoesRegKeyExist(szSrcKey))\r
-            continue;\r
-\r
-        // Try to restore as many of the keys as possible.  Report any errors at the end.\r
-\r
-        // Try to copy it\r
-        result = RegDupKeyAlt(szSrcKey, pszRegKey);\r
-        if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND))\r
-            bError = TRUE;\r
-    }\r
-\r
-       // Restore integrated login if this app was using it\r
-       if (pApp->pszNetworkProviderOrder) {\r
-               // Check if integrated login was turned on.  The IntegratedLogin key is a flag\r
-               // telling us that it was on.  If the key does not exist, integrated login was\r
-               // not on.\r
-               sprintf(szSrcKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);\r
-               if (DoesRegKeyExist(szSrcKey)) {\r
-                       if (!AddToProviderOrder(pApp->pszNetworkProviderOrder))\r
-                               bError = TRUE;\r
-               }\r
-       }\r
-\r
-    // Remove our saved copies of the config info\r
-    sprintf(szSrcKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);\r
-    RegDeleteEntryAlt(szSrcKey, REGENTRY_KEY);\r
-            \r
-    if (bError)\r
-        ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, 0);\r
-\r
-    return TRUE;\r
-}\r
-\r
-static BOOL DoSubKeysExist(char *pszKey)\r
-{\r
-    LONG result;\r
-    HKEY hKey;\r
-    char *pszSubKeys = 0;\r
-    BOOL bExist;\r
-    \r
-    result = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);\r
-    if (result != ERROR_SUCCESS)\r
-        return FALSE;\r
-        \r
-    result = RegEnumKeyAlt(hKey,  &pszSubKeys);\r
-    RegCloseKey(hKey);\r
-    \r
-    if (result != ERROR_SUCCESS)\r
-        return FALSE;\r
-   \r
-    if (pszSubKeys) {\r
-        bExist = TRUE;\r
-        free(pszSubKeys);\r
-    } else\r
-        bExist = FALSE;    \r
-\r
-    return bExist;\r
-}\r
-\r
-/*\r
- * The following definitions are taken from richedit.h:\r
- *\r
- */\r
-\r
-#define EM_SETBKGNDCOLOR               (WM_USER + 67) // from Richedit.h\r
-#define EM_STREAMIN                            (WM_USER + 73) // from Richedit.h\r
-#define SF_RTF                         0x0002\r
-\r
-typedef DWORD (CALLBACK *EDITSTREAMCALLBACK)(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);\r
-\r
-typedef struct _editstream {\r
-       DWORD dwCookie;         /* user value passed to callback as first parameter */\r
-       DWORD dwError;          /* last error */\r
-       EDITSTREAMCALLBACK pfnCallback;\r
-} EDITSTREAM;\r
-\r
-/*\r
- *\r
- */\r
-\r
-DWORD CALLBACK License_StreamText (DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)\r
-{\r
-   LPTSTR psz = (LPTSTR)dwCookie;\r
-   LONG cchAvail = lstrlen(psz);\r
-   if ((*pcb = min(cchAvail, cb)) != 0) {\r
-      memcpy (pbBuff, psz, *pcb);\r
-      memmove (psz, &psz[*pcb], cchAvail - *pcb + 1);\r
-   }\r
-   return 0;\r
-}\r
-\r
-\r
-void License_OnInitDialog (HWND hDlg, LPTSTR pszFile)\r
-{\r
-    // Open the license file and shove its text in our RichEdit control\r
-    //\r
-    HANDLE hFile;\r
-    if ((hFile = CreateFile (pszFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) {\r
-\r
-        size_t cbText;\r
-        if ((cbText = GetFileSize (hFile, NULL)) != 0) {\r
-\r
-            LPTSTR abText = (LPTSTR)GlobalAlloc (GMEM_FIXED, cbText + 3);\r
-\r
-            DWORD cbRead;\r
-            if (ReadFile (hFile, abText, cbText, &cbRead, NULL)) {\r
-                abText[ cbRead ] = 0;\r
-\r
-                EDITSTREAM Stream;\r
-                memset (&Stream, 0x00, sizeof(Stream));\r
-                Stream.dwCookie = (DWORD)abText;\r
-                Stream.pfnCallback = License_StreamText;\r
-\r
-                SendDlgItemMessage (hDlg, IDC_TEXT, EM_STREAMIN, SF_RTF, (LPARAM)&Stream);\r
-            }\r
-\r
-            GlobalFree (abText);\r
-        }\r
-\r
-        CloseHandle (hFile);\r
-    }\r
-\r
-    // Make the control's background be gray\r
-    //\r
-    SendDlgItemMessage (hDlg, IDC_TEXT, EM_SETBKGNDCOLOR, FALSE, (LPARAM)GetSysColor(COLOR_BTNFACE));\r
-}\r
-\r
-BOOL CALLBACK License_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)\r
-{\r
-    switch (msg) {\r
-        case WM_INITDIALOG:\r
-            SetWindowLong (hDlg, DWL_USER, lp);\r
-            License_OnInitDialog (hDlg, (LPTSTR)lp);\r
-            break;\r
-\r
-        case WM_COMMAND:\r
-            switch (LOWORD(wp)) {\r
-                case IDCANCEL:\r
-                case IDOK:\r
-                    EndDialog (hDlg, LOWORD(wp));\r
-                    break;\r
-\r
-                case IDC_PRINT:\r
-                    TCHAR szDir[ MAX_PATH ];\r
-                    GetCurrentDirectory (MAX_PATH, szDir);\r
-                    ShellExecute (hDlg, TEXT("print"), (LPTSTR)GetWindowLong (hDlg, DWL_USER), NULL, szDir, SW_HIDE);\r
-                    break;\r
-            }\r
-            break;\r
-    }\r
-    return FALSE;\r
-}\r
-\r
-BOOL FindAfsInstallationPathByComponent (LPTSTR pszInstallationPath, LPTSTR pszComponent)\r
-{\r
-    *pszInstallationPath = 0;\r
-\r
-    TCHAR szRegPath[ MAX_PATH ];\r
-    wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);\r
-\r
-    HKEY hk;\r
-    if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0) {\r
-        DWORD dwType = REG_SZ;\r
-        DWORD dwSize = MAX_PATH;\r
-\r
-        if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszInstallationPath, &dwSize) == 0) {\r
-            *(LPTSTR)FindBaseFileName (pszInstallationPath) = TEXT('\0');\r
-\r
-            if (pszInstallationPath[0] && (pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] == TEXT('\\')))\r
-            pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] = TEXT('\0');\r
-        }\r
-\r
-        RegCloseKey (hk);\r
-    }\r
-\r
-    return !!*pszInstallationPath;\r
-}\r
-\r
-BOOL FindAfsInstallationPath (LPTSTR pszInstallationPath)\r
-{\r
-   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Client")))\r
-      return TRUE;\r
-   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Control Center")))\r
-      return TRUE;\r
-   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Server")))\r
-      return TRUE;\r
-   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Supplemental Documentation")))\r
-      return TRUE;\r
-   return FALSE;\r
-}\r
-\r
-HINSTANCE LoadRichTextControl (void)\r
-{\r
-    HINSTANCE hInst;\r
-    if ((hInst = LoadLibrary ("riched20.dll")) != NULL)\r
-        return hInst;\r
-    if ((hInst = LoadLibrary ("riched32.dll")) != NULL)\r
-        return hInst;\r
-    if ((hInst = LoadLibrary ("riched.dll")) != NULL)\r
-        return hInst;\r
-    if ((hInst = LoadLibrary ("richedit.dll")) != NULL)\r
-        return hInst;\r
-    return NULL;\r
-}\r
-\r
-int SUCALLCONV ShowLicense (char *pszTarget, char *pszSource)\r
-{\r
-    // If the license already lives on this user's machine, don't show\r
-    // it again. This only has to be done if the user has never\r
-    // accepted the license agreement before (it's part of the setup\r
-    // program, so it gets installed if they've accepted it).\r
-    //\r
-    // We were handed a relative path of the form:\r
-    //    Documentation/html/license.rtf\r
-    //\r
-    // We'll need to find the AFS installation directory, in order to\r
-    // find that Documentation subtree.\r
-    //\r
-    BOOL fShowLicense = TRUE;\r
-\r
-    TCHAR szInstallationPath[ MAX_PATH ];\r
-    if (FindAfsInstallationPath (szInstallationPath)) {\r
-        TCHAR szLicensePath[ MAX_PATH ];\r
-        wsprintf (szLicensePath, TEXT("%s\\%s"), szInstallationPath, pszTarget);\r
-\r
-        if (GetFileAttributes (szLicensePath) != (DWORD)-1) {\r
-            fShowLicense = FALSE;\r
-        }\r
-    }\r
-\r
-    // Before we can show the license file, we have to prepare the RichEdit\r
-    // control. That means loading the appropriate library and calling its\r
-    // initialization functions.\r
-    //\r
-    HINSTANCE hRichEdit;\r
-    if ((hRichEdit = LoadRichTextControl()) != NULL) {\r
-\r
-        // If we must show the license, do so now. This is a modal dialog,\r
-        // so we'll know whether or not the user accepts the license.\r
-        //\r
-        if (ModalDialogParam (IDD_LICENSE, GetActiveWindow(), License_DlgProc, (LPARAM)pszSource) == IDCANCEL) {\r
-            // The user rejected the license; fail setup\r
-            return FALSE;\r
-        }\r
-\r
-       FreeLibrary (hRichEdit);\r
-    }\r
-\r
-    // The user accepted the license, so we can continue with Setup.\r
-    // The license file is installed as part of Setup.\r
-    return TRUE;\r
-}\r
-\r
-int SUCALLCONV UninstInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)\r
-{\r
-    char szPath[MAX_PATH];\r
-    struct APPINFO *pAppInfo;\r
-    char *pszFile = 0;\r
-    char *pszSubDir = 0;\r
-\r
-    hDlg = hIS;\r
-\r
-    bSilentMode = !IsWindowVisible(hIS);\r
-\r
-    // Which app are we uninstalling?\r
-    pAppInfo = GetApp();\r
-    if (!pAppInfo) {\r
-        ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);\r
-        return -1;\r
-    }\r
-\r
-    // Get the app's install dir\r
-    pszInstallDir = GetAppInstallDir(pAppInfo, FALSE);\r
-    if (!pszInstallDir)\r
-        return -1;\r
-\r
-    // If this app has a custom uninstall func, call it here\r
-    if (pAppInfo->pUninstallFunc)\r
-        if (!pAppInfo->pUninstallFunc())\r
-            return -1;\r
-\r
-    if (pAppInfo->pszRegKeysToPreserve)\r
-        if (!PreserveConfigInfo(pAppInfo))\r
-            return -1;\r
-\r
-    // Unconfigure the service, if there is one for this app\r
-    if (pAppInfo->pszSvcKey) {\r
-        if (IsServiceInstalled(pAppInfo->pszSvcKey))\r
-            if (UninstallService(pAppInfo) == 1)\r
-                return -1;\r
-    }\r
-\r
-    RememberInstallDir(pszInstallDir);\r
-\r
-    DeleteInUseFiles(pAppInfo, fileInfo);\r
-\r
-    // Remove the app's bin path from the system path\r
-    if (pAppInfo->pszBinPath) {\r
-        BuildShortPath(szPath, sizeof(szPath), pszInstallDir, pAppInfo->pszBinPath);\r
-        RemoveFromPath(szPath);\r
-    }\r
-\r
-    // Remove entry from NetworkProvider\Order key in registry\r
-    if (pAppInfo->pszNetworkProviderOrder)\r
-        RemoveFromNetworkProviderOrder(pAppInfo->pszNetworkProviderOrder);\r
-\r
-    // Remove any generated subdirectories\r
-    if (!bPreserveConfigInfo && pAppInfo->pszDirsToDel) {\r
-        for (pszSubDir = pAppInfo->pszDirsToDel; *pszSubDir; pszSubDir += strlen(pszSubDir) + 1)\r
-            RemoveDir(ExpandPath(pszSubDir));\r
-    }\r
-\r
-    // Remove any generated files\r
-    if (!bPreserveConfigInfo && pAppInfo->pszFilesToDel) {\r
-        for (pszFile = pAppInfo->pszFilesToDel; *pszFile; pszFile += strlen(pszFile) + 1)\r
-            RemoveFiles(ExpandPath(pszFile));\r
-    }\r
-\r
-    // Remove any registry values that IS can't handle\r
-    RemoveRegValues(pAppInfo->pRegValues);\r
-    if (IsWinNT())\r
-        RemoveRegValues(pAppInfo->pWinNTRegValues);\r
-    else    \r
-        RemoveRegValues(pAppInfo->pWin9XRegValues);\r
-\r
-    // Remove the start menu entries for this app\r
-    if (pAppInfo->pszStartMenuEntries) {\r
-        DeleteStartMenuEntries(pAppInfo->pszStartMenuEntries);\r
-        RefreshStartMenu();\r
-    }\r
-\r
-    // Remove the install dir\r
-    RemoveDirectory(pszInstallDir);\r
-\r
-    return 0;\r
-}\r
-\r
-void SUCALLCONV UninstUnInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)\r
-{\r
-    char *pszInstallDir;\r
-    char szDirPath[MAX_PATH];\r
-    char *psz;\r
-    struct APPINFO *pAppInfo;\r
-\r
-    // If we just uninstalled the last AFS app, then do some cleanup.\r
-    if (IsAppInstalled(&appServer) || IsAppInstalled(&appClient) ||\r
-        IsAppInstalled(&appControlCenter) || IsAppInstalled(&appLightClient) ||\r
-        IsAppInstalled(&appDocs))\r
-    {\r
-        return;\r
-    }\r
-\r
-    bSilentMode = !IsWindowVisible(hIS);\r
-    \r
-    // Which app did we just uninstall?\r
-    pAppInfo = GetApp();\r
-    if (!pAppInfo) {\r
-        ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);\r
-        return;\r
-    }\r
-\r
-    // Get the app's install dir\r
-    pszInstallDir = GetAppInstallDir(pAppInfo, TRUE);\r
-    if (!pszInstallDir)\r
-        return;\r
-\r
-    // Remove the reg key we used to remember the app install dir\r
-    RegDeleteEntryAlt(UNINSTALL_TEMP_INFO_KEY, REGENTRY_KEY);\r
-\r
-    // Try to remove the reg key used to store config info, but only\r
-    // if there are no app config info sub keys present.\r
-    if (!DoSubKeysExist(AFS_PRESERVED_CFG_INFO_KEY))\r
-        RegDeleteEntryAlt(AFS_PRESERVED_CFG_INFO_KEY, REGENTRY_KEY);\r
-\r
-    // Remove the install dir\r
-    RemoveDirectory(pszInstallDir);\r
-\r
-    // Attempt to remove the install root and common directories.  The are \r
-    // shared and so no single app knows to delete them.\r
-\r
-    // Strip off the app specific part of the install dir\r
-    psz = strrchr(pszInstallDir, '\\');\r
-    if (psz)\r
-        *psz = 0;\r
-\r
-    sprintf(szDirPath, "%s\\%s", pszInstallDir, "Common");\r
-    RemoveDirectory(szDirPath);\r
-\r
-    // Remove the Common directory from the system path\r
-    RemoveFromPath(szDirPath);\r
-\r
-    // Remove all of the documentation dirs\r
-    sprintf(szDirPath, "%s\\%s", pszInstallDir, "Documentation");\r
-    RemoveDirectoryTree(szDirPath);\r
-\r
-    // Ok, up to this point we have been removing files we know we\r
-    // created.  However, after this point we are into the path\r
-    // that the user chose for our install root.  The default for\r
-    // this is IBM/Afs, but they could have chosen anything,\r
-    // including a dir or dirs that have other products in them.\r
-    // We will check to see if it is IBM\AFS and if it is then we \r
-    // will attempt to remove them.\r
-    \r
-    // Back up a level and look for AFS\r
-    psz = strrchr(pszInstallDir, '\\');\r
-    if (psz) {\r
-        if (stricmp(psz + 1, "AFS") == 0) {\r
-            RemoveDirectory(pszInstallDir);\r
-            *psz = 0;\r
-        }\r
-    }\r
-\r
-    // Back up a level and look for IBM\r
-    psz = strrchr(pszInstallDir, '\\');\r
-    if (psz) {\r
-        if (stricmp(psz + 1, "IBM") == 0) {\r
-            RemoveDirectory(pszInstallDir);\r
-            *psz = 0;\r
-        }\r
-    }\r
-\r
-    // Remove the root afs start menu entry\r
-    psz = GetStartMenuRoot();\r
-    if (psz) {\r
-        if (bSilentMode) {\r
-            // Remove everything under our branch\r
-            sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack\\AFS", psz);\r
-            RemoveDirectoryTree(szDirPath);\r
-            \r
-            // Remove the IBM stuff only if the dirs are empty\r
-            sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack", psz);\r
-            if (RemoveDirectory(szDirPath)) {\r
-                sprintf(szDirPath, "%s\\IBM WebSphere", psz);\r
-                RemoveDirectory(szDirPath);\r
-            }\r
-        } else {\r
-            sprintf(szDirPath, "%s\\IBM AFS", psz);\r
-            RemoveDirectoryTree(szDirPath);\r
-        }\r
-    }\r
-}\r
-\r
-BOOLEAN _stdcall DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)\r
-{\r
-    if (reason == DLL_PROCESS_ATTACH) {\r
-        hinst = (HINSTANCE)dll;\r
-        TaLocale_LoadCorrespondingModuleByName (hinst, "afs_setup_utils.dll");\r
-    }\r
-\r
-    return TRUE;\r
-}\r
-\r
-extern "C" int WINAPI Test (HINSTANCE hInst, HINSTANCE hPrev, LPSTR psz, int nCmdShow)\r
-{\r
-   ShowLicense ("TEST", "\\\\fury\\afssetup\\license\\ja_JP.rtf");\r
-   return 0;\r
-}\r
-\r
-\r
+ * INCLUDES _________________________________________________________________
+ *
+ */
+
+extern "C" {
+#include <afs/param.h>
+#include <afs/stds.h>
+#include <afs/fileutil.h>
+}
+
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <io.h>
+#include <string.h>
+#include <SYS\STAT.H>
+#include <shellapi.h>
+
+#include <WINNT/afsreg.h>
+#include <WINNT/afssw.h>
+#include <WINNT/talocale.h>
+
+#include "resource.h"
+#include "progress_dlg.h"
+#include "sutil.h"
+#include "forceremove.h"
+
+
+/*
+ * PROTOTYPES _________________________________________________________________
+ *
+ */
+static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered);
+BOOL UninstallCredsTool();
+BOOL ServerSpecificUninstall();
+BOOL ClientSpecificUninstall();
+
+
+
+/*
+ * DEFINITIONS _________________________________________________________________
+ *
+ */
+#define SUCALLCONV  WINAPI
+
+#define UNINSTALL_TEMP_INFO_KEY     "HKEY_LOCAL_MACHINE\\Software\\AfsUninstallTempInfo"
+#define INSTALL_DIR_VALUE_NAME      "InstallDir"
+
+#define AFS_PRESERVED_CFG_INFO_KEY  "HKEY_LOCAL_MACHINE\\Software\\AfsPreservedConfigInfo"
+
+#define MS_SHARED_FILES_KEY         "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"
+
+// Log file to use when running in silent mode
+#define UNINSTALL_ERROR_LOG_NAME    "\\AfsUninstallErrorLog.txt"
+#define INSTALL_ERROR_LOG_NAME      "\\AfsInstallErrorLog.txt"
+
+#define TARGETDIR                   "<TARGETDIR>"
+#define WINDIR                      "<WINDIR>"
+#define WINSYSDIR                   "<WINSYSDIR>"
+
+#define WIN9X_START_MENU_REG_KEY    "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
+#define WIN9X_START_MENU_REG_VALUE  "Programs"
+    
+#define WINNT_START_MENU_REG_KEY    "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
+#define WINNT_START_MENU_REG_VALUE  "Common Programs"
+
+#define LOCALE_ID_LEN               4
+
+struct REGVALUE {
+    char *pszKey;
+    char *pszValue;
+};
+
+
+typedef BOOL (APP_UNINSTALL_FUNC)();
+
+
+
+struct APPINFO {
+    char *pszAppName;
+
+    // Service Info
+    char *pszSvcName;
+    char *pszSvcKey;
+    char *pszSvcDependOn;
+    char *pszSvcDisplayName;
+
+    char *pszNetworkProviderOrder;
+
+    // Message to use to tell the user that we have to stop the service
+    int nServiceShutdownMsgID;
+
+    // Message to use for the progress dialog that is shown while
+    // waiting for the service to stop.
+    int nServiceShutdownProgressMsgID;
+
+    // Location in registry of a key we can use to know that the app is installed
+    char *pszAppKey;
+
+    // Location in registry of this app's install dir
+    struct REGVALUE regInstallDir;
+
+    // Path Info
+    char *pszLocalRoot;     // The root dir below the install dir
+    char *pszBinPath;       // Path to remove from the system path
+
+    // Generated files and directories to delete.  These are both multistring lists.
+    char *pszDirsToDel;     // All files in these dirs will be deleted
+    char *pszFilesToDel;    // Use this if you want to delete files but leave the dir.  Wildcards can be used.
+
+    // Registry values to remove
+    struct REGVALUE *pRegValues;
+    struct REGVALUE *pWinNTRegValues;   // Only remove these if running WinNT
+    struct REGVALUE *pWin9XRegValues;   // Only remove these if running Win9X
+
+    // Start menu entries to delete
+    char *pszStartMenuEntries;
+
+    // Registry keys to save if a user wants to preserve config info during uninstall
+    char *pszRegKeysToPreserve;
+    int nPreserveConfigInfoMsgID;
+
+    // Uninstall func - used for things specific to this app
+    APP_UNINSTALL_FUNC *pUninstallFunc;
+};
+
+
+/*
+ * App info structure for the Server product
+ */
+struct APPINFO appServer = {
+    "AFS Server",
+    
+    AFSREG_SVR_SVC_NAME,
+    AFSREG_SVR_SVC_KEY,
+    0,  // No depend on
+    AFSREG_SVR_SVC_DISPLAYNAME_DATA,
+
+    0,  // No network provider order
+
+    IDS_MUST_STOP_SERVER,
+    IDS_WAITING_FOR_SERVER_TO_STOP,
+
+    AFSREG_SVR_SW_VERSION_KEY,
+    
+    { AFSREG_SVR_SW_VERSION_KEY, AFSREG_SVR_SW_VERSION_DIR_VALUE },
+
+    "\\Server",
+    "\\usr\\afs\\bin",
+
+    // Dirs to delete
+    TARGETDIR"\\Server\\usr\\afs\\bin\\backup\0"
+    TARGETDIR"\\Server\\usr\\afs\\bin\0"
+    TARGETDIR"\\Server\\usr\\afs\\db\0"
+    TARGETDIR"\\Server\\usr\\afs\\logs\0"
+    TARGETDIR"\\Server\\usr\\afs\\etc\0"
+    TARGETDIR"\\Server\\usr\\afs\\local\0"
+    TARGETDIR"\\Server\\usr\\afs\0"
+    TARGETDIR"\\Server\\usr\0",
+    
+    // Files to delete
+    TARGETDIR"\\Common\\*.gid\0"
+    TARGETDIR"\\Common\\*.fts\0",
+
+    0,  // No reg values
+    0,  // No NT only reg values
+    0,  // No 9x only reg values
+
+    "Server\0",
+
+    // Config info to preserve
+    AFSREG_SVR_SVC_KEY"\0", 
+    IDS_PRESERVE_SERVER_CONFIG_INFO,
+
+    0   // No special uninstall function
+};
+
+// Registry values to remove for the Client
+struct REGVALUE clientRegValues[] = {
+    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", "{DC515C27-6CAC-11D1-BAE7-00C04FD140D2}" },
+    { 0, 0 }    // This indicates there are no more entries
+};
+
+struct REGVALUE clientWinNTRegValues[] = {
+    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\File Manager\\AddOns", "AFS Client FME" },
+    { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NetBT\\Paramet
+ers", "SMBDeviceEnabled" },
+    { 0, 0 }
+};
+
+struct REGVALUE clientWin9XRegValues[] = {
+    { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\NetworkProvider\\Order", "TransarcAFSDaemon" },
+    { 0, 0 }
+};
+
+/*
+ * App info structure for the Client product
+ */
+struct APPINFO appClient = {
+    "AFS Client",
+    
+    AFSREG_CLT_SVC_NAME,
+    AFSREG_CLT_SVC_KEY,
+    "5250435353004E657462696F730000",
+    AFSREG_CLT_SVC_DISPLAYNAME_DATA,
+
+    AFSREG_CLT_SVC_NAME,
+
+    IDS_MUST_STOP_CLIENT,
+    IDS_WAITING_FOR_CLIENT_TO_STOP,
+
+    AFSREG_CLT_SW_VERSION_KEY,
+
+    { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },
+
+    "\\Client",
+    "\\Program",
+
+    // No dirs to delete
+    0,
+    
+    // Files to delete
+    TARGETDIR"\\Common\\*.gid\0"
+    TARGETDIR"\\Common\\*.fts\0"
+    WINDIR"\\..\\AFSCache\0"
+    WINDIR"\\afsd.log\0"
+    WINDIR"\\afsd.ini\0"
+    WINDIR"\\afsdsbmt.ini\0"
+    WINDIR"\\afsdcell.ini\0"
+    WINDIR"\\afsd_init.log\0",
+    
+    clientRegValues,
+    clientWinNTRegValues,
+    clientWin9XRegValues,
+
+    // Start menu entries to remove
+    "Client\0",
+
+    // Config info to preserve
+    AFSREG_CLT_SVC_KEY"\0",
+    IDS_PRESERVE_CLIENT_CONFIG_INFO,
+
+    ClientSpecificUninstall
+};
+
+
+/*
+ * App info structure for the Light Client product
+ */
+struct APPINFO appLightClient = {
+    "AFS Light",
+    
+    // No service info 
+    0,
+    0,
+    0,
+    0,
+
+    AFSREG_CLT_SVC_NAME,
+
+    // No service shutdown messages
+    0,
+    0,
+
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Light Client",
+
+    { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },
+
+    "\\Client",
+    "\\Program",
+
+    // No dirs to delete
+    0,
+    
+    // Files to delete
+    TARGETDIR"\\Common\\*.gid\0"
+    TARGETDIR"\\Common\\*.fts\0",
+
+    clientRegValues,
+    clientWinNTRegValues,
+    clientWin9XRegValues,
+
+    // Start menu entries to remove
+    "Light\0",
+
+    // Config info to preserve
+    AFSREG_CLT_SVC_KEY"\0",
+    IDS_PRESERVE_LIGHT_CLIENT_CONFIG_INFO,
+
+    UninstallCredsTool
+};
+
+
+/*
+ * App info structure for the Control Center product
+ */
+struct APPINFO appControlCenter = {
+    "AFS Control Center",
+    
+    // No service info
+    0,
+    0,
+    0,
+    0,
+
+    // No network provider order
+    0,
+
+    // No service shutdown messages    
+    0,
+    0,
+
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center",
+    
+    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center\\CurrentVersion", "PathName" },
+
+    "\\Control Center",
+    "",
+
+    // No dirs to delete
+    0,
+    
+    // Files to delete
+    TARGETDIR"\\Common\\*.gid\0"
+    TARGETDIR"\\Common\\*.fts\0",
+    
+    0,  // No reg values
+    0,  // No NT only reg values
+    0,  // No 9x only reg values
+
+    // Start menu entries to remove
+    "Control Center\0",
+
+    // Config info to preserve
+    AFSREG_CLT_SVC_KEY"\0",
+    IDS_PRESERVE_CC_CONFIG_INFO,
+
+    0   // No uninstall function
+};
+
+
+/*
+ * App info structure for the Sys Admin Doc files
+ */
+struct APPINFO appDocs = {
+    "AFS Supplemental Documentation",
+
+    // No service info
+    0,
+    0,
+    0,
+    0,
+
+    // No network provider order
+    0,
+
+    // No service shutdown messages    
+    0,
+    0,
+
+    "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation",
+    
+    { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation\\CurrentVersion", "PathName" },
+
+    "\\Documentation",
+    "",
+
+    // No dirs to delete
+    0,
+    
+    // Files to delete
+    TARGETDIR"\\Common\\*.gid\0"
+    TARGETDIR"\\Common\\*.fts\0",
+
+    0,  // No reg values
+    0,  // No NT only reg values
+    0,  // No 9x only reg values
+
+    // Start menu entries to remove
+    "Documentation\\AFS for Windows Backup Command Reference.lnk\0Documentation\\AFS Command Reference Manual.lnk\0Documentation\\AFS System Administrator's Guide.lnk\0",
+
+    0,  // No config info to preserve
+
+    0   // No uninstall function
+};
+
+
+// Shared and in-use files
+struct FILEINFO {
+    char *pszName;
+    int nUsedBy;
+};
+
+#define SERVER  1
+#define CLIENT  2
+#define LCLIENT 4
+#define CC      8
+#define DOCS    16
+
+
+struct FILEINFO fileInfo[] = {
+    { TARGETDIR"\\Common\\afsbosadmin.dll",             SERVER | CC },
+    { TARGETDIR"\\Common\\afscfgadmin.dll",             SERVER | CC },
+    { TARGETDIR"\\Common\\afsclientadmin.dll",          SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afskasadmin.dll",             SERVER | CC },
+    { TARGETDIR"\\Common\\afsptsadmin.dll",             SERVER | CC },
+    { TARGETDIR"\\Common\\afsvosadmin.dll",             SERVER | CC },
+    { TARGETDIR"\\Common\\afsadminutil.dll",            SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afsrpc.dll",                  SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afsauthent.dll",              SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\pthread.dll",                 SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\TaAfsAppLib.dll",             SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afsprocmgmt.dll",             SERVER | CLIENT | LCLIENT },
+    { TARGETDIR"\\Common\\afs_config.exe",              CLIENT | LCLIENT| CC },
+    { TARGETDIR"\\Common\\afseventmsg_????.dll",        SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afslegal_????.dll",           SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afsserver_????.dll",          SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afssvrcfg_????.dll",          SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\TaAfsAccountManager_????.dll",SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\TaAfsAppLib_????.dll",        SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\TaAfsServerManager_????.dll", SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afscreds_????.dll",           SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs_config_????.dll",         SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs_cpa_????.dll",            SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs_shl_ext_????.dll",        SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-nt.hlp",                  SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-nt.cnt",                  SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafssvrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafssvrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafsusrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafsusrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-cc.cnt",                  SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-cc.hlp",                  SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-light.cnt",               SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\afs-light.hlp",               SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafscfg.cnt",                SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Common\\taafscfg.hlp",                SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Client\\PROGRAM\\afs_shl_ext.dll",    CLIENT | LCLIENT },
+    { TARGETDIR"\\Client\\PROGRAM\\libafsconf.dll",     CLIENT | LCLIENT },
+    { TARGETDIR"\\Client\\PROGRAM\\afslogon.dll",       CLIENT },
+    { TARGETDIR"\\Client\\PROGRAM\\afslog95.dll",       LCLIENT },
+    { TARGETDIR"\\Control Center\\TaAfsAdmSvr.exe",     CC },
+    { WINSYSDIR"\\afs_cpa.cpl",                         CLIENT | LCLIENT | CC },
+    { WINSYSDIR"\\afsserver.cpl",                       SERVER },
+    { TARGETDIR"\\Common\\afsdcell.ini",                CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Documentation\\Html\\banner.gif",     SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\books.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\bot.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\index.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\index.htm",      SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\next.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\prev.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\toc.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\top.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
+    { TARGETDIR"\\Documentation\\Html\\ReleaseNotes\\relnotes.htm",
+                                                        SERVER | CLIENT | LCLIENT | CC },
+    { TARGETDIR"\\Documentation\\Html\\InstallGd\\afsnt35i.htm",
+                                                        SERVER | CLIENT | LCLIENT | CC },
+    { 0,                                                0 }     // End of list
+};
+
+
+/*
+ * VARIABLES _________________________________________________________________
+ *
+ */
+HINSTANCE hinst;
+
+static HWND hDlg;
+static BOOL bPreserveConfigInfo;
+static BOOL bSilentMode;
+static char *pszInstallDir;
+
+
+/*
+ * FUNCTIONS _________________________________________________________________
+ *
+ */
+
+static BOOL UpgradeClientIntParm(HKEY hKey, char *pszOldParm, char *pszNewParm)
+{
+    int nData;
+    LONG result = ERROR_SUCCESS;
+
+    nData = GetPrivateProfileInt("AFS Client", pszOldParm, -1, "afsd.ini");
+    if (nData > -1)
+        result = RegSetValueEx(hKey, pszNewParm, 0, REG_DWORD, (BYTE *)&nData, sizeof(nData));
+
+    return (result == ERROR_SUCCESS);
+}
+
+static BOOL UpgradeClientStringParm(HKEY hKey, char *pszOldParm, char *pszNewParm)
+{
+    char szData[1024];
+    LONG result = ERROR_SUCCESS;
+
+    GetPrivateProfileString("AFS Client", pszOldParm, "", szData, sizeof(szData), "afsd.ini");
+    if (szData[0])
+        result = RegSetValueEx(hKey, pszNewParm, 0, REG_SZ, (PBYTE)szData, strlen(szData) + 1);
+
+    return (result == ERROR_SUCCESS);
+}
+
+int SUCALLCONV Upgrade34ClientConfigInfo()
+{
+    HKEY hKey;
+    LONG result;
+    int nData;
+    
+    result = RegOpenKeyAlt(AFSREG_NULL_KEY, AFSREG_CLT_SVC_PARAM_KEY, KEY_WRITE, TRUE, &hKey, 0);
+    if (result != ERROR_SUCCESS)
+        return -1;
+
+    UpgradeClientIntParm(hKey, "CacheSize", "CacheSize");
+    UpgradeClientIntParm(hKey, "Stats", "Stats");
+    UpgradeClientIntParm(hKey, "LogoffTokenTransfer", "LogoffTokenTransfer");
+    UpgradeClientIntParm(hKey, "LogoffTokenTransferTimeout", "LogoffTokenTransferTimeout");
+    UpgradeClientIntParm(hKey, "TrapOnPanic", "TrapOnPanic");
+    UpgradeClientIntParm(hKey, "TraceBufferSize", "TraceBufferSize");
+    UpgradeClientIntParm(hKey, "TraceOnShutdown", "TraceOnShutdown");
+    UpgradeClientIntParm(hKey, "ReportSessionStartups", "ReportSessionStartups");
+    
+    UpgradeClientStringParm(hKey, "MountRoot", "MountRoot");
+    UpgradeClientStringParm(hKey, "Cell", "Cell");
+
+    /* BlockSize to ChunkSize requires convertion */
+    nData = GetPrivateProfileInt("AFS Client", "BlockSize", -1, "afsd.ini");
+    if (nData > -1) {
+       DWORD chunkSize;
+       for (chunkSize = 0; (1 << chunkSize) < nData; chunkSize++);
+        (void) RegSetValueEx(hKey, "ChunkSize", 0, REG_DWORD, (BYTE *)&chunkSize, sizeof(chunkSize));
+    }
+
+    RegCloseKey(hKey);
+
+    return 0;
+}
+
+int SUCALLCONV Eradicate34Client()
+{
+    if (Client34Eradicate(TRUE) != ERROR_SUCCESS)
+        return -1;
+
+    return 0;
+}
+
+// This function was written a long time ago by Mike Comer for use by the 
+// original DFS Client for NT install program.
+int SUCALLCONV CheckIfAdmin(void)
+{
+    HANDLE                  token = INVALID_HANDLE_VALUE;
+    PVOID                   buffer = 0;
+    DWORD                   bufLength;
+    DWORD                   realBufLength;
+    TOKEN_PRIMARY_GROUP     *pgroup;
+    TOKEN_GROUPS            *groups;
+    int                     result = -1;
+    DWORD                   groupCount;
+    LONG                    status;
+    PSID                    AdministratorSID = NULL;
+    SID_IDENTIFIER_AUTHORITY    authority = SECURITY_NT_AUTHORITY;
+
+    // allocate the SID for the Administrators group
+    if (!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorSID)) {
+        status = GetLastError();
+        goto getout;
+    }
+
+    // open the process token
+    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token)) {
+        status = GetLastError();
+        token = INVALID_HANDLE_VALUE;
+        goto getout;
+    }
+
+    // check primary group first
+    buffer = GlobalAlloc(GMEM_FIXED, sizeof(TOKEN_PRIMARY_GROUP));
+    if (!buffer) {
+        goto getout;
+    }
+
+    bufLength = sizeof(TOKEN_PRIMARY_GROUP);
+    while(1) {
+        if (!GetTokenInformation(token, TokenPrimaryGroup, buffer, bufLength, &realBufLength)) {
+            if (realBufLength > bufLength) {
+                // not enough space
+                GlobalFree(buffer);
+                bufLength = realBufLength;
+                buffer = GlobalAlloc(GMEM_FIXED, realBufLength);
+                if (!buffer) {
+                    goto getout;
+                }
+                continue;
+            }
+
+            goto getout;
+        }
+        break;
+    }
+
+    pgroup = (TOKEN_PRIMARY_GROUP *)buffer;
+    if (EqualSid(pgroup->PrimaryGroup, AdministratorSID)) {
+        result = 0;
+    } else {
+        // okay, try the secondary groups
+        while(1) {
+            if (!GetTokenInformation(token, TokenGroups, buffer, bufLength, &realBufLength)) {
+                if (realBufLength > bufLength) {
+                    // not enough space
+                    GlobalFree(buffer);
+                    bufLength = realBufLength;
+                    buffer = GlobalAlloc(GMEM_FIXED, realBufLength);
+                    if (!buffer) {
+                        goto getout;
+                    }
+                    continue;
+                }
+
+                // a real error
+                goto getout;
+            }
+            break;
+        }
+
+        // we have the list of groups here.  Process them:
+        groups = (TOKEN_GROUPS *)buffer;
+        for(groupCount = 0; groupCount < groups->GroupCount; groupCount++) {
+            if (EqualSid(groups->Groups[groupCount].Sid, AdministratorSID)) {
+                result = 0;
+                break;
+            }
+        }
+    }
+
+getout:
+
+    if (token != INVALID_HANDLE_VALUE) {
+        CloseHandle(token);
+    }
+
+    if (buffer) {
+        GlobalFree(buffer);
+    }
+
+    if (AdministratorSID) {
+        FreeSid(AdministratorSID);
+    }
+
+    return result;
+}
+
+static void SetSharedFileRefCount(char *pszFile, int nRefCount)
+{
+    LONG result;
+    HKEY hKey;
+    
+    result = RegOpenKeyAlt(AFSREG_NULL_KEY, MS_SHARED_FILES_KEY, KEY_WRITE, FALSE, &hKey, 0);
+    if (result != ERROR_SUCCESS)
+        return;
+    
+    if (nRefCount <= 0)
+        RegDeleteValue(hKey, pszFile);
+    else
+        RegSetValueEx(hKey, pszFile, 0, REG_DWORD, (BYTE *)&nRefCount, sizeof(int));    
+    
+    RegCloseKey(hKey);
+}
+
+static char *GetTimeStamp()
+{
+    char szTime[64], szDate[64];
+    static char szTimeDate[128];
+
+    _strtime(szTime);
+    _strdate(szDate);
+
+    sprintf(szTimeDate, "[%s %s] ", szTime, szDate);
+    
+    return szTimeDate;
+}
+
+int SUCALLCONV WriteToInstallErrorLog(char *pszMsg)
+{
+    static BOOL bWritten = FALSE;
+    FILE *fp;
+
+    // On the first write, recreate the file    
+    fp = fopen(INSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");
+    if (!fp)
+        return -1;
+        
+    fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);
+    
+    fclose(fp);
+    
+    bWritten = TRUE;
+    
+    return 0;
+}
+
+static void WriteToUninstallErrorLog(char *pszMsg)
+{
+    static BOOL bWritten = FALSE;
+    FILE *fp;
+
+    // On the first write, recreate the file    
+    fp = fopen(UNINSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");
+    if (!fp)
+        return;
+        
+    fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);
+    
+    fclose(fp);
+    
+    bWritten = TRUE;
+}
+
+static char *LoadResString(UINT uID)
+{
+    static char str[256];
+    GetString (str, uID);
+    return str;
+}
+
+static void ShowError(UINT nResID, LONG nError)
+{
+    char szErr[256];
+    char szPrompt[256];
+    char szMsg[256];
+    char szTitle[256];
+    char *psz;
+
+    psz = LoadResString(nResID);
+    if (psz)
+        strcpy(szErr, psz);
+    else
+        sprintf(szErr, "unknown error msg (Msg ID = %d)", nResID);
+    
+    psz = LoadResString(IDS_INSTALLATION_FAILURE);
+    strcpy(szPrompt, psz ? psz : "An error has occurred:  %s (Last Error = %ld).");
+
+    sprintf(szMsg, szPrompt, szErr, nError);
+
+    psz = LoadResString(IDS_TITLE);
+    strcpy(szTitle, psz ? psz : "AFS");
+
+    if (bSilentMode)
+        WriteToUninstallErrorLog(szMsg);
+    else
+        MessageBox(hDlg, szMsg, szTitle, MB_OK);
+}
+
+static int ShowMsg(UINT nResID, int nType)
+{
+    char szTitle[256];
+    char *psz;
+
+    psz = LoadResString(IDS_TITLE);
+    strcpy(szTitle, psz ? psz : "AFS");
+
+    return MessageBox(hDlg, LoadResString(nResID), szTitle, nType);
+}
+
+static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered)
+{
+    HKEY hKey;
+    LONG nResult;
+    DWORD dwType;
+    static char szInstallDir[256];
+    DWORD dwSize;
+    char *pszKey;
+    char *pszValue;
+
+    pszKey = bRemembered ? UNINSTALL_TEMP_INFO_KEY : pApp->regInstallDir.pszKey;
+    pszValue = bRemembered ? INSTALL_DIR_VALUE_NAME : pApp->regInstallDir.pszValue;
+
+    dwSize = sizeof(szInstallDir);
+
+    nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
+    if (nResult == ERROR_SUCCESS) {
+        nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szInstallDir, &dwSize);
+        RegCloseKey(hKey);
+    }
+
+    if (nResult != ERROR_SUCCESS) {
+        ShowError(IDS_CANT_DETERMINE_APP_PATH, nResult);
+        return 0;
+    }
+
+    FilepathNormalizeEx(szInstallDir, FPN_BACK_SLASHES);
+
+    return szInstallDir;
+}
+
+static BOOL DoesRegKeyExist(char *pszKey)
+{
+    HKEY hKey;
+    LONG nResult;
+
+    nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
+    if (nResult == ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static BOOL IsAppInstalled(struct APPINFO *pApp)
+{
+    return DoesRegKeyExist(pApp->pszAppKey);
+}
+
+static void BuildShortPath(char *pszShortPath, UINT nShortPathLen, char *pszInstallDir, char *pszPath)
+{
+    strncpy(pszShortPath, pszInstallDir, nShortPathLen);
+    strncat(pszShortPath, pszPath, nShortPathLen);
+    
+    GetShortPathName(pszShortPath, pszShortPath, nShortPathLen);
+}
+
+static BOOL IsWin95()
+{
+    OSVERSIONINFO versionInformation;
+
+    versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    
+    GetVersionEx(&versionInformation);
+    
+    if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
+        (versionInformation.dwMinorVersion == 0))
+        return TRUE;
+        
+    return FALSE;
+}
+
+int IsWin98()
+{
+    OSVERSIONINFO versionInformation;
+
+    versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    
+    GetVersionEx(&versionInformation);
+    
+    if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
+        (versionInformation.dwMinorVersion == 10))
+        return 0;
+        
+    return -1;
+}
+
+static BOOL IsServiceInstalled(char *pszServiceKey)
+{
+    HKEY hKey;
+
+    if (RegOpenKeyAlt(0, pszServiceKey, KEY_READ, FALSE, &hKey, 0) == ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+// If this fails in anyway we just return.  No error is displayed.
+static void MakeSureServiceDoesNotExist(char *pszName)
+{
+    SC_HANDLE hServer = 0, hSCM = 0;
+    SERVICE_STATUS status;
+
+    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+    if (hSCM) {
+        hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS | DELETE);
+        if (hServer) {
+            if (QueryServiceStatus(hServer, &status)) {
+                if (status.dwCurrentState != SERVICE_STOPPED) {
+                    if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {
+                        CloseServiceHandle(hServer);
+                        CloseServiceHandle(hSCM);
+                        return;
+                    }
+                }
+            }
+            
+            // Try to delete even if status query fails
+            DeleteService(hServer);
+        }
+    }
+
+    if (hServer)
+        CloseServiceHandle(hServer);
+    if (hSCM)
+        CloseServiceHandle(hSCM);
+}
+
+static int InstallService(char *pszName, char *pszDependOn, char *pszDisplayName, char *pszServicePath, BOOL bInteractive)
+{
+    SC_HANDLE hServer = 0, hSCM;
+    BOOL bRestoreOldConfig = FALSE;
+
+    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+    if (!hSCM) {
+        ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
+        return -1;
+    }
+
+/*  This code is not used, but it could be handy in the future so I am keeping it here.
+
+    // If the service exists, then we (most probably) are in the middle of an upgrade or reinstall.
+    bRestoreOldConfig = IsServiceInstalled(pszName);
+
+    if (bRestoreOldConfig) {
+        hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS);
+        if (!hServer || !ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
+            ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, GetLastError());
+            bRestoreOldConfig = FALSE;
+            // Fall through to service creation below
+        }
+    } 
+*/
+    
+    if (!bRestoreOldConfig) {
+        DWORD dwServiceType;
+
+        // If the service already exists, the create call will fail.  This can
+        // happen if uninstall failed (which is not infrequent).  Making sure the
+        // service does not exist makes it easier for a user to install over top of
+        // a previously failed uninstall.
+        MakeSureServiceDoesNotExist(pszName);
+
+        dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+        if (bInteractive)
+            dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
+    
+        hServer = CreateService(hSCM, pszName, pszDisplayName,
+            SERVICE_ALL_ACCESS, dwServiceType, SERVICE_AUTO_START, 
+            SERVICE_ERROR_NORMAL, pszServicePath, 0, 0, "RPCSS\0Netbios\0\0", 0, 0);
+    
+        if (!hServer)
+            ShowError(IDS_SERVICE_CREATE_FAILED, GetLastError());
+    }
+
+    if (hServer)
+        CloseServiceHandle(hServer);
+
+    CloseServiceHandle(hSCM);
+
+    return 0;
+}
+
+int SUCALLCONV InstallServerService(char *pszServicePath)
+{
+    return InstallService(appServer.pszSvcName, 0, appServer.pszSvcDisplayName, pszServicePath, TRUE);
+}
+
+int SUCALLCONV InstallClientService(char *pszServicePath)
+{
+    return InstallService(appClient.pszSvcName, appClient.pszSvcDependOn, appClient.pszSvcDisplayName, pszServicePath, FALSE);
+}
+
+static int UninstallService(struct APPINFO *pAppInfo)
+{
+    SC_HANDLE hServer, hSCM;
+    SERVICE_STATUS status;
+    BOOL bOk;
+    BOOL bServer = FALSE;
+    BOOL bShowingProgressDlg = FALSE;
+
+    hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+    if (!hSCM) {
+        ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
+        return -1;
+    }
+    
+    hServer = OpenService(hSCM, pAppInfo->pszSvcName, SERVICE_ALL_ACCESS | DELETE);
+    if (!hServer) {
+        ShowError(IDS_SERVICE_OPEN_FAILED, GetLastError());
+        CloseServiceHandle(hSCM);
+        return -1;
+    }
+
+    if (!QueryServiceStatus(hServer, &status)) {
+        ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
+        CloseServiceHandle(hServer);
+        CloseServiceHandle(hSCM);
+        return -1;
+    }
+
+    if (status.dwCurrentState != SERVICE_STOPPED) {
+        if (pAppInfo->nServiceShutdownMsgID) {
+            if (!bSilentMode && (ShowMsg(pAppInfo->nServiceShutdownMsgID, MB_YESNO | MB_ICONQUESTION) == IDNO)) {
+                CloseServiceHandle(hServer);
+                CloseServiceHandle(hSCM);
+                return 1;
+            }
+        }
+
+        if (!bSilentMode)
+            bShowingProgressDlg = ShowProgressDialog(LoadResString(pAppInfo->nServiceShutdownProgressMsgID));
+
+        if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {
+            if (bShowingProgressDlg)
+                HideProgressDialog();
+            ShowError(IDS_SERVICE_STOP_FAILED, GetLastError());
+            CloseServiceHandle(hServer);
+            CloseServiceHandle(hSCM);
+            return -1;
+        }
+    }
+
+    // Wait for the service to stop
+    while (status.dwCurrentState != SERVICE_STOPPED) {
+        // I stopped waiting on dwWaitHint because it seemed the wait hint was too long.
+        // The service would be stopped but we'd still be asleep for a long time yet.
+        Sleep(5000);    //status.dwWaitHint);
+
+        if (!QueryServiceStatus(hServer, &status)) {
+            if (bShowingProgressDlg)
+                HideProgressDialog();
+            ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
+            CloseServiceHandle(hServer);
+            CloseServiceHandle(hSCM);
+            return -1;
+        }
+    }
+
+    // The service has been stopped
+    if (bShowingProgressDlg)
+        HideProgressDialog();
+
+    // This code to disable the service may be of use some day so I am keeping it here.
+    // bOk = ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0); 
+
+    bOk = DeleteService(hServer);
+
+    if (!bOk)
+        ShowError(IDS_SERVICE_DELETE_FAILED, GetLastError());
+
+    CloseServiceHandle(hServer);
+    CloseServiceHandle(hSCM);
+
+    return (bOk ? 0 : -1);
+}
+
+int SUCALLCONV AddToNetworkProviderOrder(char *pszWhatToAdd)
+{
+    return AddToProviderOrder(pszWhatToAdd) ? 0 : -1;
+}
+
+static int RemoveFromNetworkProviderOrder(char *pszWhatToDel)
+{
+    return RemoveFromProviderOrder(pszWhatToDel) ? 0 : -1;
+}
+
+int SUCALLCONV AddToPath(char *pszPath)
+{
+    return AddToSystemPath(pszPath) ? 0 : -1;
+}
+
+static int RemoveFromPath(char *pszPath)
+{
+    return RemoveFromSystemPath(pszPath) ? 0 : -1;
+}
+
+static void RemoveFiles(char *pszFileSpec)
+{
+    struct _finddata_t fileinfo;
+    long hSearch;
+    char szDel[MAX_PATH];
+    char szDir[MAX_PATH];
+    char *p;
+
+    strcpy(szDir, pszFileSpec);
+    p = strrchr(szDir, '\\');
+    if (p)
+        *p = 0;
+    
+    hSearch = _findfirst(pszFileSpec, &fileinfo);
+    if (hSearch == -1)
+        return;
+
+    while (1) {
+        if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {
+            sprintf(szDel, "%s\\%s", szDir, fileinfo.name);
+            DeleteFile(szDel);
+        }
+
+        if (_findnext(hSearch, &fileinfo) == -1)
+            break;
+    }
+
+    _findclose(hSearch);
+}
+
+static void RemoveDir(char *pszDir)
+{
+    char szFileSpec[MAX_PATH];
+
+    sprintf(szFileSpec, "%s\\*.*", pszDir);
+
+    RemoveFiles(szFileSpec);
+    RemoveDirectory(pszDir);
+}
+
+static void RemoveRegValues(struct REGVALUE *pRegValues)
+{
+    struct REGVALUE *pCurValue;
+    HKEY hKey;
+    LONG nResult;
+
+    if (!pRegValues)
+        return;
+
+    for (pCurValue = pRegValues; pCurValue->pszKey; pCurValue++) {
+        nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pCurValue->pszKey, KEY_ALL_ACCESS, FALSE, &hKey, 0);
+
+        if (nResult == ERROR_SUCCESS) {
+            nResult = RegDeleteValue(hKey, pCurValue->pszValue);
+            RegCloseKey(hKey);
+        }
+
+        if (nResult != ERROR_SUCCESS)
+            ShowError(IDS_REG_DELETE_VALUE_ERROR, nResult);
+    }
+}
+
+static BOOL UninstallCredsTool()
+{
+    int nResult = WinExec("afscreds /uninstall", SW_HIDE);
+
+    if (nResult <= 31) {
+        if (nResult != ERROR_FILE_NOT_FOUND)
+            ShowError(IDS_CANT_UNINSTALL_AFSCREDS, nResult);
+    }
+
+    // Always return true.  We don't want the uninstall to completely fail just
+    // because the creds tool didn't uninstall.
+    return TRUE;
+}
+
+
+static char *GetTempDir()
+{
+    DWORD result;
+    static char szTempDir[MAX_PATH];
+
+    result = GetTempPath(sizeof(szTempDir) - 1, szTempDir);
+    if (result == 0)
+        return "\\";
+        
+    return szTempDir;
+}
+
+static char *GetRootInstallDir()
+{
+    char *psz;
+    static char szRootInstallDir[MAX_PATH] = "";
+
+    if (szRootInstallDir[0] == 0) {
+        strcpy(szRootInstallDir, pszInstallDir);
+    
+        // Strip off the app specific part of the install dir
+        psz = strrchr(szRootInstallDir, '\\');
+        if (psz)
+            *psz = 0;
+    }
+
+    return szRootInstallDir;
+}
+
+static BOOL ClientSpecificUninstall()
+{
+    int nChoice;
+
+    // This function needs to do two things.  First it needs to see if the server is
+    // installed, and if it is, ask the user if they really want to uninstall the
+    // client given that the server needs the client.  Second, if we are uninstalling
+    // the client, we need to uninstall the creds tool.
+
+    if (!bSilentMode) {
+        if (IsAppInstalled(&appServer)) {
+            nChoice = ShowMsg(IDS_CLIENT_NEEDED_BY_SERVER, MB_ICONQUESTION | MB_YESNO);
+            if (nChoice == IDNO)
+                return FALSE;       // Cancel the uninstall
+        }
+    }
+    
+    UninstallCredsTool();
+
+    return TRUE;
+}
+
+static struct APPINFO *GetApp()
+{
+#ifdef SERVER_UNINST
+    return &appServer;
+#elif CLIENT_UNINST
+    return &appClient;
+#elif CC_UNINST
+    return &appControlCenter;
+#elif LIGHT_CLIENT_UNINST
+    return &appLightClient;
+#elif DOCS_UNINST
+    return &appDocs;
+#else
+    return 0;
+#endif;
+}
+
+static void RememberInstallDir(char *pszInstallDir)
+{
+    HKEY hKey;
+
+    // We remember the install dir so that when the UninstUninitialize function is called
+    // by the InstallShield uninstaller, we can find out where we were installed to.  We
+    // have to do this because by the time that function is called, the registry values
+    // created at install time are already gone.  We need to be able to find out where we
+    // were installed so we can clean up anything IS couldn't uninstall.  If this fails in 
+    // any way then we don't care.  The only consequence is that some junk might be left on
+    // the users' system after an uninstall.
+    
+    LONG result = RegOpenKeyAlt(AFSREG_NULL_KEY, UNINSTALL_TEMP_INFO_KEY, KEY_WRITE, TRUE, &hKey, 0);
+    if (result != ERROR_SUCCESS)
+        return;
+
+    RegSetValueEx(hKey, INSTALL_DIR_VALUE_NAME, 0, REG_SZ, (PBYTE)pszInstallDir, strlen(pszInstallDir) + 1);    
+
+    RegCloseKey(hKey);
+}
+
+int SUCALLCONV SetSilentMode()
+{
+    bSilentMode = TRUE;
+
+    return 0;
+}
+
+static char *GetWinDir()
+{
+    static char szWinDir[MAX_PATH] = "";
+
+    if (!szWinDir[0]) 
+        GetWindowsDirectory(szWinDir, sizeof(szWinDir));
+    
+    return szWinDir;
+}
+
+static char *GetWinSysDir()
+{
+    static char szWinSysDir[MAX_PATH] = "";
+
+    if (!szWinSysDir[0])
+        GetSystemDirectory(szWinSysDir, sizeof(szWinSysDir));
+    
+    return szWinSysDir;
+} 
+
+static char *GetLocaleID()
+{
+    static char szID[25] = "";
+
+    if (szID[0] == 0) {
+        LCID dwID = GetSystemDefaultLCID();
+        
+         // Nuke the high word.  It contains a sort ID.
+        dwID &= 0x0000FFFF;
+        
+        // Convert locale ID to a string
+        itoa(dwID, szID, 10);
+
+        // This thing should never be more than LOCALE_ID_LEN characters long.
+        szID[LOCALE_ID_LEN] = 0;
+    }
+
+    return szID;
+}
+
+static char *ExpandPath(char *pszFile)
+{
+    static char szPath[MAX_PATH];
+    char *psz;
+
+    szPath[0] = 0;
+
+    // Convert a path containing TARGETDIR, WINDIR, or WINSYSDIR to a 
+    // real path in the file system.  One of these MUST be the start of
+    // the file path passed in.  Also convert the string ???? to an
+    // actual locale number.
+    if (strncmp(pszFile, TARGETDIR, strlen(TARGETDIR)) == 0)
+        strcpy(szPath, GetRootInstallDir());
+    else if (strncmp(pszFile, WINDIR, strlen(WINDIR)) == 0)
+        strcpy(szPath, GetWinDir());
+    else if (strncmp(pszFile, WINSYSDIR, strlen(WINSYSDIR)) == 0)
+        strcpy(szPath, GetWinSysDir());
+    
+    if (szPath[0]) {    
+        psz = strchr(pszFile, '\\');
+        if (psz)
+            strcat(szPath, psz);
+    } else
+        strcpy(szPath, pszFile);
+
+    // Is this a language dll?
+    psz = strstr(szPath, "????.");
+    
+    // If it is, replace ???? with the locale number
+    if (psz)
+        strncpy(psz, GetLocaleID(), LOCALE_ID_LEN);
+
+    return szPath;
+}
+
+static BOOL FileNeededByOtherApp(struct APPINFO *pApp, struct FILEINFO *pFileInfo)
+{
+    // If the file is used by the server, the app being uninstalled is not the server, and
+    // the server is installed, then this file is used by another app.
+    if (!IsWinNT()) {
+        if ((pFileInfo->nUsedBy & LCLIENT) && (pApp != &appLightClient) && IsAppInstalled(&appLightClient))
+            return TRUE;
+        return FALSE;
+    }
+
+    if ((pFileInfo->nUsedBy & SERVER) && (pApp != &appServer) && IsAppInstalled(&appServer))
+        return TRUE;
+
+    if ((pFileInfo->nUsedBy & CLIENT) && (pApp != &appClient) && IsAppInstalled(&appClient))
+        return TRUE;
+
+    if ((pFileInfo->nUsedBy & CC) && (pApp != &appControlCenter) && IsAppInstalled(&appControlCenter))
+        return TRUE;
+    
+    return FALSE;
+}
+
+static void DeleteInUseFiles(struct APPINFO *pAppInfo, struct FILEINFO *pFileInfo)
+{
+    char szSrcPath[MAX_PATH];
+    char szDestPath[MAX_PATH];
+    char szTempDir[MAX_PATH];
+    int ii;
+
+    // If some app's file has been loaded before the app is uninstalled, then
+    // when an uninstall is attempted, the application and all of the dlls that
+    // its uses will be in use and IS will not be able to delete them.  Normally this
+    // is not a problem because IS will tell the user to reboot to finish the uninstall.
+    // However, we must support the ability to perform a silent uninstall followed
+    // immediatly by an install of the same product to the same directories.  If we let
+    // IS handle the uninstall of these files, this is not possible.  The reason is that
+    // when IS fails to remove these in use files, it marks them for deletion after the
+    // next reboot, which is fine.  Unfortunately, it leaves them in the dirs they were
+    // installed to.  So if we don't immediately reboot and perform an install to the
+    // same dirs, once a reboot is performed, those files get deleted and we have a 
+    // broken installation.
+
+    // What we will do to fix all of this, is when the client is uninstalled, but
+    // before IS does anything, we will move the in use files and associated dlls
+    // into the temp dir and mark them for delete after a reboot.  Then an install
+    // that follows will succeed.
+
+    // Delete the files that may be in use.  If they are we actually move
+    // them to the temp dir and mark them for deletion after the next reboot.
+    for (ii = 0; pFileInfo[ii].pszName != 0; ii++) {
+        // Get the source path
+        strcpy(szSrcPath, ExpandPath(pFileInfo[ii].pszName));
+
+        // Only delete the file if it is not used by some other app
+        if (FileNeededByOtherApp(pAppInfo, &pFileInfo[ii]))
+            continue;
+
+        // If the file doesn't exist then go on to the next file.
+        if (_access(szSrcPath, 0) != 0)
+            continue;
+            
+        // See if we can do a regular delete of the file
+        if (DeleteFile(szSrcPath)) {
+            SetSharedFileRefCount(szSrcPath, 0);
+            continue;
+        }
+
+        // Get a temp dir that is on the same drive as the src path.
+        // We can't move an in use file to a different drive.
+        strcpy(szTempDir, GetTempDir());
+        if (szTempDir[0] != szSrcPath[0]) {
+            // Get the drive, colon, and slash of the src path
+            strncpy(szTempDir, szSrcPath, 3);
+            szTempDir[3] = 0;
+        }
+        
+        // Get the dest path - we will rename the file during the move
+        GetTempFileName(szTempDir, "AFS", 0, szDestPath);
+
+        // Move from source to dest, marking the file for deletion after a reboot
+        if (IsWin95()) {
+            if (MoveFile(szSrcPath, szDestPath)) {            
+                WritePrivateProfileString("rename", szSrcPath, szDestPath, "wininit.ini");
+                SetSharedFileRefCount(szSrcPath, 0);
+            }
+        } else {    // WinNT or Win98
+            if (MoveFileEx(szSrcPath, szDestPath, MOVEFILE_REPLACE_EXISTING)) {
+                SetFileAttributes(szDestPath, FILE_ATTRIBUTE_NORMAL);
+                MoveFileEx(szDestPath, 0, MOVEFILE_DELAY_UNTIL_REBOOT);
+                SetSharedFileRefCount(szSrcPath, 0);
+            }
+        }
+    }
+}
+
+// Delete a directory and all its files and subdirectories - Yee haaa!
+static void RemoveDirectoryTree(char *pszDir)
+{
+    HANDLE hFind;
+    WIN32_FIND_DATA findFileData;
+    char szSpec[MAX_PATH];
+    char szSubFileOrDir[MAX_PATH];
+    BOOL bContinue;
+
+    sprintf(szSpec, "%s\\*.*", pszDir);
+    
+    // First delete the contents of the dir
+    hFind = FindFirstFile(szSpec, &findFileData);
+    bContinue = (hFind != INVALID_HANDLE_VALUE);
+    
+    while (bContinue) {
+        if ((strcmp(findFileData.cFileName, ".") != 0) && (strcmp(findFileData.cFileName, "..") != 0)) {
+            sprintf(szSubFileOrDir, "%s\\%s", pszDir, findFileData.cFileName);
+            
+            if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                RemoveDirectoryTree(szSubFileOrDir);
+            else
+                DeleteFile(szSubFileOrDir);
+        }
+
+        bContinue = FindNextFile(hFind, &findFileData);
+    }
+
+    FindClose(hFind);
+        
+    // Now remove the dir
+    RemoveDirectory(pszDir);
+} 
+
+static char *GetStartMenuRoot()
+{
+    HKEY hKey;
+    LONG nResult;
+    DWORD dwType;
+    DWORD dwSize;
+    char *pszKey;
+    char *pszValue;
+
+    static char szStartMenuRoot[MAX_PATH] = "";
+
+    if (szStartMenuRoot[0] == 0) {
+        dwSize = sizeof(szStartMenuRoot);
+    
+        if (IsWinNT()) {
+            pszKey = WINNT_START_MENU_REG_KEY;
+            pszValue = WINNT_START_MENU_REG_VALUE;
+        } else {
+            pszKey = WIN9X_START_MENU_REG_KEY;
+            pszValue = WIN9X_START_MENU_REG_VALUE;
+        }
+        
+        nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
+        if (nResult == ERROR_SUCCESS) {
+            nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szStartMenuRoot, &dwSize);
+            RegCloseKey(hKey);
+        }
+
+        if (nResult != ERROR_SUCCESS)
+            return 0;
+    }
+
+    FilepathNormalizeEx(szStartMenuRoot, FPN_BACK_SLASHES);
+
+    return szStartMenuRoot;
+}
+
+static char *GetAfsStartMenuRoot()
+{
+    static char szAfsStartMenuRoot[MAX_PATH] = "";
+    char *pszStartMenuRoot;
+    
+    if (szAfsStartMenuRoot[0] == 0) {    
+        pszStartMenuRoot = GetStartMenuRoot();
+        if (!pszStartMenuRoot)
+            return 0;
+
+        if (bSilentMode)
+            sprintf(szAfsStartMenuRoot, "%s\\IBM WebSphere\\Performance Pack\\AFS", pszStartMenuRoot );
+        else
+            sprintf(szAfsStartMenuRoot, "%s\\IBM AFS", pszStartMenuRoot );
+    }
+
+    return szAfsStartMenuRoot;
+}
+
+static BOOL IsADir(char *pszName)
+{
+    struct _stat statbuf;
+
+    if (_stat(pszName, &statbuf) < 0)
+        return FALSE;
+
+    return statbuf.st_mode & _S_IFDIR;
+}
+
+static void DeleteStartMenuEntries(char *pszEntries)
+{
+    char szStartMenuPath[MAX_PATH];
+    char *pszAfsStartMenuRoot;
+    char *pszCurEntry;
+
+    pszAfsStartMenuRoot = GetAfsStartMenuRoot();
+
+    if (!pszAfsStartMenuRoot)
+        return;
+        
+    for (pszCurEntry = pszEntries; *pszCurEntry; pszCurEntry += strlen(pszCurEntry) + 1) {
+        sprintf(szStartMenuPath, "%s\\%s", pszAfsStartMenuRoot, pszCurEntry);
+        if (IsADir(szStartMenuPath))
+            RemoveDirectoryTree(szStartMenuPath);
+        else
+            DeleteFile(szStartMenuPath);
+    }
+}
+
+static void RefreshStartMenu()
+{
+    char *pszAfsStartMenuRoot;
+    char szTemp[MAX_PATH];
+    
+    pszAfsStartMenuRoot = GetAfsStartMenuRoot();
+    if (!pszAfsStartMenuRoot)
+        return;
+
+    sprintf(szTemp, "%s - Refresh Attempt", pszAfsStartMenuRoot);
+        
+    // Deleting items from below the root level of the start menu does not 
+    // cause it to refresh.  In order that users can see changes without
+    // rebooting we will temporarily rename our root most entry, which 
+    // does cause a refresh of the start menu.
+    MoveFileEx(pszAfsStartMenuRoot, szTemp, MOVEFILE_REPLACE_EXISTING);
+    MoveFileEx(szTemp, pszAfsStartMenuRoot, MOVEFILE_REPLACE_EXISTING);
+}
+
+static BOOL PreserveConfigInfo(struct APPINFO *pApp)
+{
+    char *pszRegKey;
+    char szDestKey[256];
+    LONG result;
+
+    bPreserveConfigInfo = TRUE;
+
+    // If not in silent mode, ask user if they want to preserve the cfg info
+    if (!bSilentMode) {
+        int nChoice = ShowMsg(pApp->nPreserveConfigInfoMsgID, MB_ICONQUESTION | MB_YESNOCANCEL);
+        if (nChoice == IDCANCEL)
+            return FALSE;                   // Cancel the uninstall
+        else if (nChoice == IDNO) {     
+            bPreserveConfigInfo = FALSE;    // User doesn't want to preserve the config info
+            return TRUE;
+        }
+    }
+
+    // Copy each reg key (and all of its subkeys and values) to another place in the registry.
+    for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
+        if (!DoesRegKeyExist(pszRegKey))
+            continue;
+
+        // Create the destination path for the copy
+        sprintf(szDestKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
+
+        // Try to copy it
+        result = RegDupKeyAlt(pszRegKey, szDestKey);
+
+        if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND)) {
+            // If the copy failed, then delete any copies that succeeded
+            sprintf(szDestKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
+            RegDeleteEntryAlt(szDestKey, REGENTRY_KEY);
+               goto done;
+        }
+    }
+
+       // Remember the integrated login setting if this app supports that and it was turned on
+       if (pApp->pszNetworkProviderOrder) {
+               // Was integerated login turned on?
+               BOOL bOn, bOk;
+               bOk = InNetworkProviderOrder(pApp->pszNetworkProviderOrder, &bOn);
+               if (bOk && bOn) {
+                       HKEY hKey;
+                       sprintf(szDestKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
+                       result = RegOpenKeyAlt(AFSREG_NULL_KEY, szDestKey, KEY_WRITE, TRUE, &hKey, 0);
+                       // The existance of the key is a flag indicating that integrated login was turned on
+                       RegCloseKey(hKey);
+               }
+       }
+       
+done:
+       if ((result == ERROR_SUCCESS) || bSilentMode)
+           return TRUE;    // Continue with uninstall
+
+    // Report the error and ask the user if they want to continue the uninstall
+    return (ShowMsg(IDS_SAVE_OF_CONFIG_INFO_FAILED, MB_ICONEXCLAMATION | MB_YESNO) == IDYES);                  
+}
+
+int SUCALLCONV RestoreConfigInfo(int nApp)
+{
+    char *pszRegKey;
+    char szSrcKey[256];
+    struct APPINFO *pApp = 0;
+    BOOL bError = FALSE;
+    LONG result;
+
+    switch (nApp) {
+        case SERVER:    pApp = &appServer;          break;
+        case CLIENT:    pApp = &appClient;          break;
+        case LCLIENT:   pApp = &appLightClient;     break;
+        case CC:        pApp = &appControlCenter;   break;
+    }
+    
+    if (!pApp)
+        return -1;
+        
+    // Copy each reg key (and all of its subkeys and values) back to its original place in the registry.
+    for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
+        // Create the source path for the copy
+        sprintf(szSrcKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
+
+        if (!DoesRegKeyExist(szSrcKey))
+            continue;
+
+        // Try to restore as many of the keys as possible.  Report any errors at the end.
+
+        // Try to copy it
+        result = RegDupKeyAlt(szSrcKey, pszRegKey);
+        if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND))
+            bError = TRUE;
+    }
+
+       // Restore integrated login if this app was using it
+       if (pApp->pszNetworkProviderOrder) {
+               // Check if integrated login was turned on.  The IntegratedLogin key is a flag
+               // telling us that it was on.  If the key does not exist, integrated login was
+               // not on.
+               sprintf(szSrcKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
+               if (DoesRegKeyExist(szSrcKey)) {
+                       if (!AddToProviderOrder(pApp->pszNetworkProviderOrder))
+                               bError = TRUE;
+               }
+       }
+
+    // Remove our saved copies of the config info
+    sprintf(szSrcKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
+    RegDeleteEntryAlt(szSrcKey, REGENTRY_KEY);
+            
+    if (bError)
+        ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, 0);
+
+    return TRUE;
+}
+
+static BOOL DoSubKeysExist(char *pszKey)
+{
+    LONG result;
+    HKEY hKey;
+    char *pszSubKeys = 0;
+    BOOL bExist;
+    
+    result = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
+    if (result != ERROR_SUCCESS)
+        return FALSE;
+        
+    result = RegEnumKeyAlt(hKey,  &pszSubKeys);
+    RegCloseKey(hKey);
+    
+    if (result != ERROR_SUCCESS)
+        return FALSE;
+   
+    if (pszSubKeys) {
+        bExist = TRUE;
+        free(pszSubKeys);
+    } else
+        bExist = FALSE;    
+
+    return bExist;
+}
+
+/*
+ * The following definitions are taken from richedit.h:
+ *
+ */
+
+#define EM_SETBKGNDCOLOR               (WM_USER + 67) // from Richedit.h
+#define EM_STREAMIN                            (WM_USER + 73) // from Richedit.h
+#define SF_RTF                         0x0002
+
+typedef DWORD (CALLBACK *EDITSTREAMCALLBACK)(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
+
+typedef struct _editstream {
+       DWORD dwCookie;         /* user value passed to callback as first parameter */
+       DWORD dwError;          /* last error */
+       EDITSTREAMCALLBACK pfnCallback;
+} EDITSTREAM;
+
+/*
+ *
+ */
+
+DWORD CALLBACK License_StreamText (DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
+{
+   LPTSTR psz = (LPTSTR)dwCookie;
+   LONG cchAvail = lstrlen(psz);
+   if ((*pcb = min(cchAvail, cb)) != 0) {
+      memcpy (pbBuff, psz, *pcb);
+      memmove (psz, &psz[*pcb], cchAvail - *pcb + 1);
+   }
+   return 0;
+}
+
+
+void License_OnInitDialog (HWND hDlg, LPTSTR pszFile)
+{
+    // Open the license file and shove its text in our RichEdit control
+    //
+    HANDLE hFile;
+    if ((hFile = CreateFile (pszFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) {
+
+        size_t cbText;
+        if ((cbText = GetFileSize (hFile, NULL)) != 0) {
+
+            LPTSTR abText = (LPTSTR)GlobalAlloc (GMEM_FIXED, cbText + 3);
+
+            DWORD cbRead;
+            if (ReadFile (hFile, abText, cbText, &cbRead, NULL)) {
+                abText[ cbRead ] = 0;
+
+                EDITSTREAM Stream;
+                memset (&Stream, 0x00, sizeof(Stream));
+                Stream.dwCookie = (DWORD)abText;
+                Stream.pfnCallback = License_StreamText;
+
+                SendDlgItemMessage (hDlg, IDC_TEXT, EM_STREAMIN, SF_RTF, (LPARAM)&Stream);
+            }
+
+            GlobalFree (abText);
+        }
+
+        CloseHandle (hFile);
+    }
+
+    // Make the control's background be gray
+    //
+    SendDlgItemMessage (hDlg, IDC_TEXT, EM_SETBKGNDCOLOR, FALSE, (LPARAM)GetSysColor(COLOR_BTNFACE));
+}
+
+BOOL CALLBACK License_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
+{
+    switch (msg) {
+        case WM_INITDIALOG:
+            SetWindowLong (hDlg, DWL_USER, lp);
+            License_OnInitDialog (hDlg, (LPTSTR)lp);
+            break;
+
+        case WM_COMMAND:
+            switch (LOWORD(wp)) {
+                case IDCANCEL:
+                case IDOK:
+                    EndDialog (hDlg, LOWORD(wp));
+                    break;
+
+                case IDC_PRINT:
+                    TCHAR szDir[ MAX_PATH ];
+                    GetCurrentDirectory (MAX_PATH, szDir);
+                    ShellExecute (hDlg, TEXT("print"), (LPTSTR)GetWindowLong (hDlg, DWL_USER), NULL, szDir, SW_HIDE);
+                    break;
+            }
+            break;
+    }
+    return FALSE;
+}
+
+BOOL FindAfsInstallationPathByComponent (LPTSTR pszInstallationPath, LPTSTR pszComponent)
+{
+    *pszInstallationPath = 0;
+
+    TCHAR szRegPath[ MAX_PATH ];
+    wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
+
+    HKEY hk;
+    if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0) {
+        DWORD dwType = REG_SZ;
+        DWORD dwSize = MAX_PATH;
+
+        if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszInstallationPath, &dwSize) == 0) {
+            *(LPTSTR)FindBaseFileName (pszInstallationPath) = TEXT('\0');
+
+            if (pszInstallationPath[0] && (pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] == TEXT('\\')))
+            pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] = TEXT('\0');
+        }
+
+        RegCloseKey (hk);
+    }
+
+    return !!*pszInstallationPath;
+}
+
+BOOL FindAfsInstallationPath (LPTSTR pszInstallationPath)
+{
+   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Client")))
+      return TRUE;
+   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Control Center")))
+      return TRUE;
+   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Server")))
+      return TRUE;
+   if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Supplemental Documentation")))
+      return TRUE;
+   return FALSE;
+}
+
+HINSTANCE LoadRichTextControl (void)
+{
+    HINSTANCE hInst;
+    if ((hInst = LoadLibrary ("riched20.dll")) != NULL)
+        return hInst;
+    if ((hInst = LoadLibrary ("riched32.dll")) != NULL)
+        return hInst;
+    if ((hInst = LoadLibrary ("riched.dll")) != NULL)
+        return hInst;
+    if ((hInst = LoadLibrary ("richedit.dll")) != NULL)
+        return hInst;
+    return NULL;
+}
+
+int SUCALLCONV ShowLicense (char *pszTarget, char *pszSource)
+{
+    // If the license already lives on this user's machine, don't show
+    // it again. This only has to be done if the user has never
+    // accepted the license agreement before (it's part of the setup
+    // program, so it gets installed if they've accepted it).
+    //
+    // We were handed a relative path of the form:
+    //    Documentation/html/license.rtf
+    //
+    // We'll need to find the AFS installation directory, in order to
+    // find that Documentation subtree.
+    //
+    BOOL fShowLicense = TRUE;
+
+    TCHAR szInstallationPath[ MAX_PATH ];
+    if (FindAfsInstallationPath (szInstallationPath)) {
+        TCHAR szLicensePath[ MAX_PATH ];
+        wsprintf (szLicensePath, TEXT("%s\\%s"), szInstallationPath, pszTarget);
+
+        if (GetFileAttributes (szLicensePath) != (DWORD)-1) {
+            fShowLicense = FALSE;
+        }
+    }
+
+    // Before we can show the license file, we have to prepare the RichEdit
+    // control. That means loading the appropriate library and calling its
+    // initialization functions.
+    //
+    HINSTANCE hRichEdit;
+    if ((hRichEdit = LoadRichTextControl()) != NULL) {
+
+        // If we must show the license, do so now. This is a modal dialog,
+        // so we'll know whether or not the user accepts the license.
+        //
+        if (ModalDialogParam (IDD_LICENSE, GetActiveWindow(), License_DlgProc, (LPARAM)pszSource) == IDCANCEL) {
+            // The user rejected the license; fail setup
+            return FALSE;
+        }
+
+       FreeLibrary (hRichEdit);
+    }
+
+    // The user accepted the license, so we can continue with Setup.
+    // The license file is installed as part of Setup.
+    return TRUE;
+}
+
+int SUCALLCONV UninstInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
+{
+    char szPath[MAX_PATH];
+    struct APPINFO *pAppInfo;
+    char *pszFile = 0;
+    char *pszSubDir = 0;
+
+    hDlg = hIS;
+
+    bSilentMode = !IsWindowVisible(hIS);
+
+    // Which app are we uninstalling?
+    pAppInfo = GetApp();
+    if (!pAppInfo) {
+        ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
+        return -1;
+    }
+
+    // Get the app's install dir
+    pszInstallDir = GetAppInstallDir(pAppInfo, FALSE);
+    if (!pszInstallDir)
+        return -1;
+
+    // If this app has a custom uninstall func, call it here
+    if (pAppInfo->pUninstallFunc)
+        if (!pAppInfo->pUninstallFunc())
+            return -1;
+
+    if (pAppInfo->pszRegKeysToPreserve)
+        if (!PreserveConfigInfo(pAppInfo))
+            return -1;
+
+    // Unconfigure the service, if there is one for this app
+    if (pAppInfo->pszSvcKey) {
+        if (IsServiceInstalled(pAppInfo->pszSvcKey))
+            if (UninstallService(pAppInfo) == 1)
+                return -1;
+    }
+
+    RememberInstallDir(pszInstallDir);
+
+    DeleteInUseFiles(pAppInfo, fileInfo);
+
+    // Remove the app's bin path from the system path
+    if (pAppInfo->pszBinPath) {
+        BuildShortPath(szPath, sizeof(szPath), pszInstallDir, pAppInfo->pszBinPath);
+        RemoveFromPath(szPath);
+    }
+
+    // Remove entry from NetworkProvider\Order key in registry
+    if (pAppInfo->pszNetworkProviderOrder)
+        RemoveFromNetworkProviderOrder(pAppInfo->pszNetworkProviderOrder);
+
+    // Remove any generated subdirectories
+    if (!bPreserveConfigInfo && pAppInfo->pszDirsToDel) {
+        for (pszSubDir = pAppInfo->pszDirsToDel; *pszSubDir; pszSubDir += strlen(pszSubDir) + 1)
+            RemoveDir(ExpandPath(pszSubDir));
+    }
+
+    // Remove any generated files
+    if (!bPreserveConfigInfo && pAppInfo->pszFilesToDel) {
+        for (pszFile = pAppInfo->pszFilesToDel; *pszFile; pszFile += strlen(pszFile) + 1)
+            RemoveFiles(ExpandPath(pszFile));
+    }
+
+    // Remove any registry values that IS can't handle
+    RemoveRegValues(pAppInfo->pRegValues);
+    if (IsWinNT())
+        RemoveRegValues(pAppInfo->pWinNTRegValues);
+    else    
+        RemoveRegValues(pAppInfo->pWin9XRegValues);
+
+    // Remove the start menu entries for this app
+    if (pAppInfo->pszStartMenuEntries) {
+        DeleteStartMenuEntries(pAppInfo->pszStartMenuEntries);
+        RefreshStartMenu();
+    }
+
+    // Remove the install dir
+    RemoveDirectory(pszInstallDir);
+
+    return 0;
+}
+
+void SUCALLCONV UninstUnInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
+{
+    char *pszInstallDir;
+    char szDirPath[MAX_PATH];
+    char *psz;
+    struct APPINFO *pAppInfo;
+
+    // If we just uninstalled the last AFS app, then do some cleanup.
+    if (IsAppInstalled(&appServer) || IsAppInstalled(&appClient) ||
+        IsAppInstalled(&appControlCenter) || IsAppInstalled(&appLightClient) ||
+        IsAppInstalled(&appDocs))
+    {
+        return;
+    }
+
+    bSilentMode = !IsWindowVisible(hIS);
+    
+    // Which app did we just uninstall?
+    pAppInfo = GetApp();
+    if (!pAppInfo) {
+        ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
+        return;
+    }
+
+    // Get the app's install dir
+    pszInstallDir = GetAppInstallDir(pAppInfo, TRUE);
+    if (!pszInstallDir)
+        return;
+
+    // Remove the reg key we used to remember the app install dir
+    RegDeleteEntryAlt(UNINSTALL_TEMP_INFO_KEY, REGENTRY_KEY);
+
+    // Try to remove the reg key used to store config info, but only
+    // if there are no app config info sub keys present.
+    if (!DoSubKeysExist(AFS_PRESERVED_CFG_INFO_KEY))
+        RegDeleteEntryAlt(AFS_PRESERVED_CFG_INFO_KEY, REGENTRY_KEY);
+
+    // Remove the install dir
+    RemoveDirectory(pszInstallDir);
+
+    // Attempt to remove the install root and common directories.  The are 
+    // shared and so no single app knows to delete them.
+
+    // Strip off the app specific part of the install dir
+    psz = strrchr(pszInstallDir, '\\');
+    if (psz)
+        *psz = 0;
+
+    sprintf(szDirPath, "%s\\%s", pszInstallDir, "Common");
+    RemoveDirectory(szDirPath);
+
+    // Remove the Common directory from the system path
+    RemoveFromPath(szDirPath);
+
+    // Remove all of the documentation dirs
+    sprintf(szDirPath, "%s\\%s", pszInstallDir, "Documentation");
+    RemoveDirectoryTree(szDirPath);
+
+    // Ok, up to this point we have been removing files we know we
+    // created.  However, after this point we are into the path
+    // that the user chose for our install root.  The default for
+    // this is IBM/Afs, but they could have chosen anything,
+    // including a dir or dirs that have other products in them.
+    // We will check to see if it is IBM\AFS and if it is then we 
+    // will attempt to remove them.
+    
+    // Back up a level and look for AFS
+    psz = strrchr(pszInstallDir, '\\');
+    if (psz) {
+        if (stricmp(psz + 1, "AFS") == 0) {
+            RemoveDirectory(pszInstallDir);
+            *psz = 0;
+        }
+    }
+
+    // Back up a level and look for IBM
+    psz = strrchr(pszInstallDir, '\\');
+    if (psz) {
+        if (stricmp(psz + 1, "IBM") == 0) {
+            RemoveDirectory(pszInstallDir);
+            *psz = 0;
+        }
+    }
+
+    // Remove the root afs start menu entry
+    psz = GetStartMenuRoot();
+    if (psz) {
+        if (bSilentMode) {
+            // Remove everything under our branch
+            sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack\\AFS", psz);
+            RemoveDirectoryTree(szDirPath);
+            
+            // Remove the IBM stuff only if the dirs are empty
+            sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack", psz);
+            if (RemoveDirectory(szDirPath)) {
+                sprintf(szDirPath, "%s\\IBM WebSphere", psz);
+                RemoveDirectory(szDirPath);
+            }
+        } else {
+            sprintf(szDirPath, "%s\\IBM AFS", psz);
+            RemoveDirectoryTree(szDirPath);
+        }
+    }
+}
+
+BOOLEAN _stdcall DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH) {
+        hinst = (HINSTANCE)dll;
+        TaLocale_LoadCorrespondingModuleByName (hinst, "afs_setup_utils.dll");
+    }
+
+    return TRUE;
+}
+
+extern "C" int WINAPI Test (HINSTANCE hInst, HINSTANCE hPrev, LPSTR psz, int nCmdShow)
+{
+   ShowLicense ("TEST", "\\\\fury\\afssetup\\license\\ja_JP.rtf");
+   return 0;
+}
+
+