1 #ifdef __NMAKE__
2 
3 # NMAKE portion.
4 # Build with : nmake /f custom.cpp
5 # Clean with : nmake /f custom.cpp clean
6 
7 # Builds custom.dll
8 
9 OUTPATH = .
10 
11 # program name macros
12 CC = cl /nologo
13 
14 LINK = link /nologo
15 
16 RM = del
17 
18 DLLFILE = $(OUTPATH)\custom.dll
19 
20 DLLEXPORTS =\
21     -EXPORT:EnableAllowTgtSessionKey \
22     -EXPORT:RevertAllowTgtSessionKey \
23     -EXPORT:AbortMsiImmediate \
24     -EXPORT:UninstallNsisInstallation \
25     -EXPORT:KillRunningProcesses \
26     -EXPORT:ListRunningProcesses \
27     -EXPORT:InstallNetProvider \
28     -EXPORT:UninstallNetProvider
29 
30 $(DLLFILE): $(OUTPATH)\custom.obj
31     $(LINK) /OUT:$@ /DLL $** $(DLLEXPORTS)
32 
33 $(OUTPATH)\custom.obj: custom.cpp custom.h
34     $(CC) /c /Fo$@ custom.cpp
35 
36 all: $(DLLFILE)
37 
38 clean:
39     $(RM) $(DLLFILE)
40     $(RM) $(OUTPATH)\custom.obj
41     $(RM) $(OUTPATH)\custom.exp
42 
43 !IFDEF __C_TEXT__
44 #else
45 /*
46 
47 Copyright 2004,2005 by the Massachusetts Institute of Technology
48 
49 All rights reserved.
50 
51 Permission to use, copy, modify, and distribute this software and its
52 documentation for any purpose and without fee is hereby granted,
53 provided that the above copyright notice appear in all copies and that
54 both that copyright notice and this permission notice appear in
55 supporting documentation, and that the name of the Massachusetts
56 Institute of Technology (M.I.T.) not be used in advertising or publicity
57 pertaining to distribution of the software without specific, written
58 prior permission.
59 
60 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
61 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
62 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
63 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
64 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
65 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
66 SOFTWARE.
67 
68 */
69 
70 /**************************************************************
71 * custom.cpp : Dll implementing custom action to install Kerberos for Windows
72 *
73 *         The functions in this file are for use as entry points
74 *         for calls from MSI only. The specific MSI parameters
75 *         are noted in the comments section of each of the
76 *         functions.
77 *
78 * rcsid: $Id$
79 **************************************************************/
80 
81 #pragma unmanaged
82 
83 // Only works for Win2k and above
84 #define _WIN32_WINNT 0x500
85 #include "custom.h"
86 #include <shellapi.h>
87 
88 // linker stuff
89 #pragma comment(lib, "msi")
90 #pragma comment(lib, "advapi32")
91 #pragma comment(lib, "shell32")
92 #pragma comment(lib, "user32")
93 
94 void ShowMsiError( MSIHANDLE hInstall, DWORD errcode, DWORD param ){
95 	MSIHANDLE hRecord;
96 
97 	hRecord = MsiCreateRecord(3);
98 	MsiRecordClearData(hRecord);
99 	MsiRecordSetInteger(hRecord, 1, errcode);
100 	MsiRecordSetInteger(hRecord, 2, param);
101 
102 	MsiProcessMessage( hInstall, INSTALLMESSAGE_ERROR, hRecord );
103 
104 	MsiCloseHandle( hRecord );
105 }
106 
107 static void ShowMsiErrorEx(MSIHANDLE hInstall, DWORD errcode, LPTSTR str,
108                            DWORD param )
109 {
110     MSIHANDLE hRecord;
111 
112     hRecord = MsiCreateRecord(3);
113     MsiRecordClearData(hRecord);
114     MsiRecordSetInteger(hRecord, 1, errcode);
115     MsiRecordSetString(hRecord, 2, str);
116     MsiRecordSetInteger(hRecord, 3, param);
117 
118     MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord);
119 
120     MsiCloseHandle(hRecord);
121 }
122 
123 #define LSA_KERBEROS_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos"
124 #define LSA_KERBEROS_PARM_KEY "SYSTEM\\CurrentControlSet\\Control\\Lsa\\Kerberos\\Parameters"
125 #define KFW_CLIENT_KEY "SOFTWARE\\MIT\\Kerberos\\Client\\"
126 #define SESSKEY_VALUE_NAME "AllowTGTSessionKey"
127 
128 #define SESSBACKUP_VALUE_NAME "AllowTGTSessionKeyBackup"
129 #define SESSXPBACKUP_VALUE_NAME "AllowTGTSessionKeyBackupXP"
130 
131 
132 /* Set the AllowTGTSessionKey registry keys on install.  Called as a deferred custom action. */
133 MSIDLLEXPORT EnableAllowTgtSessionKey( MSIHANDLE hInstall ) {
134     return SetAllowTgtSessionKey( hInstall, TRUE );
135 }
136 
137 /* Unset the AllowTGTSessionKey registry keys on uninstall.  Called as a deferred custom action. */
138 MSIDLLEXPORT RevertAllowTgtSessionKey( MSIHANDLE hInstall ) {
139     return SetAllowTgtSessionKey( hInstall, FALSE );
140 }
141 
142 UINT SetAllowTgtSessionKey( MSIHANDLE hInstall, BOOL pInstall ) {
143     TCHAR tchVersionString[1024];
144     TCHAR tchVersionKey[2048];
145     DWORD size;
146     DWORD type;
147     DWORD value;
148     HKEY hkKfwClient = NULL;
149     HKEY hkLsaKerberos = NULL;
150     HKEY hkLsaKerberosParm = NULL;
151     UINT rv;
152     DWORD phase = 0;
153 
154     // construct the backup key path
155     size = sizeof(tchVersionString) / sizeof(TCHAR);
156     rv = MsiGetProperty( hInstall, _T("CustomActionData"), tchVersionString, &size );
157     if(rv != ERROR_SUCCESS) {
158         if(pInstall) {
159             ShowMsiError( hInstall, ERR_CUSTACTDATA, rv );
160             return rv;
161         } else {
162             return ERROR_SUCCESS;
163         }
164     }
165 
166     _tcscpy( tchVersionKey, _T( KFW_CLIENT_KEY ) );
167     _tcscat( tchVersionKey, tchVersionString );
168 
169     phase = 1;
170 
171     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, tchVersionKey, 0, ((pInstall)?KEY_WRITE:KEY_READ), &hkKfwClient );
172     if(rv != ERROR_SUCCESS)
173         goto cleanup;
174 
175     phase = 2;
176 
177     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberos );
178     if(rv != ERROR_SUCCESS)
179         goto cleanup;
180 
181     phase = 3;
182 
183     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _T( LSA_KERBEROS_PARM_KEY ), 0, KEY_READ | KEY_WRITE, &hkLsaKerberosParm );
184     if(rv != ERROR_SUCCESS) {
185         hkLsaKerberosParm = NULL;
186     }
187 
188     if(pInstall) {
189         // backup the existing values
190         if(hkLsaKerberosParm) {
191             phase = 4;
192 
193             size = sizeof(value);
194             rv = RegQueryValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
195             if(rv != ERROR_SUCCESS)
196                 value = 0;
197 
198             phase = 5;
199             rv = RegSetValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
200             if(rv != ERROR_SUCCESS)
201                 goto cleanup;
202         }
203 
204         phase = 6;
205         size = sizeof(value);
206         rv = RegQueryValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
207         if(rv != ERROR_SUCCESS)
208             value = 0;
209 
210         phase = 7;
211         rv = RegSetValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
212         if(rv != ERROR_SUCCESS)
213             goto cleanup;
214 
215         // and now write the actual values
216         phase = 8;
217         value = 1;
218         rv = RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
219         if(rv != ERROR_SUCCESS)
220             goto cleanup;
221 
222         if(hkLsaKerberosParm) {
223             phase = 9;
224             value = 1;
225             rv = RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
226             if(rv != ERROR_SUCCESS)
227                 goto cleanup;
228         }
229 
230     } else { // uninstalling
231         // Don't fail no matter what goes wrong.  This is also a rollback action.
232         if(hkLsaKerberosParm) {
233             size = sizeof(value);
234             rv = RegQueryValueEx( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
235             if(rv != ERROR_SUCCESS)
236                 value = 0;
237 
238             RegSetValueEx( hkLsaKerberosParm, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
239         }
240 
241         size = sizeof(value);
242         rv = RegQueryValueEx( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ), NULL, &type, (LPBYTE) &value, &size );
243         if(rv != ERROR_SUCCESS)
244             value = 0;
245 
246         RegSetValueEx( hkLsaKerberos, _T( SESSKEY_VALUE_NAME ), 0, REG_DWORD, (LPBYTE) &value, sizeof(value));
247 
248         RegDeleteValue( hkKfwClient, _T( SESSXPBACKUP_VALUE_NAME ) );
249         RegDeleteValue( hkKfwClient, _T( SESSBACKUP_VALUE_NAME ) );
250     }
251 
252     // all done
253     rv = ERROR_SUCCESS;
254 
255 cleanup:
256     if(rv != ERROR_SUCCESS && pInstall) {
257         ShowMsiError(hInstall, 4005, phase);
258     }
259     if(hkKfwClient) RegCloseKey( hkKfwClient );
260     if(hkLsaKerberos) RegCloseKey( hkLsaKerberos );
261     if(hkLsaKerberosParm) RegCloseKey( hkLsaKerberosParm );
262 
263     return rv;
264 }
265 
266 /* Abort the installation (called as an immediate custom action) */
267 MSIDLLEXPORT AbortMsiImmediate( MSIHANDLE hInstall ) {
268     DWORD rv;
269 	DWORD dwSize = 0;
270 	LPTSTR sReason = NULL;
271 	LPTSTR sFormatted = NULL;
272 	MSIHANDLE hRecord = NULL;
273 	LPTSTR cAbortReason = _T("ABORTREASON");
274 
275 	rv = MsiGetProperty( hInstall, cAbortReason, _T(""), &dwSize );
276 	if(rv != ERROR_MORE_DATA) goto _cleanup;
277 
278 	sReason = new TCHAR[ ++dwSize ];
279 
280 	rv = MsiGetProperty( hInstall, cAbortReason, sReason, &dwSize );
281 
282 	if(rv != ERROR_SUCCESS) goto _cleanup;
283 
284     hRecord = MsiCreateRecord(3);
285 	MsiRecordClearData(hRecord);
286 	MsiRecordSetString(hRecord, 0, sReason);
287 
288 	dwSize = 0;
289 
290 	rv = MsiFormatRecord(hInstall, hRecord, "", &dwSize);
291 	if(rv != ERROR_MORE_DATA) goto _cleanup;
292 
293 	sFormatted = new TCHAR[ ++dwSize ];
294 
295 	rv = MsiFormatRecord(hInstall, hRecord, sFormatted, &dwSize);
296 
297 	if(rv != ERROR_SUCCESS) goto _cleanup;
298 
299 	MsiCloseHandle(hRecord);
300 
301 	hRecord = MsiCreateRecord(3);
302 	MsiRecordClearData(hRecord);
303 	MsiRecordSetInteger(hRecord, 1, ERR_ABORT);
304 	MsiRecordSetString(hRecord,2, sFormatted);
305 	MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecord);
306 
307 _cleanup:
308 	if(sFormatted) delete sFormatted;
309 	if(hRecord) MsiCloseHandle( hRecord );
310 	if(sReason) delete sReason;
311 
312 	return ~ERROR_SUCCESS;
313 }
314 
315 /* Kill specified processes that are running on the system */
316 /* Uses the custom table KillProcess.  Called as an immediate action. */
317 
318 #define MAX_KILL_PROCESSES 255
319 #define FIELD_SIZE 256
320 
321 struct _KillProc {
322     TCHAR * image;
323     TCHAR * desc;
324     BOOL    found;
325     DWORD   pid;
326 };
327 
328 #define RV_BAIL if(rv != ERROR_SUCCESS) goto _cleanup
329 
330 MSIDLLEXPORT KillRunningProcesses( MSIHANDLE hInstall ) {
331     return KillRunningProcessesWorker( hInstall, TRUE );
332 }
333 
334 /* When listing running processes, we populate the ListBox table with
335    values associated with the property 'KillableProcesses'.  If we
336    actually find any processes worth killing, then we also set the
337    'FoundProcceses' property to '1'.  Otherwise we set it to ''.
338 */
339 
340 MSIDLLEXPORT ListRunningProcesses( MSIHANDLE hInstall ) {
341     return KillRunningProcessesWorker( hInstall, FALSE );
342 }
343 
344 UINT KillRunningProcessesWorker( MSIHANDLE hInstall, BOOL bKill )
345 {
346     UINT rv = ERROR_SUCCESS;
347     _KillProc * kpList;
348     int nKpList = 0;
349     int i;
350     int rowNum = 1;
351     DWORD size;
352     BOOL found = FALSE;
353 
354     MSIHANDLE hDatabase = NULL;
355     MSIHANDLE hView = NULL;
356     MSIHANDLE hViewInsert = NULL;
357     MSIHANDLE hRecord = NULL;
358     MSIHANDLE hRecordInsert = NULL;
359 
360     HANDLE hSnapshot = NULL;
361 
362     PROCESSENTRY32 pe;
363 
364     kpList = new _KillProc[MAX_KILL_PROCESSES];
365     memset(kpList, 0, sizeof(*kpList) * MAX_KILL_PROCESSES);
366 
367     hDatabase = MsiGetActiveDatabase( hInstall );
368     if( hDatabase == NULL ) {
369         rv = GetLastError();
370         goto _cleanup;
371     }
372 
373     // If we are only going to list out the processes, delete all the existing
374     // entries first.
375 
376     if(!bKill) {
377 
378         rv = MsiDatabaseOpenView( hDatabase,
379             _T( "DELETE FROM `ListBox` WHERE `ListBox`.`Property` = 'KillableProcesses'" ),
380             &hView); RV_BAIL;
381 
382         rv = MsiViewExecute( hView, NULL ); RV_BAIL;
383 
384         MsiCloseHandle( hView );
385 
386         hView = NULL;
387 
388         rv = MsiDatabaseOpenView( hDatabase,
389               _T( "SELECT * FROM `ListBox` WHERE `Property` = 'KillableProcesses'" ),
390             &hViewInsert); RV_BAIL;
391 
392         MsiViewExecute(hViewInsert, NULL);
393 
394         hRecordInsert = MsiCreateRecord(4);
395 
396         if(hRecordInsert == NULL) {
397             rv = GetLastError();
398             goto _cleanup;
399         }
400     }
401 
402     // Open a view
403     rv = MsiDatabaseOpenView( hDatabase,
404         _T( "SELECT `Image`,`Desc` FROM `KillProcess`" ),
405         &hView); RV_BAIL;
406 
407     rv = MsiViewExecute( hView, NULL ); RV_BAIL;
408 
409     do {
410         rv = MsiViewFetch( hView, &hRecord );
411         if(rv != ERROR_SUCCESS) {
412             if(hRecord)
413                 MsiCloseHandle(hRecord);
414             hRecord = NULL;
415             break;
416         }
417 
418         kpList[nKpList].image = new TCHAR[ FIELD_SIZE ]; kpList[nKpList].image[0] = _T('\0');
419         kpList[nKpList].desc = new TCHAR[ FIELD_SIZE ];  kpList[nKpList].desc[0] = _T('\0');
420         nKpList++;
421 
422         size = FIELD_SIZE;
423         rv = MsiRecordGetString(hRecord, 1, kpList[nKpList-1].image, &size); RV_BAIL;
424 
425         size = FIELD_SIZE;
426         rv = MsiRecordGetString(hRecord, 2, kpList[nKpList-1].desc, &size); RV_BAIL;
427 
428         MsiCloseHandle(hRecord);
429     } while(nKpList < MAX_KILL_PROCESSES);
430 
431     hRecord = NULL;
432 
433     // now we have all the processes in the array.  Check if they are running.
434 
435     hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
436     if(hSnapshot == INVALID_HANDLE_VALUE) {
437         rv = GetLastError();
438         goto _cleanup;
439     }
440 
441     pe.dwSize = sizeof( PROCESSENTRY32 );
442 
443     if(!Process32First( hSnapshot, &pe )) {
444         // technically we should at least find the MSI process, but we let this pass
445         rv = ERROR_SUCCESS;
446         goto _cleanup;
447     }
448 
449     do {
450         for(i=0; i<nKpList; i++) {
451             if(!_tcsicmp( kpList[i].image, pe.szExeFile )) {
452                 // got one
453                 if(bKill) {
454                     // try to kill the process
455                     HANDLE hProcess = NULL;
456 
457                     // If we encounter an error, instead of bailing
458                     // out, we continue on to the next process.  We
459                     // may not have permission to kill all the
460                     // processes we want to kill anyway.  If there are
461                     // any files that we want to replace that is in
462                     // use, Windows Installer will schedule a reboot.
463                     // Also, it's not like we have an exhaustive list
464                     // of all the programs that use Kerberos anyway.
465 
466                     hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
467                     if(hProcess == NULL) {
468                         rv = GetLastError();
469                         break;
470                     }
471 
472                     if(!TerminateProcess(hProcess, 0)) {
473                         rv = GetLastError();
474                         CloseHandle(hProcess);
475                         break;
476                     }
477 
478                     CloseHandle(hProcess);
479 
480                 } else {
481                     TCHAR buf[256];
482 
483                     // we are supposed to just list out the processes
484                     rv = MsiRecordClearData( hRecordInsert ); RV_BAIL;
485                     rv = MsiRecordSetString( hRecordInsert, 1, _T("KillableProcesses"));
486                     rv = MsiRecordSetInteger( hRecordInsert, 2, rowNum++ ); RV_BAIL;
487                     _itot( rowNum, buf, 10 );
488                     rv = MsiRecordSetString( hRecordInsert, 3, buf ); RV_BAIL;
489                     if(_tcslen(kpList[i].desc)) {
490                         rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].desc ); RV_BAIL;
491                     } else {
492                         rv = MsiRecordSetString( hRecordInsert, 4, kpList[i].image ); RV_BAIL;
493                     }
494                     MsiViewModify(hViewInsert, MSIMODIFY_INSERT_TEMPORARY, hRecordInsert); RV_BAIL;
495 
496                     found = TRUE;
497                 }
498                 break;
499             }
500         }
501    } while( Process32Next( hSnapshot, &pe ) );
502 
503     if(!bKill) {
504         // set the 'FoundProcceses' property
505         if(found) {
506             MsiSetProperty( hInstall, _T("FoundProcesses"), _T("1"));
507         } else {
508             MsiSetProperty( hInstall, _T("FoundProcesses"), _T(""));
509         }
510     }
511 
512     // Finally:
513     rv = ERROR_SUCCESS;
514 
515 _cleanup:
516 
517     if(hRecordInsert) MsiCloseHandle(hRecordInsert);
518     if(hViewInsert) MsiCloseHandle(hView);
519 
520     if(hSnapshot && hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot);
521 
522     while(nKpList) {
523         nKpList--;
524         delete kpList[nKpList].image;
525         delete kpList[nKpList].desc;
526     }
527     delete kpList;
528 
529     if(hRecord) MsiCloseHandle(hRecord);
530     if(hView) MsiCloseHandle(hView);
531 
532     if(hDatabase) MsiCloseHandle(hDatabase);
533 
534     if(rv != ERROR_SUCCESS) {
535         ShowMsiError(hInstall, ERR_PROC_LIST, rv);
536     }
537 
538     return rv;
539 }
540 
541 static bool IsNSISInstalled()
542 {
543     HKEY nsisKfwKey = NULL;
544     // Note: check Wow6432 node if 64 bit build
545     HRESULT res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
546                                "SOFTWARE\\Microsoft\\Windows\\CurrentVersion"
547                                "\\Uninstall\\Kerberos for Windows",
548                                0,
549                                KEY_READ | KEY_WOW64_32KEY,
550                                &nsisKfwKey);
551     if (res != ERROR_SUCCESS)
552         return FALSE;
553 
554     RegCloseKey(nsisKfwKey);
555     return TRUE;
556 }
557 
558 static HANDLE NSISUninstallShellExecute(LPTSTR pathUninstall)
559 {
560     SHELLEXECUTEINFO   sei;
561     ZeroMemory ( &sei, sizeof(sei) );
562 
563     sei.cbSize          = sizeof(sei);
564     sei.hwnd            = GetForegroundWindow();
565     sei.fMask           = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI |
566                           SEE_MASK_NOCLOSEPROCESS;
567     sei.lpVerb          = _T("runas"); // run as administrator
568     sei.lpFile          = pathUninstall;
569     sei.lpParameters    = _T("");
570     sei.nShow           = SW_SHOWNORMAL;
571 
572     if (!ShellExecuteEx(&sei)) {
573         // FAILED! TODO: report details?
574     }
575     return sei.hProcess;
576 }
577 
578 static HANDLE NSISUninstallCreateProcess(LPTSTR pathUninstall)
579 {
580     STARTUPINFO sInfo;
581     PROCESS_INFORMATION pInfo;
582     pInfo.hProcess = NULL;
583     pInfo.hThread = NULL;
584 
585     // Create a process for the uninstaller
586     sInfo.cb = sizeof(sInfo);
587     sInfo.lpReserved = NULL;
588     sInfo.lpDesktop = _T("");
589     sInfo.lpTitle = _T("NSIS Uninstaller for Kerberos for Windows");
590     sInfo.dwX = 0;
591     sInfo.dwY = 0;
592     sInfo.dwXSize = 0;
593     sInfo.dwYSize = 0;
594     sInfo.dwXCountChars = 0;
595     sInfo.dwYCountChars = 0;
596     sInfo.dwFillAttribute = 0;
597     sInfo.dwFlags = 0;
598     sInfo.wShowWindow = 0;
599     sInfo.cbReserved2 = 0;
600     sInfo.lpReserved2 = 0;
601     sInfo.hStdInput = 0;
602     sInfo.hStdOutput = 0;
603     sInfo.hStdError = 0;
604 
605     if (!CreateProcess(pathUninstall,
606                        _T("Uninstall /S"),
607                        NULL,
608                        NULL,
609                        FALSE,
610                        CREATE_SUSPENDED,
611                        NULL,
612                        NULL,
613                        &sInfo,
614                        &pInfo)) {
615         // failure; could grab info, but we should be able to recover by
616         // using NSISUninstallShellExecute...
617     } else {
618         // success
619         // start up the thread
620         ResumeThread(pInfo.hThread);
621         // done with thread handle
622         CloseHandle(pInfo.hThread);
623     }
624     return pInfo.hProcess;
625 }
626 
627 
628 /* Uninstall NSIS */
629 MSIDLLEXPORT UninstallNsisInstallation( MSIHANDLE hInstall )
630 {
631     DWORD rv = ERROR_SUCCESS;
632     DWORD lastError;
633     // lookup the NSISUNINSTALL property value
634     LPTSTR cNsisUninstall = _T("UPGRADENSIS");
635     LPTSTR strPathUninst = NULL;
636     DWORD dwSize = 0;
637     HANDLE hProcess = NULL;
638     HANDLE hIo = NULL;
639     HANDLE hJob = NULL;
640 
641     rv = MsiGetProperty( hInstall, cNsisUninstall, _T(""), &dwSize );
642     if(rv != ERROR_MORE_DATA) goto _cleanup;
643 
644     strPathUninst = new TCHAR[ ++dwSize ];
645 
646     rv = MsiGetProperty(hInstall, cNsisUninstall, strPathUninst, &dwSize);
647     if(rv != ERROR_SUCCESS) goto _cleanup;
648 
649     hProcess = NSISUninstallCreateProcess(strPathUninst);
650     if (hProcess == NULL) // expected when run on UAC-limited account
651         hProcess = NSISUninstallShellExecute(strPathUninst);
652 
653     if (hProcess == NULL) {
654         // still no uninstall process? ick...
655         lastError = GetLastError();
656         rv = 40;
657         goto _cleanup;
658     }
659     // note that it is not suffiecient to wait for the initial process to
660     // finish; there is a whole process tree that we need to wait for.  sigh.
661     JOBOBJECT_ASSOCIATE_COMPLETION_PORT acp;
662     acp.CompletionKey = 0;
663     hJob = CreateJobObject(NULL, _T("NSISUninstallObject"));
664     if(!hJob) {
665         rv = 41;
666         goto _cleanup;
667     }
668 
669     hIo = CreateIoCompletionPort(INVALID_HANDLE_VALUE,0,0,0);
670     if(!hIo) {
671         rv = 42;
672         goto _cleanup;
673     }
674 
675     acp.CompletionPort = hIo;
676 
677     SetInformationJobObject(hJob,
678                             JobObjectAssociateCompletionPortInformation,
679                             &acp,
680                             sizeof(acp));
681 
682     AssignProcessToJobObject(hJob, hProcess);
683 
684     DWORD msgId;
685     ULONG_PTR unusedCompletionKey;
686     LPOVERLAPPED unusedOverlapped;
687     for (;;) {
688         if (!GetQueuedCompletionStatus(hIo,
689                                        &msgId,
690                                        &unusedCompletionKey,
691                                        &unusedOverlapped,
692                                        INFINITE)) {
693             Sleep(1000);
694         } else if (msgId == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
695             break;
696         }
697     }
698 
699 _cleanup:
700     if (hProcess) CloseHandle(hProcess);
701     if (hIo) CloseHandle(hIo);
702     if (hJob) CloseHandle(hJob);
703 
704     if (IsNSISInstalled()) {
705         // uninstall failed: maybe user cancelled uninstall, or something else
706         // went wrong...
707         if (rv == ERROR_SUCCESS)
708             rv = 43;
709     } else {
710         // Maybe something went wrong, but it doesn't matter as long as nsis
711         // is gone now...
712         rv = ERROR_SUCCESS;
713     }
714 
715     if (rv == 40) {
716         // CreateProcess() / ShellExecute() errors get extra data
717         ShowMsiErrorEx(hInstall, ERR_NSS_FAILED_CP, strPathUninst, lastError);
718     } else if (rv != ERROR_SUCCESS) {
719         ShowMsiError(hInstall, ERR_NSS_FAILED, rv);
720     }
721 
722     if (strPathUninst) delete strPathUninst;
723     return rv;
724 }
725 
726 /* Check and add or remove networkprovider key value
727         str : target string
728         str2: string to add/remove
729         bInst: == 1 if string should be added to target if not already there,
730 	otherwise remove string from target if present.
731 */
732 int npi_CheckAndAddRemove( LPTSTR str, LPTSTR str2, int bInst ) {
733 
734     LPTSTR target, charset, match;
735     int ret=0;
736 
737     target = new TCHAR[lstrlen(str)+3];
738     lstrcpy(target,_T(","));
739     lstrcat(target,str);
740     lstrcat(target,_T(","));
741     charset = new TCHAR[lstrlen(str2)+3];
742     lstrcpy(charset,_T(","));
743     lstrcat(charset,str2);
744     lstrcat(charset,_T(","));
745 
746     match = _tcsstr(target, charset);
747 
748     if ((match) && (bInst)) {
749         ret = INP_ERR_PRESENT;
750         goto cleanup;
751     }
752 
753     if ((!match) && (!bInst)) {
754         ret = INP_ERR_ABSENT;
755         goto cleanup;
756     }
757 
758     if (bInst) // && !match
759     {
760        lstrcat(str, _T(","));
761        lstrcat(str, str2);
762        ret = INP_ERR_ADDED;
763        goto cleanup;
764     }
765 
766     // if (!bInst) && (match)
767     {
768        lstrcpy(str+(match-target),match+lstrlen(str2)+2);
769        str[lstrlen(str)-1]=_T('\0');
770        ret = INP_ERR_REMOVED;
771        goto cleanup;
772     }
773 
774 cleanup:
775 
776     delete target;
777     delete charset;
778     return ret;
779 }
780 
781 /* Sets the registry keys required for the functioning of the network provider */
782 
783 DWORD InstNetProvider(MSIHANDLE hInstall, int bInst) {
784     LPTSTR strOrder;
785     HKEY hkOrder;
786     LONG rv;
787     DWORD dwSize;
788     HANDLE hProcHeap;
789 
790     strOrder = (LPTSTR) 0;
791 
792     CHECK(rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, STR_KEY_ORDER, 0, KEY_READ | KEY_WRITE, &hkOrder ));
793 
794     dwSize = 0;
795     CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, NULL, &dwSize ) );
796 
797     strOrder = new TCHAR[ (dwSize + STR_SERVICE_LEN + 4) * sizeof(TCHAR) ];
798 
799     CHECK(rv = RegQueryValueEx( hkOrder, STR_VAL_ORDER, NULL, NULL, (LPBYTE) strOrder, &dwSize));
800 
801     strOrder[dwSize] = '\0';	/* reg strings are not always nul terminated */
802 
803     npi_CheckAndAddRemove( strOrder, STR_SERVICE , bInst);
804 
805     dwSize = (lstrlen( strOrder ) + 1) * sizeof(TCHAR);
806 
807     CHECK(rv = RegSetValueEx( hkOrder, STR_VAL_ORDER, NULL, REG_SZ, (LPBYTE) strOrder, dwSize ));
808 
809     /* everything else should be set by the MSI tables */
810     rv = ERROR_SUCCESS;
811 _cleanup:
812 
813     if( rv != ERROR_SUCCESS ) {
814         ShowMsiError( hInstall, ERR_NPI_FAILED, rv );
815     }
816 
817     if(strOrder) delete strOrder;
818 
819     return rv;
820 }
821 
822 MSIDLLEXPORT InstallNetProvider( MSIHANDLE hInstall ) {
823     return InstNetProvider( hInstall, 1 );
824 }
825 
826 MSIDLLEXPORT UninstallNetProvider( MSIHANDLE hInstall) {
827     return InstNetProvider( hInstall, 0 );
828 }
829 
830 #endif
831 #ifdef __NMAKE__
832 !ENDIF
833 #endif
834