windows-build-updates-20030314
[openafs.git] / src / WINNT / afs_setup_utils / afs_setup_utils.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * INCLUDES _________________________________________________________________
12  *
13  */
14
15 extern "C" {
16 #include <afs/param.h>
17 #include <afs/stds.h>
18 #include <afs/fileutil.h>
19 }
20
21 #include <windows.h>
22 #include <stdio.h>
23 #include <time.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <io.h>
27 #include <string.h>
28 #include <SYS\STAT.H>
29 #include <shellapi.h>
30
31 #include <WINNT/afsreg.h>
32 #include <WINNT/afssw.h>
33 #include <WINNT/talocale.h>
34 #include <winnt/osi_malloc.h>
35
36 #include "resource.h"
37 #include "progress_dlg.h"
38 #include "sutil.h"
39 #include "forceremove.h"
40
41
42 /*
43  * PROTOTYPES _________________________________________________________________
44  *
45  */
46 static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered);
47 BOOL UninstallCredsTool();
48 BOOL ServerSpecificUninstall();
49 BOOL ClientSpecificUninstall();
50
51
52
53 /*
54  * DEFINITIONS _________________________________________________________________
55  *
56  */
57 #define SUCALLCONV  WINAPI
58
59 #define UNINSTALL_TEMP_INFO_KEY     "HKEY_LOCAL_MACHINE\\Software\\AfsUninstallTempInfo"
60 #define INSTALL_DIR_VALUE_NAME      "InstallDir"
61
62 #define AFS_PRESERVED_CFG_INFO_KEY  "HKEY_LOCAL_MACHINE\\Software\\AfsPreservedConfigInfo"
63
64 #define MS_SHARED_FILES_KEY         "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"
65
66 // Log file to use when running in silent mode
67 #define UNINSTALL_ERROR_LOG_NAME    "\\AfsUninstallErrorLog.txt"
68 #define INSTALL_ERROR_LOG_NAME      "\\AfsInstallErrorLog.txt"
69
70 #define TARGETDIR                   "<TARGETDIR>"
71 #define WINDIR                      "<WINDIR>"
72 #define WINSYSDIR                   "<WINSYSDIR>"
73
74 #define WIN9X_START_MENU_REG_KEY    "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
75 #define WIN9X_START_MENU_REG_VALUE  "Programs"
76     
77 #define WINNT_START_MENU_REG_KEY    "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
78 #define WINNT_START_MENU_REG_VALUE  "Common Programs"
79
80 #define LOCALE_ID_LEN               4
81
82 struct REGVALUE {
83     char *pszKey;
84     char *pszValue;
85 };
86
87
88 typedef BOOL (APP_UNINSTALL_FUNC)();
89
90
91
92 struct APPINFO {
93     char *pszAppName;
94
95     // Service Info
96     char *pszSvcName;
97     char *pszSvcKey;
98     char *pszSvcDependOn;
99     char *pszSvcDisplayName;
100
101     char *pszNetworkProviderOrder;
102
103     // Message to use to tell the user that we have to stop the service
104     int nServiceShutdownMsgID;
105
106     // Message to use for the progress dialog that is shown while
107     // waiting for the service to stop.
108     int nServiceShutdownProgressMsgID;
109
110     // Location in registry of a key we can use to know that the app is installed
111     char *pszAppKey;
112
113     // Location in registry of this app's install dir
114     struct REGVALUE regInstallDir;
115
116     // Path Info
117     char *pszLocalRoot;     // The root dir below the install dir
118     char *pszBinPath;       // Path to remove from the system path
119
120     // Generated files and directories to delete.  These are both multistring lists.
121     char *pszDirsToDel;     // All files in these dirs will be deleted
122     char *pszFilesToDel;    // Use this if you want to delete files but leave the dir.  Wildcards can be used.
123
124     // Registry values to remove
125     struct REGVALUE *pRegValues;
126     struct REGVALUE *pWinNTRegValues;   // Only remove these if running WinNT
127     struct REGVALUE *pWin9XRegValues;   // Only remove these if running Win9X
128
129     // Start menu entries to delete
130     char *pszStartMenuEntries;
131
132     // Registry keys to save if a user wants to preserve config info during uninstall
133     char *pszRegKeysToPreserve;
134     int nPreserveConfigInfoMsgID;
135
136     // Uninstall func - used for things specific to this app
137     APP_UNINSTALL_FUNC *pUninstallFunc;
138 };
139
140
141 /*
142  * App info structure for the Server product
143  */
144 struct APPINFO appServer = {
145     "AFS Server",
146     
147     AFSREG_SVR_SVC_NAME,
148     AFSREG_SVR_SVC_KEY,
149     0,  // No depend on
150     AFSREG_SVR_SVC_DISPLAYNAME_DATA,
151
152     0,  // No network provider order
153
154     IDS_MUST_STOP_SERVER,
155     IDS_WAITING_FOR_SERVER_TO_STOP,
156
157     AFSREG_SVR_SW_VERSION_KEY,
158     
159     { AFSREG_SVR_SW_VERSION_KEY, AFSREG_SVR_SW_VERSION_DIR_VALUE },
160
161     "\\Server",
162     "\\usr\\afs\\bin",
163
164     // Dirs to delete
165     TARGETDIR"\\Server\\usr\\afs\\bin\\backup\0"
166     TARGETDIR"\\Server\\usr\\afs\\bin\0"
167     TARGETDIR"\\Server\\usr\\afs\\db\0"
168     TARGETDIR"\\Server\\usr\\afs\\logs\0"
169     TARGETDIR"\\Server\\usr\\afs\\etc\0"
170     TARGETDIR"\\Server\\usr\\afs\\local\0"
171     TARGETDIR"\\Server\\usr\\afs\0"
172     TARGETDIR"\\Server\\usr\0",
173     
174     // Files to delete
175     TARGETDIR"\\Common\\*.gid\0"
176     TARGETDIR"\\Common\\*.fts\0",
177
178     0,  // No reg values
179     0,  // No NT only reg values
180     0,  // No 9x only reg values
181
182     "Server\0",
183
184     // Config info to preserve
185     AFSREG_SVR_SVC_KEY"\0", 
186     IDS_PRESERVE_SERVER_CONFIG_INFO,
187
188     0   // No special uninstall function
189 };
190
191 // Registry values to remove for the Client
192 struct REGVALUE clientRegValues[] = {
193     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", "{DC515C27-6CAC-11D1-BAE7-00C04FD140D2}" },
194     { 0, 0 }    // This indicates there are no more entries
195 };
196
197 struct REGVALUE clientWinNTRegValues[] = {
198     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\File Manager\\AddOns", "AFS Client FME" },
199     { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NetBT\\Parameters", "SMBDeviceEnabled" },
200     { 0, 0 }
201 };
202
203 struct REGVALUE clientWin9XRegValues[] = {
204     { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\NetworkProvider\\Order", "TransarcAFSDaemon" },
205     { 0, 0 }
206 };
207
208 /*
209  * App info structure for the Client product
210  */
211 struct APPINFO appClient = {
212     "AFS Client",
213     
214     AFSREG_CLT_SVC_NAME,
215     AFSREG_CLT_SVC_KEY,
216     "5250435353004E657462696F730000",
217     AFSREG_CLT_SVC_DISPLAYNAME_DATA,
218
219     AFSREG_CLT_SVC_NAME,
220
221     IDS_MUST_STOP_CLIENT,
222     IDS_WAITING_FOR_CLIENT_TO_STOP,
223
224     AFSREG_CLT_SW_VERSION_KEY,
225
226     { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },
227
228     "\\Client",
229     "\\Program",
230
231     // No dirs to delete
232     0,
233     
234     // Files to delete
235     TARGETDIR"\\Common\\*.gid\0"
236     TARGETDIR"\\Common\\*.fts\0"
237     WINDIR"\\..\\AFSCache\0"
238     WINDIR"\\afsd.log\0"
239     WINDIR"\\afsd.ini\0"
240     WINDIR"\\afsdsbmt.ini\0"
241     WINDIR"\\afsdcell.ini\0"
242     WINDIR"\\afsd_init.log\0",
243     
244     clientRegValues,
245     clientWinNTRegValues,
246     clientWin9XRegValues,
247
248     // Start menu entries to remove
249     "Client\0",
250
251     // Config info to preserve
252     AFSREG_CLT_SVC_KEY"\0",
253     IDS_PRESERVE_CLIENT_CONFIG_INFO,
254
255     ClientSpecificUninstall
256 };
257
258
259 /*
260  * App info structure for the Light Client product
261  */
262 struct APPINFO appLightClient = {
263     "AFS Light",
264     
265     // No service info 
266     0,
267     0,
268     0,
269     0,
270
271     AFSREG_CLT_SVC_NAME,
272
273     // No service shutdown messages
274     0,
275     0,
276
277     "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Light Client",
278
279     { AFSREG_CLT_SW_VERSION_KEY, AFSREG_CLT_SW_VERSION_DIR_VALUE },
280
281     "\\Client",
282     "\\Program",
283
284     // No dirs to delete
285     0,
286     
287     // Files to delete
288     TARGETDIR"\\Common\\*.gid\0"
289     TARGETDIR"\\Common\\*.fts\0",
290
291     clientRegValues,
292     clientWinNTRegValues,
293     clientWin9XRegValues,
294
295     // Start menu entries to remove
296     "Light\0",
297
298     // Config info to preserve
299     AFSREG_CLT_SVC_KEY"\0",
300     IDS_PRESERVE_LIGHT_CLIENT_CONFIG_INFO,
301
302     UninstallCredsTool
303 };
304
305
306 /*
307  * App info structure for the Control Center product
308  */
309 struct APPINFO appControlCenter = {
310     "AFS Control Center",
311     
312     // No service info
313     0,
314     0,
315     0,
316     0,
317
318     // No network provider order
319     0,
320
321     // No service shutdown messages    
322     0,
323     0,
324
325     "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center",
326     
327     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Control Center\\CurrentVersion", "PathName" },
328
329     "\\Control Center",
330     "",
331
332     // No dirs to delete
333     0,
334     
335     // Files to delete
336     TARGETDIR"\\Common\\*.gid\0"
337     TARGETDIR"\\Common\\*.fts\0",
338     
339     0,  // No reg values
340     0,  // No NT only reg values
341     0,  // No 9x only reg values
342
343     // Start menu entries to remove
344     "Control Center\0",
345
346     // Config info to preserve
347     AFSREG_CLT_SVC_KEY"\0",
348     IDS_PRESERVE_CC_CONFIG_INFO,
349
350     0   // No uninstall function
351 };
352
353
354 /*
355  * App info structure for the Sys Admin Doc files
356  */
357 struct APPINFO appDocs = {
358     "AFS Supplemental Documentation",
359
360     // No service info
361     0,
362     0,
363     0,
364     0,
365
366     // No network provider order
367     0,
368
369     // No service shutdown messages    
370     0,
371     0,
372
373     "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation",
374     
375     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\TransarcCorporation\\AFS Supplemental Documentation\\CurrentVersion", "PathName" },
376
377     "\\Documentation",
378     "",
379
380     // No dirs to delete
381     0,
382     
383     // Files to delete
384     TARGETDIR"\\Common\\*.gid\0"
385     TARGETDIR"\\Common\\*.fts\0",
386
387     0,  // No reg values
388     0,  // No NT only reg values
389     0,  // No 9x only reg values
390
391     // Start menu entries to remove
392     "Documentation\\AFS for Windows Backup Command Reference.lnk\0Documentation\\AFS Command Reference Manual.lnk\0Documentation\\AFS System Administrator's Guide.lnk\0",
393
394     0,  // No config info to preserve
395
396     0   // No uninstall function
397 };
398
399
400 // Shared and in-use files
401 struct FILEINFO {
402     char *pszName;
403     int nUsedBy;
404 };
405
406 #define SERVER  1
407 #define CLIENT  2
408 #define LCLIENT 4
409 #define CC      8
410 #define DOCS    16
411
412
413 struct FILEINFO fileInfo[] = {
414     { TARGETDIR"\\Common\\afsbosadmin.dll",             SERVER | CC },
415     { TARGETDIR"\\Common\\afscfgadmin.dll",             SERVER | CC },
416     { TARGETDIR"\\Common\\afsclientadmin.dll",          SERVER | CLIENT | LCLIENT | CC },
417     { TARGETDIR"\\Common\\afskasadmin.dll",             SERVER | CC },
418     { TARGETDIR"\\Common\\afsptsadmin.dll",             SERVER | CC },
419     { TARGETDIR"\\Common\\afsvosadmin.dll",             SERVER | CC },
420     { TARGETDIR"\\Common\\afsadminutil.dll",            SERVER | CLIENT | LCLIENT | CC },
421     { TARGETDIR"\\Common\\afsrpc.dll",                  SERVER | CLIENT | LCLIENT | CC },
422     { TARGETDIR"\\Common\\afsauthent.dll",              SERVER | CLIENT | LCLIENT | CC },
423     { TARGETDIR"\\Common\\afspthread.dll",              SERVER | CLIENT | LCLIENT | CC },
424     { TARGETDIR"\\Common\\TaAfsAppLib.dll",             SERVER | CLIENT | LCLIENT | CC },
425     { TARGETDIR"\\Common\\afsprocmgmt.dll",             SERVER | CLIENT | LCLIENT },
426     { TARGETDIR"\\Common\\afs_config.exe",              CLIENT | LCLIENT| CC },
427     { TARGETDIR"\\Common\\afseventmsg_????.dll",        SERVER | CLIENT | LCLIENT | CC },
428     { TARGETDIR"\\Common\\afslegal_????.dll",           SERVER | CLIENT | LCLIENT | CC },
429     { TARGETDIR"\\Common\\afsserver_????.dll",          SERVER | CLIENT | LCLIENT | CC },
430     { TARGETDIR"\\Common\\afssvrcfg_????.dll",          SERVER | CLIENT | LCLIENT | CC },
431     { TARGETDIR"\\Common\\TaAfsAccountManager_????.dll",SERVER | CLIENT | LCLIENT | CC },
432     { TARGETDIR"\\Common\\TaAfsAppLib_????.dll",        SERVER | CLIENT | LCLIENT | CC },
433     { TARGETDIR"\\Common\\TaAfsServerManager_????.dll", SERVER | CLIENT | LCLIENT | CC },
434     { TARGETDIR"\\Common\\afscreds_????.dll",           SERVER | CLIENT | LCLIENT | CC },
435     { TARGETDIR"\\Common\\afs_config_????.dll",         SERVER | CLIENT | LCLIENT | CC },
436     { TARGETDIR"\\Common\\afs_cpa_????.dll",            SERVER | CLIENT | LCLIENT | CC },
437     { TARGETDIR"\\Common\\afs_shl_ext_????.dll",        SERVER | CLIENT | LCLIENT | CC },
438     { TARGETDIR"\\Common\\afs-nt.hlp",                  SERVER | CLIENT | LCLIENT | CC },
439     { TARGETDIR"\\Common\\afs-nt.cnt",                  SERVER | CLIENT | LCLIENT | CC },
440     { TARGETDIR"\\Common\\taafssvrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },
441     { TARGETDIR"\\Common\\taafssvrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },
442     { TARGETDIR"\\Common\\taafsusrmgr.cnt",             SERVER | CLIENT | LCLIENT | CC },
443     { TARGETDIR"\\Common\\taafsusrmgr.hlp",             SERVER | CLIENT | LCLIENT | CC },
444     { TARGETDIR"\\Common\\afs-cc.cnt",                  SERVER | CLIENT | LCLIENT | CC },
445     { TARGETDIR"\\Common\\afs-cc.hlp",                  SERVER | CLIENT | LCLIENT | CC },
446     { TARGETDIR"\\Common\\afs-light.cnt",               SERVER | CLIENT | LCLIENT | CC },
447     { TARGETDIR"\\Common\\afs-light.hlp",               SERVER | CLIENT | LCLIENT | CC },
448     { TARGETDIR"\\Common\\taafscfg.cnt",                SERVER | CLIENT | LCLIENT | CC },
449     { TARGETDIR"\\Common\\taafscfg.hlp",                SERVER | CLIENT | LCLIENT | CC },
450     { TARGETDIR"\\Client\\PROGRAM\\afs_shl_ext.dll",    CLIENT | LCLIENT },
451     { TARGETDIR"\\Client\\PROGRAM\\libafsconf.dll",     CLIENT | LCLIENT },
452     { TARGETDIR"\\Client\\PROGRAM\\afslogon.dll",       CLIENT },
453     { TARGETDIR"\\Client\\PROGRAM\\afslog95.dll",       LCLIENT },
454     { TARGETDIR"\\Control Center\\TaAfsAdmSvr.exe",     CC },
455     { WINSYSDIR"\\afs_cpa.cpl",                         CLIENT | LCLIENT | CC },
456     { WINSYSDIR"\\afsserver.cpl",                       SERVER },
457     { TARGETDIR"\\Common\\afsdcell.ini",                CLIENT | LCLIENT | CC },
458     { TARGETDIR"\\Documentation\\Html\\banner.gif",     SERVER | CLIENT | LCLIENT | CC | DOCS },
459     { TARGETDIR"\\Documentation\\Html\\books.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },
460     { TARGETDIR"\\Documentation\\Html\\bot.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
461     { TARGETDIR"\\Documentation\\Html\\index.gif",      SERVER | CLIENT | LCLIENT | CC | DOCS },
462     { TARGETDIR"\\Documentation\\Html\\index.htm",      SERVER | CLIENT | LCLIENT | CC | DOCS },
463     { TARGETDIR"\\Documentation\\Html\\next.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },
464     { TARGETDIR"\\Documentation\\Html\\prev.gif",       SERVER | CLIENT | LCLIENT | CC | DOCS },
465     { TARGETDIR"\\Documentation\\Html\\toc.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
466     { TARGETDIR"\\Documentation\\Html\\top.gif",        SERVER | CLIENT | LCLIENT | CC | DOCS },
467     { TARGETDIR"\\Documentation\\Html\\ReleaseNotes\\relnotes.htm",
468                                                         SERVER | CLIENT | LCLIENT | CC },
469     { TARGETDIR"\\Documentation\\Html\\InstallGd\\afsnt35i.htm",
470                                                         SERVER | CLIENT | LCLIENT | CC },
471     { 0,                                                0 }     // End of list
472 };
473
474
475 /*
476  * VARIABLES _________________________________________________________________
477  *
478  */
479 HINSTANCE hinst;
480
481 static HWND hDlg;
482 static BOOL bPreserveConfigInfo;
483 static BOOL bSilentMode;
484 static char *pszInstallDir;
485
486
487 /*
488  * FUNCTIONS _________________________________________________________________
489  *
490  */
491
492 static BOOL UpgradeClientIntParm(HKEY hKey, char *pszOldParm, char *pszNewParm)
493 {
494     int nData;
495     LONG result = ERROR_SUCCESS;
496
497     nData = GetPrivateProfileInt("AFS Client", pszOldParm, -1, "afsd.ini");
498     if (nData > -1)
499         result = RegSetValueEx(hKey, pszNewParm, 0, REG_DWORD, (BYTE *)&nData, sizeof(nData));
500
501     return (result == ERROR_SUCCESS);
502 }
503
504 static BOOL UpgradeClientStringParm(HKEY hKey, char *pszOldParm, char *pszNewParm)
505 {
506     char szData[1024];
507     LONG result = ERROR_SUCCESS;
508
509     GetPrivateProfileString("AFS Client", pszOldParm, "", szData, sizeof(szData), "afsd.ini");
510     if (szData[0])
511         result = RegSetValueEx(hKey, pszNewParm, 0, REG_SZ, (PBYTE)szData, strlen(szData) + 1);
512
513     return (result == ERROR_SUCCESS);
514 }
515
516 int SUCALLCONV Upgrade34ClientConfigInfo()
517 {
518     HKEY hKey;
519     LONG result;
520     int nData;
521     
522     result = RegOpenKeyAlt(AFSREG_NULL_KEY, AFSREG_CLT_SVC_PARAM_KEY, KEY_WRITE, TRUE, &hKey, 0);
523     if (result != ERROR_SUCCESS)
524         return -1;
525
526     UpgradeClientIntParm(hKey, "CacheSize", "CacheSize");
527     UpgradeClientIntParm(hKey, "Stats", "Stats");
528     UpgradeClientIntParm(hKey, "LogoffTokenTransfer", "LogoffTokenTransfer");
529     UpgradeClientIntParm(hKey, "LogoffTokenTransferTimeout", "LogoffTokenTransferTimeout");
530     UpgradeClientIntParm(hKey, "TrapOnPanic", "TrapOnPanic");
531     UpgradeClientIntParm(hKey, "TraceBufferSize", "TraceBufferSize");
532     UpgradeClientIntParm(hKey, "TraceOnShutdown", "TraceOnShutdown");
533     UpgradeClientIntParm(hKey, "ReportSessionStartups", "ReportSessionStartups");
534     
535     UpgradeClientStringParm(hKey, "MountRoot", "MountRoot");
536     UpgradeClientStringParm(hKey, "Cell", "Cell");
537
538     /* BlockSize to ChunkSize requires convertion */
539     nData = GetPrivateProfileInt("AFS Client", "BlockSize", -1, "afsd.ini");
540     if (nData > -1) {
541         DWORD chunkSize;
542         for (chunkSize = 0; (1 << chunkSize) < nData; chunkSize++);
543         (void) RegSetValueEx(hKey, "ChunkSize", 0, REG_DWORD, (BYTE *)&chunkSize, sizeof(chunkSize));
544     }
545
546     RegCloseKey(hKey);
547
548     return 0;
549 }
550
551 int SUCALLCONV Eradicate34Client()
552 {
553     if (Client34Eradicate(TRUE) != ERROR_SUCCESS)
554         return -1;
555
556     return 0;
557 }
558
559 // This function was written a long time ago by Mike Comer for use by the 
560 // original DFS Client for NT install program.
561 int SUCALLCONV CheckIfAdmin(void)
562 {
563     HANDLE                  token = INVALID_HANDLE_VALUE;
564     PVOID                   buffer = 0;
565     DWORD                   bufLength;
566     DWORD                   realBufLength;
567     TOKEN_PRIMARY_GROUP     *pgroup;
568     TOKEN_GROUPS            *groups;
569     int                     result = -1;
570     DWORD                   groupCount;
571     LONG                    status;
572     PSID                    AdministratorSID = NULL;
573     SID_IDENTIFIER_AUTHORITY    authority = SECURITY_NT_AUTHORITY;
574
575     // allocate the SID for the Administrators group
576     if (!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorSID)) {
577         status = GetLastError();
578         goto getout;
579     }
580
581     // open the process token
582     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token)) {
583         status = GetLastError();
584         token = INVALID_HANDLE_VALUE;
585         goto getout;
586     }
587
588     // check primary group first
589     buffer = GlobalAlloc(GMEM_FIXED, sizeof(TOKEN_PRIMARY_GROUP));
590     if (!buffer) {
591         goto getout;
592     }
593
594     bufLength = sizeof(TOKEN_PRIMARY_GROUP);
595     while(1) {
596         if (!GetTokenInformation(token, TokenPrimaryGroup, buffer, bufLength, &realBufLength)) {
597             if (realBufLength > bufLength) {
598                 // not enough space
599                 GlobalFree(buffer);
600                 bufLength = realBufLength;
601                 buffer = GlobalAlloc(GMEM_FIXED, realBufLength);
602                 if (!buffer) {
603                     goto getout;
604                 }
605                 continue;
606             }
607
608             goto getout;
609         }
610         break;
611     }
612
613     pgroup = (TOKEN_PRIMARY_GROUP *)buffer;
614     if (EqualSid(pgroup->PrimaryGroup, AdministratorSID)) {
615         result = 0;
616     } else {
617         // okay, try the secondary groups
618         while(1) {
619             if (!GetTokenInformation(token, TokenGroups, buffer, bufLength, &realBufLength)) {
620                 if (realBufLength > bufLength) {
621                     // not enough space
622                     GlobalFree(buffer);
623                     bufLength = realBufLength;
624                     buffer = GlobalAlloc(GMEM_FIXED, realBufLength);
625                     if (!buffer) {
626                         goto getout;
627                     }
628                     continue;
629                 }
630
631                 // a real error
632                 goto getout;
633             }
634             break;
635         }
636
637         // we have the list of groups here.  Process them:
638         groups = (TOKEN_GROUPS *)buffer;
639         for(groupCount = 0; groupCount < groups->GroupCount; groupCount++) {
640             if (EqualSid(groups->Groups[groupCount].Sid, AdministratorSID)) {
641                 result = 0;
642                 break;
643             }
644         }
645     }
646
647 getout:
648
649     if (token != INVALID_HANDLE_VALUE) {
650         CloseHandle(token);
651     }
652
653     if (buffer) {
654         GlobalFree(buffer);
655     }
656
657     if (AdministratorSID) {
658         FreeSid(AdministratorSID);
659     }
660
661     return result;
662 }
663
664 static void SetSharedFileRefCount(char *pszFile, int nRefCount)
665 {
666     LONG result;
667     HKEY hKey;
668     
669     result = RegOpenKeyAlt(AFSREG_NULL_KEY, MS_SHARED_FILES_KEY, KEY_WRITE, FALSE, &hKey, 0);
670     if (result != ERROR_SUCCESS)
671         return;
672     
673     if (nRefCount <= 0)
674         RegDeleteValue(hKey, pszFile);
675     else
676         RegSetValueEx(hKey, pszFile, 0, REG_DWORD, (BYTE *)&nRefCount, sizeof(int));    
677     
678     RegCloseKey(hKey);
679 }
680
681 static char *GetTimeStamp()
682 {
683     char szTime[64], szDate[64];
684     static char szTimeDate[128];
685
686     _strtime(szTime);
687     _strdate(szDate);
688
689     sprintf(szTimeDate, "[%s %s] ", szTime, szDate);
690     
691     return szTimeDate;
692 }
693
694 int SUCALLCONV WriteToInstallErrorLog(char *pszMsg)
695 {
696     static BOOL bWritten = FALSE;
697     FILE *fp;
698
699     // On the first write, recreate the file    
700     fp = fopen(INSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");
701     if (!fp)
702         return -1;
703         
704     fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);
705     
706     fclose(fp);
707     
708     bWritten = TRUE;
709     
710     return 0;
711 }
712
713 static void WriteToUninstallErrorLog(char *pszMsg)
714 {
715     static BOOL bWritten = FALSE;
716     FILE *fp;
717
718     // On the first write, recreate the file    
719     fp = fopen(UNINSTALL_ERROR_LOG_NAME, bWritten ? "a" : "w");
720     if (!fp)
721         return;
722         
723     fprintf(fp, "%s%s\r\n", GetTimeStamp(), pszMsg);
724     
725     fclose(fp);
726     
727     bWritten = TRUE;
728 }
729
730 static char *LoadResString(UINT uID)
731 {
732     static char str[256];
733     GetString (str, uID);
734     return str;
735 }
736
737 static void ShowError(UINT nResID, LONG nError)
738 {
739     char szErr[256];
740     char szPrompt[256];
741     char szMsg[256];
742     char szTitle[256];
743     char *psz;
744
745     psz = LoadResString(nResID);
746     if (psz)
747         strcpy(szErr, psz);
748     else
749         sprintf(szErr, "unknown error msg (Msg ID = %d)", nResID);
750     
751     psz = LoadResString(IDS_INSTALLATION_FAILURE);
752     strcpy(szPrompt, psz ? psz : "An error has occurred:  %s (Last Error = %ld).");
753
754     sprintf(szMsg, szPrompt, szErr, nError);
755
756     psz = LoadResString(IDS_TITLE);
757     strcpy(szTitle, psz ? psz : "AFS");
758
759     if (bSilentMode)
760         WriteToUninstallErrorLog(szMsg);
761     else
762         MessageBox(hDlg, szMsg, szTitle, MB_OK);
763 }
764
765 static int ShowMsg(UINT nResID, int nType)
766 {
767     char szTitle[256];
768     char *psz;
769
770     psz = LoadResString(IDS_TITLE);
771     strcpy(szTitle, psz ? psz : "AFS");
772
773     return MessageBox(hDlg, LoadResString(nResID), szTitle, nType);
774 }
775
776 static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered)
777 {
778     HKEY hKey;
779     LONG nResult;
780     DWORD dwType;
781     static char szInstallDir[256];
782     DWORD dwSize;
783     char *pszKey;
784     char *pszValue;
785
786     pszKey = bRemembered ? UNINSTALL_TEMP_INFO_KEY : pApp->regInstallDir.pszKey;
787     pszValue = bRemembered ? INSTALL_DIR_VALUE_NAME : pApp->regInstallDir.pszValue;
788
789     dwSize = sizeof(szInstallDir);
790
791     nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
792     if (nResult == ERROR_SUCCESS) {
793         nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szInstallDir, &dwSize);
794         RegCloseKey(hKey);
795     }
796
797     if (nResult != ERROR_SUCCESS) {
798         ShowError(IDS_CANT_DETERMINE_APP_PATH, nResult);
799         return 0;
800     }
801
802     FilepathNormalizeEx(szInstallDir, FPN_BACK_SLASHES);
803
804     return szInstallDir;
805 }
806
807 static BOOL DoesRegKeyExist(char *pszKey)
808 {
809     HKEY hKey;
810     LONG nResult;
811
812     nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
813     if (nResult == ERROR_SUCCESS) {
814         RegCloseKey(hKey);
815         return TRUE;
816     }
817
818     return FALSE;
819 }
820
821 static BOOL IsAppInstalled(struct APPINFO *pApp)
822 {
823     return DoesRegKeyExist(pApp->pszAppKey);
824 }
825
826 static void BuildShortPath(char *pszShortPath, UINT nShortPathLen, char *pszInstallDir, char *pszPath)
827 {
828     strncpy(pszShortPath, pszInstallDir, nShortPathLen);
829     strncat(pszShortPath, pszPath, nShortPathLen);
830     
831     GetShortPathName(pszShortPath, pszShortPath, nShortPathLen);
832 }
833
834 static BOOL IsWin95()
835 {
836     OSVERSIONINFO versionInformation;
837
838     versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
839     
840     GetVersionEx(&versionInformation);
841     
842     if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
843         (versionInformation.dwMinorVersion == 0))
844         return TRUE;
845         
846     return FALSE;
847 }
848
849 int IsWin98()
850 {
851     OSVERSIONINFO versionInformation;
852
853     versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
854     
855     GetVersionEx(&versionInformation);
856     
857     if ((versionInformation.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
858         (versionInformation.dwMinorVersion == 10))
859         return 0;
860         
861     return -1;
862 }
863
864 static BOOL IsServiceInstalled(char *pszServiceKey)
865 {
866     HKEY hKey;
867
868     if (RegOpenKeyAlt(0, pszServiceKey, KEY_READ, FALSE, &hKey, 0) == ERROR_SUCCESS) {
869         RegCloseKey(hKey);
870         return TRUE;
871     }
872
873     return FALSE;
874 }
875
876 // If this fails in anyway we just return.  No error is displayed.
877 static void MakeSureServiceDoesNotExist(char *pszName)
878 {
879     SC_HANDLE hServer = 0, hSCM = 0;
880     SERVICE_STATUS status;
881
882     hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
883     if (hSCM) {
884         hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS | DELETE);
885         if (hServer) {
886             if (QueryServiceStatus(hServer, &status)) {
887                 if (status.dwCurrentState != SERVICE_STOPPED) {
888                     if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {
889                         CloseServiceHandle(hServer);
890                         CloseServiceHandle(hSCM);
891                         return;
892                     }
893                 }
894             }
895             
896             // Try to delete even if status query fails
897             DeleteService(hServer);
898         }
899     }
900
901     if (hServer)
902         CloseServiceHandle(hServer);
903     if (hSCM)
904         CloseServiceHandle(hSCM);
905 }
906
907 static int InstallService(char *pszName, char *pszDependOn, char *pszDisplayName, char *pszServicePath, BOOL bInteractive)
908 {
909     SC_HANDLE hServer = 0, hSCM;
910     BOOL bRestoreOldConfig = FALSE;
911
912     if (!AddToProviderOrder(AFSREG_CLT_SVC_NAME)) {
913         ShowError(ERROR_FILE_NOT_FOUND, GetLastError());
914                 return -1;
915     }
916     hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
917     if (!hSCM) {
918         ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
919         return -1;
920     }
921
922 /*  This code is not used, but it could be handy in the future so I am keeping it here.
923
924     // If the service exists, then we (most probably) are in the middle of an upgrade or reinstall.
925     bRestoreOldConfig = IsServiceInstalled(pszName);
926
927     if (bRestoreOldConfig) {
928         hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS);
929         if (!hServer || !ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
930             ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, GetLastError());
931             bRestoreOldConfig = FALSE;
932             // Fall through to service creation below
933         }
934     } 
935 */
936     
937     if (!bRestoreOldConfig) {
938         DWORD dwServiceType;
939
940         // If the service already exists, the create call will fail.  This can
941         // happen if uninstall failed (which is not infrequent).  Making sure the
942         // service does not exist makes it easier for a user to install over top of
943         // a previously failed uninstall.
944         MakeSureServiceDoesNotExist(pszName);
945
946         dwServiceType = SERVICE_WIN32_OWN_PROCESS;
947         if (bInteractive)
948             dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
949     
950         hServer = CreateService(hSCM, pszName, pszDisplayName,
951             SERVICE_ALL_ACCESS, dwServiceType, SERVICE_AUTO_START, 
952             SERVICE_ERROR_NORMAL, pszServicePath, 0, 0, "RPCSS\0Netbios\0\0", 0, 0);
953     
954         if (!hServer)
955             ShowError(IDS_SERVICE_CREATE_FAILED, GetLastError());
956     }
957
958     if (hServer)
959         CloseServiceHandle(hServer);
960
961     CloseServiceHandle(hSCM);
962
963     return 0;
964 }
965
966 int SUCALLCONV InstallServerService(char *pszServicePath)
967 {
968     return InstallService(appServer.pszSvcName, 0, appServer.pszSvcDisplayName, pszServicePath, TRUE);
969 }
970
971 int SUCALLCONV InstallClientService(char *pszServicePath)
972 {
973     return InstallService(appClient.pszSvcName, appClient.pszSvcDependOn, appClient.pszSvcDisplayName, pszServicePath, FALSE);
974 }
975
976 static int UninstallService(struct APPINFO *pAppInfo)
977 {
978     SC_HANDLE hServer, hSCM;
979     SERVICE_STATUS status;
980     BOOL bOk;
981     BOOL bServer = FALSE;
982     BOOL bShowingProgressDlg = FALSE;
983
984     if (!RemoveFromProviderOrder(AFSREG_CLT_SVC_NAME)) {
985         ShowError(ERROR_FILE_NOT_FOUND, GetLastError());
986                 return -1;
987     }
988     hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
989     if (!hSCM) {
990         ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
991         return -1;
992     }
993     
994     hServer = OpenService(hSCM, pAppInfo->pszSvcName, SERVICE_ALL_ACCESS | DELETE);
995     if (!hServer) {
996         ShowError(IDS_SERVICE_OPEN_FAILED, GetLastError());
997         CloseServiceHandle(hSCM);
998         return -1;
999     }
1000
1001     if (!QueryServiceStatus(hServer, &status)) {
1002         ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
1003         CloseServiceHandle(hServer);
1004         CloseServiceHandle(hSCM);
1005         return -1;
1006     }
1007
1008     if (status.dwCurrentState != SERVICE_STOPPED) {
1009         if (pAppInfo->nServiceShutdownMsgID) {
1010             if (!bSilentMode && (ShowMsg(pAppInfo->nServiceShutdownMsgID, MB_YESNO | MB_ICONQUESTION) == IDNO)) {
1011                 CloseServiceHandle(hServer);
1012                 CloseServiceHandle(hSCM);
1013                 return 1;
1014             }
1015         }
1016
1017         if (!bSilentMode)
1018             bShowingProgressDlg = ShowProgressDialog(LoadResString(pAppInfo->nServiceShutdownProgressMsgID));
1019
1020         if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {
1021             if (bShowingProgressDlg)
1022                 HideProgressDialog();
1023             ShowError(IDS_SERVICE_STOP_FAILED, GetLastError());
1024             CloseServiceHandle(hServer);
1025             CloseServiceHandle(hSCM);
1026             return -1;
1027         }
1028     }
1029
1030     // Wait for the service to stop
1031     while (status.dwCurrentState != SERVICE_STOPPED) {
1032         // I stopped waiting on dwWaitHint because it seemed the wait hint was too long.
1033         // The service would be stopped but we'd still be asleep for a long time yet.
1034         Sleep(5000);    //status.dwWaitHint);
1035
1036         if (!QueryServiceStatus(hServer, &status)) {
1037             if (bShowingProgressDlg)
1038                 HideProgressDialog();
1039             ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
1040             CloseServiceHandle(hServer);
1041             CloseServiceHandle(hSCM);
1042             return -1;
1043         }
1044     }
1045
1046     // The service has been stopped
1047     if (bShowingProgressDlg)
1048         HideProgressDialog();
1049
1050     // This code to disable the service may be of use some day so I am keeping it here.
1051     // bOk = ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0); 
1052
1053     bOk = DeleteService(hServer);
1054
1055     if (!bOk)
1056         ShowError(IDS_SERVICE_DELETE_FAILED, GetLastError());
1057
1058     CloseServiceHandle(hServer);
1059     CloseServiceHandle(hSCM);
1060
1061     return (bOk ? 0 : -1);
1062 }
1063
1064 int SUCALLCONV AddToNetworkProviderOrder(char *pszWhatToAdd)
1065 {
1066     return AddToProviderOrder(pszWhatToAdd) ? 0 : -1;
1067 }
1068
1069 static int RemoveFromNetworkProviderOrder(char *pszWhatToDel)
1070 {
1071     return RemoveFromProviderOrder(pszWhatToDel) ? 0 : -1;
1072 }
1073
1074 int SUCALLCONV AddToPath(char *pszPath)
1075 {
1076     return AddToSystemPath(pszPath) ? 0 : -1;
1077 }
1078
1079 static int RemoveFromPath(char *pszPath)
1080 {
1081     return RemoveFromSystemPath(pszPath) ? 0 : -1;
1082 }
1083
1084 static void RemoveFiles(char *pszFileSpec)
1085 {
1086     struct _finddata_t fileinfo;
1087     long hSearch;
1088     char szDel[MAX_PATH];
1089     char szDir[MAX_PATH];
1090     char *p;
1091
1092     strcpy(szDir, pszFileSpec);
1093     p = strrchr(szDir, '\\');
1094     if (p)
1095         *p = 0;
1096     
1097     hSearch = _findfirst(pszFileSpec, &fileinfo);
1098     if (hSearch == -1)
1099         return;
1100
1101     while (1) {
1102         if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {
1103             sprintf(szDel, "%s\\%s", szDir, fileinfo.name);
1104             DeleteFile(szDel);
1105         }
1106
1107         if (_findnext(hSearch, &fileinfo) == -1)
1108             break;
1109     }
1110
1111     _findclose(hSearch);
1112 }
1113
1114 static void RemoveDir(char *pszDir)
1115 {
1116     char szFileSpec[MAX_PATH];
1117
1118     sprintf(szFileSpec, "%s\\*.*", pszDir);
1119
1120     RemoveFiles(szFileSpec);
1121     RemoveDirectory(pszDir);
1122 }
1123
1124 static void RemoveRegValues(struct REGVALUE *pRegValues)
1125 {
1126     struct REGVALUE *pCurValue;
1127     HKEY hKey;
1128     LONG nResult;
1129
1130     if (!pRegValues)
1131         return;
1132
1133     for (pCurValue = pRegValues; pCurValue->pszKey; pCurValue++) {
1134         nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pCurValue->pszKey, KEY_ALL_ACCESS, FALSE, &hKey, 0);
1135
1136         if (nResult == ERROR_SUCCESS) {
1137             nResult = RegDeleteValue(hKey, pCurValue->pszValue);
1138             RegCloseKey(hKey);
1139         }
1140
1141         if (nResult != ERROR_SUCCESS)
1142             ShowError(IDS_REG_DELETE_VALUE_ERROR, nResult);
1143     }
1144 }
1145
1146 static BOOL UninstallCredsTool()
1147 {
1148     int nResult = WinExec("afscreds /uninstall", SW_HIDE);
1149
1150     if (nResult <= 31) {
1151         if (nResult != ERROR_FILE_NOT_FOUND)
1152             ShowError(IDS_CANT_UNINSTALL_AFSCREDS, nResult);
1153     }
1154
1155     // Always return true.  We don't want the uninstall to completely fail just
1156     // because the creds tool didn't uninstall.
1157     return TRUE;
1158 }
1159
1160
1161 static char *GetTempDir()
1162 {
1163     DWORD result;
1164     static char szTempDir[MAX_PATH];
1165
1166     result = GetTempPath(sizeof(szTempDir) - 1, szTempDir);
1167     if (result == 0)
1168         return "\\";
1169         
1170     return szTempDir;
1171 }
1172
1173 static char *GetRootInstallDir()
1174 {
1175     char *psz;
1176     static char szRootInstallDir[MAX_PATH] = "";
1177
1178     if (szRootInstallDir[0] == 0) {
1179         strcpy(szRootInstallDir, pszInstallDir);
1180     
1181         // Strip off the app specific part of the install dir
1182         psz = strrchr(szRootInstallDir, '\\');
1183         if (psz)
1184             *psz = 0;
1185     }
1186
1187     return szRootInstallDir;
1188 }
1189
1190 static BOOL ClientSpecificUninstall()
1191 {
1192     int nChoice;
1193
1194     // This function needs to do two things.  First it needs to see if the server is
1195     // installed, and if it is, ask the user if they really want to uninstall the
1196     // client given that the server needs the client.  Second, if we are uninstalling
1197     // the client, we need to uninstall the creds tool.
1198
1199     if (!bSilentMode) {
1200         if (IsAppInstalled(&appServer)) {
1201             nChoice = ShowMsg(IDS_CLIENT_NEEDED_BY_SERVER, MB_ICONQUESTION | MB_YESNO);
1202             if (nChoice == IDNO)
1203                 return FALSE;       // Cancel the uninstall
1204         }
1205     }
1206     
1207     UninstallCredsTool();
1208
1209     return TRUE;
1210 }
1211
1212 static struct APPINFO *GetApp()
1213 {
1214 #ifdef SERVER_UNINST
1215     return &appServer;
1216 #elif CLIENT_UNINST
1217     return &appClient;
1218 #elif CC_UNINST
1219     return &appControlCenter;
1220 #elif LIGHT_CLIENT_UNINST
1221     return &appLightClient;
1222 #elif DOCS_UNINST
1223     return &appDocs;
1224 #else
1225     return 0;
1226 #endif;
1227 }
1228
1229 static void RememberInstallDir(char *pszInstallDir)
1230 {
1231     HKEY hKey;
1232
1233     // We remember the install dir so that when the UninstUninitialize function is called
1234     // by the InstallShield uninstaller, we can find out where we were installed to.  We
1235     // have to do this because by the time that function is called, the registry values
1236     // created at install time are already gone.  We need to be able to find out where we
1237     // were installed so we can clean up anything IS couldn't uninstall.  If this fails in 
1238     // any way then we don't care.  The only consequence is that some junk might be left on
1239     // the users' system after an uninstall.
1240     
1241     LONG result = RegOpenKeyAlt(AFSREG_NULL_KEY, UNINSTALL_TEMP_INFO_KEY, KEY_WRITE, TRUE, &hKey, 0);
1242     if (result != ERROR_SUCCESS)
1243         return;
1244
1245     RegSetValueEx(hKey, INSTALL_DIR_VALUE_NAME, 0, REG_SZ, (PBYTE)pszInstallDir, strlen(pszInstallDir) + 1);    
1246
1247     RegCloseKey(hKey);
1248 }
1249
1250 int SUCALLCONV SetSilentMode()
1251 {
1252     bSilentMode = TRUE;
1253
1254     return 0;
1255 }
1256
1257 static char *GetWinDir()
1258 {
1259     static char szWinDir[MAX_PATH] = "";
1260
1261     if (!szWinDir[0]) 
1262         GetWindowsDirectory(szWinDir, sizeof(szWinDir));
1263     
1264     return szWinDir;
1265 }
1266
1267 static char *GetWinSysDir()
1268 {
1269     static char szWinSysDir[MAX_PATH] = "";
1270
1271     if (!szWinSysDir[0])
1272         GetSystemDirectory(szWinSysDir, sizeof(szWinSysDir));
1273     
1274     return szWinSysDir;
1275
1276
1277 static char *GetLocaleID()
1278 {
1279     static char szID[25] = "";
1280
1281     if (szID[0] == 0) {
1282         LCID dwID = GetSystemDefaultLCID();
1283         
1284          // Nuke the high word.  It contains a sort ID.
1285         dwID &= 0x0000FFFF;
1286         
1287         // Convert locale ID to a string
1288         itoa(dwID, szID, 10);
1289
1290         // This thing should never be more than LOCALE_ID_LEN characters long.
1291         szID[LOCALE_ID_LEN] = 0;
1292     }
1293
1294     return szID;
1295 }
1296
1297 static char *ExpandPath(char *pszFile)
1298 {
1299     static char szPath[MAX_PATH];
1300     char *psz;
1301
1302     szPath[0] = 0;
1303
1304     // Convert a path containing TARGETDIR, WINDIR, or WINSYSDIR to a 
1305     // real path in the file system.  One of these MUST be the start of
1306     // the file path passed in.  Also convert the string ???? to an
1307     // actual locale number.
1308     if (strncmp(pszFile, TARGETDIR, strlen(TARGETDIR)) == 0)
1309         strcpy(szPath, GetRootInstallDir());
1310     else if (strncmp(pszFile, WINDIR, strlen(WINDIR)) == 0)
1311         strcpy(szPath, GetWinDir());
1312     else if (strncmp(pszFile, WINSYSDIR, strlen(WINSYSDIR)) == 0)
1313         strcpy(szPath, GetWinSysDir());
1314     
1315     if (szPath[0]) {    
1316         psz = strchr(pszFile, '\\');
1317         if (psz)
1318             strcat(szPath, psz);
1319     } else
1320         strcpy(szPath, pszFile);
1321
1322     // Is this a language dll?
1323     psz = strstr(szPath, "????.");
1324     
1325     // If it is, replace ???? with the locale number
1326     if (psz)
1327         strncpy(psz, GetLocaleID(), LOCALE_ID_LEN);
1328
1329     return szPath;
1330 }
1331
1332 static BOOL FileNeededByOtherApp(struct APPINFO *pApp, struct FILEINFO *pFileInfo)
1333 {
1334     // If the file is used by the server, the app being uninstalled is not the server, and
1335     // the server is installed, then this file is used by another app.
1336     if (!IsWinNT()) {
1337         if ((pFileInfo->nUsedBy & LCLIENT) && (pApp != &appLightClient) && IsAppInstalled(&appLightClient))
1338             return TRUE;
1339         return FALSE;
1340     }
1341
1342     if ((pFileInfo->nUsedBy & SERVER) && (pApp != &appServer) && IsAppInstalled(&appServer))
1343         return TRUE;
1344
1345     if ((pFileInfo->nUsedBy & CLIENT) && (pApp != &appClient) && IsAppInstalled(&appClient))
1346         return TRUE;
1347
1348     if ((pFileInfo->nUsedBy & CC) && (pApp != &appControlCenter) && IsAppInstalled(&appControlCenter))
1349         return TRUE;
1350     
1351     return FALSE;
1352 }
1353
1354 static void DeleteInUseFiles(struct APPINFO *pAppInfo, struct FILEINFO *pFileInfo)
1355 {
1356     char szSrcPath[MAX_PATH];
1357     char szDestPath[MAX_PATH];
1358     char szTempDir[MAX_PATH];
1359     int ii;
1360
1361     // If some app's file has been loaded before the app is uninstalled, then
1362     // when an uninstall is attempted, the application and all of the dlls that
1363     // its uses will be in use and IS will not be able to delete them.  Normally this
1364     // is not a problem because IS will tell the user to reboot to finish the uninstall.
1365     // However, we must support the ability to perform a silent uninstall followed
1366     // immediatly by an install of the same product to the same directories.  If we let
1367     // IS handle the uninstall of these files, this is not possible.  The reason is that
1368     // when IS fails to remove these in use files, it marks them for deletion after the
1369     // next reboot, which is fine.  Unfortunately, it leaves them in the dirs they were
1370     // installed to.  So if we don't immediately reboot and perform an install to the
1371     // same dirs, once a reboot is performed, those files get deleted and we have a 
1372     // broken installation.
1373
1374     // What we will do to fix all of this, is when the client is uninstalled, but
1375     // before IS does anything, we will move the in use files and associated dlls
1376     // into the temp dir and mark them for delete after a reboot.  Then an install
1377     // that follows will succeed.
1378
1379     // Delete the files that may be in use.  If they are we actually move
1380     // them to the temp dir and mark them for deletion after the next reboot.
1381     for (ii = 0; pFileInfo[ii].pszName != 0; ii++) {
1382         // Get the source path
1383         strcpy(szSrcPath, ExpandPath(pFileInfo[ii].pszName));
1384
1385         // Only delete the file if it is not used by some other app
1386         if (FileNeededByOtherApp(pAppInfo, &pFileInfo[ii]))
1387             continue;
1388
1389         // If the file doesn't exist then go on to the next file.
1390         if (_access(szSrcPath, 0) != 0)
1391             continue;
1392             
1393         // See if we can do a regular delete of the file
1394         if (DeleteFile(szSrcPath)) {
1395             SetSharedFileRefCount(szSrcPath, 0);
1396             continue;
1397         }
1398
1399         // Get a temp dir that is on the same drive as the src path.
1400         // We can't move an in use file to a different drive.
1401         strcpy(szTempDir, GetTempDir());
1402         if (szTempDir[0] != szSrcPath[0]) {
1403             // Get the drive, colon, and slash of the src path
1404             strncpy(szTempDir, szSrcPath, 3);
1405             szTempDir[3] = 0;
1406         }
1407         
1408         // Get the dest path - we will rename the file during the move
1409         GetTempFileName(szTempDir, "AFS", 0, szDestPath);
1410
1411         // Move from source to dest, marking the file for deletion after a reboot
1412         if (IsWin95()) {
1413             if (MoveFile(szSrcPath, szDestPath)) {            
1414                 WritePrivateProfileString("rename", szSrcPath, szDestPath, "wininit.ini");
1415                 SetSharedFileRefCount(szSrcPath, 0);
1416             }
1417         } else {    // WinNT or Win98
1418             if (MoveFileEx(szSrcPath, szDestPath, MOVEFILE_REPLACE_EXISTING)) {
1419                 SetFileAttributes(szDestPath, FILE_ATTRIBUTE_NORMAL);
1420                 MoveFileEx(szDestPath, 0, MOVEFILE_DELAY_UNTIL_REBOOT);
1421                 SetSharedFileRefCount(szSrcPath, 0);
1422             }
1423         }
1424     }
1425 }
1426
1427 // Delete a directory and all its files and subdirectories - Yee haaa!
1428 static void RemoveDirectoryTree(char *pszDir)
1429 {
1430     HANDLE hFind;
1431     WIN32_FIND_DATA findFileData;
1432     char szSpec[MAX_PATH];
1433     char szSubFileOrDir[MAX_PATH];
1434     BOOL bContinue;
1435
1436     sprintf(szSpec, "%s\\*.*", pszDir);
1437     
1438     // First delete the contents of the dir
1439     hFind = FindFirstFile(szSpec, &findFileData);
1440     bContinue = (hFind != INVALID_HANDLE_VALUE);
1441     
1442     while (bContinue) {
1443         if ((strcmp(findFileData.cFileName, ".") != 0) && (strcmp(findFileData.cFileName, "..") != 0)) {
1444             sprintf(szSubFileOrDir, "%s\\%s", pszDir, findFileData.cFileName);
1445             
1446             if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1447                 RemoveDirectoryTree(szSubFileOrDir);
1448             else
1449                 DeleteFile(szSubFileOrDir);
1450         }
1451
1452         bContinue = FindNextFile(hFind, &findFileData);
1453     }
1454
1455     FindClose(hFind);
1456         
1457     // Now remove the dir
1458     RemoveDirectory(pszDir);
1459
1460
1461 static char *GetStartMenuRoot()
1462 {
1463     HKEY hKey;
1464     LONG nResult;
1465     DWORD dwType;
1466     DWORD dwSize;
1467     char *pszKey;
1468     char *pszValue;
1469
1470     static char szStartMenuRoot[MAX_PATH] = "";
1471
1472     if (szStartMenuRoot[0] == 0) {
1473         dwSize = sizeof(szStartMenuRoot);
1474     
1475         if (IsWinNT()) {
1476             pszKey = WINNT_START_MENU_REG_KEY;
1477             pszValue = WINNT_START_MENU_REG_VALUE;
1478         } else {
1479             pszKey = WIN9X_START_MENU_REG_KEY;
1480             pszValue = WIN9X_START_MENU_REG_VALUE;
1481         }
1482         
1483         nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
1484         if (nResult == ERROR_SUCCESS) {
1485             nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szStartMenuRoot, &dwSize);
1486             RegCloseKey(hKey);
1487         }
1488
1489         if (nResult != ERROR_SUCCESS)
1490             return 0;
1491     }
1492
1493     FilepathNormalizeEx(szStartMenuRoot, FPN_BACK_SLASHES);
1494
1495     return szStartMenuRoot;
1496 }
1497
1498 static char *GetAfsStartMenuRoot()
1499 {
1500     static char szAfsStartMenuRoot[MAX_PATH] = "";
1501     char *pszStartMenuRoot;
1502     
1503     if (szAfsStartMenuRoot[0] == 0) {    
1504         pszStartMenuRoot = GetStartMenuRoot();
1505         if (!pszStartMenuRoot)
1506             return 0;
1507
1508         if (bSilentMode)
1509             sprintf(szAfsStartMenuRoot, "%s\\IBM WebSphere\\Performance Pack\\AFS", pszStartMenuRoot );
1510         else
1511             sprintf(szAfsStartMenuRoot, "%s\\IBM AFS", pszStartMenuRoot );
1512     }
1513
1514     return szAfsStartMenuRoot;
1515 }
1516
1517 static BOOL IsADir(char *pszName)
1518 {
1519     struct _stat statbuf;
1520
1521     if (_stat(pszName, &statbuf) < 0)
1522         return FALSE;
1523
1524     return statbuf.st_mode & _S_IFDIR;
1525 }
1526
1527 static void DeleteStartMenuEntries(char *pszEntries)
1528 {
1529     char szStartMenuPath[MAX_PATH];
1530     char *pszAfsStartMenuRoot;
1531     char *pszCurEntry;
1532
1533     pszAfsStartMenuRoot = GetAfsStartMenuRoot();
1534
1535     if (!pszAfsStartMenuRoot)
1536         return;
1537         
1538     for (pszCurEntry = pszEntries; *pszCurEntry; pszCurEntry += strlen(pszCurEntry) + 1) {
1539         sprintf(szStartMenuPath, "%s\\%s", pszAfsStartMenuRoot, pszCurEntry);
1540         if (IsADir(szStartMenuPath))
1541             RemoveDirectoryTree(szStartMenuPath);
1542         else
1543             DeleteFile(szStartMenuPath);
1544     }
1545 }
1546
1547 static void RefreshStartMenu()
1548 {
1549     char *pszAfsStartMenuRoot;
1550     char szTemp[MAX_PATH];
1551     
1552     pszAfsStartMenuRoot = GetAfsStartMenuRoot();
1553     if (!pszAfsStartMenuRoot)
1554         return;
1555
1556     sprintf(szTemp, "%s - Refresh Attempt", pszAfsStartMenuRoot);
1557         
1558     // Deleting items from below the root level of the start menu does not 
1559     // cause it to refresh.  In order that users can see changes without
1560     // rebooting we will temporarily rename our root most entry, which 
1561     // does cause a refresh of the start menu.
1562     MoveFileEx(pszAfsStartMenuRoot, szTemp, MOVEFILE_REPLACE_EXISTING);
1563     MoveFileEx(szTemp, pszAfsStartMenuRoot, MOVEFILE_REPLACE_EXISTING);
1564 }
1565
1566 static BOOL PreserveConfigInfo(struct APPINFO *pApp)
1567 {
1568     char *pszRegKey;
1569     char szDestKey[256];
1570     LONG result;
1571
1572     bPreserveConfigInfo = TRUE;
1573
1574     // If not in silent mode, ask user if they want to preserve the cfg info
1575     if (!bSilentMode) {
1576         int nChoice = ShowMsg(pApp->nPreserveConfigInfoMsgID, MB_ICONQUESTION | MB_YESNOCANCEL);
1577         if (nChoice == IDCANCEL)
1578             return FALSE;                   // Cancel the uninstall
1579         else if (nChoice == IDNO) {     
1580             bPreserveConfigInfo = FALSE;    // User doesn't want to preserve the config info
1581             return TRUE;
1582         }
1583     }
1584
1585     // Copy each reg key (and all of its subkeys and values) to another place in the registry.
1586     for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
1587         if (!DoesRegKeyExist(pszRegKey))
1588             continue;
1589
1590         // Create the destination path for the copy
1591         sprintf(szDestKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
1592
1593         // Try to copy it
1594         result = RegDupKeyAlt(pszRegKey, szDestKey);
1595
1596         if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND)) {
1597             // If the copy failed, then delete any copies that succeeded
1598             sprintf(szDestKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1599             RegDeleteEntryAlt(szDestKey, REGENTRY_KEY);
1600                 goto done;
1601         }
1602     }
1603
1604         // Remember the integrated login setting if this app supports that and it was turned on
1605         if (pApp->pszNetworkProviderOrder) {
1606                 // Was integerated login turned on?
1607                 BOOL bOn, bOk;
1608                 bOk = InNetworkProviderOrder(pApp->pszNetworkProviderOrder, &bOn);
1609                 if (bOk && bOn) {
1610                         HKEY hKey;
1611                         sprintf(szDestKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1612                         result = RegOpenKeyAlt(AFSREG_NULL_KEY, szDestKey, KEY_WRITE, TRUE, &hKey, 0);
1613                         // The existance of the key is a flag indicating that integrated login was turned on
1614                         RegCloseKey(hKey);
1615                 }
1616         }
1617         
1618 done:
1619         if ((result == ERROR_SUCCESS) || bSilentMode)
1620             return TRUE;    // Continue with uninstall
1621
1622     // Report the error and ask the user if they want to continue the uninstall
1623     return (ShowMsg(IDS_SAVE_OF_CONFIG_INFO_FAILED, MB_ICONEXCLAMATION | MB_YESNO) == IDYES);                   
1624 }
1625
1626 int SUCALLCONV RestoreConfigInfo(int nApp)
1627 {
1628     char *pszRegKey;
1629     char szSrcKey[256];
1630     struct APPINFO *pApp = 0;
1631     BOOL bError = FALSE;
1632     LONG result;
1633
1634     switch (nApp) {
1635         case SERVER:    pApp = &appServer;          break;
1636         case CLIENT:    pApp = &appClient;          break;
1637         case LCLIENT:   pApp = &appLightClient;     break;
1638         case CC:        pApp = &appControlCenter;   break;
1639     }
1640     
1641     if (!pApp)
1642         return -1;
1643         
1644     // Copy each reg key (and all of its subkeys and values) back to its original place in the registry.
1645     for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
1646         // Create the source path for the copy
1647         sprintf(szSrcKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
1648
1649         if (!DoesRegKeyExist(szSrcKey))
1650             continue;
1651
1652         // Try to restore as many of the keys as possible.  Report any errors at the end.
1653
1654         // Try to copy it
1655         result = RegDupKeyAlt(szSrcKey, pszRegKey);
1656         if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND))
1657             bError = TRUE;
1658     }
1659
1660         // Restore integrated login if this app was using it
1661         if (pApp->pszNetworkProviderOrder) {
1662                 // Check if integrated login was turned on.  The IntegratedLogin key is a flag
1663                 // telling us that it was on.  If the key does not exist, integrated login was
1664                 // not on.
1665                 sprintf(szSrcKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1666                 if (DoesRegKeyExist(szSrcKey)) {
1667                         if (!AddToProviderOrder(pApp->pszNetworkProviderOrder))
1668                                 bError = TRUE;
1669                 }
1670         }
1671
1672     // Remove our saved copies of the config info
1673     sprintf(szSrcKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1674     RegDeleteEntryAlt(szSrcKey, REGENTRY_KEY);
1675             
1676     if (bError)
1677         ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, 0);
1678
1679     return TRUE;
1680 }
1681
1682 static BOOL DoSubKeysExist(char *pszKey)
1683 {
1684     LONG result;
1685     HKEY hKey;
1686     char *pszSubKeys = 0;
1687     BOOL bExist;
1688     
1689     result = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
1690     if (result != ERROR_SUCCESS)
1691         return FALSE;
1692         
1693     result = RegEnumKeyAlt(hKey,  &pszSubKeys);
1694     RegCloseKey(hKey);
1695     
1696     if (result != ERROR_SUCCESS)
1697         return FALSE;
1698    
1699     if (pszSubKeys) {
1700         bExist = TRUE;
1701         free(pszSubKeys);
1702     } else
1703         bExist = FALSE;    
1704
1705     return bExist;
1706 }
1707
1708 /*
1709  * The following definitions are taken from richedit.h:
1710  *
1711  */
1712
1713 #define EM_SETBKGNDCOLOR                (WM_USER + 67) // from Richedit.h
1714 #define EM_STREAMIN                             (WM_USER + 73) // from Richedit.h
1715 #define SF_RTF                          0x0002
1716
1717 typedef DWORD (CALLBACK *EDITSTREAMCALLBACK)(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
1718
1719 typedef struct _editstream {
1720         DWORD dwCookie;         /* user value passed to callback as first parameter */
1721         DWORD dwError;          /* last error */
1722         EDITSTREAMCALLBACK pfnCallback;
1723 } EDITSTREAM;
1724
1725 /*
1726  *
1727  */
1728
1729 DWORD CALLBACK License_StreamText (DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
1730 {
1731    LPTSTR psz = (LPTSTR)dwCookie;
1732    LONG cchAvail = lstrlen(psz);
1733    if ((*pcb = min(cchAvail, cb)) != 0) {
1734       memcpy (pbBuff, psz, *pcb);
1735       memmove (psz, &psz[*pcb], cchAvail - *pcb + 1);
1736    }
1737    return 0;
1738 }
1739
1740
1741 void License_OnInitDialog (HWND hDlg, LPTSTR pszFile)
1742 {
1743     // Open the license file and shove its text in our RichEdit control
1744     //
1745     HANDLE hFile;
1746     if ((hFile = CreateFile (pszFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) {
1747
1748         size_t cbText;
1749         if ((cbText = GetFileSize (hFile, NULL)) != 0) {
1750
1751             LPTSTR abText = (LPTSTR)GlobalAlloc (GMEM_FIXED, cbText + 3);
1752
1753             DWORD cbRead;
1754             if (ReadFile (hFile, abText, cbText, &cbRead, NULL)) {
1755                 abText[ cbRead ] = 0;
1756
1757                 EDITSTREAM Stream;
1758                 memset (&Stream, 0x00, sizeof(Stream));
1759                 Stream.dwCookie = (DWORD)abText;
1760                 Stream.pfnCallback = License_StreamText;
1761
1762                 SendDlgItemMessage (hDlg, IDC_TEXT, EM_STREAMIN, SF_RTF, (LPARAM)&Stream);
1763             }
1764
1765             GlobalFree (abText);
1766         }
1767
1768         CloseHandle (hFile);
1769     }
1770
1771     // Make the control's background be gray
1772     //
1773     SendDlgItemMessage (hDlg, IDC_TEXT, EM_SETBKGNDCOLOR, FALSE, (LPARAM)GetSysColor(COLOR_BTNFACE));
1774 }
1775
1776 BOOL CALLBACK License_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
1777 {
1778     switch (msg) {
1779         case WM_INITDIALOG:
1780             SetWindowLong (hDlg, DWL_USER, lp);
1781             License_OnInitDialog (hDlg, (LPTSTR)lp);
1782             break;
1783
1784         case WM_COMMAND:
1785             switch (LOWORD(wp)) {
1786                 case IDCANCEL:
1787                 case IDOK:
1788                     EndDialog (hDlg, LOWORD(wp));
1789                     break;
1790
1791                 case IDC_PRINT:
1792                     TCHAR szDir[ MAX_PATH ];
1793                     GetCurrentDirectory (MAX_PATH, szDir);
1794                     ShellExecute (hDlg, TEXT("print"), (LPTSTR)GetWindowLong (hDlg, DWL_USER), NULL, szDir, SW_HIDE);
1795                     break;
1796             }
1797             break;
1798     }
1799     return FALSE;
1800 }
1801
1802 BOOL FindAfsInstallationPathByComponent (LPTSTR pszInstallationPath, LPTSTR pszComponent)
1803 {
1804     *pszInstallationPath = 0;
1805
1806     TCHAR szRegPath[ MAX_PATH ];
1807     wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
1808
1809     HKEY hk;
1810     if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0) {
1811         DWORD dwType = REG_SZ;
1812         DWORD dwSize = MAX_PATH;
1813
1814         if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszInstallationPath, &dwSize) == 0) {
1815             *(LPTSTR)FindBaseFileName (pszInstallationPath) = TEXT('\0');
1816
1817             if (pszInstallationPath[0] && (pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] == TEXT('\\')))
1818             pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] = TEXT('\0');
1819         }
1820
1821         RegCloseKey (hk);
1822     }
1823
1824     return !!*pszInstallationPath;
1825 }
1826
1827 BOOL FindAfsInstallationPath (LPTSTR pszInstallationPath)
1828 {
1829    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Client")))
1830       return TRUE;
1831    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Control Center")))
1832       return TRUE;
1833    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Server")))
1834       return TRUE;
1835    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Supplemental Documentation")))
1836       return TRUE;
1837    return FALSE;
1838 }
1839
1840 HINSTANCE LoadRichTextControl (void)
1841 {
1842     HINSTANCE hInst;
1843     if ((hInst = LoadLibrary ("riched20.dll")) != NULL)
1844         return hInst;
1845     if ((hInst = LoadLibrary ("riched32.dll")) != NULL)
1846         return hInst;
1847     if ((hInst = LoadLibrary ("riched.dll")) != NULL)
1848         return hInst;
1849     if ((hInst = LoadLibrary ("richedit.dll")) != NULL)
1850         return hInst;
1851     return NULL;
1852 }
1853
1854 int SUCALLCONV ShowLicense (char *pszTarget, char *pszSource)
1855 {
1856     // If the license already lives on this user's machine, don't show
1857     // it again. This only has to be done if the user has never
1858     // accepted the license agreement before (it's part of the setup
1859     // program, so it gets installed if they've accepted it).
1860     //
1861     // We were handed a relative path of the form:
1862     //    Documentation/html/license.rtf
1863     //
1864     // We'll need to find the AFS installation directory, in order to
1865     // find that Documentation subtree.
1866     //
1867     BOOL fShowLicense = TRUE;
1868
1869     TCHAR szInstallationPath[ MAX_PATH ];
1870     if (FindAfsInstallationPath (szInstallationPath)) {
1871         TCHAR szLicensePath[ MAX_PATH ];
1872         wsprintf (szLicensePath, TEXT("%s\\%s"), szInstallationPath, pszTarget);
1873
1874         if (GetFileAttributes (szLicensePath) != (DWORD)-1) {
1875             fShowLicense = FALSE;
1876         }
1877     }
1878
1879     // Before we can show the license file, we have to prepare the RichEdit
1880     // control. That means loading the appropriate library and calling its
1881     // initialization functions.
1882     //
1883     HINSTANCE hRichEdit;
1884     if ((hRichEdit = LoadRichTextControl()) != NULL) {
1885
1886         // If we must show the license, do so now. This is a modal dialog,
1887         // so we'll know whether or not the user accepts the license.
1888         //
1889         if (ModalDialogParam (IDD_LICENSE, GetActiveWindow(), License_DlgProc, (LPARAM)pszSource) == IDCANCEL) {
1890             // The user rejected the license; fail setup
1891             return FALSE;
1892         }
1893
1894         FreeLibrary (hRichEdit);
1895     }
1896
1897     // The user accepted the license, so we can continue with Setup.
1898     // The license file is installed as part of Setup.
1899     return TRUE;
1900 }
1901
1902 int SUCALLCONV UninstInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
1903 {
1904     char szPath[MAX_PATH];
1905     struct APPINFO *pAppInfo;
1906     char *pszFile = 0;
1907     char *pszSubDir = 0;
1908
1909     hDlg = hIS;
1910
1911     bSilentMode = !IsWindowVisible(hIS);
1912
1913     // Which app are we uninstalling?
1914     pAppInfo = GetApp();
1915     if (!pAppInfo) {
1916         ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
1917         return -1;
1918     }
1919
1920     // Get the app's install dir
1921     pszInstallDir = GetAppInstallDir(pAppInfo, FALSE);
1922     if (!pszInstallDir)
1923         return -1;
1924
1925     // If this app has a custom uninstall func, call it here
1926     if (pAppInfo->pUninstallFunc)
1927         if (!pAppInfo->pUninstallFunc())
1928             return -1;
1929
1930     if (pAppInfo->pszRegKeysToPreserve)
1931         if (!PreserveConfigInfo(pAppInfo))
1932             return -1;
1933
1934     // Unconfigure the service, if there is one for this app
1935     if (pAppInfo->pszSvcKey) {
1936         if (IsServiceInstalled(pAppInfo->pszSvcKey))
1937             if (UninstallService(pAppInfo) == 1)
1938                 return -1;
1939     }
1940
1941     RememberInstallDir(pszInstallDir);
1942
1943     DeleteInUseFiles(pAppInfo, fileInfo);
1944
1945     // Remove the app's bin path from the system path
1946     if (pAppInfo->pszBinPath) {
1947         BuildShortPath(szPath, sizeof(szPath), pszInstallDir, pAppInfo->pszBinPath);
1948         RemoveFromPath(szPath);
1949     }
1950
1951     // Remove entry from NetworkProvider\Order key in registry
1952     if (pAppInfo->pszNetworkProviderOrder)
1953         RemoveFromNetworkProviderOrder(pAppInfo->pszNetworkProviderOrder);
1954
1955     // Remove any generated subdirectories
1956     if (!bPreserveConfigInfo && pAppInfo->pszDirsToDel) {
1957         for (pszSubDir = pAppInfo->pszDirsToDel; *pszSubDir; pszSubDir += strlen(pszSubDir) + 1)
1958             RemoveDir(ExpandPath(pszSubDir));
1959     }
1960
1961     // Remove any generated files
1962     if (!bPreserveConfigInfo && pAppInfo->pszFilesToDel) {
1963         for (pszFile = pAppInfo->pszFilesToDel; *pszFile; pszFile += strlen(pszFile) + 1)
1964             RemoveFiles(ExpandPath(pszFile));
1965     }
1966
1967     // Remove any registry values that IS can't handle
1968     RemoveRegValues(pAppInfo->pRegValues);
1969     if (IsWinNT())
1970         RemoveRegValues(pAppInfo->pWinNTRegValues);
1971     else    
1972         RemoveRegValues(pAppInfo->pWin9XRegValues);
1973
1974     // Remove the start menu entries for this app
1975     if (pAppInfo->pszStartMenuEntries) {
1976         DeleteStartMenuEntries(pAppInfo->pszStartMenuEntries);
1977         RefreshStartMenu();
1978     }
1979
1980     // Remove the install dir
1981     RemoveDirectory(pszInstallDir);
1982
1983     return 0;
1984 }
1985
1986 void SUCALLCONV UninstUnInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
1987 {
1988     char *pszInstallDir;
1989     char szDirPath[MAX_PATH];
1990     char *psz;
1991     struct APPINFO *pAppInfo;
1992
1993     // If we just uninstalled the last AFS app, then do some cleanup.
1994     if (IsAppInstalled(&appServer) || IsAppInstalled(&appClient) ||
1995         IsAppInstalled(&appControlCenter) || IsAppInstalled(&appLightClient) ||
1996         IsAppInstalled(&appDocs))
1997     {
1998         return;
1999     }
2000
2001     bSilentMode = !IsWindowVisible(hIS);
2002     
2003     // Which app did we just uninstall?
2004     pAppInfo = GetApp();
2005     if (!pAppInfo) {
2006         ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
2007         return;
2008     }
2009
2010     // Get the app's install dir
2011     pszInstallDir = GetAppInstallDir(pAppInfo, TRUE);
2012     if (!pszInstallDir)
2013         return;
2014
2015     // Remove the reg key we used to remember the app install dir
2016     RegDeleteEntryAlt(UNINSTALL_TEMP_INFO_KEY, REGENTRY_KEY);
2017
2018     // Try to remove the reg key used to store config info, but only
2019     // if there are no app config info sub keys present.
2020     if (!DoSubKeysExist(AFS_PRESERVED_CFG_INFO_KEY))
2021         RegDeleteEntryAlt(AFS_PRESERVED_CFG_INFO_KEY, REGENTRY_KEY);
2022
2023     // Remove the install dir
2024     RemoveDirectory(pszInstallDir);
2025
2026     // Attempt to remove the install root and common directories.  The are 
2027     // shared and so no single app knows to delete them.
2028
2029     // Strip off the app specific part of the install dir
2030     psz = strrchr(pszInstallDir, '\\');
2031     if (psz)
2032         *psz = 0;
2033
2034     sprintf(szDirPath, "%s\\%s", pszInstallDir, "Common");
2035     RemoveDirectory(szDirPath);
2036
2037     // Remove the Common directory from the system path
2038     RemoveFromPath(szDirPath);
2039
2040     // Remove all of the documentation dirs
2041     sprintf(szDirPath, "%s\\%s", pszInstallDir, "Documentation");
2042     RemoveDirectoryTree(szDirPath);
2043
2044     // Ok, up to this point we have been removing files we know we
2045     // created.  However, after this point we are into the path
2046     // that the user chose for our install root.  The default for
2047     // this is IBM/Afs, but they could have chosen anything,
2048     // including a dir or dirs that have other products in them.
2049     // We will check to see if it is IBM\AFS and if it is then we 
2050     // will attempt to remove them.
2051     
2052     // Back up a level and look for AFS
2053     psz = strrchr(pszInstallDir, '\\');
2054     if (psz) {
2055         if (stricmp(psz + 1, "AFS") == 0) {
2056             RemoveDirectory(pszInstallDir);
2057             *psz = 0;
2058         }
2059     }
2060
2061     // Back up a level and look for IBM
2062     psz = strrchr(pszInstallDir, '\\');
2063     if (psz) {
2064         if (stricmp(psz + 1, "IBM") == 0) {
2065             RemoveDirectory(pszInstallDir);
2066             *psz = 0;
2067         }
2068     }
2069
2070     // Remove the root afs start menu entry
2071     psz = GetStartMenuRoot();
2072     if (psz) {
2073         if (bSilentMode) {
2074             // Remove everything under our branch
2075             sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack\\AFS", psz);
2076             RemoveDirectoryTree(szDirPath);
2077             
2078             // Remove the IBM stuff only if the dirs are empty
2079             sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack", psz);
2080             if (RemoveDirectory(szDirPath)) {
2081                 sprintf(szDirPath, "%s\\IBM WebSphere", psz);
2082                 RemoveDirectory(szDirPath);
2083             }
2084         } else {
2085             sprintf(szDirPath, "%s\\IBM AFS", psz);
2086             RemoveDirectoryTree(szDirPath);
2087         }
2088     }
2089 }
2090
2091 BOOLEAN _stdcall DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
2092 {
2093     if (reason == DLL_PROCESS_ATTACH) {
2094         hinst = (HINSTANCE)dll;
2095         TaLocale_LoadCorrespondingModuleByName (hinst, "afs_setup_utils.dll");
2096     }
2097
2098     return TRUE;
2099 }
2100
2101 extern "C" int WINAPI Test (HINSTANCE hInst, HINSTANCE hPrev, LPSTR psz, int nCmdShow)
2102 {
2103    ShowLicense ("TEST", "\\\\fury\\afssetup\\license\\ja_JP.rtf");
2104    return 0;
2105 }
2106
2107