windows-updates-20010819
[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
35 #include "resource.h"
36 #include "progress_dlg.h"
37 #include "sutil.h"
38 #include "forceremove.h"
39
40
41 /*
42  * PROTOTYPES _________________________________________________________________
43  *
44  */
45 static char *GetAppInstallDir(struct APPINFO *pApp, BOOL bRemembered);
46 BOOL UninstallCredsTool();
47 BOOL ServerSpecificUninstall();
48 BOOL ClientSpecificUninstall();
49
50
51
52 /*
53  * DEFINITIONS _________________________________________________________________
54  *
55  */
56 #define SUCALLCONV  WINAPI
57
58 #define UNINSTALL_TEMP_INFO_KEY     "HKEY_LOCAL_MACHINE\\Software\\AfsUninstallTempInfo"
59 #define INSTALL_DIR_VALUE_NAME      "InstallDir"
60
61 #define AFS_PRESERVED_CFG_INFO_KEY  "HKEY_LOCAL_MACHINE\\Software\\AfsPreservedConfigInfo"
62
63 #define MS_SHARED_FILES_KEY         "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\SharedDLLs"
64
65 // Log file to use when running in silent mode
66 #define UNINSTALL_ERROR_LOG_NAME    "\\AfsUninstallErrorLog.txt"
67 #define INSTALL_ERROR_LOG_NAME      "\\AfsInstallErrorLog.txt"
68
69 #define TARGETDIR                   "<TARGETDIR>"
70 #define WINDIR                      "<WINDIR>"
71 #define WINSYSDIR                   "<WINSYSDIR>"
72
73 #define WIN9X_START_MENU_REG_KEY    "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
74 #define WIN9X_START_MENU_REG_VALUE  "Programs"
75     
76 #define WINNT_START_MENU_REG_KEY    "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
77 #define WINNT_START_MENU_REG_VALUE  "Common Programs"
78
79 #define LOCALE_ID_LEN               4
80
81 struct REGVALUE {
82     char *pszKey;
83     char *pszValue;
84 };
85
86
87 typedef BOOL (APP_UNINSTALL_FUNC)();
88
89
90
91 struct APPINFO {
92     char *pszAppName;
93
94     // Service Info
95     char *pszSvcName;
96     char *pszSvcKey;
97     char *pszSvcDependOn;
98     char *pszSvcDisplayName;
99
100     char *pszNetworkProviderOrder;
101
102     // Message to use to tell the user that we have to stop the service
103     int nServiceShutdownMsgID;
104
105     // Message to use for the progress dialog that is shown while
106     // waiting for the service to stop.
107     int nServiceShutdownProgressMsgID;
108
109     // Location in registry of a key we can use to know that the app is installed
110     char *pszAppKey;
111
112     // Location in registry of this app's install dir
113     struct REGVALUE regInstallDir;
114
115     // Path Info
116     char *pszLocalRoot;     // The root dir below the install dir
117     char *pszBinPath;       // Path to remove from the system path
118
119     // Generated files and directories to delete.  These are both multistring lists.
120     char *pszDirsToDel;     // All files in these dirs will be deleted
121     char *pszFilesToDel;    // Use this if you want to delete files but leave the dir.  Wildcards can be used.
122
123     // Registry values to remove
124     struct REGVALUE *pRegValues;
125     struct REGVALUE *pWinNTRegValues;   // Only remove these if running WinNT
126     struct REGVALUE *pWin9XRegValues;   // Only remove these if running Win9X
127
128     // Start menu entries to delete
129     char *pszStartMenuEntries;
130
131     // Registry keys to save if a user wants to preserve config info during uninstall
132     char *pszRegKeysToPreserve;
133     int nPreserveConfigInfoMsgID;
134
135     // Uninstall func - used for things specific to this app
136     APP_UNINSTALL_FUNC *pUninstallFunc;
137 };
138
139
140 /*
141  * App info structure for the Server product
142  */
143 struct APPINFO appServer = {
144     "AFS Server",
145     
146     AFSREG_SVR_SVC_NAME,
147     AFSREG_SVR_SVC_KEY,
148     0,  // No depend on
149     AFSREG_SVR_SVC_DISPLAYNAME_DATA,
150
151     0,  // No network provider order
152
153     IDS_MUST_STOP_SERVER,
154     IDS_WAITING_FOR_SERVER_TO_STOP,
155
156     AFSREG_SVR_SW_VERSION_KEY,
157     
158     { AFSREG_SVR_SW_VERSION_KEY, AFSREG_SVR_SW_VERSION_DIR_VALUE },
159
160     "\\Server",
161     "\\usr\\afs\\bin",
162
163     // Dirs to delete
164     TARGETDIR"\\Server\\usr\\afs\\bin\\backup\0"
165     TARGETDIR"\\Server\\usr\\afs\\bin\0"
166     TARGETDIR"\\Server\\usr\\afs\\db\0"
167     TARGETDIR"\\Server\\usr\\afs\\logs\0"
168     TARGETDIR"\\Server\\usr\\afs\\etc\0"
169     TARGETDIR"\\Server\\usr\\afs\\local\0"
170     TARGETDIR"\\Server\\usr\\afs\0"
171     TARGETDIR"\\Server\\usr\0",
172     
173     // Files to delete
174     TARGETDIR"\\Common\\*.gid\0"
175     TARGETDIR"\\Common\\*.fts\0",
176
177     0,  // No reg values
178     0,  // No NT only reg values
179     0,  // No 9x only reg values
180
181     "Server\0",
182
183     // Config info to preserve
184     AFSREG_SVR_SVC_KEY"\0", 
185     IDS_PRESERVE_SERVER_CONFIG_INFO,
186
187     0   // No special uninstall function
188 };
189
190 // Registry values to remove for the Client
191 struct REGVALUE clientRegValues[] = {
192     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", "{DC515C27-6CAC-11D1-BAE7-00C04FD140D2}" },
193     { 0, 0 }    // This indicates there are no more entries
194 };
195
196 struct REGVALUE clientWinNTRegValues[] = {
197     { "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\File Manager\\AddOns", "AFS Client FME" },
198     { "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NetBT\\Paramet
199 ers", "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\\pthread.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     hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
913     if (!hSCM) {
914         ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
915         return -1;
916     }
917
918 /*  This code is not used, but it could be handy in the future so I am keeping it here.
919
920     // If the service exists, then we (most probably) are in the middle of an upgrade or reinstall.
921     bRestoreOldConfig = IsServiceInstalled(pszName);
922
923     if (bRestoreOldConfig) {
924         hServer = OpenService(hSCM, pszName, SERVICE_ALL_ACCESS);
925         if (!hServer || !ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_AUTO_START, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
926             ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, GetLastError());
927             bRestoreOldConfig = FALSE;
928             // Fall through to service creation below
929         }
930     } 
931 */
932     
933     if (!bRestoreOldConfig) {
934         DWORD dwServiceType;
935
936         // If the service already exists, the create call will fail.  This can
937         // happen if uninstall failed (which is not infrequent).  Making sure the
938         // service does not exist makes it easier for a user to install over top of
939         // a previously failed uninstall.
940         MakeSureServiceDoesNotExist(pszName);
941
942         dwServiceType = SERVICE_WIN32_OWN_PROCESS;
943         if (bInteractive)
944             dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
945     
946         hServer = CreateService(hSCM, pszName, pszDisplayName,
947             SERVICE_ALL_ACCESS, dwServiceType, SERVICE_AUTO_START, 
948             SERVICE_ERROR_NORMAL, pszServicePath, 0, 0, "RPCSS\0Netbios\0\0", 0, 0);
949     
950         if (!hServer)
951             ShowError(IDS_SERVICE_CREATE_FAILED, GetLastError());
952     }
953
954     if (hServer)
955         CloseServiceHandle(hServer);
956
957     CloseServiceHandle(hSCM);
958
959     return 0;
960 }
961
962 int SUCALLCONV InstallServerService(char *pszServicePath)
963 {
964     return InstallService(appServer.pszSvcName, 0, appServer.pszSvcDisplayName, pszServicePath, TRUE);
965 }
966
967 int SUCALLCONV InstallClientService(char *pszServicePath)
968 {
969     return InstallService(appClient.pszSvcName, appClient.pszSvcDependOn, appClient.pszSvcDisplayName, pszServicePath, FALSE);
970 }
971
972 static int UninstallService(struct APPINFO *pAppInfo)
973 {
974     SC_HANDLE hServer, hSCM;
975     SERVICE_STATUS status;
976     BOOL bOk;
977     BOOL bServer = FALSE;
978     BOOL bShowingProgressDlg = FALSE;
979
980     hSCM = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
981     if (!hSCM) {
982         ShowError(IDS_SCM_OPEN_FAILED, GetLastError());
983         return -1;
984     }
985     
986     hServer = OpenService(hSCM, pAppInfo->pszSvcName, SERVICE_ALL_ACCESS | DELETE);
987     if (!hServer) {
988         ShowError(IDS_SERVICE_OPEN_FAILED, GetLastError());
989         CloseServiceHandle(hSCM);
990         return -1;
991     }
992
993     if (!QueryServiceStatus(hServer, &status)) {
994         ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
995         CloseServiceHandle(hServer);
996         CloseServiceHandle(hSCM);
997         return -1;
998     }
999
1000     if (status.dwCurrentState != SERVICE_STOPPED) {
1001         if (pAppInfo->nServiceShutdownMsgID) {
1002             if (!bSilentMode && (ShowMsg(pAppInfo->nServiceShutdownMsgID, MB_YESNO | MB_ICONQUESTION) == IDNO)) {
1003                 CloseServiceHandle(hServer);
1004                 CloseServiceHandle(hSCM);
1005                 return 1;
1006             }
1007         }
1008
1009         if (!bSilentMode)
1010             bShowingProgressDlg = ShowProgressDialog(LoadResString(pAppInfo->nServiceShutdownProgressMsgID));
1011
1012         if (!ControlService(hServer, SERVICE_CONTROL_STOP, &status)) {
1013             if (bShowingProgressDlg)
1014                 HideProgressDialog();
1015             ShowError(IDS_SERVICE_STOP_FAILED, GetLastError());
1016             CloseServiceHandle(hServer);
1017             CloseServiceHandle(hSCM);
1018             return -1;
1019         }
1020     }
1021
1022     // Wait for the service to stop
1023     while (status.dwCurrentState != SERVICE_STOPPED) {
1024         // I stopped waiting on dwWaitHint because it seemed the wait hint was too long.
1025         // The service would be stopped but we'd still be asleep for a long time yet.
1026         Sleep(5000);    //status.dwWaitHint);
1027
1028         if (!QueryServiceStatus(hServer, &status)) {
1029             if (bShowingProgressDlg)
1030                 HideProgressDialog();
1031             ShowError(IDS_SERVICE_QUERY_FAILED, GetLastError());
1032             CloseServiceHandle(hServer);
1033             CloseServiceHandle(hSCM);
1034             return -1;
1035         }
1036     }
1037
1038     // The service has been stopped
1039     if (bShowingProgressDlg)
1040         HideProgressDialog();
1041
1042     // This code to disable the service may be of use some day so I am keeping it here.
1043     // bOk = ChangeServiceConfig(hServer, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0); 
1044
1045     bOk = DeleteService(hServer);
1046
1047     if (!bOk)
1048         ShowError(IDS_SERVICE_DELETE_FAILED, GetLastError());
1049
1050     CloseServiceHandle(hServer);
1051     CloseServiceHandle(hSCM);
1052
1053     return (bOk ? 0 : -1);
1054 }
1055
1056 int SUCALLCONV AddToNetworkProviderOrder(char *pszWhatToAdd)
1057 {
1058     return AddToProviderOrder(pszWhatToAdd) ? 0 : -1;
1059 }
1060
1061 static int RemoveFromNetworkProviderOrder(char *pszWhatToDel)
1062 {
1063     return RemoveFromProviderOrder(pszWhatToDel) ? 0 : -1;
1064 }
1065
1066 int SUCALLCONV AddToPath(char *pszPath)
1067 {
1068     return AddToSystemPath(pszPath) ? 0 : -1;
1069 }
1070
1071 static int RemoveFromPath(char *pszPath)
1072 {
1073     return RemoveFromSystemPath(pszPath) ? 0 : -1;
1074 }
1075
1076 static void RemoveFiles(char *pszFileSpec)
1077 {
1078     struct _finddata_t fileinfo;
1079     long hSearch;
1080     char szDel[MAX_PATH];
1081     char szDir[MAX_PATH];
1082     char *p;
1083
1084     strcpy(szDir, pszFileSpec);
1085     p = strrchr(szDir, '\\');
1086     if (p)
1087         *p = 0;
1088     
1089     hSearch = _findfirst(pszFileSpec, &fileinfo);
1090     if (hSearch == -1)
1091         return;
1092
1093     while (1) {
1094         if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {
1095             sprintf(szDel, "%s\\%s", szDir, fileinfo.name);
1096             DeleteFile(szDel);
1097         }
1098
1099         if (_findnext(hSearch, &fileinfo) == -1)
1100             break;
1101     }
1102
1103     _findclose(hSearch);
1104 }
1105
1106 static void RemoveDir(char *pszDir)
1107 {
1108     char szFileSpec[MAX_PATH];
1109
1110     sprintf(szFileSpec, "%s\\*.*", pszDir);
1111
1112     RemoveFiles(szFileSpec);
1113     RemoveDirectory(pszDir);
1114 }
1115
1116 static void RemoveRegValues(struct REGVALUE *pRegValues)
1117 {
1118     struct REGVALUE *pCurValue;
1119     HKEY hKey;
1120     LONG nResult;
1121
1122     if (!pRegValues)
1123         return;
1124
1125     for (pCurValue = pRegValues; pCurValue->pszKey; pCurValue++) {
1126         nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pCurValue->pszKey, KEY_ALL_ACCESS, FALSE, &hKey, 0);
1127
1128         if (nResult == ERROR_SUCCESS) {
1129             nResult = RegDeleteValue(hKey, pCurValue->pszValue);
1130             RegCloseKey(hKey);
1131         }
1132
1133         if (nResult != ERROR_SUCCESS)
1134             ShowError(IDS_REG_DELETE_VALUE_ERROR, nResult);
1135     }
1136 }
1137
1138 static BOOL UninstallCredsTool()
1139 {
1140     int nResult = WinExec("afscreds /uninstall", SW_HIDE);
1141
1142     if (nResult <= 31) {
1143         if (nResult != ERROR_FILE_NOT_FOUND)
1144             ShowError(IDS_CANT_UNINSTALL_AFSCREDS, nResult);
1145     }
1146
1147     // Always return true.  We don't want the uninstall to completely fail just
1148     // because the creds tool didn't uninstall.
1149     return TRUE;
1150 }
1151
1152
1153 static char *GetTempDir()
1154 {
1155     DWORD result;
1156     static char szTempDir[MAX_PATH];
1157
1158     result = GetTempPath(sizeof(szTempDir) - 1, szTempDir);
1159     if (result == 0)
1160         return "\\";
1161         
1162     return szTempDir;
1163 }
1164
1165 static char *GetRootInstallDir()
1166 {
1167     char *psz;
1168     static char szRootInstallDir[MAX_PATH] = "";
1169
1170     if (szRootInstallDir[0] == 0) {
1171         strcpy(szRootInstallDir, pszInstallDir);
1172     
1173         // Strip off the app specific part of the install dir
1174         psz = strrchr(szRootInstallDir, '\\');
1175         if (psz)
1176             *psz = 0;
1177     }
1178
1179     return szRootInstallDir;
1180 }
1181
1182 static BOOL ClientSpecificUninstall()
1183 {
1184     int nChoice;
1185
1186     // This function needs to do two things.  First it needs to see if the server is
1187     // installed, and if it is, ask the user if they really want to uninstall the
1188     // client given that the server needs the client.  Second, if we are uninstalling
1189     // the client, we need to uninstall the creds tool.
1190
1191     if (!bSilentMode) {
1192         if (IsAppInstalled(&appServer)) {
1193             nChoice = ShowMsg(IDS_CLIENT_NEEDED_BY_SERVER, MB_ICONQUESTION | MB_YESNO);
1194             if (nChoice == IDNO)
1195                 return FALSE;       // Cancel the uninstall
1196         }
1197     }
1198     
1199     UninstallCredsTool();
1200
1201     return TRUE;
1202 }
1203
1204 static struct APPINFO *GetApp()
1205 {
1206 #ifdef SERVER_UNINST
1207     return &appServer;
1208 #elif CLIENT_UNINST
1209     return &appClient;
1210 #elif CC_UNINST
1211     return &appControlCenter;
1212 #elif LIGHT_CLIENT_UNINST
1213     return &appLightClient;
1214 #elif DOCS_UNINST
1215     return &appDocs;
1216 #else
1217     return 0;
1218 #endif;
1219 }
1220
1221 static void RememberInstallDir(char *pszInstallDir)
1222 {
1223     HKEY hKey;
1224
1225     // We remember the install dir so that when the UninstUninitialize function is called
1226     // by the InstallShield uninstaller, we can find out where we were installed to.  We
1227     // have to do this because by the time that function is called, the registry values
1228     // created at install time are already gone.  We need to be able to find out where we
1229     // were installed so we can clean up anything IS couldn't uninstall.  If this fails in 
1230     // any way then we don't care.  The only consequence is that some junk might be left on
1231     // the users' system after an uninstall.
1232     
1233     LONG result = RegOpenKeyAlt(AFSREG_NULL_KEY, UNINSTALL_TEMP_INFO_KEY, KEY_WRITE, TRUE, &hKey, 0);
1234     if (result != ERROR_SUCCESS)
1235         return;
1236
1237     RegSetValueEx(hKey, INSTALL_DIR_VALUE_NAME, 0, REG_SZ, (PBYTE)pszInstallDir, strlen(pszInstallDir) + 1);    
1238
1239     RegCloseKey(hKey);
1240 }
1241
1242 int SUCALLCONV SetSilentMode()
1243 {
1244     bSilentMode = TRUE;
1245
1246     return 0;
1247 }
1248
1249 static char *GetWinDir()
1250 {
1251     static char szWinDir[MAX_PATH] = "";
1252
1253     if (!szWinDir[0]) 
1254         GetWindowsDirectory(szWinDir, sizeof(szWinDir));
1255     
1256     return szWinDir;
1257 }
1258
1259 static char *GetWinSysDir()
1260 {
1261     static char szWinSysDir[MAX_PATH] = "";
1262
1263     if (!szWinSysDir[0])
1264         GetSystemDirectory(szWinSysDir, sizeof(szWinSysDir));
1265     
1266     return szWinSysDir;
1267
1268
1269 static char *GetLocaleID()
1270 {
1271     static char szID[25] = "";
1272
1273     if (szID[0] == 0) {
1274         LCID dwID = GetSystemDefaultLCID();
1275         
1276          // Nuke the high word.  It contains a sort ID.
1277         dwID &= 0x0000FFFF;
1278         
1279         // Convert locale ID to a string
1280         itoa(dwID, szID, 10);
1281
1282         // This thing should never be more than LOCALE_ID_LEN characters long.
1283         szID[LOCALE_ID_LEN] = 0;
1284     }
1285
1286     return szID;
1287 }
1288
1289 static char *ExpandPath(char *pszFile)
1290 {
1291     static char szPath[MAX_PATH];
1292     char *psz;
1293
1294     szPath[0] = 0;
1295
1296     // Convert a path containing TARGETDIR, WINDIR, or WINSYSDIR to a 
1297     // real path in the file system.  One of these MUST be the start of
1298     // the file path passed in.  Also convert the string ???? to an
1299     // actual locale number.
1300     if (strncmp(pszFile, TARGETDIR, strlen(TARGETDIR)) == 0)
1301         strcpy(szPath, GetRootInstallDir());
1302     else if (strncmp(pszFile, WINDIR, strlen(WINDIR)) == 0)
1303         strcpy(szPath, GetWinDir());
1304     else if (strncmp(pszFile, WINSYSDIR, strlen(WINSYSDIR)) == 0)
1305         strcpy(szPath, GetWinSysDir());
1306     
1307     if (szPath[0]) {    
1308         psz = strchr(pszFile, '\\');
1309         if (psz)
1310             strcat(szPath, psz);
1311     } else
1312         strcpy(szPath, pszFile);
1313
1314     // Is this a language dll?
1315     psz = strstr(szPath, "????.");
1316     
1317     // If it is, replace ???? with the locale number
1318     if (psz)
1319         strncpy(psz, GetLocaleID(), LOCALE_ID_LEN);
1320
1321     return szPath;
1322 }
1323
1324 static BOOL FileNeededByOtherApp(struct APPINFO *pApp, struct FILEINFO *pFileInfo)
1325 {
1326     // If the file is used by the server, the app being uninstalled is not the server, and
1327     // the server is installed, then this file is used by another app.
1328     if (!IsWinNT()) {
1329         if ((pFileInfo->nUsedBy & LCLIENT) && (pApp != &appLightClient) && IsAppInstalled(&appLightClient))
1330             return TRUE;
1331         return FALSE;
1332     }
1333
1334     if ((pFileInfo->nUsedBy & SERVER) && (pApp != &appServer) && IsAppInstalled(&appServer))
1335         return TRUE;
1336
1337     if ((pFileInfo->nUsedBy & CLIENT) && (pApp != &appClient) && IsAppInstalled(&appClient))
1338         return TRUE;
1339
1340     if ((pFileInfo->nUsedBy & CC) && (pApp != &appControlCenter) && IsAppInstalled(&appControlCenter))
1341         return TRUE;
1342     
1343     return FALSE;
1344 }
1345
1346 static void DeleteInUseFiles(struct APPINFO *pAppInfo, struct FILEINFO *pFileInfo)
1347 {
1348     char szSrcPath[MAX_PATH];
1349     char szDestPath[MAX_PATH];
1350     char szTempDir[MAX_PATH];
1351     int ii;
1352
1353     // If some app's file has been loaded before the app is uninstalled, then
1354     // when an uninstall is attempted, the application and all of the dlls that
1355     // its uses will be in use and IS will not be able to delete them.  Normally this
1356     // is not a problem because IS will tell the user to reboot to finish the uninstall.
1357     // However, we must support the ability to perform a silent uninstall followed
1358     // immediatly by an install of the same product to the same directories.  If we let
1359     // IS handle the uninstall of these files, this is not possible.  The reason is that
1360     // when IS fails to remove these in use files, it marks them for deletion after the
1361     // next reboot, which is fine.  Unfortunately, it leaves them in the dirs they were
1362     // installed to.  So if we don't immediately reboot and perform an install to the
1363     // same dirs, once a reboot is performed, those files get deleted and we have a 
1364     // broken installation.
1365
1366     // What we will do to fix all of this, is when the client is uninstalled, but
1367     // before IS does anything, we will move the in use files and associated dlls
1368     // into the temp dir and mark them for delete after a reboot.  Then an install
1369     // that follows will succeed.
1370
1371     // Delete the files that may be in use.  If they are we actually move
1372     // them to the temp dir and mark them for deletion after the next reboot.
1373     for (ii = 0; pFileInfo[ii].pszName != 0; ii++) {
1374         // Get the source path
1375         strcpy(szSrcPath, ExpandPath(pFileInfo[ii].pszName));
1376
1377         // Only delete the file if it is not used by some other app
1378         if (FileNeededByOtherApp(pAppInfo, &pFileInfo[ii]))
1379             continue;
1380
1381         // If the file doesn't exist then go on to the next file.
1382         if (_access(szSrcPath, 0) != 0)
1383             continue;
1384             
1385         // See if we can do a regular delete of the file
1386         if (DeleteFile(szSrcPath)) {
1387             SetSharedFileRefCount(szSrcPath, 0);
1388             continue;
1389         }
1390
1391         // Get a temp dir that is on the same drive as the src path.
1392         // We can't move an in use file to a different drive.
1393         strcpy(szTempDir, GetTempDir());
1394         if (szTempDir[0] != szSrcPath[0]) {
1395             // Get the drive, colon, and slash of the src path
1396             strncpy(szTempDir, szSrcPath, 3);
1397             szTempDir[3] = 0;
1398         }
1399         
1400         // Get the dest path - we will rename the file during the move
1401         GetTempFileName(szTempDir, "AFS", 0, szDestPath);
1402
1403         // Move from source to dest, marking the file for deletion after a reboot
1404         if (IsWin95()) {
1405             if (MoveFile(szSrcPath, szDestPath)) {            
1406                 WritePrivateProfileString("rename", szSrcPath, szDestPath, "wininit.ini");
1407                 SetSharedFileRefCount(szSrcPath, 0);
1408             }
1409         } else {    // WinNT or Win98
1410             if (MoveFileEx(szSrcPath, szDestPath, MOVEFILE_REPLACE_EXISTING)) {
1411                 SetFileAttributes(szDestPath, FILE_ATTRIBUTE_NORMAL);
1412                 MoveFileEx(szDestPath, 0, MOVEFILE_DELAY_UNTIL_REBOOT);
1413                 SetSharedFileRefCount(szSrcPath, 0);
1414             }
1415         }
1416     }
1417 }
1418
1419 // Delete a directory and all its files and subdirectories - Yee haaa!
1420 static void RemoveDirectoryTree(char *pszDir)
1421 {
1422     HANDLE hFind;
1423     WIN32_FIND_DATA findFileData;
1424     char szSpec[MAX_PATH];
1425     char szSubFileOrDir[MAX_PATH];
1426     BOOL bContinue;
1427
1428     sprintf(szSpec, "%s\\*.*", pszDir);
1429     
1430     // First delete the contents of the dir
1431     hFind = FindFirstFile(szSpec, &findFileData);
1432     bContinue = (hFind != INVALID_HANDLE_VALUE);
1433     
1434     while (bContinue) {
1435         if ((strcmp(findFileData.cFileName, ".") != 0) && (strcmp(findFileData.cFileName, "..") != 0)) {
1436             sprintf(szSubFileOrDir, "%s\\%s", pszDir, findFileData.cFileName);
1437             
1438             if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1439                 RemoveDirectoryTree(szSubFileOrDir);
1440             else
1441                 DeleteFile(szSubFileOrDir);
1442         }
1443
1444         bContinue = FindNextFile(hFind, &findFileData);
1445     }
1446
1447     FindClose(hFind);
1448         
1449     // Now remove the dir
1450     RemoveDirectory(pszDir);
1451
1452
1453 static char *GetStartMenuRoot()
1454 {
1455     HKEY hKey;
1456     LONG nResult;
1457     DWORD dwType;
1458     DWORD dwSize;
1459     char *pszKey;
1460     char *pszValue;
1461
1462     static char szStartMenuRoot[MAX_PATH] = "";
1463
1464     if (szStartMenuRoot[0] == 0) {
1465         dwSize = sizeof(szStartMenuRoot);
1466     
1467         if (IsWinNT()) {
1468             pszKey = WINNT_START_MENU_REG_KEY;
1469             pszValue = WINNT_START_MENU_REG_VALUE;
1470         } else {
1471             pszKey = WIN9X_START_MENU_REG_KEY;
1472             pszValue = WIN9X_START_MENU_REG_VALUE;
1473         }
1474         
1475         nResult = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
1476         if (nResult == ERROR_SUCCESS) {
1477             nResult = RegQueryValueEx(hKey, pszValue, 0, &dwType, (PBYTE)szStartMenuRoot, &dwSize);
1478             RegCloseKey(hKey);
1479         }
1480
1481         if (nResult != ERROR_SUCCESS)
1482             return 0;
1483     }
1484
1485     FilepathNormalizeEx(szStartMenuRoot, FPN_BACK_SLASHES);
1486
1487     return szStartMenuRoot;
1488 }
1489
1490 static char *GetAfsStartMenuRoot()
1491 {
1492     static char szAfsStartMenuRoot[MAX_PATH] = "";
1493     char *pszStartMenuRoot;
1494     
1495     if (szAfsStartMenuRoot[0] == 0) {    
1496         pszStartMenuRoot = GetStartMenuRoot();
1497         if (!pszStartMenuRoot)
1498             return 0;
1499
1500         if (bSilentMode)
1501             sprintf(szAfsStartMenuRoot, "%s\\IBM WebSphere\\Performance Pack\\AFS", pszStartMenuRoot );
1502         else
1503             sprintf(szAfsStartMenuRoot, "%s\\IBM AFS", pszStartMenuRoot );
1504     }
1505
1506     return szAfsStartMenuRoot;
1507 }
1508
1509 static BOOL IsADir(char *pszName)
1510 {
1511     struct _stat statbuf;
1512
1513     if (_stat(pszName, &statbuf) < 0)
1514         return FALSE;
1515
1516     return statbuf.st_mode & _S_IFDIR;
1517 }
1518
1519 static void DeleteStartMenuEntries(char *pszEntries)
1520 {
1521     char szStartMenuPath[MAX_PATH];
1522     char *pszAfsStartMenuRoot;
1523     char *pszCurEntry;
1524
1525     pszAfsStartMenuRoot = GetAfsStartMenuRoot();
1526
1527     if (!pszAfsStartMenuRoot)
1528         return;
1529         
1530     for (pszCurEntry = pszEntries; *pszCurEntry; pszCurEntry += strlen(pszCurEntry) + 1) {
1531         sprintf(szStartMenuPath, "%s\\%s", pszAfsStartMenuRoot, pszCurEntry);
1532         if (IsADir(szStartMenuPath))
1533             RemoveDirectoryTree(szStartMenuPath);
1534         else
1535             DeleteFile(szStartMenuPath);
1536     }
1537 }
1538
1539 static void RefreshStartMenu()
1540 {
1541     char *pszAfsStartMenuRoot;
1542     char szTemp[MAX_PATH];
1543     
1544     pszAfsStartMenuRoot = GetAfsStartMenuRoot();
1545     if (!pszAfsStartMenuRoot)
1546         return;
1547
1548     sprintf(szTemp, "%s - Refresh Attempt", pszAfsStartMenuRoot);
1549         
1550     // Deleting items from below the root level of the start menu does not 
1551     // cause it to refresh.  In order that users can see changes without
1552     // rebooting we will temporarily rename our root most entry, which 
1553     // does cause a refresh of the start menu.
1554     MoveFileEx(pszAfsStartMenuRoot, szTemp, MOVEFILE_REPLACE_EXISTING);
1555     MoveFileEx(szTemp, pszAfsStartMenuRoot, MOVEFILE_REPLACE_EXISTING);
1556 }
1557
1558 static BOOL PreserveConfigInfo(struct APPINFO *pApp)
1559 {
1560     char *pszRegKey;
1561     char szDestKey[256];
1562     LONG result;
1563
1564     bPreserveConfigInfo = TRUE;
1565
1566     // If not in silent mode, ask user if they want to preserve the cfg info
1567     if (!bSilentMode) {
1568         int nChoice = ShowMsg(pApp->nPreserveConfigInfoMsgID, MB_ICONQUESTION | MB_YESNOCANCEL);
1569         if (nChoice == IDCANCEL)
1570             return FALSE;                   // Cancel the uninstall
1571         else if (nChoice == IDNO) {     
1572             bPreserveConfigInfo = FALSE;    // User doesn't want to preserve the config info
1573             return TRUE;
1574         }
1575     }
1576
1577     // Copy each reg key (and all of its subkeys and values) to another place in the registry.
1578     for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
1579         if (!DoesRegKeyExist(pszRegKey))
1580             continue;
1581
1582         // Create the destination path for the copy
1583         sprintf(szDestKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
1584
1585         // Try to copy it
1586         result = RegDupKeyAlt(pszRegKey, szDestKey);
1587
1588         if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND)) {
1589             // If the copy failed, then delete any copies that succeeded
1590             sprintf(szDestKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1591             RegDeleteEntryAlt(szDestKey, REGENTRY_KEY);
1592                 goto done;
1593         }
1594     }
1595
1596         // Remember the integrated login setting if this app supports that and it was turned on
1597         if (pApp->pszNetworkProviderOrder) {
1598                 // Was integerated login turned on?
1599                 BOOL bOn, bOk;
1600                 bOk = InNetworkProviderOrder(pApp->pszNetworkProviderOrder, &bOn);
1601                 if (bOk && bOn) {
1602                         HKEY hKey;
1603                         sprintf(szDestKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1604                         result = RegOpenKeyAlt(AFSREG_NULL_KEY, szDestKey, KEY_WRITE, TRUE, &hKey, 0);
1605                         // The existance of the key is a flag indicating that integrated login was turned on
1606                         RegCloseKey(hKey);
1607                 }
1608         }
1609         
1610 done:
1611         if ((result == ERROR_SUCCESS) || bSilentMode)
1612             return TRUE;    // Continue with uninstall
1613
1614     // Report the error and ask the user if they want to continue the uninstall
1615     return (ShowMsg(IDS_SAVE_OF_CONFIG_INFO_FAILED, MB_ICONEXCLAMATION | MB_YESNO) == IDYES);                   
1616 }
1617
1618 int SUCALLCONV RestoreConfigInfo(int nApp)
1619 {
1620     char *pszRegKey;
1621     char szSrcKey[256];
1622     struct APPINFO *pApp = 0;
1623     BOOL bError = FALSE;
1624     LONG result;
1625
1626     switch (nApp) {
1627         case SERVER:    pApp = &appServer;          break;
1628         case CLIENT:    pApp = &appClient;          break;
1629         case LCLIENT:   pApp = &appLightClient;     break;
1630         case CC:        pApp = &appControlCenter;   break;
1631     }
1632     
1633     if (!pApp)
1634         return -1;
1635         
1636     // Copy each reg key (and all of its subkeys and values) back to its original place in the registry.
1637     for (pszRegKey = pApp->pszRegKeysToPreserve; *pszRegKey; pszRegKey += strlen(pszRegKey) + 1) {
1638         // Create the source path for the copy
1639         sprintf(szSrcKey, "%s\\%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName, pszRegKey);
1640
1641         if (!DoesRegKeyExist(szSrcKey))
1642             continue;
1643
1644         // Try to restore as many of the keys as possible.  Report any errors at the end.
1645
1646         // Try to copy it
1647         result = RegDupKeyAlt(szSrcKey, pszRegKey);
1648         if ((result != ERROR_SUCCESS) && (result != ERROR_FILE_NOT_FOUND))
1649             bError = TRUE;
1650     }
1651
1652         // Restore integrated login if this app was using it
1653         if (pApp->pszNetworkProviderOrder) {
1654                 // Check if integrated login was turned on.  The IntegratedLogin key is a flag
1655                 // telling us that it was on.  If the key does not exist, integrated login was
1656                 // not on.
1657                 sprintf(szSrcKey, "%s\\%s\\IntegratedLogin", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1658                 if (DoesRegKeyExist(szSrcKey)) {
1659                         if (!AddToProviderOrder(pApp->pszNetworkProviderOrder))
1660                                 bError = TRUE;
1661                 }
1662         }
1663
1664     // Remove our saved copies of the config info
1665     sprintf(szSrcKey, "%s\\%s", AFS_PRESERVED_CFG_INFO_KEY, pApp->pszAppName);
1666     RegDeleteEntryAlt(szSrcKey, REGENTRY_KEY);
1667             
1668     if (bError)
1669         ShowError(IDS_RESTORE_OF_PREVIOUS_CONFIG_FAILED, 0);
1670
1671     return TRUE;
1672 }
1673
1674 static BOOL DoSubKeysExist(char *pszKey)
1675 {
1676     LONG result;
1677     HKEY hKey;
1678     char *pszSubKeys = 0;
1679     BOOL bExist;
1680     
1681     result = RegOpenKeyAlt(AFSREG_NULL_KEY, pszKey, KEY_READ, FALSE, &hKey, 0);
1682     if (result != ERROR_SUCCESS)
1683         return FALSE;
1684         
1685     result = RegEnumKeyAlt(hKey,  &pszSubKeys);
1686     RegCloseKey(hKey);
1687     
1688     if (result != ERROR_SUCCESS)
1689         return FALSE;
1690    
1691     if (pszSubKeys) {
1692         bExist = TRUE;
1693         free(pszSubKeys);
1694     } else
1695         bExist = FALSE;    
1696
1697     return bExist;
1698 }
1699
1700 /*
1701  * The following definitions are taken from richedit.h:
1702  *
1703  */
1704
1705 #define EM_SETBKGNDCOLOR                (WM_USER + 67) // from Richedit.h
1706 #define EM_STREAMIN                             (WM_USER + 73) // from Richedit.h
1707 #define SF_RTF                          0x0002
1708
1709 typedef DWORD (CALLBACK *EDITSTREAMCALLBACK)(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb);
1710
1711 typedef struct _editstream {
1712         DWORD dwCookie;         /* user value passed to callback as first parameter */
1713         DWORD dwError;          /* last error */
1714         EDITSTREAMCALLBACK pfnCallback;
1715 } EDITSTREAM;
1716
1717 /*
1718  *
1719  */
1720
1721 DWORD CALLBACK License_StreamText (DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
1722 {
1723    LPTSTR psz = (LPTSTR)dwCookie;
1724    LONG cchAvail = lstrlen(psz);
1725    if ((*pcb = min(cchAvail, cb)) != 0) {
1726       memcpy (pbBuff, psz, *pcb);
1727       memmove (psz, &psz[*pcb], cchAvail - *pcb + 1);
1728    }
1729    return 0;
1730 }
1731
1732
1733 void License_OnInitDialog (HWND hDlg, LPTSTR pszFile)
1734 {
1735     // Open the license file and shove its text in our RichEdit control
1736     //
1737     HANDLE hFile;
1738     if ((hFile = CreateFile (pszFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) {
1739
1740         size_t cbText;
1741         if ((cbText = GetFileSize (hFile, NULL)) != 0) {
1742
1743             LPTSTR abText = (LPTSTR)GlobalAlloc (GMEM_FIXED, cbText + 3);
1744
1745             DWORD cbRead;
1746             if (ReadFile (hFile, abText, cbText, &cbRead, NULL)) {
1747                 abText[ cbRead ] = 0;
1748
1749                 EDITSTREAM Stream;
1750                 memset (&Stream, 0x00, sizeof(Stream));
1751                 Stream.dwCookie = (DWORD)abText;
1752                 Stream.pfnCallback = License_StreamText;
1753
1754                 SendDlgItemMessage (hDlg, IDC_TEXT, EM_STREAMIN, SF_RTF, (LPARAM)&Stream);
1755             }
1756
1757             GlobalFree (abText);
1758         }
1759
1760         CloseHandle (hFile);
1761     }
1762
1763     // Make the control's background be gray
1764     //
1765     SendDlgItemMessage (hDlg, IDC_TEXT, EM_SETBKGNDCOLOR, FALSE, (LPARAM)GetSysColor(COLOR_BTNFACE));
1766 }
1767
1768 BOOL CALLBACK License_DlgProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
1769 {
1770     switch (msg) {
1771         case WM_INITDIALOG:
1772             SetWindowLong (hDlg, DWL_USER, lp);
1773             License_OnInitDialog (hDlg, (LPTSTR)lp);
1774             break;
1775
1776         case WM_COMMAND:
1777             switch (LOWORD(wp)) {
1778                 case IDCANCEL:
1779                 case IDOK:
1780                     EndDialog (hDlg, LOWORD(wp));
1781                     break;
1782
1783                 case IDC_PRINT:
1784                     TCHAR szDir[ MAX_PATH ];
1785                     GetCurrentDirectory (MAX_PATH, szDir);
1786                     ShellExecute (hDlg, TEXT("print"), (LPTSTR)GetWindowLong (hDlg, DWL_USER), NULL, szDir, SW_HIDE);
1787                     break;
1788             }
1789             break;
1790     }
1791     return FALSE;
1792 }
1793
1794 BOOL FindAfsInstallationPathByComponent (LPTSTR pszInstallationPath, LPTSTR pszComponent)
1795 {
1796     *pszInstallationPath = 0;
1797
1798     TCHAR szRegPath[ MAX_PATH ];
1799     wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
1800
1801     HKEY hk;
1802     if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0) {
1803         DWORD dwType = REG_SZ;
1804         DWORD dwSize = MAX_PATH;
1805
1806         if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszInstallationPath, &dwSize) == 0) {
1807             *(LPTSTR)FindBaseFileName (pszInstallationPath) = TEXT('\0');
1808
1809             if (pszInstallationPath[0] && (pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] == TEXT('\\')))
1810             pszInstallationPath[ lstrlen(pszInstallationPath)-1 ] = TEXT('\0');
1811         }
1812
1813         RegCloseKey (hk);
1814     }
1815
1816     return !!*pszInstallationPath;
1817 }
1818
1819 BOOL FindAfsInstallationPath (LPTSTR pszInstallationPath)
1820 {
1821    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Client")))
1822       return TRUE;
1823    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Control Center")))
1824       return TRUE;
1825    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Server")))
1826       return TRUE;
1827    if (FindAfsInstallationPathByComponent (pszInstallationPath, TEXT("AFS Supplemental Documentation")))
1828       return TRUE;
1829    return FALSE;
1830 }
1831
1832 HINSTANCE LoadRichTextControl (void)
1833 {
1834     HINSTANCE hInst;
1835     if ((hInst = LoadLibrary ("riched20.dll")) != NULL)
1836         return hInst;
1837     if ((hInst = LoadLibrary ("riched32.dll")) != NULL)
1838         return hInst;
1839     if ((hInst = LoadLibrary ("riched.dll")) != NULL)
1840         return hInst;
1841     if ((hInst = LoadLibrary ("richedit.dll")) != NULL)
1842         return hInst;
1843     return NULL;
1844 }
1845
1846 int SUCALLCONV ShowLicense (char *pszTarget, char *pszSource)
1847 {
1848     // If the license already lives on this user's machine, don't show
1849     // it again. This only has to be done if the user has never
1850     // accepted the license agreement before (it's part of the setup
1851     // program, so it gets installed if they've accepted it).
1852     //
1853     // We were handed a relative path of the form:
1854     //    Documentation/html/license.rtf
1855     //
1856     // We'll need to find the AFS installation directory, in order to
1857     // find that Documentation subtree.
1858     //
1859     BOOL fShowLicense = TRUE;
1860
1861     TCHAR szInstallationPath[ MAX_PATH ];
1862     if (FindAfsInstallationPath (szInstallationPath)) {
1863         TCHAR szLicensePath[ MAX_PATH ];
1864         wsprintf (szLicensePath, TEXT("%s\\%s"), szInstallationPath, pszTarget);
1865
1866         if (GetFileAttributes (szLicensePath) != (DWORD)-1) {
1867             fShowLicense = FALSE;
1868         }
1869     }
1870
1871     // Before we can show the license file, we have to prepare the RichEdit
1872     // control. That means loading the appropriate library and calling its
1873     // initialization functions.
1874     //
1875     HINSTANCE hRichEdit;
1876     if ((hRichEdit = LoadRichTextControl()) != NULL) {
1877
1878         // If we must show the license, do so now. This is a modal dialog,
1879         // so we'll know whether or not the user accepts the license.
1880         //
1881         if (ModalDialogParam (IDD_LICENSE, GetActiveWindow(), License_DlgProc, (LPARAM)pszSource) == IDCANCEL) {
1882             // The user rejected the license; fail setup
1883             return FALSE;
1884         }
1885
1886         FreeLibrary (hRichEdit);
1887     }
1888
1889     // The user accepted the license, so we can continue with Setup.
1890     // The license file is installed as part of Setup.
1891     return TRUE;
1892 }
1893
1894 int SUCALLCONV UninstInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
1895 {
1896     char szPath[MAX_PATH];
1897     struct APPINFO *pAppInfo;
1898     char *pszFile = 0;
1899     char *pszSubDir = 0;
1900
1901     hDlg = hIS;
1902
1903     bSilentMode = !IsWindowVisible(hIS);
1904
1905     // Which app are we uninstalling?
1906     pAppInfo = GetApp();
1907     if (!pAppInfo) {
1908         ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
1909         return -1;
1910     }
1911
1912     // Get the app's install dir
1913     pszInstallDir = GetAppInstallDir(pAppInfo, FALSE);
1914     if (!pszInstallDir)
1915         return -1;
1916
1917     // If this app has a custom uninstall func, call it here
1918     if (pAppInfo->pUninstallFunc)
1919         if (!pAppInfo->pUninstallFunc())
1920             return -1;
1921
1922     if (pAppInfo->pszRegKeysToPreserve)
1923         if (!PreserveConfigInfo(pAppInfo))
1924             return -1;
1925
1926     // Unconfigure the service, if there is one for this app
1927     if (pAppInfo->pszSvcKey) {
1928         if (IsServiceInstalled(pAppInfo->pszSvcKey))
1929             if (UninstallService(pAppInfo) == 1)
1930                 return -1;
1931     }
1932
1933     RememberInstallDir(pszInstallDir);
1934
1935     DeleteInUseFiles(pAppInfo, fileInfo);
1936
1937     // Remove the app's bin path from the system path
1938     if (pAppInfo->pszBinPath) {
1939         BuildShortPath(szPath, sizeof(szPath), pszInstallDir, pAppInfo->pszBinPath);
1940         RemoveFromPath(szPath);
1941     }
1942
1943     // Remove entry from NetworkProvider\Order key in registry
1944     if (pAppInfo->pszNetworkProviderOrder)
1945         RemoveFromNetworkProviderOrder(pAppInfo->pszNetworkProviderOrder);
1946
1947     // Remove any generated subdirectories
1948     if (!bPreserveConfigInfo && pAppInfo->pszDirsToDel) {
1949         for (pszSubDir = pAppInfo->pszDirsToDel; *pszSubDir; pszSubDir += strlen(pszSubDir) + 1)
1950             RemoveDir(ExpandPath(pszSubDir));
1951     }
1952
1953     // Remove any generated files
1954     if (!bPreserveConfigInfo && pAppInfo->pszFilesToDel) {
1955         for (pszFile = pAppInfo->pszFilesToDel; *pszFile; pszFile += strlen(pszFile) + 1)
1956             RemoveFiles(ExpandPath(pszFile));
1957     }
1958
1959     // Remove any registry values that IS can't handle
1960     RemoveRegValues(pAppInfo->pRegValues);
1961     if (IsWinNT())
1962         RemoveRegValues(pAppInfo->pWinNTRegValues);
1963     else    
1964         RemoveRegValues(pAppInfo->pWin9XRegValues);
1965
1966     // Remove the start menu entries for this app
1967     if (pAppInfo->pszStartMenuEntries) {
1968         DeleteStartMenuEntries(pAppInfo->pszStartMenuEntries);
1969         RefreshStartMenu();
1970     }
1971
1972     // Remove the install dir
1973     RemoveDirectory(pszInstallDir);
1974
1975     return 0;
1976 }
1977
1978 void SUCALLCONV UninstUnInitialize(HWND hIS, HINSTANCE hIS5, long Reserved)
1979 {
1980     char *pszInstallDir;
1981     char szDirPath[MAX_PATH];
1982     char *psz;
1983     struct APPINFO *pAppInfo;
1984
1985     // If we just uninstalled the last AFS app, then do some cleanup.
1986     if (IsAppInstalled(&appServer) || IsAppInstalled(&appClient) ||
1987         IsAppInstalled(&appControlCenter) || IsAppInstalled(&appLightClient) ||
1988         IsAppInstalled(&appDocs))
1989     {
1990         return;
1991     }
1992
1993     bSilentMode = !IsWindowVisible(hIS);
1994     
1995     // Which app did we just uninstall?
1996     pAppInfo = GetApp();
1997     if (!pAppInfo) {
1998         ShowError(IDS_CANT_DETERMINE_PRODUCT, 0);
1999         return;
2000     }
2001
2002     // Get the app's install dir
2003     pszInstallDir = GetAppInstallDir(pAppInfo, TRUE);
2004     if (!pszInstallDir)
2005         return;
2006
2007     // Remove the reg key we used to remember the app install dir
2008     RegDeleteEntryAlt(UNINSTALL_TEMP_INFO_KEY, REGENTRY_KEY);
2009
2010     // Try to remove the reg key used to store config info, but only
2011     // if there are no app config info sub keys present.
2012     if (!DoSubKeysExist(AFS_PRESERVED_CFG_INFO_KEY))
2013         RegDeleteEntryAlt(AFS_PRESERVED_CFG_INFO_KEY, REGENTRY_KEY);
2014
2015     // Remove the install dir
2016     RemoveDirectory(pszInstallDir);
2017
2018     // Attempt to remove the install root and common directories.  The are 
2019     // shared and so no single app knows to delete them.
2020
2021     // Strip off the app specific part of the install dir
2022     psz = strrchr(pszInstallDir, '\\');
2023     if (psz)
2024         *psz = 0;
2025
2026     sprintf(szDirPath, "%s\\%s", pszInstallDir, "Common");
2027     RemoveDirectory(szDirPath);
2028
2029     // Remove the Common directory from the system path
2030     RemoveFromPath(szDirPath);
2031
2032     // Remove all of the documentation dirs
2033     sprintf(szDirPath, "%s\\%s", pszInstallDir, "Documentation");
2034     RemoveDirectoryTree(szDirPath);
2035
2036     // Ok, up to this point we have been removing files we know we
2037     // created.  However, after this point we are into the path
2038     // that the user chose for our install root.  The default for
2039     // this is IBM/Afs, but they could have chosen anything,
2040     // including a dir or dirs that have other products in them.
2041     // We will check to see if it is IBM\AFS and if it is then we 
2042     // will attempt to remove them.
2043     
2044     // Back up a level and look for AFS
2045     psz = strrchr(pszInstallDir, '\\');
2046     if (psz) {
2047         if (stricmp(psz + 1, "AFS") == 0) {
2048             RemoveDirectory(pszInstallDir);
2049             *psz = 0;
2050         }
2051     }
2052
2053     // Back up a level and look for IBM
2054     psz = strrchr(pszInstallDir, '\\');
2055     if (psz) {
2056         if (stricmp(psz + 1, "IBM") == 0) {
2057             RemoveDirectory(pszInstallDir);
2058             *psz = 0;
2059         }
2060     }
2061
2062     // Remove the root afs start menu entry
2063     psz = GetStartMenuRoot();
2064     if (psz) {
2065         if (bSilentMode) {
2066             // Remove everything under our branch
2067             sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack\\AFS", psz);
2068             RemoveDirectoryTree(szDirPath);
2069             
2070             // Remove the IBM stuff only if the dirs are empty
2071             sprintf(szDirPath, "%s\\IBM WebSphere\\Performance Pack", psz);
2072             if (RemoveDirectory(szDirPath)) {
2073                 sprintf(szDirPath, "%s\\IBM WebSphere", psz);
2074                 RemoveDirectory(szDirPath);
2075             }
2076         } else {
2077             sprintf(szDirPath, "%s\\IBM AFS", psz);
2078             RemoveDirectoryTree(szDirPath);
2079         }
2080     }
2081 }
2082
2083 BOOLEAN _stdcall DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
2084 {
2085     if (reason == DLL_PROCESS_ATTACH) {
2086         hinst = (HINSTANCE)dll;
2087         TaLocale_LoadCorrespondingModuleByName (hinst, "afs_setup_utils.dll");
2088     }
2089
2090     return TRUE;
2091 }
2092
2093 extern "C" int WINAPI Test (HINSTANCE hInst, HINSTANCE hPrev, LPSTR psz, int nCmdShow)
2094 {
2095    ShowLicense ("TEST", "\\\\fury\\afssetup\\license\\ja_JP.rtf");
2096    return 0;
2097 }
2098
2099