1 /*
2   IMPORTANT NOTE: IF THIS FILE IS CHANGED, WININST-6.EXE MUST BE RECOMPILED
3   WITH THE MSVC6 WININST.DSW WORKSPACE FILE MANUALLY, AND WININST-7.1.EXE MUST
4   BE RECOMPILED WITH THE MSVC 2003.NET WININST-7.1.VCPROJ FILE MANUALLY.
5 
6   IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES
7   MUST BE CHECKED IN AS WELL!
8 */
9 
10 /*
11  * Written by Thomas Heller, May 2000
12  *
13  * $Id$
14  */
15 
16 /*
17  * Windows Installer program for distutils.
18  *
19  * (a kind of self-extracting zip-file)
20  *
21  * At runtime, the exefile has appended:
22  * - compressed setup-data in ini-format, containing the following sections:
23  *      [metadata]
24  *      author=Greg Ward
25  *      author_email=gward@python.net
26  *      description=Python Distribution Utilities
27  *      licence=Python
28  *      name=Distutils
29  *      url=http://www.python.org/sigs/distutils-sig/
30  *      version=0.9pre
31  *
32  *      [Setup]
33  *      info= text to be displayed in the edit-box
34  *      title= to be displayed by this program
35  *      target_version = if present, python version required
36  *      pyc_compile = if 0, do not compile py to pyc
37  *      pyo_compile = if 0, do not compile py to pyo
38  *
39  * - a struct meta_data_hdr, describing the above
40  * - a zip-file, containing the modules to be installed.
41  *   for the format see http://www.pkware.com/appnote.html
42  *
43  * What does this program do?
44  * - the setup-data is uncompressed and written to a temporary file.
45  * - setup-data is queried with GetPrivateProfile... calls
46  * - [metadata] - info is displayed in the dialog box
47  * - The registry is searched for installations of python
48  * - The user can select the python version to use.
49  * - The python-installation directory (sys.prefix) is displayed
50  * - When the start-button is pressed, files from the zip-archive
51  *   are extracted to the file system. All .py filenames are stored
52  *   in a list.
53  */
54 /*
55  * Includes now an uninstaller.
56  */
57 
58 /*
59  * To Do:
60  *
61  * display some explanation when no python version is found
62  * instead showing the user an empty listbox to select something from.
63  *
64  * Finish the code so that we can use other python installations
65  * additionally to those found in the registry,
66  * and then #define USE_OTHER_PYTHON_VERSIONS
67  *
68  *  - install a help-button, which will display something meaningful
69  *    to the poor user.
70  *    text to the user
71  *  - should there be a possibility to display a README file
72  *    before starting the installation (if one is present in the archive)
73  *  - more comments about what the code does(?)
74  *
75  *  - evolve this into a full blown installer (???)
76  */
77 
78 #include <windows.h>
79 #include <commctrl.h>
80 #include <imagehlp.h>
81 #include <objbase.h>
82 #include <shlobj.h>
83 #include <objidl.h>
84 #include "resource.h"
85 
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <stdarg.h>
89 #include <string.h>
90 #include <time.h>
91 #include <sys/types.h>
92 #include <sys/stat.h>
93 #include <malloc.h>
94 #include <io.h>
95 #include <fcntl.h>
96 
97 #include "archive.h"
98 
99 /* Only for debugging!
100    static int dprintf(char *fmt, ...)
101    {
102    char Buffer[4096];
103    va_list marker;
104    int result;
105 
106    va_start(marker, fmt);
107    result = wvsprintf(Buffer, fmt, marker);
108    OutputDebugString(Buffer);
109    return result;
110    }
111 */
112 
113 /* Bah: global variables */
114 FILE *logfile;
115 
116 char modulename[MAX_PATH];
117 
118 HWND hwndMain;
119 HWND hDialog;
120 
121 char *ini_file;                 /* Full pathname of ini-file */
122 /* From ini-file */
123 char info[4096];                /* [Setup] info= */
124 char title[80];                 /* [Setup] title=, contains package name
125                                    including version: "Distutils-1.0.1" */
126 char target_version[10];        /* [Setup] target_version=, required python
127                                    version or empty string */
128 char build_info[80];            /* [Setup] build_info=, distutils version
129                                    and build date */
130 
131 char meta_name[80];             /* package name without version like
132                                    'Distutils' */
133 char install_script[MAX_PATH];
134 char *pre_install_script; /* run before we install a single file */
135 
136 char user_access_control[10]; // one of 'auto', 'force', otherwise none.
137 
138 int py_major, py_minor;         /* Python version selected for installation */
139 
140 char *arc_data;                 /* memory mapped archive */
141 DWORD arc_size;                 /* number of bytes in archive */
142 int exe_size;                   /* number of bytes for exe-file portion */
143 char python_dir[MAX_PATH];
144 char pythondll[MAX_PATH];
145 BOOL pyc_compile, pyo_compile;
146 /* Either HKLM or HKCU, depending on where Python itself is registered, and
147    the permissions of the current user. */
148 HKEY hkey_root = (HKEY)-1;
149 
150 BOOL success;                   /* Installation successful? */
151 char *failure_reason = NULL;
152 
153 HANDLE hBitmap;
154 char *bitmap_bytes;
155 
156 
157 #define WM_NUMFILES WM_USER+1
158 /* wParam: 0, lParam: total number of files */
159 #define WM_NEXTFILE WM_USER+2
160 /* wParam: number of this file */
161 /* lParam: points to pathname */
162 
163 static BOOL notify(int code, char *fmt, ...);
164 
165 /* Note: If scheme.prefix is nonempty, it must end with a '\'! */
166 /* Note: purelib must be the FIRST entry! */
167 SCHEME old_scheme[] = {
168     { "PURELIB", "" },
169     { "PLATLIB", "" },
170     { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
171     { "SCRIPTS", "Scripts\\" },
172     { "DATA", "" },
173     { NULL, NULL },
174 };
175 
176 SCHEME new_scheme[] = {
177     { "PURELIB", "Lib\\site-packages\\" },
178     { "PLATLIB", "Lib\\site-packages\\" },
179     { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
180     { "SCRIPTS", "Scripts\\" },
181     { "DATA", "" },
182     { NULL, NULL },
183 };
184 
unescape(char * dst,char * src,unsigned size)185 static void unescape(char *dst, char *src, unsigned size)
186 {
187     char *eon;
188     char ch;
189 
190     while (src && *src && (size > 2)) {
191         if (*src == '\\') {
192             switch (*++src) {
193             case 'n':
194                 ++src;
195                 *dst++ = '\r';
196                 *dst++ = '\n';
197                 size -= 2;
198                 break;
199             case 'r':
200                 ++src;
201                 *dst++ = '\r';
202                 --size;
203                 break;
204             case '0': case '1': case '2': case '3':
205                 ch = (char)strtol(src, &eon, 8);
206                 if (ch == '\n') {
207                     *dst++ = '\r';
208                     --size;
209                 }
210                 *dst++ = ch;
211                 --size;
212                 src = eon;
213             }
214         } else {
215             *dst++ = *src++;
216             --size;
217         }
218     }
219     *dst = '\0';
220 }
221 
222 static struct tagFile {
223     char *path;
224     struct tagFile *next;
225 } *file_list = NULL;
226 
set_failure_reason(char * reason)227 static void set_failure_reason(char *reason)
228 {
229     if (failure_reason)
230     free(failure_reason);
231     failure_reason = strdup(reason);
232     success = FALSE;
233 }
get_failure_reason()234 static char *get_failure_reason()
235 {
236     if (!failure_reason)
237     return "Installation failed.";
238     return failure_reason;
239 }
240 
add_to_filelist(char * path)241 static void add_to_filelist(char *path)
242 {
243     struct tagFile *p;
244     p = (struct tagFile *)malloc(sizeof(struct tagFile));
245     p->path = strdup(path);
246     p->next = file_list;
247     file_list = p;
248 }
249 
do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),int optimize)250 static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
251                              int optimize)
252 {
253     struct tagFile *p;
254     int total, n;
255     char Buffer[MAX_PATH + 64];
256     int errors = 0;
257 
258     total = 0;
259     p = file_list;
260     while (p) {
261         ++total;
262         p = p->next;
263     }
264     SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
265                         MAKELPARAM(0, total));
266     SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
267 
268     n = 0;
269     p = file_list;
270     while (p) {
271         ++n;
272         wsprintf(Buffer,
273                   "import py_compile; py_compile.compile (r'%s')",
274                   p->path);
275         if (PyRun_SimpleString(Buffer)) {
276             ++errors;
277         }
278         /* We send the notification even if the files could not
279          * be created so that the uninstaller will remove them
280          * in case they are created later.
281          */
282         wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c');
283         notify(FILE_CREATED, Buffer);
284 
285         SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
286         SetDlgItemText(hDialog, IDC_INFO, p->path);
287         p = p->next;
288     }
289     return errors;
290 }
291 
292 #define DECLPROC(dll, result, name, args)\
293     typedef result (*__PROC__##name) args;\
294     result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
295 
296 
297 #define DECLVAR(dll, type, name)\
298     type *name = (type*)GetProcAddress(dll, #name)
299 
300 typedef void PyObject;
301 
302 
303 /*
304  * Returns number of files which failed to compile,
305  * -1 if python could not be loaded at all
306  */
compile_filelist(HINSTANCE hPython,BOOL optimize_flag)307 static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag)
308 {
309     DECLPROC(hPython, void, Py_Initialize, (void));
310     DECLPROC(hPython, void, Py_SetProgramName, (char *));
311     DECLPROC(hPython, void, Py_Finalize, (void));
312     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
313     DECLPROC(hPython, PyObject *, PySys_GetObject, (char *));
314     DECLVAR(hPython, int, Py_OptimizeFlag);
315 
316     int errors = 0;
317     struct tagFile *p = file_list;
318 
319     if (!p)
320         return 0;
321 
322     if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
323         return -1;
324 
325     if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
326         return -1;
327 
328     *Py_OptimizeFlag = optimize_flag ? 1 : 0;
329     Py_SetProgramName(modulename);
330     Py_Initialize();
331 
332     errors += do_compile_files(PyRun_SimpleString, optimize_flag);
333     Py_Finalize();
334 
335     return errors;
336 }
337 
338 typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
339 
340 struct PyMethodDef {
341     char        *ml_name;
342     PyCFunction  ml_meth;
343     int                  ml_flags;
344     char        *ml_doc;
345 };
346 typedef struct PyMethodDef PyMethodDef;
347 
348 // XXX - all of these are potentially fragile!  We load and unload
349 // the Python DLL multiple times - so storing functions pointers
350 // is dangerous (although things *look* OK at present)
351 // Better might be to roll prepare_script_environment() into
352 // LoadPythonDll(), and create a new UnloadPythonDLL() which also
353 // clears the global pointers.
354 void *(*g_Py_BuildValue)(char *, ...);
355 int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
356 PyObject * (*g_PyLong_FromVoidPtr)(void *);
357 
358 PyObject *g_PyExc_ValueError;
359 PyObject *g_PyExc_OSError;
360 
361 PyObject *(*g_PyErr_Format)(PyObject *, char *, ...);
362 
363 #define DEF_CSIDL(name) { name, #name }
364 
365 struct {
366     int nFolder;
367     char *name;
368 } csidl_names[] = {
369     /* Startup menu for all users.
370        NT only */
371     DEF_CSIDL(CSIDL_COMMON_STARTMENU),
372     /* Startup menu. */
373     DEF_CSIDL(CSIDL_STARTMENU),
374 
375 /*    DEF_CSIDL(CSIDL_COMMON_APPDATA), */
376 /*    DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
377     /* Repository for application-specific data.
378        Needs Internet Explorer 4.0 */
379     DEF_CSIDL(CSIDL_APPDATA),
380 
381     /* The desktop for all users.
382        NT only */
383     DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
384     /* The desktop. */
385     DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
386 
387     /* Startup folder for all users.
388        NT only */
389     DEF_CSIDL(CSIDL_COMMON_STARTUP),
390     /* Startup folder. */
391     DEF_CSIDL(CSIDL_STARTUP),
392 
393     /* Programs item in the start menu for all users.
394        NT only */
395     DEF_CSIDL(CSIDL_COMMON_PROGRAMS),
396     /* Program item in the user's start menu. */
397     DEF_CSIDL(CSIDL_PROGRAMS),
398 
399 /*    DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
400 /*    DEF_CSIDL(CSIDL_PROGRAM_FILES), */
401 
402     /* Virtual folder containing fonts. */
403     DEF_CSIDL(CSIDL_FONTS),
404 };
405 
406 #define DIM(a) (sizeof(a) / sizeof((a)[0]))
407 
FileCreated(PyObject * self,PyObject * args)408 static PyObject *FileCreated(PyObject *self, PyObject *args)
409 {
410     char *path;
411     if (!g_PyArg_ParseTuple(args, "s", &path))
412         return NULL;
413     notify(FILE_CREATED, path);
414     return g_Py_BuildValue("");
415 }
416 
DirectoryCreated(PyObject * self,PyObject * args)417 static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
418 {
419     char *path;
420     if (!g_PyArg_ParseTuple(args, "s", &path))
421         return NULL;
422     notify(DIR_CREATED, path);
423     return g_Py_BuildValue("");
424 }
425 
GetSpecialFolderPath(PyObject * self,PyObject * args)426 static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
427 {
428     char *name;
429     char lpszPath[MAX_PATH];
430     int i;
431     static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd,
432                                                        LPTSTR lpszPath,
433                                                        int nFolder,
434                                                        BOOL fCreate);
435 
436     if (!My_SHGetSpecialFolderPath) {
437         HINSTANCE hLib = LoadLibrary("shell32.dll");
438         if (!hLib) {
439             g_PyErr_Format(g_PyExc_OSError,
440                            "function not available");
441             return NULL;
442         }
443         My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR,
444                                                      int, BOOL))
445             GetProcAddress(hLib,
446                            "SHGetSpecialFolderPathA");
447     }
448 
449     if (!g_PyArg_ParseTuple(args, "s", &name))
450         return NULL;
451 
452     if (!My_SHGetSpecialFolderPath) {
453         g_PyErr_Format(g_PyExc_OSError, "function not available");
454         return NULL;
455     }
456 
457     for (i = 0; i < DIM(csidl_names); ++i) {
458         if (0 == strcmpi(csidl_names[i].name, name)) {
459             int nFolder;
460             nFolder = csidl_names[i].nFolder;
461             if (My_SHGetSpecialFolderPath(NULL, lpszPath,
462                                           nFolder, 0))
463                 return g_Py_BuildValue("s", lpszPath);
464             else {
465                 g_PyErr_Format(g_PyExc_OSError,
466                                "no such folder (%s)", name);
467                 return NULL;
468             }
469 
470         }
471     };
472     g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name);
473     return NULL;
474 }
475 
CreateShortcut(PyObject * self,PyObject * args)476 static PyObject *CreateShortcut(PyObject *self, PyObject *args)
477 {
478     char *path; /* path and filename */
479     char *description;
480     char *filename;
481 
482     char *arguments = NULL;
483     char *iconpath = NULL;
484     int iconindex = 0;
485     char *workdir = NULL;
486 
487     WCHAR wszFilename[MAX_PATH];
488 
489     IShellLink *ps1 = NULL;
490     IPersistFile *pPf = NULL;
491 
492     HRESULT hr;
493 
494     hr = CoInitialize(NULL);
495     if (FAILED(hr)) {
496         g_PyErr_Format(g_PyExc_OSError,
497                        "CoInitialize failed, error 0x%x", hr);
498         goto error;
499     }
500 
501     if (!g_PyArg_ParseTuple(args, "sss|sssi",
502                             &path, &description, &filename,
503                             &arguments, &workdir, &iconpath, &iconindex))
504         return NULL;
505 
506     hr = CoCreateInstance(&CLSID_ShellLink,
507                           NULL,
508                           CLSCTX_INPROC_SERVER,
509                           &IID_IShellLink,
510                           &ps1);
511     if (FAILED(hr)) {
512         g_PyErr_Format(g_PyExc_OSError,
513                        "CoCreateInstance failed, error 0x%x", hr);
514         goto error;
515     }
516 
517     hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile,
518                                      (void **)&pPf);
519     if (FAILED(hr)) {
520         g_PyErr_Format(g_PyExc_OSError,
521                        "QueryInterface(IPersistFile) error 0x%x", hr);
522         goto error;
523     }
524 
525 
526     hr = ps1->lpVtbl->SetPath(ps1, path);
527     if (FAILED(hr)) {
528         g_PyErr_Format(g_PyExc_OSError,
529                        "SetPath() failed, error 0x%x", hr);
530         goto error;
531     }
532 
533     hr = ps1->lpVtbl->SetDescription(ps1, description);
534     if (FAILED(hr)) {
535         g_PyErr_Format(g_PyExc_OSError,
536                        "SetDescription() failed, error 0x%x", hr);
537         goto error;
538     }
539 
540     if (arguments) {
541         hr = ps1->lpVtbl->SetArguments(ps1, arguments);
542         if (FAILED(hr)) {
543             g_PyErr_Format(g_PyExc_OSError,
544                            "SetArguments() error 0x%x", hr);
545             goto error;
546         }
547     }
548 
549     if (iconpath) {
550         hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
551         if (FAILED(hr)) {
552             g_PyErr_Format(g_PyExc_OSError,
553                            "SetIconLocation() error 0x%x", hr);
554             goto error;
555         }
556     }
557 
558     if (workdir) {
559         hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
560         if (FAILED(hr)) {
561             g_PyErr_Format(g_PyExc_OSError,
562                            "SetWorkingDirectory() error 0x%x", hr);
563             goto error;
564         }
565     }
566 
567     MultiByteToWideChar(CP_ACP, 0,
568                         filename, -1,
569                         wszFilename, MAX_PATH);
570 
571     hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
572     if (FAILED(hr)) {
573         g_PyErr_Format(g_PyExc_OSError,
574                        "Failed to create shortcut '%s' - error 0x%x", filename, hr);
575         goto error;
576     }
577 
578     pPf->lpVtbl->Release(pPf);
579     ps1->lpVtbl->Release(ps1);
580     CoUninitialize();
581     return g_Py_BuildValue("");
582 
583   error:
584     if (pPf)
585         pPf->lpVtbl->Release(pPf);
586 
587     if (ps1)
588         ps1->lpVtbl->Release(ps1);
589 
590     CoUninitialize();
591 
592     return NULL;
593 }
594 
PyMessageBox(PyObject * self,PyObject * args)595 static PyObject *PyMessageBox(PyObject *self, PyObject *args)
596 {
597     int rc;
598     char *text, *caption;
599     int flags;
600     if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
601         return NULL;
602     rc = MessageBox(GetFocus(), text, caption, flags);
603     return g_Py_BuildValue("i", rc);
604 }
605 
GetRootHKey(PyObject * self)606 static PyObject *GetRootHKey(PyObject *self)
607 {
608     return g_PyLong_FromVoidPtr(hkey_root);
609 }
610 
611 #define METH_VARARGS 0x0001
612 #define METH_NOARGS   0x0004
613 typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
614 
615 PyMethodDef meth[] = {
616     {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
617     {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
618     {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
619     {"file_created", FileCreated, METH_VARARGS, NULL},
620     {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
621     {"message_box", PyMessageBox, METH_VARARGS, NULL},
622 };
623 
LoadPythonDll(char * fname)624 static HINSTANCE LoadPythonDll(char *fname)
625 {
626     char fullpath[_MAX_PATH];
627     LONG size = sizeof(fullpath);
628     char subkey_name[80];
629     char buffer[260 + 12];
630     HINSTANCE h;
631 
632     /* make sure PYTHONHOME is set, to that sys.path is initialized correctly */
633     wsprintf(buffer, "PYTHONHOME=%s", python_dir);
634     _putenv(buffer);
635     h = LoadLibrary(fname);
636     if (h)
637         return h;
638     wsprintf(subkey_name,
639              "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
640              py_major, py_minor);
641     if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
642                                        fullpath, &size) &&
643         ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
644                                        fullpath, &size))
645         return NULL;
646     strcat(fullpath, "\\");
647     strcat(fullpath, fname);
648     return LoadLibrary(fullpath);
649 }
650 
prepare_script_environment(HINSTANCE hPython)651 static int prepare_script_environment(HINSTANCE hPython)
652 {
653     PyObject *mod;
654     DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
655     DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
656     DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
657     DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
658     DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
659     DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
660     DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
661     DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
662     if (!PyImport_ImportModule || !PyObject_GetAttrString ||
663         !PyObject_SetAttrString || !PyCFunction_New)
664         return 1;
665     if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
666         return 1;
667 
668     mod = PyImport_ImportModule("__builtin__");
669     if (mod) {
670         int i;
671         g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
672         g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
673         for (i = 0; i < DIM(meth); ++i) {
674             PyObject_SetAttrString(mod, meth[i].ml_name,
675                                    PyCFunction_New(&meth[i], NULL));
676         }
677     }
678     g_Py_BuildValue = Py_BuildValue;
679     g_PyArg_ParseTuple = PyArg_ParseTuple;
680     g_PyErr_Format = PyErr_Format;
681     g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
682 
683     return 0;
684 }
685 
686 /*
687  * This function returns one of the following error codes:
688  * 1 if the Python-dll does not export the functions we need
689  * 2 if no install-script is specified in pathname
690  * 3 if the install-script file could not be opened
691  * the return value of PyRun_SimpleString() otherwise,
692  * which is 0 if everything is ok, -1 if an exception had occurred
693  * in the install-script.
694  */
695 
696 static int
do_run_installscript(HINSTANCE hPython,char * pathname,int argc,char ** argv)697 do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
698 {
699     int fh, result;
700     DECLPROC(hPython, void, Py_Initialize, (void));
701     DECLPROC(hPython, int, PySys_SetArgv, (int, char **));
702     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
703     DECLPROC(hPython, void, Py_Finalize, (void));
704     DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
705     DECLPROC(hPython, PyObject *, PyCFunction_New,
706              (PyMethodDef *, PyObject *));
707     DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
708     DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
709 
710     if (!Py_Initialize || !PySys_SetArgv
711         || !PyRun_SimpleString || !Py_Finalize)
712         return 1;
713 
714     if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
715         return 1;
716 
717     if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
718         return 1;
719 
720     if (pathname == NULL || pathname[0] == '\0')
721         return 2;
722 
723     fh = open(pathname, _O_RDONLY);
724     if (-1 == fh) {
725         fprintf(stderr, "Could not open postinstall-script %s\n",
726             pathname);
727         return 3;
728     }
729 
730     SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
731 
732     Py_Initialize();
733 
734     prepare_script_environment(hPython);
735     PySys_SetArgv(argc, argv);
736     result = 3;
737     {
738         struct _stat statbuf;
739         if(0 == _fstat(fh, &statbuf)) {
740             char *script = alloca(statbuf.st_size + 5);
741             int n = read(fh, script, statbuf.st_size);
742             if (n > 0) {
743                 script[n] = '\n';
744                 script[n+1] = 0;
745                 result = PyRun_SimpleString(script);
746             }
747         }
748     }
749     Py_Finalize();
750 
751     close(fh);
752     return result;
753 }
754 
755 static int
run_installscript(char * pathname,int argc,char ** argv,char ** pOutput)756 run_installscript(char *pathname, int argc, char **argv, char **pOutput)
757 {
758     HINSTANCE hPython;
759     int result = 1;
760     int out_buf_size;
761     HANDLE redirected, old_stderr, old_stdout;
762     char *tempname;
763 
764     *pOutput = NULL;
765 
766     tempname = tempnam(NULL, NULL);
767     // We use a static CRT while the Python version we load uses
768     // the CRT from one of various possible DLLs.  As a result we
769     // need to redirect the standard handles using the API rather
770     // than the CRT.
771     redirected = CreateFile(
772                                     tempname,
773                                     GENERIC_WRITE | GENERIC_READ,
774                                     FILE_SHARE_READ,
775                                     NULL,
776                                     CREATE_ALWAYS,
777                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
778                                     NULL);
779     old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
780     old_stderr = GetStdHandle(STD_ERROR_HANDLE);
781     SetStdHandle(STD_OUTPUT_HANDLE, redirected);
782     SetStdHandle(STD_ERROR_HANDLE, redirected);
783 
784     hPython = LoadPythonDll(pythondll);
785     if (hPython) {
786         result = do_run_installscript(hPython, pathname, argc, argv);
787         FreeLibrary(hPython);
788     } else {
789         fprintf(stderr, "*** Could not load Python ***");
790     }
791     SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
792     SetStdHandle(STD_ERROR_HANDLE, old_stderr);
793     out_buf_size = min(GetFileSize(redirected, NULL), 4096);
794     *pOutput = malloc(out_buf_size+1);
795     if (*pOutput) {
796         DWORD nread = 0;
797         SetFilePointer(redirected, 0, 0, FILE_BEGIN);
798         ReadFile(redirected, *pOutput, out_buf_size, &nread, NULL);
799         (*pOutput)[nread] = '\0';
800     }
801     CloseHandle(redirected);
802     DeleteFile(tempname);
803     return result;
804 }
805 
do_run_simple_script(HINSTANCE hPython,char * script)806 static int do_run_simple_script(HINSTANCE hPython, char *script)
807 {
808     int rc;
809     DECLPROC(hPython, void, Py_Initialize, (void));
810     DECLPROC(hPython, void, Py_SetProgramName, (char *));
811     DECLPROC(hPython, void, Py_Finalize, (void));
812     DECLPROC(hPython, int, PyRun_SimpleString, (char *));
813     DECLPROC(hPython, void, PyErr_Print, (void));
814 
815     if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
816         !PyRun_SimpleString || !PyErr_Print)
817         return -1;
818 
819     Py_SetProgramName(modulename);
820     Py_Initialize();
821     prepare_script_environment(hPython);
822     rc = PyRun_SimpleString(script);
823     if (rc)
824         PyErr_Print();
825     Py_Finalize();
826     return rc;
827 }
828 
run_simple_script(char * script)829 static int run_simple_script(char *script)
830 {
831     int rc;
832     HINSTANCE hPython;
833     char *tempname = tempnam(NULL, NULL);
834     // Redirect output using win32 API - see comments above...
835     HANDLE redirected = CreateFile(
836                                     tempname,
837                                     GENERIC_WRITE | GENERIC_READ,
838                                     FILE_SHARE_READ,
839                                     NULL,
840                                     CREATE_ALWAYS,
841                                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
842                                     NULL);
843     HANDLE old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
844     HANDLE old_stderr = GetStdHandle(STD_ERROR_HANDLE);
845     SetStdHandle(STD_OUTPUT_HANDLE, redirected);
846     SetStdHandle(STD_ERROR_HANDLE, redirected);
847 
848     hPython = LoadPythonDll(pythondll);
849     if (!hPython) {
850         char reason[128];
851         wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
852         set_failure_reason(reason);
853         return -1;
854     }
855     rc = do_run_simple_script(hPython, script);
856     FreeLibrary(hPython);
857     SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
858     SetStdHandle(STD_ERROR_HANDLE, old_stderr);
859     /* We only care about the output when we fail.  If the script works
860        OK, then we discard it
861     */
862     if (rc) {
863         int err_buf_size;
864         char *err_buf;
865         const char *prefix = "Running the pre-installation script failed\r\n";
866         int prefix_len = strlen(prefix);
867         err_buf_size = GetFileSize(redirected, NULL);
868         if (err_buf_size==INVALID_FILE_SIZE) // an error - let's try anyway...
869             err_buf_size = 4096;
870         err_buf = malloc(prefix_len + err_buf_size + 1);
871         if (err_buf) {
872             DWORD n = 0;
873             strcpy(err_buf, prefix);
874             SetFilePointer(redirected, 0, 0, FILE_BEGIN);
875             ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
876             err_buf[prefix_len+n] = '\0';
877             set_failure_reason(err_buf);
878             free(err_buf);
879         } else {
880             set_failure_reason("Out of memory!");
881         }
882     }
883     CloseHandle(redirected);
884     DeleteFile(tempname);
885     return rc;
886 }
887 
888 
SystemError(int error,char * msg)889 static BOOL SystemError(int error, char *msg)
890 {
891     char Buffer[1024];
892     int n;
893 
894     if (error) {
895         LPVOID lpMsgBuf;
896         FormatMessage(
897             FORMAT_MESSAGE_ALLOCATE_BUFFER |
898             FORMAT_MESSAGE_FROM_SYSTEM |
899             FORMAT_MESSAGE_IGNORE_INSERTS,
900             NULL,
901             error,
902             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
903             (LPSTR)&lpMsgBuf,
904             0,
905             NULL
906             );
907         strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
908         LocalFree(lpMsgBuf);
909     } else
910         Buffer[0] = '\0';
911     n = lstrlen(Buffer);
912     _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
913     MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
914     return FALSE;
915 }
916 
notify(int code,char * fmt,...)917 static BOOL notify (int code, char *fmt, ...)
918 {
919     char Buffer[1024];
920     va_list marker;
921     BOOL result = TRUE;
922     int a, b;
923     char *cp;
924 
925     va_start(marker, fmt);
926     _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
927 
928     switch (code) {
929 /* Questions */
930     case CAN_OVERWRITE:
931         break;
932 
933 /* Information notification */
934     case DIR_CREATED:
935         if (logfile)
936             fprintf(logfile, "100 Made Dir: %s\n", fmt);
937         break;
938 
939     case FILE_CREATED:
940         if (logfile)
941             fprintf(logfile, "200 File Copy: %s\n", fmt);
942         goto add_to_filelist_label;
943         break;
944 
945     case FILE_OVERWRITTEN:
946         if (logfile)
947             fprintf(logfile, "200 File Overwrite: %s\n", fmt);
948       add_to_filelist_label:
949         if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
950             add_to_filelist(fmt);
951         break;
952 
953 /* Error Messages */
954     case ZLIB_ERROR:
955         MessageBox(GetFocus(), Buffer, "Error",
956                     MB_OK | MB_ICONWARNING);
957         break;
958 
959     case SYSTEM_ERROR:
960         SystemError(GetLastError(), Buffer);
961         break;
962 
963     case NUM_FILES:
964         a = va_arg(marker, int);
965         b = va_arg(marker, int);
966         SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
967         SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
968     }
969     va_end(marker);
970 
971     return result;
972 }
973 
MapExistingFile(char * pathname,DWORD * psize)974 static char *MapExistingFile(char *pathname, DWORD *psize)
975 {
976     HANDLE hFile, hFileMapping;
977     DWORD nSizeLow, nSizeHigh;
978     char *data;
979 
980     hFile = CreateFile(pathname,
981                         GENERIC_READ, FILE_SHARE_READ, NULL,
982                         OPEN_EXISTING,
983                         FILE_ATTRIBUTE_NORMAL, NULL);
984     if (hFile == INVALID_HANDLE_VALUE)
985         return NULL;
986     nSizeLow = GetFileSize(hFile, &nSizeHigh);
987     hFileMapping = CreateFileMapping(hFile,
988                                       NULL, PAGE_READONLY, 0, 0, NULL);
989     CloseHandle(hFile);
990 
991     if (hFileMapping == INVALID_HANDLE_VALUE)
992         return NULL;
993 
994     data = MapViewOfFile(hFileMapping,
995                           FILE_MAP_READ, 0, 0, 0);
996 
997     CloseHandle(hFileMapping);
998     *psize = nSizeLow;
999     return data;
1000 }
1001 
1002 
create_bitmap(HWND hwnd)1003 static void create_bitmap(HWND hwnd)
1004 {
1005     BITMAPFILEHEADER *bfh;
1006     BITMAPINFO *bi;
1007     HDC hdc;
1008 
1009     if (!bitmap_bytes)
1010         return;
1011 
1012     if (hBitmap)
1013         return;
1014 
1015     hdc = GetDC(hwnd);
1016 
1017     bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1018     bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
1019 
1020     hBitmap = CreateDIBitmap(hdc,
1021                              &bi->bmiHeader,
1022                              CBM_INIT,
1023                              bitmap_bytes + bfh->bfOffBits,
1024                              bi,
1025                              DIB_RGB_COLORS);
1026     ReleaseDC(hwnd, hdc);
1027 }
1028 
1029 /* Extract everything we need to begin the installation.  Currently this
1030    is the INI filename with install data, and the raw pre-install script
1031 */
ExtractInstallData(char * data,DWORD size,int * pexe_size,char ** out_ini_file,char ** out_preinstall_script)1032 static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
1033                                char **out_ini_file, char **out_preinstall_script)
1034 {
1035     /* read the end of central directory record */
1036     struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1037                                                    (struct eof_cdir)];
1038 
1039     int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1040         pe->ofsCDir;
1041 
1042     int ofs = arc_start - sizeof (struct meta_data_hdr);
1043 
1044     /* read meta_data info */
1045     struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
1046     char *src, *dst;
1047     char *ini_file;
1048     char tempdir[MAX_PATH];
1049 
1050     /* ensure that if we fail, we don't have garbage out pointers */
1051     *out_ini_file = *out_preinstall_script = NULL;
1052 
1053     if (pe->tag != 0x06054b50) {
1054         return FALSE;
1055     }
1056 
1057     if (pmd->tag != 0x1234567B) {
1058         return SystemError(0,
1059                    "Invalid cfgdata magic number (see bdist_wininst.py)");
1060     }
1061     if (ofs < 0) {
1062         return FALSE;
1063     }
1064 
1065     if (pmd->bitmap_size) {
1066         /* Store pointer to bitmap bytes */
1067         bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1068     }
1069 
1070     *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
1071 
1072     src = ((char *)pmd) - pmd->uncomp_size;
1073     ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
1074     if (!ini_file)
1075         return FALSE;
1076     if (!GetTempPath(sizeof(tempdir), tempdir)
1077         || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1078         SystemError(GetLastError(),
1079                      "Could not create temporary file");
1080         return FALSE;
1081     }
1082 
1083     dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1084                         0, 0, NULL/*notify*/);
1085     if (!dst)
1086         return FALSE;
1087     /* Up to the first \0 is the INI file data. */
1088     strncpy(dst, src, pmd->uncomp_size);
1089     src += strlen(dst) + 1;
1090     /* Up to next \0 is the pre-install script */
1091     *out_preinstall_script = strdup(src);
1092     *out_ini_file = ini_file;
1093     UnmapViewOfFile(dst);
1094     return TRUE;
1095 }
1096 
PumpMessages(void)1097 static void PumpMessages(void)
1098 {
1099     MSG msg;
1100     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1101         TranslateMessage(&msg);
1102         DispatchMessage(&msg);
1103     }
1104 }
1105 
1106 LRESULT CALLBACK
WindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1107 WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1108 {
1109     HDC hdc;
1110     HFONT hFont;
1111     int h;
1112     PAINTSTRUCT ps;
1113     switch (msg) {
1114     case WM_PAINT:
1115         hdc = BeginPaint(hwnd, &ps);
1116         h = GetSystemMetrics(SM_CYSCREEN) / 10;
1117         hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
1118                             0, 0, 0, 0, 0, 0, 0, "Times Roman");
1119         hFont = SelectObject(hdc, hFont);
1120         SetBkMode(hdc, TRANSPARENT);
1121         TextOut(hdc, 15, 15, title, strlen(title));
1122         SetTextColor(hdc, RGB(255, 255, 255));
1123         TextOut(hdc, 10, 10, title, strlen(title));
1124         DeleteObject(SelectObject(hdc, hFont));
1125         EndPaint(hwnd, &ps);
1126         return 0;
1127     }
1128     return DefWindowProc(hwnd, msg, wParam, lParam);
1129 }
1130 
CreateBackground(char * title)1131 static HWND CreateBackground(char *title)
1132 {
1133     WNDCLASS wc;
1134     HWND hwnd;
1135     char buffer[4096];
1136 
1137     wc.style = CS_VREDRAW | CS_HREDRAW;
1138     wc.lpfnWndProc = WindowProc;
1139     wc.cbWndExtra = 0;
1140     wc.cbClsExtra = 0;
1141     wc.hInstance = GetModuleHandle(NULL);
1142     wc.hIcon = NULL;
1143     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1144     wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
1145     wc.lpszMenuName = NULL;
1146     wc.lpszClassName = "SetupWindowClass";
1147 
1148     if (!RegisterClass(&wc))
1149         MessageBox(hwndMain,
1150                     "Could not register window class",
1151                     "Setup.exe", MB_OK);
1152 
1153     wsprintf(buffer, "Setup %s", title);
1154     hwnd = CreateWindow("SetupWindowClass",
1155                          buffer,
1156                          0,
1157                          0, 0,
1158                          GetSystemMetrics(SM_CXFULLSCREEN),
1159                          GetSystemMetrics(SM_CYFULLSCREEN),
1160                          NULL,
1161                          NULL,
1162                          GetModuleHandle(NULL),
1163                          NULL);
1164     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
1165     UpdateWindow(hwnd);
1166     return hwnd;
1167 }
1168 
1169 /*
1170  * Center a window on the screen
1171  */
CenterWindow(HWND hwnd)1172 static void CenterWindow(HWND hwnd)
1173 {
1174     RECT rc;
1175     int w, h;
1176 
1177     GetWindowRect(hwnd, &rc);
1178     w = GetSystemMetrics(SM_CXSCREEN);
1179     h = GetSystemMetrics(SM_CYSCREEN);
1180     MoveWindow(hwnd,
1181                (w - (rc.right-rc.left))/2,
1182                (h - (rc.bottom-rc.top))/2,
1183                 rc.right-rc.left, rc.bottom-rc.top, FALSE);
1184 }
1185 
1186 #include <prsht.h>
1187 
1188 BOOL CALLBACK
IntroDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1189 IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1190 {
1191     LPNMHDR lpnm;
1192     char Buffer[4096];
1193 
1194     switch (msg) {
1195     case WM_INITDIALOG:
1196         create_bitmap(hwnd);
1197         if(hBitmap)
1198             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1199                                IMAGE_BITMAP, (LPARAM)hBitmap);
1200         CenterWindow(GetParent(hwnd));
1201         wsprintf(Buffer,
1202                   "This Wizard will install %s on your computer. "
1203                   "Click Next to continue "
1204                   "or Cancel to exit the Setup Wizard.",
1205                   meta_name);
1206         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1207         SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
1208         SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
1209         return FALSE;
1210 
1211     case WM_NOTIFY:
1212         lpnm = (LPNMHDR) lParam;
1213 
1214         switch (lpnm->code) {
1215         case PSN_SETACTIVE:
1216             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1217             break;
1218 
1219         case PSN_WIZNEXT:
1220             break;
1221 
1222         case PSN_RESET:
1223             break;
1224 
1225         default:
1226             break;
1227         }
1228     }
1229     return FALSE;
1230 }
1231 
1232 #ifdef USE_OTHER_PYTHON_VERSIONS
1233 /* These are really private variables used to communicate
1234  * between StatusRoutine and CheckPythonExe
1235  */
1236 char bound_image_dll[_MAX_PATH];
1237 int bound_image_major;
1238 int bound_image_minor;
1239 
StatusRoutine(IMAGEHLP_STATUS_REASON reason,PSTR ImageName,PSTR DllName,ULONG Va,ULONG Parameter)1240 static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
1241                                     PSTR ImageName,
1242                                     PSTR DllName,
1243                                     ULONG Va,
1244                                     ULONG Parameter)
1245 {
1246     char fname[_MAX_PATH];
1247     int int_version;
1248 
1249     switch(reason) {
1250     case BindOutOfMemory:
1251     case BindRvaToVaFailed:
1252     case BindNoRoomInImage:
1253     case BindImportProcedureFailed:
1254         break;
1255 
1256     case BindImportProcedure:
1257     case BindForwarder:
1258     case BindForwarderNOT:
1259     case BindImageModified:
1260     case BindExpandFileHeaders:
1261     case BindImageComplete:
1262     case BindSymbolsNotUpdated:
1263     case BindMismatchedSymbols:
1264     case BindImportModuleFailed:
1265         break;
1266 
1267     case BindImportModule:
1268         if (1 == sscanf(DllName, "python%d", &int_version)) {
1269             SearchPath(NULL, DllName, NULL, sizeof(fname),
1270                        fname, NULL);
1271             strcpy(bound_image_dll, fname);
1272             bound_image_major = int_version / 10;
1273             bound_image_minor = int_version % 10;
1274             OutputDebugString("BOUND ");
1275             OutputDebugString(fname);
1276             OutputDebugString("\n");
1277         }
1278         break;
1279     }
1280     return TRUE;
1281 }
1282 
1283 /*
1284  */
get_sys_prefix(LPSTR exe,LPSTR dll)1285 static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1286 {
1287     void (__cdecl * Py_Initialize)(void);
1288     void (__cdecl * Py_SetProgramName)(char *);
1289     void (__cdecl * Py_Finalize)(void);
1290     void* (__cdecl * PySys_GetObject)(char *);
1291     void (__cdecl * PySys_SetArgv)(int, char **);
1292     char* (__cdecl * Py_GetPrefix)(void);
1293     char* (__cdecl * Py_GetPath)(void);
1294     HINSTANCE hPython;
1295     LPSTR prefix = NULL;
1296     int (__cdecl * PyRun_SimpleString)(char *);
1297 
1298     {
1299         char Buffer[256];
1300         wsprintf(Buffer, "PYTHONHOME=%s", exe);
1301         *strrchr(Buffer, '\\') = '\0';
1302 //      MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1303                 _putenv(Buffer);
1304                 _putenv("PYTHONPATH=");
1305     }
1306 
1307     hPython = LoadLibrary(dll);
1308     if (!hPython)
1309         return NULL;
1310     Py_Initialize = (void (*)(void))GetProcAddress
1311         (hPython,"Py_Initialize");
1312 
1313     PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1314         (hPython,"PySys_SetArgv");
1315 
1316     PyRun_SimpleString = (int (*)(char *))GetProcAddress
1317         (hPython,"PyRun_SimpleString");
1318 
1319     Py_SetProgramName = (void (*)(char *))GetProcAddress
1320         (hPython,"Py_SetProgramName");
1321 
1322     PySys_GetObject = (void* (*)(char *))GetProcAddress
1323         (hPython,"PySys_GetObject");
1324 
1325     Py_GetPrefix = (char * (*)(void))GetProcAddress
1326         (hPython,"Py_GetPrefix");
1327 
1328     Py_GetPath = (char * (*)(void))GetProcAddress
1329         (hPython,"Py_GetPath");
1330 
1331     Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1332                                                   "Py_Finalize");
1333     Py_SetProgramName(exe);
1334     Py_Initialize();
1335     PySys_SetArgv(1, &exe);
1336 
1337     MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1338     MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
1339 
1340     Py_Finalize();
1341     FreeLibrary(hPython);
1342 
1343     return prefix;
1344 }
1345 
1346 static BOOL
CheckPythonExe(LPSTR pathname,LPSTR version,int * pmajor,int * pminor)1347 CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1348 {
1349     bound_image_dll[0] = '\0';
1350     if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
1351                      pathname,
1352                      NULL,
1353                      NULL,
1354                      StatusRoutine))
1355         return SystemError(0, "Could not bind image");
1356     if (bound_image_dll[0] == '\0')
1357         return SystemError(0, "Does not seem to be a python executable");
1358     *pmajor = bound_image_major;
1359     *pminor = bound_image_minor;
1360     if (version && *version) {
1361         char core_version[12];
1362         wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
1363         if (strcmp(version, core_version))
1364             return SystemError(0, "Wrong Python version");
1365     }
1366     get_sys_prefix(pathname, bound_image_dll);
1367     return TRUE;
1368 }
1369 
1370 /*
1371  * Browse for other python versions. Insert it into the listbox specified
1372  * by hwnd. version, if not NULL or empty, is the version required.
1373  */
GetOtherPythonVersion(HWND hwnd,LPSTR version)1374 static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
1375 {
1376     char vers_name[_MAX_PATH + 80];
1377     DWORD itemindex;
1378     OPENFILENAME of;
1379     char pathname[_MAX_PATH];
1380     DWORD result;
1381 
1382     strcpy(pathname, "python.exe");
1383 
1384     memset(&of, 0, sizeof(of));
1385     of.lStructSize = sizeof(OPENFILENAME);
1386     of.hwndOwner = GetParent(hwnd);
1387     of.hInstance = NULL;
1388     of.lpstrFilter = "python.exe\0python.exe\0";
1389     of.lpstrCustomFilter = NULL;
1390     of.nMaxCustFilter = 0;
1391     of.nFilterIndex = 1;
1392     of.lpstrFile = pathname;
1393     of.nMaxFile = sizeof(pathname);
1394     of.lpstrFileTitle = NULL;
1395     of.nMaxFileTitle = 0;
1396     of.lpstrInitialDir = NULL;
1397     of.lpstrTitle = "Python executable";
1398     of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
1399     of.lpstrDefExt = "exe";
1400 
1401     result = GetOpenFileName(&of);
1402     if (result) {
1403         int major, minor;
1404         if (!CheckPythonExe(pathname, version, &major, &minor)) {
1405             return FALSE;
1406         }
1407         *strrchr(pathname, '\\') = '\0';
1408         wsprintf(vers_name, "Python Version %d.%d in %s",
1409                   major, minor, pathname);
1410         itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
1411                                 (LPARAM)(LPSTR)vers_name);
1412         SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
1413         SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1414                     (LPARAM)(LPSTR)strdup(pathname));
1415         return TRUE;
1416     }
1417     return FALSE;
1418 }
1419 #endif /* USE_OTHER_PYTHON_VERSIONS */
1420 
1421 typedef struct _InstalledVersionInfo {
1422     char prefix[MAX_PATH+1]; // sys.prefix directory.
1423     HKEY hkey; // Is this Python in HKCU or HKLM?
1424 } InstalledVersionInfo;
1425 
1426 
1427 /*
1428  * Fill the listbox specified by hwnd with all python versions found
1429  * in the registry. version, if not NULL or empty, is the version
1430  * required.
1431  */
GetPythonVersions(HWND hwnd,HKEY hkRoot,LPSTR version)1432 static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
1433 {
1434     DWORD index = 0;
1435     char core_version[80];
1436     HKEY hKey;
1437     BOOL result = TRUE;
1438     DWORD bufsize;
1439 
1440     if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
1441                                        "Software\\Python\\PythonCore",
1442                                        0,       KEY_READ, &hKey))
1443         return FALSE;
1444     bufsize = sizeof(core_version);
1445     while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
1446                                           core_version, &bufsize, NULL,
1447                                           NULL, NULL, NULL)) {
1448         char subkey_name[80], vers_name[80];
1449         int itemindex;
1450         DWORD value_size;
1451         HKEY hk;
1452 
1453         bufsize = sizeof(core_version);
1454         ++index;
1455         if (version && *version && strcmp(version, core_version))
1456             continue;
1457 
1458         wsprintf(vers_name, "Python Version %s (found in registry)",
1459                   core_version);
1460         wsprintf(subkey_name,
1461                   "Software\\Python\\PythonCore\\%s\\InstallPath",
1462                   core_version);
1463         if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
1464             InstalledVersionInfo *ivi =
1465                   (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
1466             value_size = sizeof(ivi->prefix);
1467             if (ivi &&
1468                 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
1469                                                  ivi->prefix, &value_size)) {
1470                 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
1471                                         (LPARAM)(LPSTR)vers_name);
1472                 ivi->hkey = hkRoot;
1473                 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1474                             (LPARAM)(LPSTR)ivi);
1475             }
1476             RegCloseKey(hk);
1477         }
1478     }
1479     RegCloseKey(hKey);
1480     return result;
1481 }
1482 
1483 /* Determine if the current user can write to HKEY_LOCAL_MACHINE */
HasLocalMachinePrivs()1484 BOOL HasLocalMachinePrivs()
1485 {
1486                 HKEY hKey;
1487                 DWORD result;
1488                 static char KeyName[] =
1489                         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1490 
1491                 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1492                                           KeyName,
1493                                           0,
1494                                           KEY_CREATE_SUB_KEY,
1495                                           &hKey);
1496                 if (result==0)
1497                         RegCloseKey(hKey);
1498                 return result==0;
1499 }
1500 
1501 // Check the root registry key to use - either HKLM or HKCU.
1502 // If Python is installed in HKCU, then our extension also must be installed
1503 // in HKCU - as Python won't be available for other users, we shouldn't either
1504 // (and will fail if we are!)
1505 // If Python is installed in HKLM, then we will also prefer to use HKLM, but
1506 // this may not be possible - so we silently fall back to HKCU.
1507 //
1508 // We assume hkey_root is already set to where Python itself is installed.
CheckRootKey(HWND hwnd)1509 void CheckRootKey(HWND hwnd)
1510 {
1511     if (hkey_root==HKEY_CURRENT_USER) {
1512         ; // as above, always install ourself in HKCU too.
1513     } else if (hkey_root==HKEY_LOCAL_MACHINE) {
1514         // Python in HKLM, but we may or may not have permissions there.
1515         // Open the uninstall key with 'create' permissions - if this fails,
1516         // we don't have permission.
1517         if (!HasLocalMachinePrivs())
1518             hkey_root = HKEY_CURRENT_USER;
1519     } else {
1520         MessageBox(hwnd, "Don't know Python's installation type",
1521                            "Strange", MB_OK | MB_ICONSTOP);
1522         /* Default to wherever they can, but preferring HKLM */
1523         hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1524     }
1525 }
1526 
1527 /* Return the installation scheme depending on Python version number */
GetScheme(int major,int minor)1528 SCHEME *GetScheme(int major, int minor)
1529 {
1530     if (major > 2)
1531         return new_scheme;
1532     else if((major == 2) && (minor >= 2))
1533         return new_scheme;
1534     return old_scheme;
1535 }
1536 
1537 BOOL CALLBACK
SelectPythonDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1538 SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1539 {
1540     LPNMHDR lpnm;
1541 
1542     switch (msg) {
1543     case WM_INITDIALOG:
1544         if (hBitmap)
1545             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1546                                IMAGE_BITMAP, (LPARAM)hBitmap);
1547         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1548                            HKEY_LOCAL_MACHINE, target_version);
1549         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1550                            HKEY_CURRENT_USER, target_version);
1551         {               /* select the last entry which is the highest python
1552                    version found */
1553             int count;
1554             count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1555                                         LB_GETCOUNT, 0, 0);
1556             if (count && count != LB_ERR)
1557                 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
1558                                     count-1, 0);
1559 
1560             /* If a specific Python version is required,
1561              * display a prominent notice showing this fact.
1562              */
1563             if (target_version && target_version[0]) {
1564                 char buffer[4096];
1565                 wsprintf(buffer,
1566                          "Python %s is required for this package. "
1567                          "Select installation to use:",
1568                          target_version);
1569                 SetDlgItemText(hwnd, IDC_TITLE, buffer);
1570             }
1571 
1572             if (count == 0) {
1573                 char Buffer[4096];
1574                 char *msg;
1575                 if (target_version && target_version[0]) {
1576                     wsprintf(Buffer,
1577                              "Python version %s required, which was not found"
1578                              " in the registry.", target_version);
1579                     msg = Buffer;
1580                 } else
1581                     msg = "No Python installation found in the registry.";
1582                 MessageBox(hwnd, msg, "Cannot install",
1583                            MB_OK | MB_ICONSTOP);
1584             }
1585         }
1586         goto UpdateInstallDir;
1587         break;
1588 
1589     case WM_COMMAND:
1590         switch (LOWORD(wParam)) {
1591 /*
1592   case IDC_OTHERPYTHON:
1593   if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1594   target_version))
1595   goto UpdateInstallDir;
1596   break;
1597 */
1598         case IDC_VERSIONS_LIST:
1599             switch (HIWORD(wParam)) {
1600                 int id;
1601             case LBN_SELCHANGE:
1602               UpdateInstallDir:
1603                 PropSheet_SetWizButtons(GetParent(hwnd),
1604                                         PSWIZB_BACK | PSWIZB_NEXT);
1605                 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1606                                          LB_GETCURSEL, 0, 0);
1607                 if (id == LB_ERR) {
1608                     PropSheet_SetWizButtons(GetParent(hwnd),
1609                                             PSWIZB_BACK);
1610                     SetDlgItemText(hwnd, IDC_PATH, "");
1611                     SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
1612                     strcpy(python_dir, "");
1613                     strcpy(pythondll, "");
1614                 } else {
1615                     char *pbuf;
1616                     int result;
1617                     InstalledVersionInfo *ivi;
1618                     PropSheet_SetWizButtons(GetParent(hwnd),
1619                                             PSWIZB_BACK | PSWIZB_NEXT);
1620                     /* Get the python directory */
1621                     ivi = (InstalledVersionInfo *)
1622                         SendDlgItemMessage(hwnd,
1623                             IDC_VERSIONS_LIST,
1624                             LB_GETITEMDATA,
1625                             id,
1626                             0);
1627                     hkey_root = ivi->hkey;
1628                     strcpy(python_dir, ivi->prefix);
1629                     SetDlgItemText(hwnd, IDC_PATH, python_dir);
1630                     /* retrieve the python version and pythondll to use */
1631                     result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1632                                                  LB_GETTEXTLEN, (WPARAM)id, 0);
1633                     pbuf = (char *)malloc(result + 1);
1634                     if (pbuf) {
1635                         /* guess the name of the python-dll */
1636                         SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1637                                             LB_GETTEXT, (WPARAM)id,
1638                                             (LPARAM)pbuf);
1639                         result = sscanf(pbuf, "Python Version %d.%d",
1640                                          &py_major, &py_minor);
1641                         if (result == 2) {
1642 #ifdef _DEBUG
1643                             wsprintf(pythondll, "python%d%d_d.dll",
1644                                      py_major, py_minor);
1645 #else
1646                             wsprintf(pythondll, "python%d%d.dll",
1647                                      py_major, py_minor);
1648 #endif
1649                         }
1650                         free(pbuf);
1651                     } else
1652                         strcpy(pythondll, "");
1653                     /* retrieve the scheme for this version */
1654                     {
1655                         char install_path[_MAX_PATH];
1656                         SCHEME *scheme = GetScheme(py_major, py_minor);
1657                         strcpy(install_path, python_dir);
1658                         if (install_path[strlen(install_path)-1] != '\\')
1659                             strcat(install_path, "\\");
1660                         strcat(install_path, scheme[0].prefix);
1661                         SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
1662                     }
1663                 }
1664             }
1665             break;
1666         }
1667         return 0;
1668 
1669     case WM_NOTIFY:
1670         lpnm = (LPNMHDR) lParam;
1671 
1672         switch (lpnm->code) {
1673             int id;
1674         case PSN_SETACTIVE:
1675             id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1676                                      LB_GETCURSEL, 0, 0);
1677             if (id == LB_ERR)
1678                 PropSheet_SetWizButtons(GetParent(hwnd),
1679                                         PSWIZB_BACK);
1680             else
1681                 PropSheet_SetWizButtons(GetParent(hwnd),
1682                                         PSWIZB_BACK | PSWIZB_NEXT);
1683             break;
1684 
1685         case PSN_WIZNEXT:
1686             break;
1687 
1688         case PSN_WIZFINISH:
1689             break;
1690 
1691         case PSN_RESET:
1692             break;
1693 
1694         default:
1695             break;
1696         }
1697     }
1698     return 0;
1699 }
1700 
OpenLogfile(char * dir)1701 static BOOL OpenLogfile(char *dir)
1702 {
1703     char buffer[_MAX_PATH+1];
1704     time_t ltime;
1705     struct tm *now;
1706     long result;
1707     HKEY hKey, hSubkey;
1708     char subkey_name[256];
1709     static char KeyName[] =
1710         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1711     const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1712                             "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
1713     DWORD disposition;
1714 
1715     /* Use Create, as the Uninstall subkey may not exist under HKCU.
1716        Use CreateKeyEx, so we can specify a SAM specifying write access
1717     */
1718         result = RegCreateKeyEx(hkey_root,
1719                       KeyName,
1720                       0, /* reserved */
1721                   NULL, /* class */
1722                   0, /* options */
1723                   KEY_CREATE_SUB_KEY, /* sam */
1724                   NULL, /* security */
1725                   &hKey, /* result key */
1726                   NULL); /* disposition */
1727     if (result != ERROR_SUCCESS) {
1728         if (result == ERROR_ACCESS_DENIED) {
1729             /* This should no longer be able to happen - we have already
1730                checked if they have permissions in HKLM, and all users
1731                should have write access to HKCU.
1732             */
1733             MessageBox(GetFocus(),
1734                        "You do not seem to have sufficient access rights\n"
1735                        "on this machine to install this software",
1736                        NULL,
1737                        MB_OK | MB_ICONSTOP);
1738             return FALSE;
1739         } else {
1740             MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
1741         }
1742     }
1743 
1744     sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
1745     logfile = fopen(buffer, "a");
1746     if (!logfile) {
1747         char error[1024];
1748 
1749         sprintf(error, "Can't create \"%s\" (%s).\n\n"
1750                 "Try to execute the installer as administrator.",
1751                 buffer, strerror(errno));
1752         MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
1753         return FALSE;
1754     }
1755 
1756     time(&ltime);
1757     now = localtime(&ltime);
1758     strftime(buffer, sizeof(buffer),
1759              "*** Installation started %Y/%m/%d %H:%M ***\n",
1760              localtime(&ltime));
1761     fprintf(logfile, buffer);
1762     fprintf(logfile, "Source: %s\n", modulename);
1763 
1764     /* Root key must be first entry processed by uninstaller. */
1765     fprintf(logfile, "999 Root Key: %s\n", root_name);
1766 
1767     sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
1768 
1769     result = RegCreateKeyEx(hKey, subkey_name,
1770                             0, NULL, 0,
1771                             KEY_WRITE,
1772                             NULL,
1773                             &hSubkey,
1774                             &disposition);
1775 
1776     if (result != ERROR_SUCCESS)
1777         MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
1778 
1779     RegCloseKey(hKey);
1780 
1781     if (disposition == REG_CREATED_NEW_KEY)
1782         fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
1783 
1784     sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
1785 
1786     result = RegSetValueEx(hSubkey, "DisplayName",
1787                            0,
1788                            REG_SZ,
1789                            buffer,
1790                            strlen(buffer)+1);
1791 
1792     if (result != ERROR_SUCCESS)
1793         MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1794 
1795     fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1796         KeyName, subkey_name, "DisplayName", buffer);
1797 
1798     {
1799         FILE *fp;
1800         sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1801         fp = fopen(buffer, "wb");
1802         fwrite(arc_data, exe_size, 1, fp);
1803         fclose(fp);
1804 
1805         sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1806             dir, meta_name, dir, meta_name);
1807 
1808         result = RegSetValueEx(hSubkey, "UninstallString",
1809                                0,
1810                                REG_SZ,
1811                                buffer,
1812                                strlen(buffer)+1);
1813 
1814         if (result != ERROR_SUCCESS)
1815             MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1816 
1817         fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1818             KeyName, subkey_name, "UninstallString", buffer);
1819     }
1820     return TRUE;
1821 }
1822 
CloseLogfile(void)1823 static void CloseLogfile(void)
1824 {
1825     char buffer[_MAX_PATH+1];
1826     time_t ltime;
1827     struct tm *now;
1828 
1829     time(&ltime);
1830     now = localtime(&ltime);
1831     strftime(buffer, sizeof(buffer),
1832              "*** Installation finished %Y/%m/%d %H:%M ***\n",
1833              localtime(&ltime));
1834     fprintf(logfile, buffer);
1835     if (logfile)
1836         fclose(logfile);
1837 }
1838 
1839 BOOL CALLBACK
InstallFilesDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1840 InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1841 {
1842     LPNMHDR lpnm;
1843     char Buffer[4096];
1844     SCHEME *scheme;
1845 
1846     switch (msg) {
1847     case WM_INITDIALOG:
1848         if (hBitmap)
1849             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1850                                IMAGE_BITMAP, (LPARAM)hBitmap);
1851         wsprintf(Buffer,
1852                   "Click Next to begin the installation of %s. "
1853                   "If you want to review or change any of your "
1854                   " installation settings, click Back. "
1855                   "Click Cancel to exit the wizard.",
1856                   meta_name);
1857         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1858         SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1859         break;
1860 
1861     case WM_NUMFILES:
1862         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1863         PumpMessages();
1864         return TRUE;
1865 
1866     case WM_NEXTFILE:
1867         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1868                             0);
1869         SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1870         PumpMessages();
1871         return TRUE;
1872 
1873     case WM_NOTIFY:
1874         lpnm = (LPNMHDR) lParam;
1875 
1876         switch (lpnm->code) {
1877         case PSN_SETACTIVE:
1878             PropSheet_SetWizButtons(GetParent(hwnd),
1879                                     PSWIZB_BACK | PSWIZB_NEXT);
1880             break;
1881 
1882         case PSN_WIZFINISH:
1883             break;
1884 
1885         case PSN_WIZNEXT:
1886             /* Handle a Next button click here */
1887             hDialog = hwnd;
1888             success = TRUE;
1889 
1890             /* Disable the buttons while we work.  Sending CANCELTOCLOSE has
1891               the effect of disabling the cancel button, which is a) as we
1892               do everything synchronously we can't cancel, and b) the next
1893               step is 'finished', when it is too late to cancel anyway.
1894               The next step being 'Finished' means we also don't need to
1895               restore the button state back */
1896             PropSheet_SetWizButtons(GetParent(hwnd), 0);
1897             SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
1898             /* Make sure the installation directory name ends in a */
1899             /* backslash */
1900             if (python_dir[strlen(python_dir)-1] != '\\')
1901                 strcat(python_dir, "\\");
1902             /* Strip the trailing backslash again */
1903             python_dir[strlen(python_dir)-1] = '\0';
1904 
1905             CheckRootKey(hwnd);
1906 
1907             if (!OpenLogfile(python_dir))
1908                 break;
1909 
1910 /*
1911  * The scheme we have to use depends on the Python version...
1912  if sys.version < "2.2":
1913     WINDOWS_SCHEME = {
1914     'purelib': '$base',
1915     'platlib': '$base',
1916     'headers': '$base/Include/$dist_name',
1917     'scripts': '$base/Scripts',
1918     'data'   : '$base',
1919     }
1920  else:
1921     WINDOWS_SCHEME = {
1922     'purelib': '$base/Lib/site-packages',
1923     'platlib': '$base/Lib/site-packages',
1924     'headers': '$base/Include/$dist_name',
1925     'scripts': '$base/Scripts',
1926     'data'   : '$base',
1927     }
1928 */
1929             scheme = GetScheme(py_major, py_minor);
1930             /* Run the pre-install script. */
1931             if (pre_install_script && *pre_install_script) {
1932                 SetDlgItemText (hwnd, IDC_TITLE,
1933                                 "Running pre-installation script");
1934                 run_simple_script(pre_install_script);
1935             }
1936             if (!success) {
1937                 break;
1938             }
1939             /* Extract all files from the archive */
1940             SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1941             if (!unzip_archive (scheme,
1942                                 python_dir, arc_data,
1943                                 arc_size, notify))
1944                 set_failure_reason("Failed to unzip installation files");
1945             /* Compile the py-files */
1946             if (success && pyc_compile) {
1947                 int errors;
1948                 HINSTANCE hPython;
1949                 SetDlgItemText(hwnd, IDC_TITLE,
1950                                 "Compiling files to .pyc...");
1951 
1952                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1953                 hPython = LoadPythonDll(pythondll);
1954                 if (hPython) {
1955                     errors = compile_filelist(hPython, FALSE);
1956                     FreeLibrary(hPython);
1957                 }
1958                 /* Compilation errors are intentionally ignored:
1959                  * Python2.0 contains a bug which will result
1960                  * in sys.path containing garbage under certain
1961                  * circumstances, and an error message will only
1962                  * confuse the user.
1963                  */
1964             }
1965             if (success && pyo_compile) {
1966                 int errors;
1967                 HINSTANCE hPython;
1968                 SetDlgItemText(hwnd, IDC_TITLE,
1969                                 "Compiling files to .pyo...");
1970 
1971                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1972                 hPython = LoadPythonDll(pythondll);
1973                 if (hPython) {
1974                     errors = compile_filelist(hPython, TRUE);
1975                     FreeLibrary(hPython);
1976                 }
1977                 /* Errors ignored: see above */
1978             }
1979 
1980 
1981             break;
1982 
1983         case PSN_RESET:
1984             break;
1985 
1986         default:
1987             break;
1988         }
1989     }
1990     return 0;
1991 }
1992 
1993 
1994 BOOL CALLBACK
FinishedDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1995 FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1996 {
1997     LPNMHDR lpnm;
1998 
1999     switch (msg) {
2000     case WM_INITDIALOG:
2001         if (hBitmap)
2002             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2003                                IMAGE_BITMAP, (LPARAM)hBitmap);
2004         if (!success)
2005             SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
2006 
2007         /* async delay: will show the dialog box completely before
2008            the install_script is started */
2009         PostMessage(hwnd, WM_USER, 0, 0L);
2010         return TRUE;
2011 
2012     case WM_USER:
2013 
2014         if (success && install_script && install_script[0]) {
2015             char fname[MAX_PATH];
2016             char *buffer;
2017             HCURSOR hCursor;
2018             int result;
2019 
2020             char *argv[3] = {NULL, "-install", NULL};
2021 
2022             SetDlgItemText(hwnd, IDC_TITLE,
2023                             "Please wait while running postinstall script...");
2024             strcpy(fname, python_dir);
2025             strcat(fname, "\\Scripts\\");
2026             strcat(fname, install_script);
2027 
2028             if (logfile)
2029                 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
2030 
2031             hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
2032 
2033             argv[0] = fname;
2034 
2035             result = run_installscript(fname, 2, argv, &buffer);
2036             if (0 != result) {
2037                 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2038             }
2039             if (buffer)
2040                 SetDlgItemText(hwnd, IDC_INFO, buffer);
2041             SetDlgItemText(hwnd, IDC_TITLE,
2042                             "Postinstall script finished.\n"
2043                             "Click the Finish button to exit the Setup wizard.");
2044 
2045             free(buffer);
2046             SetCursor(hCursor);
2047             CloseLogfile();
2048         }
2049 
2050         return TRUE;
2051 
2052     case WM_NOTIFY:
2053         lpnm = (LPNMHDR) lParam;
2054 
2055         switch (lpnm->code) {
2056         case PSN_SETACTIVE: /* Enable the Finish button */
2057             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2058             break;
2059 
2060         case PSN_WIZNEXT:
2061             break;
2062 
2063         case PSN_WIZFINISH:
2064             break;
2065 
2066         case PSN_RESET:
2067             break;
2068 
2069         default:
2070             break;
2071         }
2072     }
2073     return 0;
2074 }
2075 
RunWizard(HWND hwnd)2076 void RunWizard(HWND hwnd)
2077 {
2078     PROPSHEETPAGE   psp =       {0};
2079     HPROPSHEETPAGE  ahpsp[4] =  {0};
2080     PROPSHEETHEADER psh =       {0};
2081 
2082     /* Display module information */
2083     psp.dwSize =        sizeof(psp);
2084     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
2085     psp.hInstance =     GetModuleHandle (NULL);
2086     psp.lParam =        0;
2087     psp.pfnDlgProc =    IntroDlgProc;
2088     psp.pszTemplate =   MAKEINTRESOURCE(IDD_INTRO);
2089 
2090     ahpsp[0] =          CreatePropertySheetPage(&psp);
2091 
2092     /* Select python version to use */
2093     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
2094     psp.pszTemplate =       MAKEINTRESOURCE(IDD_SELECTPYTHON);
2095     psp.pfnDlgProc =        SelectPythonDlgProc;
2096 
2097     ahpsp[1] =              CreatePropertySheetPage(&psp);
2098 
2099     /* Install the files */
2100     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
2101     psp.pszTemplate =       MAKEINTRESOURCE(IDD_INSTALLFILES);
2102     psp.pfnDlgProc =        InstallFilesDlgProc;
2103 
2104     ahpsp[2] =              CreatePropertySheetPage(&psp);
2105 
2106     /* Show success or failure */
2107     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
2108     psp.pszTemplate =       MAKEINTRESOURCE(IDD_FINISHED);
2109     psp.pfnDlgProc =        FinishedDlgProc;
2110 
2111     ahpsp[3] =              CreatePropertySheetPage(&psp);
2112 
2113     /* Create the property sheet */
2114     psh.dwSize =            sizeof(psh);
2115     psh.hInstance =         GetModuleHandle(NULL);
2116     psh.hwndParent =        hwnd;
2117     psh.phpage =            ahpsp;
2118     psh.dwFlags =           PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2119         psh.pszbmWatermark =    NULL;
2120         psh.pszbmHeader =       NULL;
2121         psh.nStartPage =        0;
2122         psh.nPages =            4;
2123 
2124         PropertySheet(&psh);
2125 }
2126 
2127 // subtly different from HasLocalMachinePrivs(), in that after executing
2128 // an 'elevated' process, we expect this to return TRUE - but there is no
2129 // such implication for HasLocalMachinePrivs
MyIsUserAnAdmin()2130 BOOL MyIsUserAnAdmin()
2131 {
2132     typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2133     static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2134     HMODULE shell32;
2135     // This function isn't guaranteed to be available (and it can't hurt
2136     // to leave the library loaded)
2137     if (0 == (shell32=LoadLibrary("shell32.dll")))
2138         return FALSE;
2139     if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2140         return FALSE;
2141     return (*pfnIsUserAnAdmin)();
2142 }
2143 
2144 // Some magic for Vista's UAC.  If there is a target_version, and
2145 // if that target version is installed in the registry under
2146 // HKLM, and we are not current administrator, then
2147 // re-execute ourselves requesting elevation.
2148 // Split into 2 functions - "should we elevate" and "spawn elevated"
2149 
2150 // Returns TRUE if we should spawn an elevated child
NeedAutoUAC()2151 BOOL NeedAutoUAC()
2152 {
2153     HKEY hk;
2154     char key_name[80];
2155     // no Python version info == we can't know yet.
2156     if (target_version[0] == '\0')
2157         return FALSE;
2158     // see how python is current installed
2159     wsprintf(key_name,
2160                      "Software\\Python\\PythonCore\\%s\\InstallPath",
2161                      target_version);
2162     if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2163                                       key_name, 0, KEY_READ, &hk))
2164         return FALSE;
2165     RegCloseKey(hk);
2166     // Python is installed in HKLM - we must elevate.
2167     return TRUE;
2168 }
2169 
2170 // Returns TRUE if the platform supports UAC.
PlatformSupportsUAC()2171 BOOL PlatformSupportsUAC()
2172 {
2173     // Note that win2k does seem to support ShellExecute with 'runas',
2174     // but does *not* support IsUserAnAdmin - so we just pretend things
2175     // only work on XP and later.
2176     BOOL bIsWindowsXPorLater;
2177     OSVERSIONINFO winverinfo;
2178     winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
2179     if (!GetVersionEx(&winverinfo))
2180         return FALSE; // something bad has gone wrong
2181     bIsWindowsXPorLater =
2182        ( (winverinfo.dwMajorVersion > 5) ||
2183        ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
2184     return bIsWindowsXPorLater;
2185 }
2186 
2187 // Spawn ourself as an elevated application.  On failure, a message is
2188 // displayed to the user - but this app will always terminate, even
2189 // on error.
SpawnUAC()2190 void SpawnUAC()
2191 {
2192     // interesting failure scenario that has been seen: initial executable
2193     // runs from a network drive - but once elevated, that network share
2194     // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2195     int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2196                                 SW_SHOWNORMAL);
2197     if (ret <= 32) {
2198         char msg[128];
2199         wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2200         MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2201     }
2202 }
2203 
DoInstall(void)2204 int DoInstall(void)
2205 {
2206     char ini_buffer[4096];
2207 
2208     /* Read installation information */
2209     GetPrivateProfileString("Setup", "title", "", ini_buffer,
2210                              sizeof(ini_buffer), ini_file);
2211     unescape(title, ini_buffer, sizeof(title));
2212 
2213     GetPrivateProfileString("Setup", "info", "", ini_buffer,
2214                              sizeof(ini_buffer), ini_file);
2215     unescape(info, ini_buffer, sizeof(info));
2216 
2217     GetPrivateProfileString("Setup", "build_info", "", build_info,
2218                              sizeof(build_info), ini_file);
2219 
2220     pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2221                                         ini_file);
2222     pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2223                                         ini_file);
2224 
2225     GetPrivateProfileString("Setup", "target_version", "",
2226                              target_version, sizeof(target_version),
2227                              ini_file);
2228 
2229     GetPrivateProfileString("metadata", "name", "",
2230                              meta_name, sizeof(meta_name),
2231                              ini_file);
2232 
2233     GetPrivateProfileString("Setup", "install_script", "",
2234                              install_script, sizeof(install_script),
2235                              ini_file);
2236 
2237     GetPrivateProfileString("Setup", "user_access_control", "",
2238                              user_access_control, sizeof(user_access_control), ini_file);
2239 
2240     // See if we need to do the Vista UAC magic.
2241     if (strcmp(user_access_control, "force")==0) {
2242         if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
2243             SpawnUAC();
2244             return 0;
2245         }
2246         // already admin - keep going
2247     } else if (strcmp(user_access_control, "auto")==0) {
2248         // Check if it looks like we need UAC control, based
2249         // on how Python itself was installed.
2250         if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
2251             SpawnUAC();
2252             return 0;
2253         }
2254     } else {
2255         // display a warning about unknown values - only the developer
2256         // of the extension will see it (until they fix it!)
2257         if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2258             MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2259         // nothing to do.
2260         }
2261     }
2262 
2263     hwndMain = CreateBackground(title);
2264 
2265     RunWizard(hwndMain);
2266 
2267     /* Clean up */
2268     UnmapViewOfFile(arc_data);
2269     if (ini_file)
2270         DeleteFile(ini_file);
2271 
2272     if (hBitmap)
2273         DeleteObject(hBitmap);
2274 
2275     return 0;
2276 }
2277 
2278 /*********************** uninstall section ******************************/
2279 
compare(const void * p1,const void * p2)2280 static int compare(const void *p1, const void *p2)
2281 {
2282     return strcmp(*(char **)p2, *(char **)p1);
2283 }
2284 
2285 /*
2286  * Commit suicide (remove the uninstaller itself).
2287  *
2288  * Create a batch file to first remove the uninstaller
2289  * (will succeed after it has finished), then the batch file itself.
2290  *
2291  * This technique has been demonstrated by Jeff Richter,
2292  * MSJ 1/1996
2293  */
remove_exe(void)2294 void remove_exe(void)
2295 {
2296     char exename[_MAX_PATH];
2297     char batname[_MAX_PATH];
2298     FILE *fp;
2299     STARTUPINFO si;
2300     PROCESS_INFORMATION pi;
2301 
2302     GetModuleFileName(NULL, exename, sizeof(exename));
2303     sprintf(batname, "%s.bat", exename);
2304     fp = fopen(batname, "w");
2305     fprintf(fp, ":Repeat\n");
2306     fprintf(fp, "del \"%s\"\n", exename);
2307     fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2308     fprintf(fp, "del \"%s\"\n", batname);
2309     fclose(fp);
2310 
2311     ZeroMemory(&si, sizeof(si));
2312     si.cb = sizeof(si);
2313     si.dwFlags = STARTF_USESHOWWINDOW;
2314     si.wShowWindow = SW_HIDE;
2315     if (CreateProcess(NULL,
2316                       batname,
2317                       NULL,
2318                       NULL,
2319                       FALSE,
2320                       CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2321                       NULL,
2322                       "\\",
2323                       &si,
2324                       &pi)) {
2325         SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2326         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2327         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2328         CloseHandle(pi.hProcess);
2329         ResumeThread(pi.hThread);
2330         CloseHandle(pi.hThread);
2331     }
2332 }
2333 
DeleteRegistryKey(char * string)2334 void DeleteRegistryKey(char *string)
2335 {
2336     char *keyname;
2337     char *subkeyname;
2338     char *delim;
2339     HKEY hKey;
2340     long result;
2341     char *line;
2342 
2343     line = strdup(string); /* so we can change it */
2344 
2345     keyname = strchr(line, '[');
2346     if (!keyname)
2347         return;
2348     ++keyname;
2349 
2350     subkeyname = strchr(keyname, ']');
2351     if (!subkeyname)
2352         return;
2353     *subkeyname++='\0';
2354     delim = strchr(subkeyname, '\n');
2355     if (delim)
2356         *delim = '\0';
2357 
2358     result = RegOpenKeyEx(hkey_root,
2359                           keyname,
2360                           0,
2361                           KEY_WRITE,
2362                           &hKey);
2363 
2364     if (result != ERROR_SUCCESS)
2365         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2366     else {
2367         result = RegDeleteKey(hKey, subkeyname);
2368         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2369             MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2370         RegCloseKey(hKey);
2371     }
2372     free(line);
2373 }
2374 
DeleteRegistryValue(char * string)2375 void DeleteRegistryValue(char *string)
2376 {
2377     char *keyname;
2378     char *valuename;
2379     char *value;
2380     HKEY hKey;
2381     long result;
2382     char *line;
2383 
2384     line = strdup(string); /* so we can change it */
2385 
2386 /* Format is 'Reg DB Value: [key]name=value' */
2387     keyname = strchr(line, '[');
2388     if (!keyname)
2389         return;
2390     ++keyname;
2391     valuename = strchr(keyname, ']');
2392     if (!valuename)
2393         return;
2394     *valuename++ = '\0';
2395     value = strchr(valuename, '=');
2396     if (!value)
2397         return;
2398 
2399     *value++ = '\0';
2400 
2401     result = RegOpenKeyEx(hkey_root,
2402                           keyname,
2403                           0,
2404                           KEY_WRITE,
2405                           &hKey);
2406     if (result != ERROR_SUCCESS)
2407         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2408     else {
2409         result = RegDeleteValue(hKey, valuename);
2410         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2411             MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2412         RegCloseKey(hKey);
2413     }
2414     free(line);
2415 }
2416 
MyDeleteFile(char * line)2417 BOOL MyDeleteFile(char *line)
2418 {
2419     char *pathname = strchr(line, ':');
2420     if (!pathname)
2421         return FALSE;
2422     ++pathname;
2423     while (isspace(*pathname))
2424         ++pathname;
2425     return DeleteFile(pathname);
2426 }
2427 
MyRemoveDirectory(char * line)2428 BOOL MyRemoveDirectory(char *line)
2429 {
2430     char *pathname = strchr(line, ':');
2431     if (!pathname)
2432         return FALSE;
2433     ++pathname;
2434     while (isspace(*pathname))
2435         ++pathname;
2436     return RemoveDirectory(pathname);
2437 }
2438 
Run_RemoveScript(char * line)2439 BOOL Run_RemoveScript(char *line)
2440 {
2441     char *dllname;
2442     char *scriptname;
2443     static char lastscript[MAX_PATH];
2444 
2445 /* Format is 'Run Scripts: [pythondll]scriptname' */
2446 /* XXX Currently, pythondll carries no path!!! */
2447     dllname = strchr(line, '[');
2448     if (!dllname)
2449         return FALSE;
2450     ++dllname;
2451     scriptname = strchr(dllname, ']');
2452     if (!scriptname)
2453         return FALSE;
2454     *scriptname++ = '\0';
2455     /* this function may be called more than one time with the same
2456        script, only run it one time */
2457     if (strcmp(lastscript, scriptname)) {
2458         char *argv[3] = {NULL, "-remove", NULL};
2459         char *buffer = NULL;
2460 
2461         argv[0] = scriptname;
2462 
2463         if (0 != run_installscript(scriptname, 2, argv, &buffer))
2464             fprintf(stderr, "*** Could not run installation script ***");
2465 
2466         if (buffer && buffer[0])
2467             MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2468         free(buffer);
2469 
2470         strcpy(lastscript, scriptname);
2471     }
2472     return TRUE;
2473 }
2474 
DoUninstall(int argc,char ** argv)2475 int DoUninstall(int argc, char **argv)
2476 {
2477     FILE *logfile;
2478     char buffer[4096];
2479     int nLines = 0;
2480     int i;
2481     char *cp;
2482     int nFiles = 0;
2483     int nDirs = 0;
2484     int nErrors = 0;
2485     char **lines;
2486     int lines_buffer_size = 10;
2487 
2488     if (argc != 3) {
2489         MessageBox(NULL,
2490                    "Wrong number of args",
2491                    NULL,
2492                    MB_OK);
2493         return 1; /* Error */
2494     }
2495     if (strcmp(argv[1], "-u")) {
2496         MessageBox(NULL,
2497                    "2. arg is not -u",
2498                    NULL,
2499                    MB_OK);
2500         return 1; /* Error */
2501     }
2502 
2503     logfile = fopen(argv[2], "r");
2504     if (!logfile) {
2505         MessageBox(NULL,
2506                    "could not open logfile",
2507                    NULL,
2508                    MB_OK);
2509         return 1; /* Error */
2510     }
2511 
2512     lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2513     if (!lines)
2514         return SystemError(0, "Out of memory");
2515 
2516     /* Read the whole logfile, realloacting the buffer */
2517     while (fgets(buffer, sizeof(buffer), logfile)) {
2518         int len = strlen(buffer);
2519         /* remove trailing white space */
2520         while (isspace(buffer[len-1]))
2521             len -= 1;
2522         buffer[len] = '\0';
2523         lines[nLines++] = strdup(buffer);
2524         if (nLines >= lines_buffer_size) {
2525             lines_buffer_size += 10;
2526             lines = (char **)realloc(lines,
2527                                      sizeof(char *) * lines_buffer_size);
2528             if (!lines)
2529                 return SystemError(0, "Out of memory");
2530         }
2531     }
2532     fclose(logfile);
2533 
2534     /* Sort all the lines, so that highest 3-digit codes are first */
2535     qsort(&lines[0], nLines, sizeof(char *),
2536           compare);
2537 
2538     if (IDYES != MessageBox(NULL,
2539                             "Are you sure you want to remove\n"
2540                             "this package from your computer?",
2541                             "Please confirm",
2542                             MB_YESNO | MB_ICONQUESTION))
2543         return 0;
2544 
2545     hkey_root = HKEY_LOCAL_MACHINE;
2546     cp = "";
2547     for (i = 0; i < nLines; ++i) {
2548         /* Ignore duplicate lines */
2549         if (strcmp(cp, lines[i])) {
2550             int ign;
2551             cp = lines[i];
2552             /* Parse the lines */
2553             if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2554                 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2555                     hkey_root = HKEY_CURRENT_USER;
2556                 else {
2557                     // HKLM - check they have permissions.
2558                     if (!HasLocalMachinePrivs()) {
2559                         MessageBox(GetFocus(),
2560                                    "You do not seem to have sufficient access rights\n"
2561                                    "on this machine to uninstall this software",
2562                                    NULL,
2563                                    MB_OK | MB_ICONSTOP);
2564                         return 1; /* Error */
2565                     }
2566                 }
2567             } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
2568                 if (MyRemoveDirectory(cp))
2569                     ++nDirs;
2570                 else {
2571                     int code = GetLastError();
2572                     if (code != 2 && code != 3) { /* file or path not found */
2573                         ++nErrors;
2574                     }
2575                 }
2576             } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2577                 if (MyDeleteFile(cp))
2578                     ++nFiles;
2579                 else {
2580                     int code = GetLastError();
2581                     if (code != 2 && code != 3) { /* file or path not found */
2582                         ++nErrors;
2583                     }
2584                 }
2585             } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2586                 if (MyDeleteFile(cp))
2587                     ++nFiles;
2588                 else {
2589                     int code = GetLastError();
2590                     if (code != 2 && code != 3) { /* file or path not found */
2591                         ++nErrors;
2592                     }
2593                 }
2594             } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2595                 DeleteRegistryKey(cp);
2596             } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2597                 DeleteRegistryValue(cp);
2598             } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2599                 Run_RemoveScript(cp);
2600             }
2601         }
2602     }
2603 
2604     if (DeleteFile(argv[2])) {
2605         ++nFiles;
2606     } else {
2607         ++nErrors;
2608         SystemError(GetLastError(), argv[2]);
2609     }
2610     if (nErrors)
2611         wsprintf(buffer,
2612                  "%d files and %d directories removed\n"
2613                  "%d files or directories could not be removed",
2614                  nFiles, nDirs, nErrors);
2615     else
2616         wsprintf(buffer, "%d files and %d directories removed",
2617                  nFiles, nDirs);
2618     MessageBox(NULL, buffer, "Uninstall Finished!",
2619                MB_OK | MB_ICONINFORMATION);
2620     remove_exe();
2621     return 0;
2622 }
2623 
WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpszCmdLine,INT nCmdShow)2624 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
2625                     LPSTR lpszCmdLine, INT nCmdShow)
2626 {
2627     extern int __argc;
2628     extern char **__argv;
2629     char *basename;
2630 
2631     GetModuleFileName(NULL, modulename, sizeof(modulename));
2632 
2633     /* Map the executable file to memory */
2634     arc_data = MapExistingFile(modulename, &arc_size);
2635     if (!arc_data) {
2636         SystemError(GetLastError(), "Could not open archive");
2637         return 1;
2638     }
2639 
2640     /* OK. So this program can act as installer (self-extracting
2641      * zip-file, or as uninstaller when started with '-u logfile'
2642      * command line flags.
2643      *
2644      * The installer is usually started without command line flags,
2645      * and the uninstaller is usually started with the '-u logfile'
2646      * flag. What to do if some innocent user double-clicks the
2647      * exe-file?
2648      * The following implements a defensive strategy...
2649      */
2650 
2651     /* Try to extract the configuration data into a temporary file */
2652     if (ExtractInstallData(arc_data, arc_size, &exe_size,
2653                            &ini_file, &pre_install_script))
2654         return DoInstall();
2655 
2656     if (!ini_file && __argc > 1) {
2657         return DoUninstall(__argc, __argv);
2658     }
2659 
2660 
2661     basename = strrchr(modulename, '\\');
2662     if (basename)
2663         ++basename;
2664 
2665     /* Last guess about the purpose of this program */
2666     if (basename && (0 == strncmp(basename, "Remove", 6)))
2667         SystemError(0, "This program is normally started by windows");
2668     else
2669         SystemError(0, "Setup program invalid or damaged");
2670     return 1;
2671 }
2672