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