Home | History | Annotate | Download | only in PC
      1 /* Helper library for MSI creation with Python.
      2  * Copyright (C) 2005 Martin v. Lwis
      3  * Licensed to PSF under a contributor agreement.
      4  */
      5 
      6 #include <Python.h>
      7 #include <fci.h>
      8 #include <fcntl.h>
      9 #include <windows.h>
     10 #include <msi.h>
     11 #include <msiquery.h>
     12 #include <msidefs.h>
     13 #include <rpc.h>
     14 
     15 static PyObject *MSIError;
     16 
     17 static PyObject*
     18 uuidcreate(PyObject* obj, PyObject*args)
     19 {
     20     UUID result;
     21     char *cresult;
     22     PyObject *oresult;
     23 
     24     /* May return ok, local only, and no address.
     25        For local only, the documentation says we still get a uuid.
     26        For RPC_S_UUID_NO_ADDRESS, it's not clear whether we can
     27        use the result. */
     28     if (UuidCreate(&result) == RPC_S_UUID_NO_ADDRESS) {
     29         PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result");
     30         return NULL;
     31     }
     32 
     33     if (UuidToString(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
     34         PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
     35         return NULL;
     36     }
     37 
     38     oresult = PyString_FromString(cresult);
     39     RpcStringFree(&cresult);
     40     return oresult;
     41 
     42 }
     43 
     44 /* FCI callback functions */
     45 
     46 static FNFCIALLOC(cb_alloc)
     47 {
     48     return malloc(cb);
     49 }
     50 
     51 static FNFCIFREE(cb_free)
     52 {
     53     free(memory);
     54 }
     55 
     56 static FNFCIOPEN(cb_open)
     57 {
     58     int result = _open(pszFile, oflag, pmode);
     59     if (result == -1)
     60         *err = errno;
     61     return result;
     62 }
     63 
     64 static FNFCIREAD(cb_read)
     65 {
     66     UINT result = (UINT)_read(hf, memory, cb);
     67     if (result != cb)
     68         *err = errno;
     69     return result;
     70 }
     71 
     72 static FNFCIWRITE(cb_write)
     73 {
     74     UINT result = (UINT)_write(hf, memory, cb);
     75     if (result != cb)
     76         *err = errno;
     77     return result;
     78 }
     79 
     80 static FNFCICLOSE(cb_close)
     81 {
     82     int result = _close(hf);
     83     if (result != 0)
     84         *err = errno;
     85     return result;
     86 }
     87 
     88 static FNFCISEEK(cb_seek)
     89 {
     90     long result = (long)_lseek(hf, dist, seektype);
     91     if (result == -1)
     92         *err = errno;
     93     return result;
     94 }
     95 
     96 static FNFCIDELETE(cb_delete)
     97 {
     98     int result = remove(pszFile);
     99     if (result != 0)
    100         *err = errno;
    101     return result;
    102 }
    103 
    104 static FNFCIFILEPLACED(cb_fileplaced)
    105 {
    106     return 0;
    107 }
    108 
    109 static FNFCIGETTEMPFILE(cb_gettempfile)
    110 {
    111     char *name = _tempnam("", "tmp");
    112     if ((name != NULL) && ((int)strlen(name) < cbTempName)) {
    113         strcpy(pszTempName, name);
    114         free(name);
    115         return TRUE;
    116     }
    117 
    118     if (name) free(name);
    119     return FALSE;
    120 }
    121 
    122 static FNFCISTATUS(cb_status)
    123 {
    124     if (pv) {
    125         PyObject *result = PyObject_CallMethod(pv, "status", "iii", typeStatus, cb1, cb2);
    126         if (result == NULL)
    127             return -1;
    128         Py_DECREF(result);
    129     }
    130     return 0;
    131 }
    132 
    133 static FNFCIGETNEXTCABINET(cb_getnextcabinet)
    134 {
    135     if (pv) {
    136         PyObject *result = PyObject_CallMethod(pv, "getnextcabinet", "i", pccab->iCab);
    137         if (result == NULL)
    138             return -1;
    139         if (!PyString_Check(result)) {
    140             PyErr_Format(PyExc_TypeError,
    141                 "Incorrect return type %s from getnextcabinet",
    142                 result->ob_type->tp_name);
    143             Py_DECREF(result);
    144             return FALSE;
    145         }
    146         strncpy(pccab->szCab, PyString_AsString(result), sizeof(pccab->szCab));
    147         return TRUE;
    148     }
    149     return FALSE;
    150 }
    151 
    152 static FNFCIGETOPENINFO(cb_getopeninfo)
    153 {
    154     BY_HANDLE_FILE_INFORMATION bhfi;
    155     FILETIME filetime;
    156     HANDLE handle;
    157 
    158     /* Need Win32 handle to get time stamps */
    159     handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
    160         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    161     if (handle == INVALID_HANDLE_VALUE)
    162         return -1;
    163 
    164     if (GetFileInformationByHandle(handle, &bhfi) == FALSE)
    165     {
    166         CloseHandle(handle);
    167         return -1;
    168     }
    169 
    170     FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
    171     FileTimeToDosDateTime(&filetime, pdate, ptime);
    172 
    173     *pattribs = (int)(bhfi.dwFileAttributes &
    174         (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
    175 
    176     CloseHandle(handle);
    177 
    178     return _open(pszName, _O_RDONLY | _O_BINARY);
    179 }
    180 
    181 static PyObject* fcicreate(PyObject* obj, PyObject* args)
    182 {
    183     char *cabname, *p;
    184     PyObject *files;
    185     CCAB ccab;
    186     HFCI hfci;
    187     ERF erf;
    188     Py_ssize_t i;
    189 
    190 
    191     if (!PyArg_ParseTuple(args, "sO:FCICreate", &cabname, &files))
    192         return NULL;
    193 
    194     if (!PyList_Check(files)) {
    195         PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
    196         return NULL;
    197     }
    198 
    199     ccab.cb = INT_MAX; /* no need to split CAB into multiple media */
    200     ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */
    201     ccab.cbReserveCFData = 0;
    202     ccab.cbReserveCFFolder = 0;
    203     ccab.cbReserveCFHeader = 0;
    204 
    205     ccab.iCab = 1;
    206     ccab.iDisk = 1;
    207 
    208     ccab.setID = 0;
    209     ccab.szDisk[0] = '\0';
    210 
    211     for (i = 0, p = cabname; *p; p = CharNext(p))
    212         if (*p == '\\' || *p == '/')
    213             i = p - cabname + 1;
    214 
    215     if (i >= sizeof(ccab.szCabPath) ||
    216         strlen(cabname+i) >= sizeof(ccab.szCab)) {
    217         PyErr_SetString(PyExc_ValueError, "path name too long");
    218         return 0;
    219     }
    220 
    221     if (i > 0) {
    222         memcpy(ccab.szCabPath, cabname, i);
    223         ccab.szCabPath[i] = '\0';
    224         strcpy(ccab.szCab, cabname+i);
    225     } else {
    226         strcpy(ccab.szCabPath, ".\\");
    227         strcpy(ccab.szCab, cabname);
    228     }
    229 
    230     hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
    231         cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
    232         cb_gettempfile, &ccab, NULL);
    233 
    234     if (hfci == NULL) {
    235         PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
    236         return NULL;
    237     }
    238 
    239     for (i=0; i < PyList_GET_SIZE(files); i++) {
    240         PyObject *item = PyList_GET_ITEM(files, i);
    241         char *filename, *cabname;
    242         if (!PyArg_ParseTuple(item, "ss", &filename, &cabname))
    243             goto err;
    244         if (!FCIAddFile(hfci, filename, cabname, FALSE,
    245             cb_getnextcabinet, cb_status, cb_getopeninfo,
    246             tcompTYPE_MSZIP))
    247             goto err;
    248     }
    249 
    250     if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
    251         goto err;
    252 
    253     if (!FCIDestroy(hfci))
    254         goto err;
    255 
    256     Py_INCREF(Py_None);
    257     return Py_None;
    258 err:
    259     PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */
    260     FCIDestroy(hfci);
    261     return NULL;
    262 }
    263 
    264 typedef struct msiobj{
    265     PyObject_HEAD
    266     MSIHANDLE h;
    267 }msiobj;
    268 
    269 static void
    270 msiobj_dealloc(msiobj* msidb)
    271 {
    272     MsiCloseHandle(msidb->h);
    273     msidb->h = 0;
    274 }
    275 
    276 static PyObject*
    277 msiobj_close(msiobj* msidb, PyObject *args)
    278 {
    279     MsiCloseHandle(msidb->h);
    280     msidb->h = 0;
    281     Py_INCREF(Py_None);
    282     return Py_None;
    283 }
    284 
    285 static PyObject*
    286 msierror(int status)
    287 {
    288     int code;
    289     char buf[2000];
    290     char *res = buf;
    291     DWORD size = sizeof(buf);
    292     MSIHANDLE err = MsiGetLastErrorRecord();
    293 
    294     if (err == 0) {
    295         switch(status) {
    296         case ERROR_ACCESS_DENIED:
    297             PyErr_SetString(MSIError, "access denied");
    298             return NULL;
    299         case ERROR_FUNCTION_FAILED:
    300             PyErr_SetString(MSIError, "function failed");
    301             return NULL;
    302         case ERROR_INVALID_DATA:
    303             PyErr_SetString(MSIError, "invalid data");
    304             return NULL;
    305         case ERROR_INVALID_HANDLE:
    306             PyErr_SetString(MSIError, "invalid handle");
    307             return NULL;
    308         case ERROR_INVALID_STATE:
    309             PyErr_SetString(MSIError, "invalid state");
    310             return NULL;
    311         case ERROR_INVALID_PARAMETER:
    312             PyErr_SetString(MSIError, "invalid parameter");
    313             return NULL;
    314         default:
    315             PyErr_Format(MSIError, "unknown error %x", status);
    316             return NULL;
    317         }
    318     }
    319 
    320     code = MsiRecordGetInteger(err, 1); /* XXX code */
    321     if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
    322         res = malloc(size+1);
    323         MsiFormatRecord(0, err, res, &size);
    324         res[size]='\0';
    325     }
    326     MsiCloseHandle(err);
    327     PyErr_SetString(MSIError, res);
    328     if (res != buf)
    329         free(res);
    330     return NULL;
    331 }
    332 
    333 /*************************** Record objects **********************/
    334 
    335 static PyObject*
    336 record_getfieldcount(msiobj* record, PyObject* args)
    337 {
    338     return PyInt_FromLong(MsiRecordGetFieldCount(record->h));
    339 }
    340 
    341 static PyObject*
    342 record_getinteger(msiobj* record, PyObject* args)
    343 {
    344     unsigned int field;
    345     int status;
    346 
    347     if (!PyArg_ParseTuple(args, "I:GetInteger", &field))
    348         return NULL;
    349     status = MsiRecordGetInteger(record->h, field);
    350     if (status == MSI_NULL_INTEGER){
    351         PyErr_SetString(MSIError, "could not convert record field to integer");
    352         return NULL;
    353     }
    354     return PyInt_FromLong((long) status);
    355 }
    356 
    357 static PyObject*
    358 record_getstring(msiobj* record, PyObject* args)
    359 {
    360     unsigned int field;
    361     unsigned int status;
    362     char buf[2000];
    363     char *res = buf;
    364     DWORD size = sizeof(buf);
    365     PyObject* string;
    366 
    367     if (!PyArg_ParseTuple(args, "I:GetString", &field))
    368         return NULL;
    369     status = MsiRecordGetString(record->h, field, res, &size);
    370     if (status == ERROR_MORE_DATA) {
    371         res = (char*) malloc(size + 1);
    372         if (res == NULL)
    373             return PyErr_NoMemory();
    374         status = MsiRecordGetString(record->h, field, res, &size);
    375     }
    376     if (status != ERROR_SUCCESS)
    377         return msierror((int) status);
    378     string = PyString_FromString(res);
    379     if (buf != res)
    380         free(res);
    381     return string;
    382 }
    383 
    384 static PyObject*
    385 record_cleardata(msiobj* record, PyObject *args)
    386 {
    387     int status = MsiRecordClearData(record->h);
    388     if (status != ERROR_SUCCESS)
    389         return msierror(status);
    390 
    391     Py_INCREF(Py_None);
    392     return Py_None;
    393 }
    394 
    395 static PyObject*
    396 record_setstring(msiobj* record, PyObject *args)
    397 {
    398     int status;
    399     int field;
    400     char *data;
    401 
    402     if (!PyArg_ParseTuple(args, "is:SetString", &field, &data))
    403         return NULL;
    404 
    405     if ((status = MsiRecordSetString(record->h, field, data)) != ERROR_SUCCESS)
    406         return msierror(status);
    407 
    408     Py_INCREF(Py_None);
    409     return Py_None;
    410 }
    411 
    412 static PyObject*
    413 record_setstream(msiobj* record, PyObject *args)
    414 {
    415     int status;
    416     int field;
    417     char *data;
    418 
    419     if (!PyArg_ParseTuple(args, "is:SetStream", &field, &data))
    420         return NULL;
    421 
    422     if ((status = MsiRecordSetStream(record->h, field, data)) != ERROR_SUCCESS)
    423         return msierror(status);
    424 
    425     Py_INCREF(Py_None);
    426     return Py_None;
    427 }
    428 
    429 static PyObject*
    430 record_setinteger(msiobj* record, PyObject *args)
    431 {
    432     int status;
    433     int field;
    434     int data;
    435 
    436     if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data))
    437         return NULL;
    438 
    439     if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS)
    440         return msierror(status);
    441 
    442     Py_INCREF(Py_None);
    443     return Py_None;
    444 }
    445 
    446 
    447 
    448 static PyMethodDef record_methods[] = {
    449     { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS,
    450         PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")},
    451     { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS,
    452     PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")},
    453     { "GetString", (PyCFunction)record_getstring, METH_VARARGS,
    454     PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")},
    455     { "SetString", (PyCFunction)record_setstring, METH_VARARGS,
    456         PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")},
    457     { "SetStream", (PyCFunction)record_setstream, METH_VARARGS,
    458         PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")},
    459     { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS,
    460         PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")},
    461     { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS,
    462         PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")},
    463     { NULL, NULL }
    464 };
    465 
    466 static PyTypeObject record_Type = {
    467         PyVarObject_HEAD_INIT(NULL, 0)
    468         "_msi.Record",          /*tp_name*/
    469         sizeof(msiobj), /*tp_basicsize*/
    470         0,                      /*tp_itemsize*/
    471         /* methods */
    472         (destructor)msiobj_dealloc, /*tp_dealloc*/
    473         0,                      /*tp_print*/
    474         0,                      /*tp_getattr*/
    475         0,                      /*tp_setattr*/
    476         0,                      /*tp_compare*/
    477         0,                      /*tp_repr*/
    478         0,                      /*tp_as_number*/
    479         0,                      /*tp_as_sequence*/
    480         0,                      /*tp_as_mapping*/
    481         0,                      /*tp_hash*/
    482         0,                      /*tp_call*/
    483         0,                      /*tp_str*/
    484         PyObject_GenericGetAttr,/*tp_getattro*/
    485         PyObject_GenericSetAttr,/*tp_setattro*/
    486         0,                      /*tp_as_buffer*/
    487         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    488         0,                      /*tp_doc*/
    489         0,                      /*tp_traverse*/
    490         0,                      /*tp_clear*/
    491         0,                      /*tp_richcompare*/
    492         0,                      /*tp_weaklistoffset*/
    493         0,                      /*tp_iter*/
    494         0,                      /*tp_iternext*/
    495         record_methods,           /*tp_methods*/
    496         0,                      /*tp_members*/
    497         0,                      /*tp_getset*/
    498         0,                      /*tp_base*/
    499         0,                      /*tp_dict*/
    500         0,                      /*tp_descr_get*/
    501         0,                      /*tp_descr_set*/
    502         0,                      /*tp_dictoffset*/
    503         0,                      /*tp_init*/
    504         0,                      /*tp_alloc*/
    505         0,                      /*tp_new*/
    506         0,                      /*tp_free*/
    507         0,                      /*tp_is_gc*/
    508 };
    509 
    510 static PyObject*
    511 record_new(MSIHANDLE h)
    512 {
    513     msiobj *result = PyObject_NEW(struct msiobj, &record_Type);
    514 
    515     if (!result) {
    516         MsiCloseHandle(h);
    517         return NULL;
    518     }
    519 
    520     result->h = h;
    521     return (PyObject*)result;
    522 }
    523 
    524 /*************************** SummaryInformation objects **************/
    525 
    526 static PyObject*
    527 summary_getproperty(msiobj* si, PyObject *args)
    528 {
    529     int status;
    530     int field;
    531     PyObject *result;
    532     UINT type;
    533     INT ival;
    534     FILETIME fval;
    535     char sbuf[1000];
    536     char *sval = sbuf;
    537     DWORD ssize = sizeof(sval);
    538 
    539     if (!PyArg_ParseTuple(args, "i:GetProperty", &field))
    540         return NULL;
    541 
    542     status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
    543         &fval, sval, &ssize);
    544     if (status == ERROR_MORE_DATA) {
    545         sval = malloc(ssize);
    546         status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
    547             &fval, sval, &ssize);
    548     }
    549 
    550     switch(type) {
    551         case VT_I2: case VT_I4:
    552             return PyInt_FromLong(ival);
    553         case VT_FILETIME:
    554             PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
    555             return NULL;
    556         case VT_LPSTR:
    557             result = PyString_FromStringAndSize(sval, ssize);
    558             if (sval != sbuf)
    559                 free(sval);
    560             return result;
    561     }
    562     PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
    563     return NULL;
    564 }
    565 
    566 static PyObject*
    567 summary_getpropertycount(msiobj* si, PyObject *args)
    568 {
    569     int status;
    570     UINT result;
    571 
    572     status = MsiSummaryInfoGetPropertyCount(si->h, &result);
    573     if (status != ERROR_SUCCESS)
    574         return msierror(status);
    575 
    576     return PyInt_FromLong(result);
    577 }
    578 
    579 static PyObject*
    580 summary_setproperty(msiobj* si, PyObject *args)
    581 {
    582     int status;
    583     int field;
    584     PyObject* data;
    585 
    586     if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data))
    587         return NULL;
    588 
    589     if (PyString_Check(data)) {
    590         status = MsiSummaryInfoSetProperty(si->h, field, VT_LPSTR,
    591             0, NULL, PyString_AsString(data));
    592     } else if (PyInt_Check(data)) {
    593         status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
    594             PyInt_AsLong(data), NULL, NULL);
    595     } else {
    596         PyErr_SetString(PyExc_TypeError, "unsupported type");
    597         return NULL;
    598     }
    599 
    600     if (status != ERROR_SUCCESS)
    601         return msierror(status);
    602 
    603     Py_INCREF(Py_None);
    604     return Py_None;
    605 }
    606 
    607 
    608 static PyObject*
    609 summary_persist(msiobj* si, PyObject *args)
    610 {
    611     int status;
    612 
    613     status = MsiSummaryInfoPersist(si->h);
    614     if (status != ERROR_SUCCESS)
    615         return msierror(status);
    616     Py_INCREF(Py_None);
    617     return Py_None;
    618 }
    619 
    620 static PyMethodDef summary_methods[] = {
    621     { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
    622         PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
    623     { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
    624         PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
    625     { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
    626         PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
    627     { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
    628         PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
    629     { NULL, NULL }
    630 };
    631 
    632 static PyTypeObject summary_Type = {
    633         PyVarObject_HEAD_INIT(NULL, 0)
    634         "_msi.SummaryInformation",              /*tp_name*/
    635         sizeof(msiobj), /*tp_basicsize*/
    636         0,                      /*tp_itemsize*/
    637         /* methods */
    638         (destructor)msiobj_dealloc, /*tp_dealloc*/
    639         0,                      /*tp_print*/
    640         0,                      /*tp_getattr*/
    641         0,                      /*tp_setattr*/
    642         0,                      /*tp_compare*/
    643         0,                      /*tp_repr*/
    644         0,                      /*tp_as_number*/
    645         0,                      /*tp_as_sequence*/
    646         0,                      /*tp_as_mapping*/
    647         0,                      /*tp_hash*/
    648         0,                      /*tp_call*/
    649         0,                      /*tp_str*/
    650         PyObject_GenericGetAttr,/*tp_getattro*/
    651         PyObject_GenericSetAttr,/*tp_setattro*/
    652         0,                      /*tp_as_buffer*/
    653         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    654         0,                      /*tp_doc*/
    655         0,                      /*tp_traverse*/
    656         0,                      /*tp_clear*/
    657         0,                      /*tp_richcompare*/
    658         0,                      /*tp_weaklistoffset*/
    659         0,                      /*tp_iter*/
    660         0,                      /*tp_iternext*/
    661         summary_methods,        /*tp_methods*/
    662         0,                      /*tp_members*/
    663         0,                      /*tp_getset*/
    664         0,                      /*tp_base*/
    665         0,                      /*tp_dict*/
    666         0,                      /*tp_descr_get*/
    667         0,                      /*tp_descr_set*/
    668         0,                      /*tp_dictoffset*/
    669         0,                      /*tp_init*/
    670         0,                      /*tp_alloc*/
    671         0,                      /*tp_new*/
    672         0,                      /*tp_free*/
    673         0,                      /*tp_is_gc*/
    674 };
    675 
    676 /*************************** View objects **************/
    677 
    678 static PyObject*
    679 view_execute(msiobj *view, PyObject*args)
    680 {
    681     int status;
    682     MSIHANDLE params = 0;
    683     PyObject *oparams = Py_None;
    684 
    685     if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
    686         return NULL;
    687 
    688     if (oparams != Py_None) {
    689         if (oparams->ob_type != &record_Type) {
    690             PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
    691             return NULL;
    692         }
    693         params = ((msiobj*)oparams)->h;
    694     }
    695 
    696     status = MsiViewExecute(view->h, params);
    697     if (status != ERROR_SUCCESS)
    698         return msierror(status);
    699 
    700     Py_INCREF(Py_None);
    701     return Py_None;
    702 }
    703 
    704 static PyObject*
    705 view_fetch(msiobj *view, PyObject*args)
    706 {
    707     int status;
    708     MSIHANDLE result;
    709 
    710     if ((status = MsiViewFetch(view->h, &result)) != ERROR_SUCCESS)
    711         return msierror(status);
    712 
    713     return record_new(result);
    714 }
    715 
    716 static PyObject*
    717 view_getcolumninfo(msiobj *view, PyObject *args)
    718 {
    719     int status;
    720     int kind;
    721     MSIHANDLE result;
    722 
    723     if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
    724         return NULL;
    725 
    726     if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
    727         return msierror(status);
    728 
    729     return record_new(result);
    730 }
    731 
    732 static PyObject*
    733 view_modify(msiobj *view, PyObject *args)
    734 {
    735     int kind;
    736     PyObject *data;
    737     int status;
    738 
    739     if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
    740         return NULL;
    741 
    742     if (data->ob_type != &record_Type) {
    743         PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
    744         return NULL;
    745     }
    746 
    747     if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
    748         return msierror(status);
    749 
    750     Py_INCREF(Py_None);
    751     return Py_None;
    752 }
    753 
    754 static PyObject*
    755 view_close(msiobj *view, PyObject*args)
    756 {
    757     int status;
    758 
    759     if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
    760         return msierror(status);
    761 
    762     Py_INCREF(Py_None);
    763     return Py_None;
    764 }
    765 
    766 static PyMethodDef view_methods[] = {
    767     { "Execute", (PyCFunction)view_execute, METH_VARARGS,
    768         PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
    769     { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
    770         PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
    771     { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
    772         PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
    773     { "Modify", (PyCFunction)view_modify, METH_VARARGS,
    774         PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
    775     { "Close", (PyCFunction)view_close, METH_NOARGS,
    776         PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
    777     { NULL, NULL }
    778 };
    779 
    780 static PyTypeObject msiview_Type = {
    781         PyVarObject_HEAD_INIT(NULL, 0)
    782         "_msi.View",            /*tp_name*/
    783         sizeof(msiobj), /*tp_basicsize*/
    784         0,                      /*tp_itemsize*/
    785         /* methods */
    786         (destructor)msiobj_dealloc, /*tp_dealloc*/
    787         0,                      /*tp_print*/
    788         0,                      /*tp_getattr*/
    789         0,                      /*tp_setattr*/
    790         0,                      /*tp_compare*/
    791         0,                      /*tp_repr*/
    792         0,                      /*tp_as_number*/
    793         0,                      /*tp_as_sequence*/
    794         0,                      /*tp_as_mapping*/
    795         0,                      /*tp_hash*/
    796         0,                      /*tp_call*/
    797         0,                      /*tp_str*/
    798         PyObject_GenericGetAttr,/*tp_getattro*/
    799         PyObject_GenericSetAttr,/*tp_setattro*/
    800         0,                      /*tp_as_buffer*/
    801         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    802         0,                      /*tp_doc*/
    803         0,                      /*tp_traverse*/
    804         0,                      /*tp_clear*/
    805         0,                      /*tp_richcompare*/
    806         0,                      /*tp_weaklistoffset*/
    807         0,                      /*tp_iter*/
    808         0,                      /*tp_iternext*/
    809         view_methods,           /*tp_methods*/
    810         0,                      /*tp_members*/
    811         0,                      /*tp_getset*/
    812         0,                      /*tp_base*/
    813         0,                      /*tp_dict*/
    814         0,                      /*tp_descr_get*/
    815         0,                      /*tp_descr_set*/
    816         0,                      /*tp_dictoffset*/
    817         0,                      /*tp_init*/
    818         0,                      /*tp_alloc*/
    819         0,                      /*tp_new*/
    820         0,                      /*tp_free*/
    821         0,                      /*tp_is_gc*/
    822 };
    823 
    824 /*************************** Database objects **************/
    825 
    826 static PyObject*
    827 msidb_openview(msiobj *msidb, PyObject *args)
    828 {
    829     int status;
    830     char *sql;
    831     MSIHANDLE hView;
    832     msiobj *result;
    833 
    834     if (!PyArg_ParseTuple(args, "s:OpenView", &sql))
    835         return NULL;
    836 
    837     if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS)
    838         return msierror(status);
    839 
    840     result = PyObject_NEW(struct msiobj, &msiview_Type);
    841     if (!result) {
    842         MsiCloseHandle(hView);
    843         return NULL;
    844     }
    845 
    846     result->h = hView;
    847     return (PyObject*)result;
    848 }
    849 
    850 static PyObject*
    851 msidb_commit(msiobj *msidb, PyObject *args)
    852 {
    853     int status;
    854 
    855     if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
    856         return msierror(status);
    857 
    858     Py_INCREF(Py_None);
    859     return Py_None;
    860 }
    861 
    862 static PyObject*
    863 msidb_getsummaryinformation(msiobj *db, PyObject *args)
    864 {
    865     int status;
    866     int count;
    867     MSIHANDLE result;
    868     msiobj *oresult;
    869 
    870     if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
    871         return NULL;
    872 
    873     status = MsiGetSummaryInformation(db->h, NULL, count, &result);
    874     if (status != ERROR_SUCCESS)
    875         return msierror(status);
    876 
    877     oresult = PyObject_NEW(struct msiobj, &summary_Type);
    878     if (!result) {
    879         MsiCloseHandle(result);
    880         return NULL;
    881     }
    882 
    883     oresult->h = result;
    884     return (PyObject*)oresult;
    885 }
    886 
    887 static PyMethodDef db_methods[] = {
    888     { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
    889         PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
    890     { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
    891         PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
    892     { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
    893         PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
    894     { NULL, NULL }
    895 };
    896 
    897 static PyTypeObject msidb_Type = {
    898         PyVarObject_HEAD_INIT(NULL, 0)
    899         "_msi.Database",                /*tp_name*/
    900         sizeof(msiobj), /*tp_basicsize*/
    901         0,                      /*tp_itemsize*/
    902         /* methods */
    903         (destructor)msiobj_dealloc, /*tp_dealloc*/
    904         0,                      /*tp_print*/
    905         0,                      /*tp_getattr*/
    906         0,                      /*tp_setattr*/
    907         0,                      /*tp_compare*/
    908         0,                      /*tp_repr*/
    909         0,                      /*tp_as_number*/
    910         0,                      /*tp_as_sequence*/
    911         0,                      /*tp_as_mapping*/
    912         0,                      /*tp_hash*/
    913         0,                      /*tp_call*/
    914         0,                      /*tp_str*/
    915         PyObject_GenericGetAttr,/*tp_getattro*/
    916         PyObject_GenericSetAttr,/*tp_setattro*/
    917         0,                      /*tp_as_buffer*/
    918         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    919         0,                      /*tp_doc*/
    920         0,                      /*tp_traverse*/
    921         0,                      /*tp_clear*/
    922         0,                      /*tp_richcompare*/
    923         0,                      /*tp_weaklistoffset*/
    924         0,                      /*tp_iter*/
    925         0,                      /*tp_iternext*/
    926         db_methods,             /*tp_methods*/
    927         0,                      /*tp_members*/
    928         0,                      /*tp_getset*/
    929         0,                      /*tp_base*/
    930         0,                      /*tp_dict*/
    931         0,                      /*tp_descr_get*/
    932         0,                      /*tp_descr_set*/
    933         0,                      /*tp_dictoffset*/
    934         0,                      /*tp_init*/
    935         0,                      /*tp_alloc*/
    936         0,                      /*tp_new*/
    937         0,                      /*tp_free*/
    938         0,                      /*tp_is_gc*/
    939 };
    940 
    941 #define Py_NOT_PERSIST(x, flag)                        \
    942     (x != (int)(flag) &&                      \
    943     x != ((int)(flag) | MSIDBOPEN_PATCHFILE))
    944 
    945 #define Py_INVALID_PERSIST(x)                \
    946     (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) &&  \
    947     Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) &&   \
    948     Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) &&     \
    949     Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) &&     \
    950     Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
    951 
    952 static PyObject* msiopendb(PyObject *obj, PyObject *args)
    953 {
    954     int status;
    955     char *path;
    956     int persist;
    957     MSIHANDLE h;
    958     msiobj *result;
    959     if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist))
    960         return NULL;
    961     /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
    962        MsiOpenDatabase may treat the value as a pointer, leading to unexpected
    963        behavior. */
    964     if (Py_INVALID_PERSIST(persist))
    965         return msierror(ERROR_INVALID_PARAMETER);
    966     status = MsiOpenDatabase(path, (LPCSTR)persist, &h);
    967     if (status != ERROR_SUCCESS)
    968         return msierror(status);
    969 
    970     result = PyObject_NEW(struct msiobj, &msidb_Type);
    971     if (!result) {
    972         MsiCloseHandle(h);
    973         return NULL;
    974     }
    975     result->h = h;
    976     return (PyObject*)result;
    977 }
    978 
    979 static PyObject*
    980 createrecord(PyObject *o, PyObject *args)
    981 {
    982     int count;
    983     MSIHANDLE h;
    984 
    985     if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
    986         return NULL;
    987 
    988     h = MsiCreateRecord(count);
    989     if (h == 0)
    990         return msierror(0);
    991 
    992     return record_new(h);
    993 }
    994 
    995 
    996 static PyMethodDef msi_methods[] = {
    997         {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
    998                 PyDoc_STR("UuidCreate() -> string")},
    999         {"FCICreate",   (PyCFunction)fcicreate, METH_VARARGS,
   1000                 PyDoc_STR("fcicreate(cabname,files) -> None")},
   1001         {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
   1002         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
   1003         {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
   1004         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
   1005         {NULL,          NULL}           /* sentinel */
   1006 };
   1007 
   1008 static char msi_doc[] = "Documentation";
   1009 
   1010 PyMODINIT_FUNC
   1011 init_msi(void)
   1012 {
   1013     PyObject *m;
   1014 
   1015     m = Py_InitModule3("_msi", msi_methods, msi_doc);
   1016     if (m == NULL)
   1017         return;
   1018 
   1019     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (int)MSIDBOPEN_CREATEDIRECT);
   1020     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (int)MSIDBOPEN_CREATE);
   1021     PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (int)MSIDBOPEN_DIRECT);
   1022     PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (int)MSIDBOPEN_READONLY);
   1023     PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (int)MSIDBOPEN_TRANSACT);
   1024     PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (int)MSIDBOPEN_PATCHFILE);
   1025 
   1026     PyModule_AddIntConstant(m, "MSICOLINFO_NAMES", MSICOLINFO_NAMES);
   1027     PyModule_AddIntConstant(m, "MSICOLINFO_TYPES", MSICOLINFO_TYPES);
   1028 
   1029     PyModule_AddIntConstant(m, "MSIMODIFY_SEEK", MSIMODIFY_SEEK);
   1030     PyModule_AddIntConstant(m, "MSIMODIFY_REFRESH", MSIMODIFY_REFRESH);
   1031     PyModule_AddIntConstant(m, "MSIMODIFY_INSERT", MSIMODIFY_INSERT);
   1032     PyModule_AddIntConstant(m, "MSIMODIFY_UPDATE", MSIMODIFY_UPDATE);
   1033     PyModule_AddIntConstant(m, "MSIMODIFY_ASSIGN", MSIMODIFY_ASSIGN);
   1034     PyModule_AddIntConstant(m, "MSIMODIFY_REPLACE", MSIMODIFY_REPLACE);
   1035     PyModule_AddIntConstant(m, "MSIMODIFY_MERGE", MSIMODIFY_MERGE);
   1036     PyModule_AddIntConstant(m, "MSIMODIFY_DELETE", MSIMODIFY_DELETE);
   1037     PyModule_AddIntConstant(m, "MSIMODIFY_INSERT_TEMPORARY", MSIMODIFY_INSERT_TEMPORARY);
   1038     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE", MSIMODIFY_VALIDATE);
   1039     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_NEW", MSIMODIFY_VALIDATE_NEW);
   1040     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_FIELD", MSIMODIFY_VALIDATE_FIELD);
   1041     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_DELETE", MSIMODIFY_VALIDATE_DELETE);
   1042 
   1043     PyModule_AddIntConstant(m, "PID_CODEPAGE", PID_CODEPAGE);
   1044     PyModule_AddIntConstant(m, "PID_TITLE", PID_TITLE);
   1045     PyModule_AddIntConstant(m, "PID_SUBJECT", PID_SUBJECT);
   1046     PyModule_AddIntConstant(m, "PID_AUTHOR", PID_AUTHOR);
   1047     PyModule_AddIntConstant(m, "PID_KEYWORDS", PID_KEYWORDS);
   1048     PyModule_AddIntConstant(m, "PID_COMMENTS", PID_COMMENTS);
   1049     PyModule_AddIntConstant(m, "PID_TEMPLATE", PID_TEMPLATE);
   1050     PyModule_AddIntConstant(m, "PID_LASTAUTHOR", PID_LASTAUTHOR);
   1051     PyModule_AddIntConstant(m, "PID_REVNUMBER", PID_REVNUMBER);
   1052     PyModule_AddIntConstant(m, "PID_LASTPRINTED", PID_LASTPRINTED);
   1053     PyModule_AddIntConstant(m, "PID_CREATE_DTM", PID_CREATE_DTM);
   1054     PyModule_AddIntConstant(m, "PID_LASTSAVE_DTM", PID_LASTSAVE_DTM);
   1055     PyModule_AddIntConstant(m, "PID_PAGECOUNT", PID_PAGECOUNT);
   1056     PyModule_AddIntConstant(m, "PID_WORDCOUNT", PID_WORDCOUNT);
   1057     PyModule_AddIntConstant(m, "PID_CHARCOUNT", PID_CHARCOUNT);
   1058     PyModule_AddIntConstant(m, "PID_APPNAME", PID_APPNAME);
   1059     PyModule_AddIntConstant(m, "PID_SECURITY", PID_SECURITY);
   1060 
   1061     MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
   1062     if (!MSIError)
   1063         return;
   1064     PyModule_AddObject(m, "MSIError", MSIError);
   1065 }
   1066