Windows: remove trailing whitespace
[openafs.git] / src / WINNT / install / loopback / loopbackutils.cpp
1 /*
2    Copyright 2004 by the Massachusetts Institute of Technology
3
4    All rights reserved.
5
6    Permission to use, copy, modify, and distribute this software and its
7    documentation for any purpose and without fee is hereby granted,
8    provided that the above copyright notice appear in all copies and that
9    both that copyright notice and this permission notice appear in
10    supporting documentation, and that the name of the Massachusetts
11    Institute of Technology (M.I.T.) not be used in advertising or publicity
12    pertaining to distribution of the software without specific, written
13    prior permission.
14
15    M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18    ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19    WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21    SOFTWARE.
22 */
23
24 #define _WIN32_DCOM
25 #include <windows.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 struct Args {
30         bool bQuiet;
31         LPWSTR lpIPAddr;
32         LPWSTR lpSubnetMask;
33     LPWSTR lpConnectionName;
34         Args () : bQuiet (0), lpIPAddr (0), lpSubnetMask (0), lpConnectionName (0) { }
35         ~Args () {
36                 if (lpIPAddr) free (lpIPAddr);
37                 if (lpSubnetMask) free (lpSubnetMask);
38         if (lpConnectionName) free (lpConnectionName);
39         }
40 };
41
42 #include <shellapi.h>
43 #define INITGUID
44 #include <guiddef.h>
45 #include <devguid.h>
46 #include <objbase.h>
47 #include <setupapi.h>
48 #include <tchar.h>
49 #include <Msi.h>
50 #include <Msiquery.h>
51 #include "loopbackutils.h"
52
53 extern "C" DWORD UnInstallLoopBack(void)
54 {
55     BOOL ok;
56     DWORD ret = 0;
57     GUID netGuid;
58     HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
59     SP_DEVINFO_DATA DeviceInfoData;
60     DWORD index = 0;
61     BOOL found = FALSE;
62     DWORD size = 0;
63
64     // initialize the structure size
65     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
66
67     // copy the net class GUID
68     memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
69
70     // return a device info set contains all installed devices of the Net class
71     hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
72
73     if (hDeviceInfo == INVALID_HANDLE_VALUE)
74         return GetLastError();
75
76     // enumerate the driver info list
77     while (TRUE)
78     {
79         TCHAR * deviceHwid;
80
81         ok = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
82
83         if(!ok)
84         {
85           if(GetLastError() == ERROR_NO_MORE_ITEMS)
86               break;
87           else
88           {
89               index++;
90               continue;
91           }
92         }
93
94         // try to get the DeviceDesc registry property
95         ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
96                                               &DeviceInfoData,
97                                               SPDRP_HARDWAREID,
98                                               NULL,
99                                               NULL,
100                                               0,
101                                               &size);
102         if (!ok)
103         {
104             ret = GetLastError();
105             if (ret != ERROR_INSUFFICIENT_BUFFER) {
106                 index++;
107                 continue;
108             }
109
110             deviceHwid = (TCHAR *)malloc(size);
111             ok = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
112                                                   &DeviceInfoData,
113                                                   SPDRP_HARDWAREID,
114                                                   NULL,
115                                                   (PBYTE)deviceHwid,
116                                                   size,
117                                                   NULL);
118             if (!ok) {
119                 free(deviceHwid);
120                 deviceHwid = NULL;
121                 index++;
122                 continue;
123             }
124         } else {
125             // something is wrong.  This shouldn't have worked with a NULL buffer
126             ReportMessage(0, "GetDeviceRegistryProperty succeeded with a NULL buffer", NULL,  NULL, 0);
127             index++;
128             continue;
129         }
130
131         for (TCHAR *t = deviceHwid; t && *t && t < &deviceHwid[size / sizeof(TCHAR)]; t += _tcslen(t) + 1)
132         {
133             if(!_tcsicmp(DRIVERHWID, t)) {
134                 found = TRUE;
135                 break;
136             }
137         }
138
139         if (deviceHwid) {
140             free(deviceHwid);
141             deviceHwid = NULL;
142         }
143
144         if (found)
145             break;
146
147         index++;
148     }
149
150     if (found == FALSE)
151     {
152         ret = GetLastError();
153         ReportMessage(0,"Driver does not seem to be installed", DRIVER_DESC, NULL, ret);
154         goto cleanup;
155     }
156
157     ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
158     if (!ok)
159     {
160         ret = GetLastError();
161         goto cleanup;
162     }
163
164     ok = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
165     if (!ok)
166     {
167         ret = GetLastError();
168         goto cleanup;
169     }
170
171     ret = 0;
172
173 cleanup:
174     // clean up the device info set
175     if (hDeviceInfo != INVALID_HANDLE_VALUE)
176         SetupDiDestroyDeviceInfoList(hDeviceInfo);
177
178     return ret;
179 }
180
181 BOOL IsLoopbackInstalled(void)
182 {
183     HDEVINFO DeviceInfoSet;
184     SP_DEVINFO_DATA DeviceInfoData;
185     DWORD i,err;
186     BOOL found;
187
188     //
189     // Create a Device Information Set with all present devices.
190     //
191     DeviceInfoSet = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT ); // All devices present on system
192     if (DeviceInfoSet == INVALID_HANDLE_VALUE)
193     {
194         return FALSE; // nothing installed?
195     }
196
197     //
198     //  Enumerate through all Devices.
199     //
200     found = FALSE;
201     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
202     for (i=0; ;i++)
203     {
204         BOOL ok;
205         DWORD DataT;
206         TCHAR *p, *buffer = NULL;
207         DWORD buffersize = 0;
208
209         ok = SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DeviceInfoData);
210
211         if(!ok) {
212           if(GetLastError() == ERROR_NO_MORE_ITEMS)
213             break;
214           else
215             continue;
216         }
217
218         //
219         // We won't know the size of the HardwareID buffer until we call
220         // this function. So call it with a null to begin with, and then
221         // use the required buffer size to Alloc the nessicary space.
222         // Keep calling we have success or an unknown failure.
223         //
224         while (!SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
225                                                  &DeviceInfoData,
226                                                  SPDRP_HARDWAREID,
227                                                  &DataT,
228                                                  (PBYTE)buffer,
229                                                  buffersize,
230                                                  &buffersize))
231         {
232             if (GetLastError() == ERROR_INVALID_DATA)
233             {
234                 // May be a Legacy Device with no hwid. Continue.
235                 break;
236             }
237             else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
238             {
239                 // We need to change the buffer size.
240                 if (buffer)
241                     LocalFree(buffer);
242                 buffer = (TCHAR *)LocalAlloc(LPTR,buffersize);
243             }
244             else
245             {
246                 if (buffer)
247                     LocalFree(buffer);
248                 goto cleanup_DeviceInfo;
249             }
250         }
251
252         if (GetLastError() == ERROR_INVALID_DATA) {
253             if (buffer)
254                 LocalFree(buffer);
255             continue;
256         }
257
258         // Compare each entry in the buffer multi-sz list with our hwid.
259         for (p=buffer; *p && (p < &buffer[buffersize]); p += _tcslen(p)+1)
260         {
261             if (!_tcsicmp(DRIVERHWID,p))
262             {
263                 found = TRUE;
264                 break;
265             }
266         }
267
268         if (buffer)
269             LocalFree(buffer);
270         if (found)
271             break;
272     }
273
274     //  Cleanup.
275 cleanup_DeviceInfo:
276     err = GetLastError();
277     SetupDiDestroyDeviceInfoList(DeviceInfoSet);
278     SetLastError(err);
279
280     return found;
281 }
282
283
284 extern "C" DWORD InstallLoopBack(LPCTSTR pConnectionName, LPCTSTR ip, LPCTSTR mask)
285 {
286     BOOL ok;
287     DWORD ret = 0;
288     GUID netGuid;
289     HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
290     SP_DEVINFO_DATA DeviceInfoData;
291     SP_DRVINFO_DATA DriverInfoData;
292     SP_DEVINSTALL_PARAMS  DeviceInstallParams;
293     TCHAR className[MAX_PATH];
294     TCHAR temp[MAX_PATH];
295     DWORD index = 0;
296     BOOL found = FALSE;
297     BOOL registered = FALSE;
298     BOOL destroyList = FALSE;
299     PSP_DRVINFO_DETAIL_DATA pDriverInfoDetail;
300     DWORD detailBuf[2048];    // for our purposes, 8k buffer is more
301                               // than enough to obtain the hardware ID
302                               // of the loopback driver.
303
304     HKEY hkey = NULL;
305     DWORD cbSize;
306     DWORD dwValueType;
307     TCHAR pCfgGuidString[40];
308
309     // initialize the structure size
310     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
311     DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
312
313     // copy the net class GUID
314     memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof(GUID_DEVCLASS_NET));
315
316     // create an empty device info set associated with the net class GUID
317     hDeviceInfo = SetupDiCreateDeviceInfoList(&netGuid, NULL);
318     if (hDeviceInfo == INVALID_HANDLE_VALUE)
319         return GetLastError();
320
321     // get the class name from GUID
322     ok = SetupDiClassNameFromGuid(&netGuid, className, MAX_PATH, NULL);
323     if (!ok)
324     {
325         ret = GetLastError();
326         goto cleanup;
327     }
328
329     // create a device info element and add the new device instance
330     // key to registry
331     ok = SetupDiCreateDeviceInfo(hDeviceInfo, className, &netGuid, NULL, NULL,
332                                  DICD_GENERATE_ID, &DeviceInfoData);
333     if (!ok)
334     {
335         ret = GetLastError();
336         goto cleanup;
337     }
338
339     // select the newly created device info to be the currently
340     // selected member
341     ok = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
342     if (!ok)
343     {
344         ret = GetLastError();
345         goto cleanup;
346     }
347
348     // build a list of class drivers
349     ok = SetupDiBuildDriverInfoList(hDeviceInfo, &DeviceInfoData,
350                                     SPDIT_CLASSDRIVER);
351     if (!ok)
352     {
353         ret = GetLastError();
354         goto cleanup;
355     }
356
357     destroyList = TRUE;
358
359     // enumerate the driver info list
360     while (TRUE)
361     {
362         BOOL ret;
363
364         ret = SetupDiEnumDriverInfo(hDeviceInfo, &DeviceInfoData,
365                                   SPDIT_CLASSDRIVER, index, &DriverInfoData);
366
367         // if the function failed and GetLastError() returned
368         // ERROR_NO_MORE_ITEMS, then we have reached the end of the
369         // list.  Othewise there was something wrong with this
370         // particular driver.
371         if(!ret) {
372           if(GetLastError() == ERROR_NO_MORE_ITEMS)
373             break;
374           else {
375             index++;
376             continue;
377           }
378         }
379
380         pDriverInfoDetail = (PSP_DRVINFO_DETAIL_DATA) detailBuf;
381         pDriverInfoDetail->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
382
383         // if we successfully find the hardware ID and it turns out to
384         // be the one for the loopback driver, then we are done.
385         if (SetupDiGetDriverInfoDetail(hDeviceInfo,
386                                       &DeviceInfoData,
387                                       &DriverInfoData,
388                                       pDriverInfoDetail,
389                                       sizeof(detailBuf),
390                                       NULL)) {
391             TCHAR * t;
392
393             // pDriverInfoDetail->HardwareID is a MULTISZ string.  Go through the
394             // whole list and see if there is a match somewhere.
395             t = pDriverInfoDetail->HardwareID;
396             while (t && *t && t < (TCHAR *) &detailBuf[sizeof(detailBuf)/sizeof(detailBuf[0])]) {
397               if (!_tcsicmp(t, DRIVERHWID))
398                 break;
399
400               t += _tcslen(t) + 1;
401             }
402
403             if (t && *t && t < (TCHAR *) &detailBuf[sizeof(detailBuf)/sizeof(detailBuf[0])]) {
404               found = TRUE;
405               break;
406             }
407         }
408
409         index++;
410     }
411
412     if (!found)
413     {
414         ret = GetLastError();
415         ReportMessage(0,"Could not find the driver to install", DRIVER_DESC, NULL, 0);
416         goto cleanup;
417     }
418
419     // set the loopback driver to be the currently selected
420     ok = SetupDiSetSelectedDriver(hDeviceInfo, &DeviceInfoData,
421                                   &DriverInfoData);
422     if (!ok)
423     {
424         ret = GetLastError();
425         goto cleanup;
426     }
427
428     // register the phantom device to repare for install
429     ok = SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDeviceInfo,
430                                    &DeviceInfoData);
431     if (!ok)
432     {
433         ret = GetLastError();
434         goto cleanup;
435     }
436
437     // registered, but remove if errors occur in the following code
438     registered = TRUE;
439
440     // ask the installer if we can install the device
441     ok = SetupDiCallClassInstaller(DIF_ALLOW_INSTALL, hDeviceInfo,
442                                    &DeviceInfoData);
443     if (!ok)
444     {
445         ret = GetLastError();
446         if (ret != ERROR_DI_DO_DEFAULT)
447         {
448             goto cleanup;
449         }
450         else
451             ret = 0;
452     }
453
454     // install the files first
455     ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES, hDeviceInfo,
456                                    &DeviceInfoData);
457     if (!ok)
458     {
459         ret = GetLastError();
460         goto cleanup;
461     }
462
463     // get the device install parameters and disable filecopy
464     DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
465     ok = SetupDiGetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
466                                        &DeviceInstallParams);
467     if (ok)
468     {
469         DeviceInstallParams.Flags |= DI_NOFILECOPY;
470         ok = SetupDiSetDeviceInstallParams(hDeviceInfo, &DeviceInfoData,
471                                            &DeviceInstallParams);
472         if (!ok)
473         {
474             ret = GetLastError();
475             goto cleanup;
476         }
477     }
478
479     //
480     // Register any device-specific co-installers for this device,
481     //
482
483     ok = SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
484                                    hDeviceInfo,
485                                    &DeviceInfoData);
486     if (!ok)
487     {
488         ret = GetLastError();
489         goto cleanup;
490     }
491
492     //
493     // install any  installer-specified interfaces.
494     // and then do the real install
495     //
496     ok = SetupDiCallClassInstaller(DIF_INSTALLINTERFACES,
497                                    hDeviceInfo,
498                                    &DeviceInfoData);
499     if (!ok)
500     {
501         ret = GetLastError();
502         goto cleanup;
503     }
504     PAUSE;
505     ok = SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
506                                    hDeviceInfo,
507                                    &DeviceInfoData);
508     if (!ok)
509     {
510         ret = GetLastError();
511         PAUSE;
512         goto cleanup;
513     }
514
515     /* Skip to the end if we aren't setting the name */
516     if (!pConnectionName) goto cleanup;
517
518     // Figure out NetCfgInstanceId
519     hkey = SetupDiOpenDevRegKey(hDeviceInfo,
520                                 &DeviceInfoData,
521                                 DICS_FLAG_GLOBAL,
522                                 0,
523                                 DIREG_DRV,
524                                 KEY_READ);
525     if (hkey == INVALID_HANDLE_VALUE)
526     {
527         ret = GetLastError();
528         goto cleanup;
529     }
530
531     cbSize = sizeof(pCfgGuidString);
532     ret = RegQueryValueEx(hkey, _T("NetCfgInstanceId"), NULL,
533                           &dwValueType, (LPBYTE)pCfgGuidString, &cbSize);
534     RegCloseKey(hkey);
535
536     ret = RenameConnection(pCfgGuidString, pConnectionName);
537     if (ret)
538     {
539         ReportMessage(0,"Could not set the connection name", NULL, pConnectionName, 0);
540         goto cleanup;
541     }
542
543     if (!ip) goto cleanup;
544     ret = SetIpAddress(pCfgGuidString, ip, mask);
545     if (ret)
546     {
547         ReportMessage(0,"Could not set the ip address and network mask",NULL,NULL,ret);
548         goto cleanup;
549     }
550     ret = LoopbackBindings(pCfgGuidString);
551     if (ret)
552     {
553         ReportMessage(0,"Could not properly set the bindings",NULL,NULL,0);
554         goto cleanup;
555     }
556     ret = !UpdateHostsFile( pConnectionName, ip, "hosts", FALSE );
557     if (ret)
558     {
559         ReportMessage(0,"Could not update hosts file",NULL,NULL,0);
560         goto cleanup;
561     }
562     ret = !UpdateHostsFile( pConnectionName, ip, "lmhosts", TRUE );
563     if (ret)
564     {
565         ReportMessage(0,"Could not update lmhosts file",NULL,NULL,0);
566         goto cleanup;
567     }
568
569
570 cleanup:
571     // an error has occured, but the device is registered, we must remove it
572     if (ret != 0 && registered)
573         SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
574
575     found = SetupDiDeleteDeviceInfo(hDeviceInfo, &DeviceInfoData);
576
577     // destroy the driver info list
578     if (destroyList)
579         SetupDiDestroyDriverInfoList(hDeviceInfo, &DeviceInfoData,
580                                      SPDIT_CLASSDRIVER);
581     // clean up the device info set
582     if (hDeviceInfo != INVALID_HANDLE_VALUE)
583         SetupDiDestroyDeviceInfoList(hDeviceInfo);
584
585     return ret;
586 };
587
588 /* The following functions provide the RunDll32 interface
589  *    RunDll32 loopback_install.dll doLoopBackEntry [Interface Name] [IP address] [Subnet Mask]
590  */
591
592 static void wcsMallocAndCpy (LPWSTR * dst, const LPWSTR src) {
593         *dst = (LPWSTR) malloc ((wcslen (src) + 1) * sizeof (WCHAR));
594         wcscpy (*dst, src);
595 }
596
597 static void display_usage()
598 {
599     MessageBoxW( NULL,
600                  L"Installation utility for the MS Loopback Adapter\r\n\r\n"
601                  L"Usage:\r\n"
602                  L"RunDll32 loopback_install.dll doLoopBackEntry [q|quiet] [Connection Name] [IP address] [Submask]\r\n",
603                  L"loopback_install", MB_ICONINFORMATION | MB_OK );
604 }
605
606 static int process_args (LPWSTR lpCmdLine, int skip, Args & args) {
607         int i, iNumArgs;
608         LPWSTR * argvW;
609
610         argvW = CommandLineToArgvW (lpCmdLine, &iNumArgs);
611         // Skip over the command name
612         for (i = skip; i < iNumArgs; i++)
613         {
614                 if (wcsstr (argvW[i], L"help")
615                         || !_wcsicmp (argvW[i], L"?")
616                         || (wcslen(argvW[i]) == 2 && argvW[i][1] == L'?'))
617                 {
618                         display_usage();
619                         GlobalFree (argvW);
620                         return 0;
621                 }
622
623                 if (!_wcsicmp (argvW[i], L"q") || !_wcsicmp (argvW[i], L"quiet")) {
624                         args.bQuiet = true;
625                         continue;
626                 }
627
628                 if (!args.lpConnectionName) {
629                         wcsMallocAndCpy (&args.lpConnectionName, argvW[i]);
630                         continue;
631                 }
632
633                 if (!args.lpIPAddr) {
634                         wcsMallocAndCpy (&args.lpIPAddr, argvW[i]);
635                         continue;
636                 }
637
638                 if (!args.lpSubnetMask) {
639                         wcsMallocAndCpy (&args.lpSubnetMask, argvW[i]);
640                         continue;
641                 }
642
643                 display_usage();
644                 GlobalFree (argvW);
645                 return 0;
646         }
647
648         if (!args.lpConnectionName)
649                 wcsMallocAndCpy (&args.lpConnectionName, DEFAULT_NAME);
650         if (!args.lpIPAddr)
651                 wcsMallocAndCpy (&args.lpIPAddr, DEFAULT_IP);
652         if (!args.lpSubnetMask)
653                 wcsMallocAndCpy (&args.lpSubnetMask, DEFAULT_MASK);
654
655         GlobalFree (argvW);
656
657         return 1;
658 }
659
660 void CALLBACK doLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdShow)
661 {
662         Args args;
663
664         if (!process_args(lpCmdLine, 0, args))
665         return;
666
667         InstallLoopBack(args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
668 }
669
670 void CALLBACK uninstallLoopBackEntryW (HWND hwnd, HINSTANCE hinst, LPWSTR lpCmdLine, int nCmdSHow)
671 {
672     Args args;
673
674     // initialize COM
675         // This and CoInitializeSecurity fail when running under the MSI
676         // engine, but there seems to be no ill effect (the security is now
677         // set on the specific object via CoSetProxyBlanket in loopback_configure)
678     if(CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED ))
679     {
680                 //Don't fail (MSI install will have already initialized COM)
681         //EasyErrorBox(0, L"Failed to initialize COM.");
682         //return 1;
683     }
684
685         // Initialize COM security (otherwise we'll get permission denied when we try to use WMI or NetCfg)
686     CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
687
688     UnInstallLoopBack();
689
690         CoUninitialize();
691 }
692
693 /* And an MSI installer interface */
694
695 UINT __stdcall installLoopbackMSI (MSIHANDLE hInstall)
696 {
697         LPWSTR szValueBuf;
698         DWORD cbValueBuf = 256;
699         Args args;
700         UINT rc;
701
702         SetMsiReporter("InstallLoopback", "Installing loopback adapter", hInstall);
703
704     /* check if there is already one installed.  If there is, we shouldn't try to
705      * install another.
706      */
707         if(IsLoopbackInstalled())
708         return ERROR_SUCCESS;
709
710         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
711         while (rc = MsiGetPropertyW(hInstall, L"CustomActionData", szValueBuf, &cbValueBuf)) {
712                 free (szValueBuf);
713                 if (rc == ERROR_MORE_DATA) {
714                         cbValueBuf++;
715                         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
716                 }
717         else
718             return ERROR_INSTALL_FAILURE;
719         }
720
721         if (!process_args(szValueBuf, 1, args))
722         return ERROR_INSTALL_FAILURE;
723
724         rc = InstallLoopBack (args.lpConnectionName, args.lpIPAddr, args.lpSubnetMask);
725
726         if (rc != 2 && rc != 0)
727         return ERROR_INSTALL_FAILURE;
728
729         if (rc == 2) {
730                 MsiDoActionW (hInstall, L"ScheduleReboot");
731         }
732
733         return ERROR_SUCCESS;
734 }
735
736 UINT __stdcall uninstallLoopbackMSI (MSIHANDLE hInstall)
737 {
738         LPWSTR szValueBuf;
739         DWORD cbValueBuf = 256;
740         Args args;
741         UINT rc;
742
743         SetMsiReporter("RemoveLoopback", "Removing loopback adapter",  hInstall);
744
745         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
746         while (rc = MsiGetPropertyW(hInstall, L"CustomActionData", szValueBuf, &cbValueBuf)) {
747                 free (szValueBuf);
748                 if (rc == ERROR_MORE_DATA) {
749                         cbValueBuf++;
750                         szValueBuf = (LPWSTR) malloc (cbValueBuf * sizeof (WCHAR));
751                 }
752         else
753             return ERROR_INSTALL_FAILURE;
754         }
755
756         if (!process_args(szValueBuf, 1, args))
757         return ERROR_INSTALL_FAILURE;
758
759         rc = UnInstallLoopBack ();
760
761         if (rc == 1)
762         return ERROR_INSTALL_FAILURE;
763
764         if (rc == 2) {
765                 MsiDoActionW (hInstall, L"ScheduleReboot");
766         }
767
768         return ERROR_SUCCESS;
769 }
770
771 DWORD hMsiHandle = 0;
772 DWORD dwReporterType = REPORT_PRINTF;
773
774 extern "C" void ReportMessage(int level, LPCSTR msg, LPCSTR str, LPCWSTR wstr, DWORD dw) {
775     if(dwReporterType == REPORT_PRINTF)
776                 printf("%s:[%s][%S][%d]\n", (msg?msg:""), (str?str:""), (wstr?wstr:L""), dw);
777         else if(dwReporterType == REPORT_MSI && hMsiHandle && level == 0) {
778                 MSIHANDLE hRec = MsiCreateRecord(5);
779
780                 MsiRecordClearData(hRec);
781                 MsiRecordSetStringA(hRec,1,(msg)?msg:"");
782                 MsiRecordSetStringA(hRec,2,(str)?str:"");
783                 MsiRecordSetStringW(hRec,3,(wstr)?wstr:L"");
784                 MsiRecordSetInteger(hRec,4,dw);
785
786                 MsiProcessMessage(hMsiHandle,INSTALLMESSAGE_ACTIONDATA,hRec);
787
788                 MsiCloseHandle(hRec);
789         }
790 }
791
792 extern "C" void SetMsiReporter(LPCSTR strAction, LPCSTR strDesc,DWORD h) {
793         dwReporterType = REPORT_MSI;
794         hMsiHandle = h;
795
796 #ifdef DONT_NEED
797     /* this is performed in the Wix installer */
798         MSIHANDLE hRec = MsiCreateRecord(4);
799
800     MsiRecordClearData(hRec);
801         MsiRecordSetStringA(hRec,1,strAction);
802         MsiRecordSetStringA(hRec,2,strDesc);
803         MsiRecordSetStringA(hRec,3,"[1]:([2])([3])([4])");
804
805         MsiProcessMessage(h,INSTALLMESSAGE_ACTIONSTART, hRec);
806
807     MsiCloseHandle(hRec);
808 #endif
809 }