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