• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             NULL,
900             error,
901             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
902             (LPSTR)&lpMsgBuf,
903             0,
904             NULL
905             );
906         strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
907         LocalFree(lpMsgBuf);
908     } else
909         Buffer[0] = '\0';
910     n = lstrlen(Buffer);
911     _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
912     MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
913     return FALSE;
914 }
915 
notify(int code,char * fmt,...)916 static BOOL notify (int code, char *fmt, ...)
917 {
918     char Buffer[1024];
919     va_list marker;
920     BOOL result = TRUE;
921     int a, b;
922     char *cp;
923 
924     va_start(marker, fmt);
925     _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
926 
927     switch (code) {
928 /* Questions */
929     case CAN_OVERWRITE:
930         break;
931 
932 /* Information notification */
933     case DIR_CREATED:
934         if (logfile)
935             fprintf(logfile, "100 Made Dir: %s\n", fmt);
936         break;
937 
938     case FILE_CREATED:
939         if (logfile)
940             fprintf(logfile, "200 File Copy: %s\n", fmt);
941         goto add_to_filelist_label;
942         break;
943 
944     case FILE_OVERWRITTEN:
945         if (logfile)
946             fprintf(logfile, "200 File Overwrite: %s\n", fmt);
947       add_to_filelist_label:
948         if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
949             add_to_filelist(fmt);
950         break;
951 
952 /* Error Messages */
953     case ZLIB_ERROR:
954         MessageBox(GetFocus(), Buffer, "Error",
955                     MB_OK | MB_ICONWARNING);
956         break;
957 
958     case SYSTEM_ERROR:
959         SystemError(GetLastError(), Buffer);
960         break;
961 
962     case NUM_FILES:
963         a = va_arg(marker, int);
964         b = va_arg(marker, int);
965         SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
966         SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
967     }
968     va_end(marker);
969 
970     return result;
971 }
972 
MapExistingFile(char * pathname,DWORD * psize)973 static char *MapExistingFile(char *pathname, DWORD *psize)
974 {
975     HANDLE hFile, hFileMapping;
976     DWORD nSizeLow, nSizeHigh;
977     char *data;
978 
979     hFile = CreateFile(pathname,
980                         GENERIC_READ, FILE_SHARE_READ, NULL,
981                         OPEN_EXISTING,
982                         FILE_ATTRIBUTE_NORMAL, NULL);
983     if (hFile == INVALID_HANDLE_VALUE)
984         return NULL;
985     nSizeLow = GetFileSize(hFile, &nSizeHigh);
986     hFileMapping = CreateFileMapping(hFile,
987                                       NULL, PAGE_READONLY, 0, 0, NULL);
988     CloseHandle(hFile);
989 
990     if (hFileMapping == INVALID_HANDLE_VALUE)
991         return NULL;
992 
993     data = MapViewOfFile(hFileMapping,
994                           FILE_MAP_READ, 0, 0, 0);
995 
996     CloseHandle(hFileMapping);
997     *psize = nSizeLow;
998     return data;
999 }
1000 
1001 
create_bitmap(HWND hwnd)1002 static void create_bitmap(HWND hwnd)
1003 {
1004     BITMAPFILEHEADER *bfh;
1005     BITMAPINFO *bi;
1006     HDC hdc;
1007 
1008     if (!bitmap_bytes)
1009         return;
1010 
1011     if (hBitmap)
1012         return;
1013 
1014     hdc = GetDC(hwnd);
1015 
1016     bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1017     bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
1018 
1019     hBitmap = CreateDIBitmap(hdc,
1020                              &bi->bmiHeader,
1021                              CBM_INIT,
1022                              bitmap_bytes + bfh->bfOffBits,
1023                              bi,
1024                              DIB_RGB_COLORS);
1025     ReleaseDC(hwnd, hdc);
1026 }
1027 
1028 /* Extract everything we need to begin the installation.  Currently this
1029    is the INI filename with install data, and the raw pre-install script
1030 */
ExtractInstallData(char * data,DWORD size,int * pexe_size,char ** out_ini_file,char ** out_preinstall_script)1031 static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
1032                                char **out_ini_file, char **out_preinstall_script)
1033 {
1034     /* read the end of central directory record */
1035     struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1036                                                    (struct eof_cdir)];
1037 
1038     int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1039         pe->ofsCDir;
1040 
1041     int ofs = arc_start - sizeof (struct meta_data_hdr);
1042 
1043     /* read meta_data info */
1044     struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
1045     char *src, *dst;
1046     char *ini_file;
1047     char tempdir[MAX_PATH];
1048 
1049     /* ensure that if we fail, we don't have garbage out pointers */
1050     *out_ini_file = *out_preinstall_script = NULL;
1051 
1052     if (pe->tag != 0x06054b50) {
1053         return FALSE;
1054     }
1055 
1056     if (pmd->tag != 0x1234567B) {
1057         return SystemError(0,
1058                    "Invalid cfgdata magic number (see bdist_wininst.py)");
1059     }
1060     if (ofs < 0) {
1061         return FALSE;
1062     }
1063 
1064     if (pmd->bitmap_size) {
1065         /* Store pointer to bitmap bytes */
1066         bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1067     }
1068 
1069     *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
1070 
1071     src = ((char *)pmd) - pmd->uncomp_size;
1072     ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
1073     if (!ini_file)
1074         return FALSE;
1075     if (!GetTempPath(sizeof(tempdir), tempdir)
1076         || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1077         SystemError(GetLastError(),
1078                      "Could not create temporary file");
1079         return FALSE;
1080     }
1081 
1082     dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1083                         0, 0, NULL/*notify*/);
1084     if (!dst)
1085         return FALSE;
1086     /* Up to the first \0 is the INI file data. */
1087     strncpy(dst, src, pmd->uncomp_size);
1088     src += strlen(dst) + 1;
1089     /* Up to next \0 is the pre-install script */
1090     *out_preinstall_script = strdup(src);
1091     *out_ini_file = ini_file;
1092     UnmapViewOfFile(dst);
1093     return TRUE;
1094 }
1095 
PumpMessages(void)1096 static void PumpMessages(void)
1097 {
1098     MSG msg;
1099     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1100         TranslateMessage(&msg);
1101         DispatchMessage(&msg);
1102     }
1103 }
1104 
1105 LRESULT CALLBACK
WindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1106 WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1107 {
1108     HDC hdc;
1109     HFONT hFont;
1110     int h;
1111     PAINTSTRUCT ps;
1112     switch (msg) {
1113     case WM_PAINT:
1114         hdc = BeginPaint(hwnd, &ps);
1115         h = GetSystemMetrics(SM_CYSCREEN) / 10;
1116         hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
1117                             0, 0, 0, 0, 0, 0, 0, "Times Roman");
1118         hFont = SelectObject(hdc, hFont);
1119         SetBkMode(hdc, TRANSPARENT);
1120         TextOut(hdc, 15, 15, title, strlen(title));
1121         SetTextColor(hdc, RGB(255, 255, 255));
1122         TextOut(hdc, 10, 10, title, strlen(title));
1123         DeleteObject(SelectObject(hdc, hFont));
1124         EndPaint(hwnd, &ps);
1125         return 0;
1126     }
1127     return DefWindowProc(hwnd, msg, wParam, lParam);
1128 }
1129 
CreateBackground(char * title)1130 static HWND CreateBackground(char *title)
1131 {
1132     WNDCLASS wc;
1133     HWND hwnd;
1134     char buffer[4096];
1135 
1136     wc.style = CS_VREDRAW | CS_HREDRAW;
1137     wc.lpfnWndProc = WindowProc;
1138     wc.cbWndExtra = 0;
1139     wc.cbClsExtra = 0;
1140     wc.hInstance = GetModuleHandle(NULL);
1141     wc.hIcon = NULL;
1142     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1143     wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
1144     wc.lpszMenuName = NULL;
1145     wc.lpszClassName = "SetupWindowClass";
1146 
1147     if (!RegisterClass(&wc))
1148         MessageBox(hwndMain,
1149                     "Could not register window class",
1150                     "Setup.exe", MB_OK);
1151 
1152     wsprintf(buffer, "Setup %s", title);
1153     hwnd = CreateWindow("SetupWindowClass",
1154                          buffer,
1155                          0,
1156                          0, 0,
1157                          GetSystemMetrics(SM_CXFULLSCREEN),
1158                          GetSystemMetrics(SM_CYFULLSCREEN),
1159                          NULL,
1160                          NULL,
1161                          GetModuleHandle(NULL),
1162                          NULL);
1163     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
1164     UpdateWindow(hwnd);
1165     return hwnd;
1166 }
1167 
1168 /*
1169  * Center a window on the screen
1170  */
CenterWindow(HWND hwnd)1171 static void CenterWindow(HWND hwnd)
1172 {
1173     RECT rc;
1174     int w, h;
1175 
1176     GetWindowRect(hwnd, &rc);
1177     w = GetSystemMetrics(SM_CXSCREEN);
1178     h = GetSystemMetrics(SM_CYSCREEN);
1179     MoveWindow(hwnd,
1180                (w - (rc.right-rc.left))/2,
1181                (h - (rc.bottom-rc.top))/2,
1182                 rc.right-rc.left, rc.bottom-rc.top, FALSE);
1183 }
1184 
1185 #include <prsht.h>
1186 
1187 BOOL CALLBACK
IntroDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1188 IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1189 {
1190     LPNMHDR lpnm;
1191     char Buffer[4096];
1192 
1193     switch (msg) {
1194     case WM_INITDIALOG:
1195         create_bitmap(hwnd);
1196         if(hBitmap)
1197             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1198                                IMAGE_BITMAP, (LPARAM)hBitmap);
1199         CenterWindow(GetParent(hwnd));
1200         wsprintf(Buffer,
1201                   "This Wizard will install %s on your computer. "
1202                   "Click Next to continue "
1203                   "or Cancel to exit the Setup Wizard.",
1204                   meta_name);
1205         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1206         SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
1207         SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
1208         return FALSE;
1209 
1210     case WM_NOTIFY:
1211         lpnm = (LPNMHDR) lParam;
1212 
1213         switch (lpnm->code) {
1214         case PSN_SETACTIVE:
1215             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1216             break;
1217 
1218         case PSN_WIZNEXT:
1219             break;
1220 
1221         case PSN_RESET:
1222             break;
1223 
1224         default:
1225             break;
1226         }
1227     }
1228     return FALSE;
1229 }
1230 
1231 #ifdef USE_OTHER_PYTHON_VERSIONS
1232 /* These are really private variables used to communicate
1233  * between StatusRoutine and CheckPythonExe
1234  */
1235 char bound_image_dll[_MAX_PATH];
1236 int bound_image_major;
1237 int bound_image_minor;
1238 
StatusRoutine(IMAGEHLP_STATUS_REASON reason,PSTR ImageName,PSTR DllName,ULONG Va,ULONG Parameter)1239 static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
1240                                     PSTR ImageName,
1241                                     PSTR DllName,
1242                                     ULONG Va,
1243                                     ULONG Parameter)
1244 {
1245     char fname[_MAX_PATH];
1246     int int_version;
1247 
1248     switch(reason) {
1249     case BindOutOfMemory:
1250     case BindRvaToVaFailed:
1251     case BindNoRoomInImage:
1252     case BindImportProcedureFailed:
1253         break;
1254 
1255     case BindImportProcedure:
1256     case BindForwarder:
1257     case BindForwarderNOT:
1258     case BindImageModified:
1259     case BindExpandFileHeaders:
1260     case BindImageComplete:
1261     case BindSymbolsNotUpdated:
1262     case BindMismatchedSymbols:
1263     case BindImportModuleFailed:
1264         break;
1265 
1266     case BindImportModule:
1267         if (1 == sscanf(DllName, "python%d", &int_version)) {
1268             SearchPath(NULL, DllName, NULL, sizeof(fname),
1269                        fname, NULL);
1270             strcpy(bound_image_dll, fname);
1271             bound_image_major = int_version / 10;
1272             bound_image_minor = int_version % 10;
1273             OutputDebugString("BOUND ");
1274             OutputDebugString(fname);
1275             OutputDebugString("\n");
1276         }
1277         break;
1278     }
1279     return TRUE;
1280 }
1281 
1282 /*
1283  */
get_sys_prefix(LPSTR exe,LPSTR dll)1284 static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1285 {
1286     void (__cdecl * Py_Initialize)(void);
1287     void (__cdecl * Py_SetProgramName)(char *);
1288     void (__cdecl * Py_Finalize)(void);
1289     void* (__cdecl * PySys_GetObject)(char *);
1290     void (__cdecl * PySys_SetArgv)(int, char **);
1291     char* (__cdecl * Py_GetPrefix)(void);
1292     char* (__cdecl * Py_GetPath)(void);
1293     HINSTANCE hPython;
1294     LPSTR prefix = NULL;
1295     int (__cdecl * PyRun_SimpleString)(char *);
1296 
1297     {
1298         char Buffer[256];
1299         wsprintf(Buffer, "PYTHONHOME=%s", exe);
1300         *strrchr(Buffer, '\\') = '\0';
1301 //      MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1302                 _putenv(Buffer);
1303                 _putenv("PYTHONPATH=");
1304     }
1305 
1306     hPython = LoadLibrary(dll);
1307     if (!hPython)
1308         return NULL;
1309     Py_Initialize = (void (*)(void))GetProcAddress
1310         (hPython,"Py_Initialize");
1311 
1312     PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1313         (hPython,"PySys_SetArgv");
1314 
1315     PyRun_SimpleString = (int (*)(char *))GetProcAddress
1316         (hPython,"PyRun_SimpleString");
1317 
1318     Py_SetProgramName = (void (*)(char *))GetProcAddress
1319         (hPython,"Py_SetProgramName");
1320 
1321     PySys_GetObject = (void* (*)(char *))GetProcAddress
1322         (hPython,"PySys_GetObject");
1323 
1324     Py_GetPrefix = (char * (*)(void))GetProcAddress
1325         (hPython,"Py_GetPrefix");
1326 
1327     Py_GetPath = (char * (*)(void))GetProcAddress
1328         (hPython,"Py_GetPath");
1329 
1330     Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1331                                                   "Py_Finalize");
1332     Py_SetProgramName(exe);
1333     Py_Initialize();
1334     PySys_SetArgv(1, &exe);
1335 
1336     MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1337     MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
1338 
1339     Py_Finalize();
1340     FreeLibrary(hPython);
1341 
1342     return prefix;
1343 }
1344 
1345 static BOOL
CheckPythonExe(LPSTR pathname,LPSTR version,int * pmajor,int * pminor)1346 CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1347 {
1348     bound_image_dll[0] = '\0';
1349     if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
1350                      pathname,
1351                      NULL,
1352                      NULL,
1353                      StatusRoutine))
1354         return SystemError(0, "Could not bind image");
1355     if (bound_image_dll[0] == '\0')
1356         return SystemError(0, "Does not seem to be a python executable");
1357     *pmajor = bound_image_major;
1358     *pminor = bound_image_minor;
1359     if (version && *version) {
1360         char core_version[12];
1361         wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
1362         if (strcmp(version, core_version))
1363             return SystemError(0, "Wrong Python version");
1364     }
1365     get_sys_prefix(pathname, bound_image_dll);
1366     return TRUE;
1367 }
1368 
1369 /*
1370  * Browse for other python versions. Insert it into the listbox specified
1371  * by hwnd. version, if not NULL or empty, is the version required.
1372  */
GetOtherPythonVersion(HWND hwnd,LPSTR version)1373 static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
1374 {
1375     char vers_name[_MAX_PATH + 80];
1376     DWORD itemindex;
1377     OPENFILENAME of;
1378     char pathname[_MAX_PATH];
1379     DWORD result;
1380 
1381     strcpy(pathname, "python.exe");
1382 
1383     memset(&of, 0, sizeof(of));
1384     of.lStructSize = sizeof(OPENFILENAME);
1385     of.hwndOwner = GetParent(hwnd);
1386     of.hInstance = NULL;
1387     of.lpstrFilter = "python.exe\0python.exe\0";
1388     of.lpstrCustomFilter = NULL;
1389     of.nMaxCustFilter = 0;
1390     of.nFilterIndex = 1;
1391     of.lpstrFile = pathname;
1392     of.nMaxFile = sizeof(pathname);
1393     of.lpstrFileTitle = NULL;
1394     of.nMaxFileTitle = 0;
1395     of.lpstrInitialDir = NULL;
1396     of.lpstrTitle = "Python executable";
1397     of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
1398     of.lpstrDefExt = "exe";
1399 
1400     result = GetOpenFileName(&of);
1401     if (result) {
1402         int major, minor;
1403         if (!CheckPythonExe(pathname, version, &major, &minor)) {
1404             return FALSE;
1405         }
1406         *strrchr(pathname, '\\') = '\0';
1407         wsprintf(vers_name, "Python Version %d.%d in %s",
1408                   major, minor, pathname);
1409         itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
1410                                 (LPARAM)(LPSTR)vers_name);
1411         SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
1412         SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1413                     (LPARAM)(LPSTR)strdup(pathname));
1414         return TRUE;
1415     }
1416     return FALSE;
1417 }
1418 #endif /* USE_OTHER_PYTHON_VERSIONS */
1419 
1420 typedef struct _InstalledVersionInfo {
1421     char prefix[MAX_PATH+1]; // sys.prefix directory.
1422     HKEY hkey; // Is this Python in HKCU or HKLM?
1423 } InstalledVersionInfo;
1424 
1425 
1426 /*
1427  * Fill the listbox specified by hwnd with all python versions found
1428  * in the registry. version, if not NULL or empty, is the version
1429  * required.
1430  */
GetPythonVersions(HWND hwnd,HKEY hkRoot,LPSTR version)1431 static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
1432 {
1433     DWORD index = 0;
1434     char core_version[80];
1435     HKEY hKey;
1436     BOOL result = TRUE;
1437     DWORD bufsize;
1438 
1439     if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
1440                                        "Software\\Python\\PythonCore",
1441                                        0,       KEY_READ, &hKey))
1442         return FALSE;
1443     bufsize = sizeof(core_version);
1444     while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
1445                                           core_version, &bufsize, NULL,
1446                                           NULL, NULL, NULL)) {
1447         char subkey_name[80], vers_name[80];
1448         int itemindex;
1449         DWORD value_size;
1450         HKEY hk;
1451 
1452         bufsize = sizeof(core_version);
1453         ++index;
1454         if (version && *version && strcmp(version, core_version))
1455             continue;
1456 
1457         wsprintf(vers_name, "Python Version %s (found in registry)",
1458                   core_version);
1459         wsprintf(subkey_name,
1460                   "Software\\Python\\PythonCore\\%s\\InstallPath",
1461                   core_version);
1462         if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
1463             InstalledVersionInfo *ivi =
1464                   (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
1465             value_size = sizeof(ivi->prefix);
1466             if (ivi &&
1467                 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
1468                                                  ivi->prefix, &value_size)) {
1469                 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
1470                                         (LPARAM)(LPSTR)vers_name);
1471                 ivi->hkey = hkRoot;
1472                 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1473                             (LPARAM)(LPSTR)ivi);
1474             }
1475             RegCloseKey(hk);
1476         }
1477     }
1478     RegCloseKey(hKey);
1479     return result;
1480 }
1481 
1482 /* Determine if the current user can write to HKEY_LOCAL_MACHINE */
HasLocalMachinePrivs()1483 BOOL HasLocalMachinePrivs()
1484 {
1485                 HKEY hKey;
1486                 DWORD result;
1487                 static char KeyName[] =
1488                         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1489 
1490                 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1491                                           KeyName,
1492                                           0,
1493                                           KEY_CREATE_SUB_KEY,
1494                                           &hKey);
1495                 if (result==0)
1496                         RegCloseKey(hKey);
1497                 return result==0;
1498 }
1499 
1500 // Check the root registry key to use - either HKLM or HKCU.
1501 // If Python is installed in HKCU, then our extension also must be installed
1502 // in HKCU - as Python won't be available for other users, we shouldn't either
1503 // (and will fail if we are!)
1504 // If Python is installed in HKLM, then we will also prefer to use HKLM, but
1505 // this may not be possible - so we silently fall back to HKCU.
1506 //
1507 // We assume hkey_root is already set to where Python itself is installed.
CheckRootKey(HWND hwnd)1508 void CheckRootKey(HWND hwnd)
1509 {
1510     if (hkey_root==HKEY_CURRENT_USER) {
1511         ; // as above, always install ourself in HKCU too.
1512     } else if (hkey_root==HKEY_LOCAL_MACHINE) {
1513         // Python in HKLM, but we may or may not have permissions there.
1514         // Open the uninstall key with 'create' permissions - if this fails,
1515         // we don't have permission.
1516         if (!HasLocalMachinePrivs())
1517             hkey_root = HKEY_CURRENT_USER;
1518     } else {
1519         MessageBox(hwnd, "Don't know Python's installation type",
1520                            "Strange", MB_OK | MB_ICONSTOP);
1521         /* Default to wherever they can, but preferring HKLM */
1522         hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1523     }
1524 }
1525 
1526 /* Return the installation scheme depending on Python version number */
GetScheme(int major,int minor)1527 SCHEME *GetScheme(int major, int minor)
1528 {
1529     if (major > 2)
1530         return new_scheme;
1531     else if((major == 2) && (minor >= 2))
1532         return new_scheme;
1533     return old_scheme;
1534 }
1535 
1536 BOOL CALLBACK
SelectPythonDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1537 SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1538 {
1539     LPNMHDR lpnm;
1540 
1541     switch (msg) {
1542     case WM_INITDIALOG:
1543         if (hBitmap)
1544             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1545                                IMAGE_BITMAP, (LPARAM)hBitmap);
1546         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1547                            HKEY_LOCAL_MACHINE, target_version);
1548         GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1549                            HKEY_CURRENT_USER, target_version);
1550         {               /* select the last entry which is the highest python
1551                    version found */
1552             int count;
1553             count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1554                                         LB_GETCOUNT, 0, 0);
1555             if (count && count != LB_ERR)
1556                 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
1557                                     count-1, 0);
1558 
1559             /* If a specific Python version is required,
1560              * display a prominent notice showing this fact.
1561              */
1562             if (target_version && target_version[0]) {
1563                 char buffer[4096];
1564                 wsprintf(buffer,
1565                          "Python %s is required for this package. "
1566                          "Select installation to use:",
1567                          target_version);
1568                 SetDlgItemText(hwnd, IDC_TITLE, buffer);
1569             }
1570 
1571             if (count == 0) {
1572                 char Buffer[4096];
1573                 char *msg;
1574                 if (target_version && target_version[0]) {
1575                     wsprintf(Buffer,
1576                              "Python version %s required, which was not found"
1577                              " in the registry.", target_version);
1578                     msg = Buffer;
1579                 } else
1580                     msg = "No Python installation found in the registry.";
1581                 MessageBox(hwnd, msg, "Cannot install",
1582                            MB_OK | MB_ICONSTOP);
1583             }
1584         }
1585         goto UpdateInstallDir;
1586         break;
1587 
1588     case WM_COMMAND:
1589         switch (LOWORD(wParam)) {
1590 /*
1591   case IDC_OTHERPYTHON:
1592   if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1593   target_version))
1594   goto UpdateInstallDir;
1595   break;
1596 */
1597         case IDC_VERSIONS_LIST:
1598             switch (HIWORD(wParam)) {
1599                 int id;
1600             case LBN_SELCHANGE:
1601               UpdateInstallDir:
1602                 PropSheet_SetWizButtons(GetParent(hwnd),
1603                                         PSWIZB_BACK | PSWIZB_NEXT);
1604                 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1605                                          LB_GETCURSEL, 0, 0);
1606                 if (id == LB_ERR) {
1607                     PropSheet_SetWizButtons(GetParent(hwnd),
1608                                             PSWIZB_BACK);
1609                     SetDlgItemText(hwnd, IDC_PATH, "");
1610                     SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
1611                     strcpy(python_dir, "");
1612                     strcpy(pythondll, "");
1613                 } else {
1614                     char *pbuf;
1615                     int result;
1616                     InstalledVersionInfo *ivi;
1617                     PropSheet_SetWizButtons(GetParent(hwnd),
1618                                             PSWIZB_BACK | PSWIZB_NEXT);
1619                     /* Get the python directory */
1620                     ivi = (InstalledVersionInfo *)
1621                         SendDlgItemMessage(hwnd,
1622                             IDC_VERSIONS_LIST,
1623                             LB_GETITEMDATA,
1624                             id,
1625                             0);
1626                     hkey_root = ivi->hkey;
1627                     strcpy(python_dir, ivi->prefix);
1628                     SetDlgItemText(hwnd, IDC_PATH, python_dir);
1629                     /* retrieve the python version and pythondll to use */
1630                     result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1631                                                  LB_GETTEXTLEN, (WPARAM)id, 0);
1632                     pbuf = (char *)malloc(result + 1);
1633                     if (pbuf) {
1634                         /* guess the name of the python-dll */
1635                         SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1636                                             LB_GETTEXT, (WPARAM)id,
1637                                             (LPARAM)pbuf);
1638                         result = sscanf(pbuf, "Python Version %d.%d",
1639                                          &py_major, &py_minor);
1640                         if (result == 2) {
1641 #ifdef _DEBUG
1642                             wsprintf(pythondll, "python%d%d_d.dll",
1643                                      py_major, py_minor);
1644 #else
1645                             wsprintf(pythondll, "python%d%d.dll",
1646                                      py_major, py_minor);
1647 #endif
1648                         }
1649                         free(pbuf);
1650                     } else
1651                         strcpy(pythondll, "");
1652                     /* retrieve the scheme for this version */
1653                     {
1654                         char install_path[_MAX_PATH];
1655                         SCHEME *scheme = GetScheme(py_major, py_minor);
1656                         strcpy(install_path, python_dir);
1657                         if (install_path[strlen(install_path)-1] != '\\')
1658                             strcat(install_path, "\\");
1659                         strcat(install_path, scheme[0].prefix);
1660                         SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
1661                     }
1662                 }
1663             }
1664             break;
1665         }
1666         return 0;
1667 
1668     case WM_NOTIFY:
1669         lpnm = (LPNMHDR) lParam;
1670 
1671         switch (lpnm->code) {
1672             int id;
1673         case PSN_SETACTIVE:
1674             id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1675                                      LB_GETCURSEL, 0, 0);
1676             if (id == LB_ERR)
1677                 PropSheet_SetWizButtons(GetParent(hwnd),
1678                                         PSWIZB_BACK);
1679             else
1680                 PropSheet_SetWizButtons(GetParent(hwnd),
1681                                         PSWIZB_BACK | PSWIZB_NEXT);
1682             break;
1683 
1684         case PSN_WIZNEXT:
1685             break;
1686 
1687         case PSN_WIZFINISH:
1688             break;
1689 
1690         case PSN_RESET:
1691             break;
1692 
1693         default:
1694             break;
1695         }
1696     }
1697     return 0;
1698 }
1699 
OpenLogfile(char * dir)1700 static BOOL OpenLogfile(char *dir)
1701 {
1702     char buffer[_MAX_PATH+1];
1703     time_t ltime;
1704     struct tm *now;
1705     long result;
1706     HKEY hKey, hSubkey;
1707     char subkey_name[256];
1708     static char KeyName[] =
1709         "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1710     const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1711                             "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
1712     DWORD disposition;
1713 
1714     /* Use Create, as the Uninstall subkey may not exist under HKCU.
1715        Use CreateKeyEx, so we can specify a SAM specifying write access
1716     */
1717         result = RegCreateKeyEx(hkey_root,
1718                       KeyName,
1719                       0, /* reserved */
1720                   NULL, /* class */
1721                   0, /* options */
1722                   KEY_CREATE_SUB_KEY, /* sam */
1723                   NULL, /* security */
1724                   &hKey, /* result key */
1725                   NULL); /* disposition */
1726     if (result != ERROR_SUCCESS) {
1727         if (result == ERROR_ACCESS_DENIED) {
1728             /* This should no longer be able to happen - we have already
1729                checked if they have permissions in HKLM, and all users
1730                should have write access to HKCU.
1731             */
1732             MessageBox(GetFocus(),
1733                        "You do not seem to have sufficient access rights\n"
1734                        "on this machine to install this software",
1735                        NULL,
1736                        MB_OK | MB_ICONSTOP);
1737             return FALSE;
1738         } else {
1739             MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
1740         }
1741     }
1742 
1743     sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
1744     logfile = fopen(buffer, "a");
1745     if (!logfile) {
1746         char error[1024];
1747 
1748         sprintf(error, "Can't create \"%s\" (%s).\n\n"
1749                 "Try to execute the installer as administrator.",
1750                 buffer, strerror(errno));
1751         MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
1752         return FALSE;
1753     }
1754 
1755     time(&ltime);
1756     now = localtime(&ltime);
1757     strftime(buffer, sizeof(buffer),
1758              "*** Installation started %Y/%m/%d %H:%M ***\n",
1759              localtime(&ltime));
1760     fprintf(logfile, buffer);
1761     fprintf(logfile, "Source: %s\n", modulename);
1762 
1763     /* Root key must be first entry processed by uninstaller. */
1764     fprintf(logfile, "999 Root Key: %s\n", root_name);
1765 
1766     sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
1767 
1768     result = RegCreateKeyEx(hKey, subkey_name,
1769                             0, NULL, 0,
1770                             KEY_WRITE,
1771                             NULL,
1772                             &hSubkey,
1773                             &disposition);
1774 
1775     if (result != ERROR_SUCCESS)
1776         MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
1777 
1778     RegCloseKey(hKey);
1779 
1780     if (disposition == REG_CREATED_NEW_KEY)
1781         fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
1782 
1783     sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
1784 
1785     result = RegSetValueEx(hSubkey, "DisplayName",
1786                            0,
1787                            REG_SZ,
1788                            buffer,
1789                            strlen(buffer)+1);
1790 
1791     if (result != ERROR_SUCCESS)
1792         MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1793 
1794     fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1795         KeyName, subkey_name, "DisplayName", buffer);
1796 
1797     {
1798         FILE *fp;
1799         sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1800         fp = fopen(buffer, "wb");
1801         fwrite(arc_data, exe_size, 1, fp);
1802         fclose(fp);
1803 
1804         sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1805             dir, meta_name, dir, meta_name);
1806 
1807         result = RegSetValueEx(hSubkey, "UninstallString",
1808                                0,
1809                                REG_SZ,
1810                                buffer,
1811                                strlen(buffer)+1);
1812 
1813         if (result != ERROR_SUCCESS)
1814             MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1815 
1816         fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1817             KeyName, subkey_name, "UninstallString", buffer);
1818     }
1819     return TRUE;
1820 }
1821 
CloseLogfile(void)1822 static void CloseLogfile(void)
1823 {
1824     char buffer[_MAX_PATH+1];
1825     time_t ltime;
1826     struct tm *now;
1827 
1828     time(&ltime);
1829     now = localtime(&ltime);
1830     strftime(buffer, sizeof(buffer),
1831              "*** Installation finished %Y/%m/%d %H:%M ***\n",
1832              localtime(&ltime));
1833     fprintf(logfile, buffer);
1834     if (logfile)
1835         fclose(logfile);
1836 }
1837 
1838 BOOL CALLBACK
InstallFilesDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1839 InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1840 {
1841     LPNMHDR lpnm;
1842     char Buffer[4096];
1843     SCHEME *scheme;
1844 
1845     switch (msg) {
1846     case WM_INITDIALOG:
1847         if (hBitmap)
1848             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1849                                IMAGE_BITMAP, (LPARAM)hBitmap);
1850         wsprintf(Buffer,
1851                   "Click Next to begin the installation of %s. "
1852                   "If you want to review or change any of your "
1853                   " installation settings, click Back. "
1854                   "Click Cancel to exit the wizard.",
1855                   meta_name);
1856         SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1857         SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1858         break;
1859 
1860     case WM_NUMFILES:
1861         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1862         PumpMessages();
1863         return TRUE;
1864 
1865     case WM_NEXTFILE:
1866         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1867                             0);
1868         SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1869         PumpMessages();
1870         return TRUE;
1871 
1872     case WM_NOTIFY:
1873         lpnm = (LPNMHDR) lParam;
1874 
1875         switch (lpnm->code) {
1876         case PSN_SETACTIVE:
1877             PropSheet_SetWizButtons(GetParent(hwnd),
1878                                     PSWIZB_BACK | PSWIZB_NEXT);
1879             break;
1880 
1881         case PSN_WIZFINISH:
1882             break;
1883 
1884         case PSN_WIZNEXT:
1885             /* Handle a Next button click here */
1886             hDialog = hwnd;
1887             success = TRUE;
1888 
1889             /* Disable the buttons while we work.  Sending CANCELTOCLOSE has
1890               the effect of disabling the cancel button, which is a) as we
1891               do everything synchronously we can't cancel, and b) the next
1892               step is 'finished', when it is too late to cancel anyway.
1893               The next step being 'Finished' means we also don't need to
1894               restore the button state back */
1895             PropSheet_SetWizButtons(GetParent(hwnd), 0);
1896             SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
1897             /* Make sure the installation directory name ends in a */
1898             /* backslash */
1899             if (python_dir[strlen(python_dir)-1] != '\\')
1900                 strcat(python_dir, "\\");
1901             /* Strip the trailing backslash again */
1902             python_dir[strlen(python_dir)-1] = '\0';
1903 
1904             CheckRootKey(hwnd);
1905 
1906             if (!OpenLogfile(python_dir))
1907                 break;
1908 
1909 /*
1910  * The scheme we have to use depends on the Python version...
1911  if sys.version < "2.2":
1912     WINDOWS_SCHEME = {
1913     'purelib': '$base',
1914     'platlib': '$base',
1915     'headers': '$base/Include/$dist_name',
1916     'scripts': '$base/Scripts',
1917     'data'   : '$base',
1918     }
1919  else:
1920     WINDOWS_SCHEME = {
1921     'purelib': '$base/Lib/site-packages',
1922     'platlib': '$base/Lib/site-packages',
1923     'headers': '$base/Include/$dist_name',
1924     'scripts': '$base/Scripts',
1925     'data'   : '$base',
1926     }
1927 */
1928             scheme = GetScheme(py_major, py_minor);
1929             /* Run the pre-install script. */
1930             if (pre_install_script && *pre_install_script) {
1931                 SetDlgItemText (hwnd, IDC_TITLE,
1932                                 "Running pre-installation script");
1933                 run_simple_script(pre_install_script);
1934             }
1935             if (!success) {
1936                 break;
1937             }
1938             /* Extract all files from the archive */
1939             SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1940             if (!unzip_archive (scheme,
1941                                 python_dir, arc_data,
1942                                 arc_size, notify))
1943                 set_failure_reason("Failed to unzip installation files");
1944             /* Compile the py-files */
1945             if (success && pyc_compile) {
1946                 int errors;
1947                 HINSTANCE hPython;
1948                 SetDlgItemText(hwnd, IDC_TITLE,
1949                                 "Compiling files to .pyc...");
1950 
1951                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1952                 hPython = LoadPythonDll(pythondll);
1953                 if (hPython) {
1954                     errors = compile_filelist(hPython, FALSE);
1955                     FreeLibrary(hPython);
1956                 }
1957                 /* Compilation errors are intentionally ignored:
1958                  * Python2.0 contains a bug which will result
1959                  * in sys.path containing garbage under certain
1960                  * circumstances, and an error message will only
1961                  * confuse the user.
1962                  */
1963             }
1964             if (success && pyo_compile) {
1965                 int errors;
1966                 HINSTANCE hPython;
1967                 SetDlgItemText(hwnd, IDC_TITLE,
1968                                 "Compiling files to .pyo...");
1969 
1970                 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1971                 hPython = LoadPythonDll(pythondll);
1972                 if (hPython) {
1973                     errors = compile_filelist(hPython, TRUE);
1974                     FreeLibrary(hPython);
1975                 }
1976                 /* Errors ignored: see above */
1977             }
1978 
1979 
1980             break;
1981 
1982         case PSN_RESET:
1983             break;
1984 
1985         default:
1986             break;
1987         }
1988     }
1989     return 0;
1990 }
1991 
1992 
1993 BOOL CALLBACK
FinishedDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)1994 FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1995 {
1996     LPNMHDR lpnm;
1997 
1998     switch (msg) {
1999     case WM_INITDIALOG:
2000         if (hBitmap)
2001             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2002                                IMAGE_BITMAP, (LPARAM)hBitmap);
2003         if (!success)
2004             SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
2005 
2006         /* async delay: will show the dialog box completely before
2007            the install_script is started */
2008         PostMessage(hwnd, WM_USER, 0, 0L);
2009         return TRUE;
2010 
2011     case WM_USER:
2012 
2013         if (success && install_script && install_script[0]) {
2014             char fname[MAX_PATH];
2015             char *buffer;
2016             HCURSOR hCursor;
2017             int result;
2018 
2019             char *argv[3] = {NULL, "-install", NULL};
2020 
2021             SetDlgItemText(hwnd, IDC_TITLE,
2022                             "Please wait while running postinstall script...");
2023             strcpy(fname, python_dir);
2024             strcat(fname, "\\Scripts\\");
2025             strcat(fname, install_script);
2026 
2027             if (logfile)
2028                 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
2029 
2030             hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
2031 
2032             argv[0] = fname;
2033 
2034             result = run_installscript(fname, 2, argv, &buffer);
2035             if (0 != result) {
2036                 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2037             }
2038             if (buffer)
2039                 SetDlgItemText(hwnd, IDC_INFO, buffer);
2040             SetDlgItemText(hwnd, IDC_TITLE,
2041                             "Postinstall script finished.\n"
2042                             "Click the Finish button to exit the Setup wizard.");
2043 
2044             free(buffer);
2045             SetCursor(hCursor);
2046             CloseLogfile();
2047         }
2048 
2049         return TRUE;
2050 
2051     case WM_NOTIFY:
2052         lpnm = (LPNMHDR) lParam;
2053 
2054         switch (lpnm->code) {
2055         case PSN_SETACTIVE: /* Enable the Finish button */
2056             PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2057             break;
2058 
2059         case PSN_WIZNEXT:
2060             break;
2061 
2062         case PSN_WIZFINISH:
2063             break;
2064 
2065         case PSN_RESET:
2066             break;
2067 
2068         default:
2069             break;
2070         }
2071     }
2072     return 0;
2073 }
2074 
RunWizard(HWND hwnd)2075 void RunWizard(HWND hwnd)
2076 {
2077     PROPSHEETPAGE   psp =       {0};
2078     HPROPSHEETPAGE  ahpsp[4] =  {0};
2079     PROPSHEETHEADER psh =       {0};
2080 
2081     /* Display module information */
2082     psp.dwSize =        sizeof(psp);
2083     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
2084     psp.hInstance =     GetModuleHandle (NULL);
2085     psp.lParam =        0;
2086     psp.pfnDlgProc =    IntroDlgProc;
2087     psp.pszTemplate =   MAKEINTRESOURCE(IDD_INTRO);
2088 
2089     ahpsp[0] =          CreatePropertySheetPage(&psp);
2090 
2091     /* Select python version to use */
2092     psp.dwFlags =       PSP_DEFAULT|PSP_HIDEHEADER;
2093     psp.pszTemplate =       MAKEINTRESOURCE(IDD_SELECTPYTHON);
2094     psp.pfnDlgProc =        SelectPythonDlgProc;
2095 
2096     ahpsp[1] =              CreatePropertySheetPage(&psp);
2097 
2098     /* Install the files */
2099     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
2100     psp.pszTemplate =       MAKEINTRESOURCE(IDD_INSTALLFILES);
2101     psp.pfnDlgProc =        InstallFilesDlgProc;
2102 
2103     ahpsp[2] =              CreatePropertySheetPage(&psp);
2104 
2105     /* Show success or failure */
2106     psp.dwFlags =           PSP_DEFAULT|PSP_HIDEHEADER;
2107     psp.pszTemplate =       MAKEINTRESOURCE(IDD_FINISHED);
2108     psp.pfnDlgProc =        FinishedDlgProc;
2109 
2110     ahpsp[3] =              CreatePropertySheetPage(&psp);
2111 
2112     /* Create the property sheet */
2113     psh.dwSize =            sizeof(psh);
2114     psh.hInstance =         GetModuleHandle(NULL);
2115     psh.hwndParent =        hwnd;
2116     psh.phpage =            ahpsp;
2117     psh.dwFlags =           PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2118         psh.pszbmWatermark =    NULL;
2119         psh.pszbmHeader =       NULL;
2120         psh.nStartPage =        0;
2121         psh.nPages =            4;
2122 
2123         PropertySheet(&psh);
2124 }
2125 
2126 // subtly different from HasLocalMachinePrivs(), in that after executing
2127 // an 'elevated' process, we expect this to return TRUE - but there is no
2128 // such implication for HasLocalMachinePrivs
MyIsUserAnAdmin()2129 BOOL MyIsUserAnAdmin()
2130 {
2131     typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2132     static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2133     HMODULE shell32;
2134     // This function isn't guaranteed to be available (and it can't hurt
2135     // to leave the library loaded)
2136     if (0 == (shell32=LoadLibrary("shell32.dll")))
2137         return FALSE;
2138     if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2139         return FALSE;
2140     return (*pfnIsUserAnAdmin)();
2141 }
2142 
2143 // Some magic for Vista's UAC.  If there is a target_version, and
2144 // if that target version is installed in the registry under
2145 // HKLM, and we are not current administrator, then
2146 // re-execute ourselves requesting elevation.
2147 // Split into 2 functions - "should we elevate" and "spawn elevated"
2148 
2149 // Returns TRUE if we should spawn an elevated child
NeedAutoUAC()2150 BOOL NeedAutoUAC()
2151 {
2152     HKEY hk;
2153     char key_name[80];
2154     // no Python version info == we can't know yet.
2155     if (target_version[0] == '\0')
2156         return FALSE;
2157     // see how python is current installed
2158     wsprintf(key_name,
2159                      "Software\\Python\\PythonCore\\%s\\InstallPath",
2160                      target_version);
2161     if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2162                                       key_name, 0, KEY_READ, &hk))
2163         return FALSE;
2164     RegCloseKey(hk);
2165     // Python is installed in HKLM - we must elevate.
2166     return TRUE;
2167 }
2168 
2169 // Returns TRUE if the platform supports UAC.
PlatformSupportsUAC()2170 BOOL PlatformSupportsUAC()
2171 {
2172     // Note that win2k does seem to support ShellExecute with 'runas',
2173     // but does *not* support IsUserAnAdmin - so we just pretend things
2174     // only work on XP and later.
2175     BOOL bIsWindowsXPorLater;
2176     OSVERSIONINFO winverinfo;
2177     winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
2178     if (!GetVersionEx(&winverinfo))
2179         return FALSE; // something bad has gone wrong
2180     bIsWindowsXPorLater =
2181        ( (winverinfo.dwMajorVersion > 5) ||
2182        ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
2183     return bIsWindowsXPorLater;
2184 }
2185 
2186 // Spawn ourself as an elevated application.  On failure, a message is
2187 // displayed to the user - but this app will always terminate, even
2188 // on error.
SpawnUAC()2189 void SpawnUAC()
2190 {
2191     // interesting failure scenario that has been seen: initial executable
2192     // runs from a network drive - but once elevated, that network share
2193     // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2194     int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2195                                 SW_SHOWNORMAL);
2196     if (ret <= 32) {
2197         char msg[128];
2198         wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2199         MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2200     }
2201 }
2202 
DoInstall(void)2203 int DoInstall(void)
2204 {
2205     char ini_buffer[4096];
2206 
2207     /* Read installation information */
2208     GetPrivateProfileString("Setup", "title", "", ini_buffer,
2209                              sizeof(ini_buffer), ini_file);
2210     unescape(title, ini_buffer, sizeof(title));
2211 
2212     GetPrivateProfileString("Setup", "info", "", ini_buffer,
2213                              sizeof(ini_buffer), ini_file);
2214     unescape(info, ini_buffer, sizeof(info));
2215 
2216     GetPrivateProfileString("Setup", "build_info", "", build_info,
2217                              sizeof(build_info), ini_file);
2218 
2219     pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2220                                         ini_file);
2221     pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2222                                         ini_file);
2223 
2224     GetPrivateProfileString("Setup", "target_version", "",
2225                              target_version, sizeof(target_version),
2226                              ini_file);
2227 
2228     GetPrivateProfileString("metadata", "name", "",
2229                              meta_name, sizeof(meta_name),
2230                              ini_file);
2231 
2232     GetPrivateProfileString("Setup", "install_script", "",
2233                              install_script, sizeof(install_script),
2234                              ini_file);
2235 
2236     GetPrivateProfileString("Setup", "user_access_control", "",
2237                              user_access_control, sizeof(user_access_control), ini_file);
2238 
2239     // See if we need to do the Vista UAC magic.
2240     if (strcmp(user_access_control, "force")==0) {
2241         if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
2242             SpawnUAC();
2243             return 0;
2244         }
2245         // already admin - keep going
2246     } else if (strcmp(user_access_control, "auto")==0) {
2247         // Check if it looks like we need UAC control, based
2248         // on how Python itself was installed.
2249         if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
2250             SpawnUAC();
2251             return 0;
2252         }
2253     } else {
2254         // display a warning about unknown values - only the developer
2255         // of the extension will see it (until they fix it!)
2256         if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2257             MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2258         // nothing to do.
2259         }
2260     }
2261 
2262     hwndMain = CreateBackground(title);
2263 
2264     RunWizard(hwndMain);
2265 
2266     /* Clean up */
2267     UnmapViewOfFile(arc_data);
2268     if (ini_file)
2269         DeleteFile(ini_file);
2270 
2271     if (hBitmap)
2272         DeleteObject(hBitmap);
2273 
2274     return 0;
2275 }
2276 
2277 /*********************** uninstall section ******************************/
2278 
compare(const void * p1,const void * p2)2279 static int compare(const void *p1, const void *p2)
2280 {
2281     return strcmp(*(char **)p2, *(char **)p1);
2282 }
2283 
2284 /*
2285  * Commit suicide (remove the uninstaller itself).
2286  *
2287  * Create a batch file to first remove the uninstaller
2288  * (will succeed after it has finished), then the batch file itself.
2289  *
2290  * This technique has been demonstrated by Jeff Richter,
2291  * MSJ 1/1996
2292  */
remove_exe(void)2293 void remove_exe(void)
2294 {
2295     char exename[_MAX_PATH];
2296     char batname[_MAX_PATH];
2297     FILE *fp;
2298     STARTUPINFO si;
2299     PROCESS_INFORMATION pi;
2300 
2301     GetModuleFileName(NULL, exename, sizeof(exename));
2302     sprintf(batname, "%s.bat", exename);
2303     fp = fopen(batname, "w");
2304     fprintf(fp, ":Repeat\n");
2305     fprintf(fp, "del \"%s\"\n", exename);
2306     fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2307     fprintf(fp, "del \"%s\"\n", batname);
2308     fclose(fp);
2309 
2310     ZeroMemory(&si, sizeof(si));
2311     si.cb = sizeof(si);
2312     si.dwFlags = STARTF_USESHOWWINDOW;
2313     si.wShowWindow = SW_HIDE;
2314     if (CreateProcess(NULL,
2315                       batname,
2316                       NULL,
2317                       NULL,
2318                       FALSE,
2319                       CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2320                       NULL,
2321                       "\\",
2322                       &si,
2323                       &pi)) {
2324         SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2325         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2326         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2327         CloseHandle(pi.hProcess);
2328         ResumeThread(pi.hThread);
2329         CloseHandle(pi.hThread);
2330     }
2331 }
2332 
DeleteRegistryKey(char * string)2333 void DeleteRegistryKey(char *string)
2334 {
2335     char *keyname;
2336     char *subkeyname;
2337     char *delim;
2338     HKEY hKey;
2339     long result;
2340     char *line;
2341 
2342     line = strdup(string); /* so we can change it */
2343 
2344     keyname = strchr(line, '[');
2345     if (!keyname)
2346         return;
2347     ++keyname;
2348 
2349     subkeyname = strchr(keyname, ']');
2350     if (!subkeyname)
2351         return;
2352     *subkeyname++='\0';
2353     delim = strchr(subkeyname, '\n');
2354     if (delim)
2355         *delim = '\0';
2356 
2357     result = RegOpenKeyEx(hkey_root,
2358                           keyname,
2359                           0,
2360                           KEY_WRITE,
2361                           &hKey);
2362 
2363     if (result != ERROR_SUCCESS)
2364         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2365     else {
2366         result = RegDeleteKey(hKey, subkeyname);
2367         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2368             MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2369         RegCloseKey(hKey);
2370     }
2371     free(line);
2372 }
2373 
DeleteRegistryValue(char * string)2374 void DeleteRegistryValue(char *string)
2375 {
2376     char *keyname;
2377     char *valuename;
2378     char *value;
2379     HKEY hKey;
2380     long result;
2381     char *line;
2382 
2383     line = strdup(string); /* so we can change it */
2384 
2385 /* Format is 'Reg DB Value: [key]name=value' */
2386     keyname = strchr(line, '[');
2387     if (!keyname)
2388         return;
2389     ++keyname;
2390     valuename = strchr(keyname, ']');
2391     if (!valuename)
2392         return;
2393     *valuename++ = '\0';
2394     value = strchr(valuename, '=');
2395     if (!value)
2396         return;
2397 
2398     *value++ = '\0';
2399 
2400     result = RegOpenKeyEx(hkey_root,
2401                           keyname,
2402                           0,
2403                           KEY_WRITE,
2404                           &hKey);
2405     if (result != ERROR_SUCCESS)
2406         MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2407     else {
2408         result = RegDeleteValue(hKey, valuename);
2409         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2410             MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2411         RegCloseKey(hKey);
2412     }
2413     free(line);
2414 }
2415 
MyDeleteFile(char * line)2416 BOOL MyDeleteFile(char *line)
2417 {
2418     char *pathname = strchr(line, ':');
2419     if (!pathname)
2420         return FALSE;
2421     ++pathname;
2422     while (isspace(*pathname))
2423         ++pathname;
2424     return DeleteFile(pathname);
2425 }
2426 
MyRemoveDirectory(char * line)2427 BOOL MyRemoveDirectory(char *line)
2428 {
2429     char *pathname = strchr(line, ':');
2430     if (!pathname)
2431         return FALSE;
2432     ++pathname;
2433     while (isspace(*pathname))
2434         ++pathname;
2435     return RemoveDirectory(pathname);
2436 }
2437 
Run_RemoveScript(char * line)2438 BOOL Run_RemoveScript(char *line)
2439 {
2440     char *dllname;
2441     char *scriptname;
2442     static char lastscript[MAX_PATH];
2443 
2444 /* Format is 'Run Scripts: [pythondll]scriptname' */
2445 /* XXX Currently, pythondll carries no path!!! */
2446     dllname = strchr(line, '[');
2447     if (!dllname)
2448         return FALSE;
2449     ++dllname;
2450     scriptname = strchr(dllname, ']');
2451     if (!scriptname)
2452         return FALSE;
2453     *scriptname++ = '\0';
2454     /* this function may be called more than one time with the same
2455        script, only run it one time */
2456     if (strcmp(lastscript, scriptname)) {
2457         char *argv[3] = {NULL, "-remove", NULL};
2458         char *buffer = NULL;
2459 
2460         argv[0] = scriptname;
2461 
2462         if (0 != run_installscript(scriptname, 2, argv, &buffer))
2463             fprintf(stderr, "*** Could not run installation script ***");
2464 
2465         if (buffer && buffer[0])
2466             MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2467         free(buffer);
2468 
2469         strcpy(lastscript, scriptname);
2470     }
2471     return TRUE;
2472 }
2473 
DoUninstall(int argc,char ** argv)2474 int DoUninstall(int argc, char **argv)
2475 {
2476     FILE *logfile;
2477     char buffer[4096];
2478     int nLines = 0;
2479     int i;
2480     char *cp;
2481     int nFiles = 0;
2482     int nDirs = 0;
2483     int nErrors = 0;
2484     char **lines;
2485     int lines_buffer_size = 10;
2486 
2487     if (argc != 3) {
2488         MessageBox(NULL,
2489                    "Wrong number of args",
2490                    NULL,
2491                    MB_OK);
2492         return 1; /* Error */
2493     }
2494     if (strcmp(argv[1], "-u")) {
2495         MessageBox(NULL,
2496                    "2. arg is not -u",
2497                    NULL,
2498                    MB_OK);
2499         return 1; /* Error */
2500     }
2501 
2502     logfile = fopen(argv[2], "r");
2503     if (!logfile) {
2504         MessageBox(NULL,
2505                    "could not open logfile",
2506                    NULL,
2507                    MB_OK);
2508         return 1; /* Error */
2509     }
2510 
2511     lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2512     if (!lines)
2513         return SystemError(0, "Out of memory");
2514 
2515     /* Read the whole logfile, realloacting the buffer */
2516     while (fgets(buffer, sizeof(buffer), logfile)) {
2517         int len = strlen(buffer);
2518         /* remove trailing white space */
2519         while (isspace(buffer[len-1]))
2520             len -= 1;
2521         buffer[len] = '\0';
2522         lines[nLines++] = strdup(buffer);
2523         if (nLines >= lines_buffer_size) {
2524             lines_buffer_size += 10;
2525             lines = (char **)realloc(lines,
2526                                      sizeof(char *) * lines_buffer_size);
2527             if (!lines)
2528                 return SystemError(0, "Out of memory");
2529         }
2530     }
2531     fclose(logfile);
2532 
2533     /* Sort all the lines, so that highest 3-digit codes are first */
2534     qsort(&lines[0], nLines, sizeof(char *),
2535           compare);
2536 
2537     if (IDYES != MessageBox(NULL,
2538                             "Are you sure you want to remove\n"
2539                             "this package from your computer?",
2540                             "Please confirm",
2541                             MB_YESNO | MB_ICONQUESTION))
2542         return 0;
2543 
2544     hkey_root = HKEY_LOCAL_MACHINE;
2545     cp = "";
2546     for (i = 0; i < nLines; ++i) {
2547         /* Ignore duplicate lines */
2548         if (strcmp(cp, lines[i])) {
2549             int ign;
2550             cp = lines[i];
2551             /* Parse the lines */
2552             if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2553                 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2554                     hkey_root = HKEY_CURRENT_USER;
2555                 else {
2556                     // HKLM - check they have permissions.
2557                     if (!HasLocalMachinePrivs()) {
2558                         MessageBox(GetFocus(),
2559                                    "You do not seem to have sufficient access rights\n"
2560                                    "on this machine to uninstall this software",
2561                                    NULL,
2562                                    MB_OK | MB_ICONSTOP);
2563                         return 1; /* Error */
2564                     }
2565                 }
2566             } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
2567                 if (MyRemoveDirectory(cp))
2568                     ++nDirs;
2569                 else {
2570                     int code = GetLastError();
2571                     if (code != 2 && code != 3) { /* file or path not found */
2572                         ++nErrors;
2573                     }
2574                 }
2575             } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2576                 if (MyDeleteFile(cp))
2577                     ++nFiles;
2578                 else {
2579                     int code = GetLastError();
2580                     if (code != 2 && code != 3) { /* file or path not found */
2581                         ++nErrors;
2582                     }
2583                 }
2584             } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2585                 if (MyDeleteFile(cp))
2586                     ++nFiles;
2587                 else {
2588                     int code = GetLastError();
2589                     if (code != 2 && code != 3) { /* file or path not found */
2590                         ++nErrors;
2591                     }
2592                 }
2593             } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2594                 DeleteRegistryKey(cp);
2595             } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2596                 DeleteRegistryValue(cp);
2597             } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2598                 Run_RemoveScript(cp);
2599             }
2600         }
2601     }
2602 
2603     if (DeleteFile(argv[2])) {
2604         ++nFiles;
2605     } else {
2606         ++nErrors;
2607         SystemError(GetLastError(), argv[2]);
2608     }
2609     if (nErrors)
2610         wsprintf(buffer,
2611                  "%d files and %d directories removed\n"
2612                  "%d files or directories could not be removed",
2613                  nFiles, nDirs, nErrors);
2614     else
2615         wsprintf(buffer, "%d files and %d directories removed",
2616                  nFiles, nDirs);
2617     MessageBox(NULL, buffer, "Uninstall Finished!",
2618                MB_OK | MB_ICONINFORMATION);
2619     remove_exe();
2620     return 0;
2621 }
2622 
WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpszCmdLine,INT nCmdShow)2623 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
2624                     LPSTR lpszCmdLine, INT nCmdShow)
2625 {
2626     extern int __argc;
2627     extern char **__argv;
2628     char *basename;
2629 
2630     GetModuleFileName(NULL, modulename, sizeof(modulename));
2631 
2632     /* Map the executable file to memory */
2633     arc_data = MapExistingFile(modulename, &arc_size);
2634     if (!arc_data) {
2635         SystemError(GetLastError(), "Could not open archive");
2636         return 1;
2637     }
2638 
2639     /* OK. So this program can act as installer (self-extracting
2640      * zip-file, or as uninstaller when started with '-u logfile'
2641      * command line flags.
2642      *
2643      * The installer is usually started without command line flags,
2644      * and the uninstaller is usually started with the '-u logfile'
2645      * flag. What to do if some innocent user double-clicks the
2646      * exe-file?
2647      * The following implements a defensive strategy...
2648      */
2649 
2650     /* Try to extract the configuration data into a temporary file */
2651     if (ExtractInstallData(arc_data, arc_size, &exe_size,
2652                            &ini_file, &pre_install_script))
2653         return DoInstall();
2654 
2655     if (!ini_file && __argc > 1) {
2656         return DoUninstall(__argc, __argv);
2657     }
2658 
2659 
2660     basename = strrchr(modulename, '\\');
2661     if (basename)
2662         ++basename;
2663 
2664     /* Last guess about the purpose of this program */
2665     if (basename && (0 == strncmp(basename, "Remove", 6)))
2666         SystemError(0, "This program is normally started by windows");
2667     else
2668         SystemError(0, "Setup program invalid or damaged");
2669     return 1;
2670 }
2671