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