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