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     PyObject_Del(msidb);
    275 }
    276 
    277 static PyObject*
    278 msiobj_close(msiobj* msidb, PyObject *args)
    279 {
    280     MsiCloseHandle(msidb->h);
    281     msidb->h = 0;
    282     Py_INCREF(Py_None);
    283     return Py_None;
    284 }
    285 
    286 static PyObject*
    287 msierror(int status)
    288 {
    289     int code;
    290     char buf[2000];
    291     char *res = buf;
    292     DWORD size = sizeof(buf);
    293     MSIHANDLE err = MsiGetLastErrorRecord();
    294 
    295     if (err == 0) {
    296         switch(status) {
    297         case ERROR_ACCESS_DENIED:
    298             PyErr_SetString(MSIError, "access denied");
    299             return NULL;
    300         case ERROR_FUNCTION_FAILED:
    301             PyErr_SetString(MSIError, "function failed");
    302             return NULL;
    303         case ERROR_INVALID_DATA:
    304             PyErr_SetString(MSIError, "invalid data");
    305             return NULL;
    306         case ERROR_INVALID_HANDLE:
    307             PyErr_SetString(MSIError, "invalid handle");
    308             return NULL;
    309         case ERROR_INVALID_STATE:
    310             PyErr_SetString(MSIError, "invalid state");
    311             return NULL;
    312         case ERROR_INVALID_PARAMETER:
    313             PyErr_SetString(MSIError, "invalid parameter");
    314             return NULL;
    315         default:
    316             PyErr_Format(MSIError, "unknown error %x", status);
    317             return NULL;
    318         }
    319     }
    320 
    321     code = MsiRecordGetInteger(err, 1); /* XXX code */
    322     if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
    323         res = malloc(size+1);
    324         if (res == NULL) {
    325             MsiCloseHandle(err);
    326             return PyErr_NoMemory();
    327         }
    328         MsiFormatRecord(0, err, res, &size);
    329         res[size]='\0';
    330     }
    331     MsiCloseHandle(err);
    332     PyErr_SetString(MSIError, res);
    333     if (res != buf)
    334         free(res);
    335     return NULL;
    336 }
    337 
    338 /*************************** Record objects **********************/
    339 
    340 static PyObject*
    341 record_getfieldcount(msiobj* record, PyObject* args)
    342 {
    343     return PyInt_FromLong(MsiRecordGetFieldCount(record->h));
    344 }
    345 
    346 static PyObject*
    347 record_getinteger(msiobj* record, PyObject* args)
    348 {
    349     unsigned int field;
    350     int status;
    351 
    352     if (!PyArg_ParseTuple(args, "I:GetInteger", &field))
    353         return NULL;
    354     status = MsiRecordGetInteger(record->h, field);
    355     if (status == MSI_NULL_INTEGER){
    356         PyErr_SetString(MSIError, "could not convert record field to integer");
    357         return NULL;
    358     }
    359     return PyInt_FromLong((long) status);
    360 }
    361 
    362 static PyObject*
    363 record_getstring(msiobj* record, PyObject* args)
    364 {
    365     unsigned int field;
    366     unsigned int status;
    367     char buf[2000];
    368     char *res = buf;
    369     DWORD size = sizeof(buf);
    370     PyObject* string;
    371 
    372     if (!PyArg_ParseTuple(args, "I:GetString", &field))
    373         return NULL;
    374     status = MsiRecordGetString(record->h, field, res, &size);
    375     if (status == ERROR_MORE_DATA) {
    376         res = (char*) malloc(size + 1);
    377         if (res == NULL)
    378             return PyErr_NoMemory();
    379         status = MsiRecordGetString(record->h, field, res, &size);
    380     }
    381     if (status != ERROR_SUCCESS)
    382         return msierror((int) status);
    383     string = PyString_FromString(res);
    384     if (buf != res)
    385         free(res);
    386     return string;
    387 }
    388 
    389 static PyObject*
    390 record_cleardata(msiobj* record, PyObject *args)
    391 {
    392     int status = MsiRecordClearData(record->h);
    393     if (status != ERROR_SUCCESS)
    394         return msierror(status);
    395 
    396     Py_INCREF(Py_None);
    397     return Py_None;
    398 }
    399 
    400 static PyObject*
    401 record_setstring(msiobj* record, PyObject *args)
    402 {
    403     int status;
    404     int field;
    405     char *data;
    406 
    407     if (!PyArg_ParseTuple(args, "is:SetString", &field, &data))
    408         return NULL;
    409 
    410     if ((status = MsiRecordSetString(record->h, field, data)) != ERROR_SUCCESS)
    411         return msierror(status);
    412 
    413     Py_INCREF(Py_None);
    414     return Py_None;
    415 }
    416 
    417 static PyObject*
    418 record_setstream(msiobj* record, PyObject *args)
    419 {
    420     int status;
    421     int field;
    422     char *data;
    423 
    424     if (!PyArg_ParseTuple(args, "is:SetStream", &field, &data))
    425         return NULL;
    426 
    427     if ((status = MsiRecordSetStream(record->h, field, data)) != ERROR_SUCCESS)
    428         return msierror(status);
    429 
    430     Py_INCREF(Py_None);
    431     return Py_None;
    432 }
    433 
    434 static PyObject*
    435 record_setinteger(msiobj* record, PyObject *args)
    436 {
    437     int status;
    438     int field;
    439     int data;
    440 
    441     if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data))
    442         return NULL;
    443 
    444     if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS)
    445         return msierror(status);
    446 
    447     Py_INCREF(Py_None);
    448     return Py_None;
    449 }
    450 
    451 
    452 
    453 static PyMethodDef record_methods[] = {
    454     { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS,
    455         PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")},
    456     { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS,
    457     PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")},
    458     { "GetString", (PyCFunction)record_getstring, METH_VARARGS,
    459     PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")},
    460     { "SetString", (PyCFunction)record_setstring, METH_VARARGS,
    461         PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")},
    462     { "SetStream", (PyCFunction)record_setstream, METH_VARARGS,
    463         PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")},
    464     { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS,
    465         PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")},
    466     { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS,
    467         PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")},
    468     { NULL, NULL }
    469 };
    470 
    471 static PyTypeObject record_Type = {
    472         PyVarObject_HEAD_INIT(NULL, 0)
    473         "_msi.Record",          /*tp_name*/
    474         sizeof(msiobj), /*tp_basicsize*/
    475         0,                      /*tp_itemsize*/
    476         /* methods */
    477         (destructor)msiobj_dealloc, /*tp_dealloc*/
    478         0,                      /*tp_print*/
    479         0,                      /*tp_getattr*/
    480         0,                      /*tp_setattr*/
    481         0,                      /*tp_compare*/
    482         0,                      /*tp_repr*/
    483         0,                      /*tp_as_number*/
    484         0,                      /*tp_as_sequence*/
    485         0,                      /*tp_as_mapping*/
    486         0,                      /*tp_hash*/
    487         0,                      /*tp_call*/
    488         0,                      /*tp_str*/
    489         PyObject_GenericGetAttr,/*tp_getattro*/
    490         PyObject_GenericSetAttr,/*tp_setattro*/
    491         0,                      /*tp_as_buffer*/
    492         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    493         0,                      /*tp_doc*/
    494         0,                      /*tp_traverse*/
    495         0,                      /*tp_clear*/
    496         0,                      /*tp_richcompare*/
    497         0,                      /*tp_weaklistoffset*/
    498         0,                      /*tp_iter*/
    499         0,                      /*tp_iternext*/
    500         record_methods,           /*tp_methods*/
    501         0,                      /*tp_members*/
    502         0,                      /*tp_getset*/
    503         0,                      /*tp_base*/
    504         0,                      /*tp_dict*/
    505         0,                      /*tp_descr_get*/
    506         0,                      /*tp_descr_set*/
    507         0,                      /*tp_dictoffset*/
    508         0,                      /*tp_init*/
    509         0,                      /*tp_alloc*/
    510         0,                      /*tp_new*/
    511         0,                      /*tp_free*/
    512         0,                      /*tp_is_gc*/
    513 };
    514 
    515 static PyObject*
    516 record_new(MSIHANDLE h)
    517 {
    518     msiobj *result = PyObject_NEW(struct msiobj, &record_Type);
    519 
    520     if (!result) {
    521         MsiCloseHandle(h);
    522         return NULL;
    523     }
    524 
    525     result->h = h;
    526     return (PyObject*)result;
    527 }
    528 
    529 /*************************** SummaryInformation objects **************/
    530 
    531 static PyObject*
    532 summary_getproperty(msiobj* si, PyObject *args)
    533 {
    534     int status;
    535     int field;
    536     PyObject *result;
    537     UINT type;
    538     INT ival;
    539     FILETIME fval;
    540     char sbuf[1000];
    541     char *sval = sbuf;
    542     DWORD ssize = sizeof(sval);
    543 
    544     if (!PyArg_ParseTuple(args, "i:GetProperty", &field))
    545         return NULL;
    546 
    547     status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
    548         &fval, sval, &ssize);
    549     if (status == ERROR_MORE_DATA) {
    550         sval = malloc(ssize);
    551         if (sval == NULL) {
    552             return PyErr_NoMemory();
    553         }
    554         status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
    555             &fval, sval, &ssize);
    556     }
    557 
    558     switch(type) {
    559         case VT_I2: case VT_I4:
    560             return PyInt_FromLong(ival);
    561         case VT_FILETIME:
    562             PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
    563             return NULL;
    564         case VT_LPSTR:
    565             result = PyString_FromStringAndSize(sval, ssize);
    566             if (sval != sbuf)
    567                 free(sval);
    568             return result;
    569     }
    570     PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
    571     return NULL;
    572 }
    573 
    574 static PyObject*
    575 summary_getpropertycount(msiobj* si, PyObject *args)
    576 {
    577     int status;
    578     UINT result;
    579 
    580     status = MsiSummaryInfoGetPropertyCount(si->h, &result);
    581     if (status != ERROR_SUCCESS)
    582         return msierror(status);
    583 
    584     return PyInt_FromLong(result);
    585 }
    586 
    587 static PyObject*
    588 summary_setproperty(msiobj* si, PyObject *args)
    589 {
    590     int status;
    591     int field;
    592     PyObject* data;
    593 
    594     if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data))
    595         return NULL;
    596 
    597     if (PyString_Check(data)) {
    598         status = MsiSummaryInfoSetProperty(si->h, field, VT_LPSTR,
    599             0, NULL, PyString_AsString(data));
    600     } else if (PyInt_Check(data)) {
    601         status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
    602             PyInt_AsLong(data), NULL, NULL);
    603     } else {
    604         PyErr_SetString(PyExc_TypeError, "unsupported type");
    605         return NULL;
    606     }
    607 
    608     if (status != ERROR_SUCCESS)
    609         return msierror(status);
    610 
    611     Py_INCREF(Py_None);
    612     return Py_None;
    613 }
    614 
    615 
    616 static PyObject*
    617 summary_persist(msiobj* si, PyObject *args)
    618 {
    619     int status;
    620 
    621     status = MsiSummaryInfoPersist(si->h);
    622     if (status != ERROR_SUCCESS)
    623         return msierror(status);
    624     Py_INCREF(Py_None);
    625     return Py_None;
    626 }
    627 
    628 static PyMethodDef summary_methods[] = {
    629     { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
    630         PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
    631     { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
    632         PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
    633     { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
    634         PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
    635     { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
    636         PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
    637     { NULL, NULL }
    638 };
    639 
    640 static PyTypeObject summary_Type = {
    641         PyVarObject_HEAD_INIT(NULL, 0)
    642         "_msi.SummaryInformation",              /*tp_name*/
    643         sizeof(msiobj), /*tp_basicsize*/
    644         0,                      /*tp_itemsize*/
    645         /* methods */
    646         (destructor)msiobj_dealloc, /*tp_dealloc*/
    647         0,                      /*tp_print*/
    648         0,                      /*tp_getattr*/
    649         0,                      /*tp_setattr*/
    650         0,                      /*tp_compare*/
    651         0,                      /*tp_repr*/
    652         0,                      /*tp_as_number*/
    653         0,                      /*tp_as_sequence*/
    654         0,                      /*tp_as_mapping*/
    655         0,                      /*tp_hash*/
    656         0,                      /*tp_call*/
    657         0,                      /*tp_str*/
    658         PyObject_GenericGetAttr,/*tp_getattro*/
    659         PyObject_GenericSetAttr,/*tp_setattro*/
    660         0,                      /*tp_as_buffer*/
    661         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    662         0,                      /*tp_doc*/
    663         0,                      /*tp_traverse*/
    664         0,                      /*tp_clear*/
    665         0,                      /*tp_richcompare*/
    666         0,                      /*tp_weaklistoffset*/
    667         0,                      /*tp_iter*/
    668         0,                      /*tp_iternext*/
    669         summary_methods,        /*tp_methods*/
    670         0,                      /*tp_members*/
    671         0,                      /*tp_getset*/
    672         0,                      /*tp_base*/
    673         0,                      /*tp_dict*/
    674         0,                      /*tp_descr_get*/
    675         0,                      /*tp_descr_set*/
    676         0,                      /*tp_dictoffset*/
    677         0,                      /*tp_init*/
    678         0,                      /*tp_alloc*/
    679         0,                      /*tp_new*/
    680         0,                      /*tp_free*/
    681         0,                      /*tp_is_gc*/
    682 };
    683 
    684 /*************************** View objects **************/
    685 
    686 static PyObject*
    687 view_execute(msiobj *view, PyObject*args)
    688 {
    689     int status;
    690     MSIHANDLE params = 0;
    691     PyObject *oparams = Py_None;
    692 
    693     if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
    694         return NULL;
    695 
    696     if (oparams != Py_None) {
    697         if (oparams->ob_type != &record_Type) {
    698             PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
    699             return NULL;
    700         }
    701         params = ((msiobj*)oparams)->h;
    702     }
    703 
    704     status = MsiViewExecute(view->h, params);
    705     if (status != ERROR_SUCCESS)
    706         return msierror(status);
    707 
    708     Py_INCREF(Py_None);
    709     return Py_None;
    710 }
    711 
    712 static PyObject*
    713 view_fetch(msiobj *view, PyObject*args)
    714 {
    715     int status;
    716     MSIHANDLE result;
    717 
    718     if ((status = MsiViewFetch(view->h, &result)) != ERROR_SUCCESS)
    719         return msierror(status);
    720 
    721     return record_new(result);
    722 }
    723 
    724 static PyObject*
    725 view_getcolumninfo(msiobj *view, PyObject *args)
    726 {
    727     int status;
    728     int kind;
    729     MSIHANDLE result;
    730 
    731     if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
    732         return NULL;
    733 
    734     if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
    735         return msierror(status);
    736 
    737     return record_new(result);
    738 }
    739 
    740 static PyObject*
    741 view_modify(msiobj *view, PyObject *args)
    742 {
    743     int kind;
    744     PyObject *data;
    745     int status;
    746 
    747     if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
    748         return NULL;
    749 
    750     if (data->ob_type != &record_Type) {
    751         PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
    752         return NULL;
    753     }
    754 
    755     if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
    756         return msierror(status);
    757 
    758     Py_INCREF(Py_None);
    759     return Py_None;
    760 }
    761 
    762 static PyObject*
    763 view_close(msiobj *view, PyObject*args)
    764 {
    765     int status;
    766 
    767     if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
    768         return msierror(status);
    769 
    770     Py_INCREF(Py_None);
    771     return Py_None;
    772 }
    773 
    774 static PyMethodDef view_methods[] = {
    775     { "Execute", (PyCFunction)view_execute, METH_VARARGS,
    776         PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
    777     { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
    778         PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
    779     { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
    780         PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
    781     { "Modify", (PyCFunction)view_modify, METH_VARARGS,
    782         PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
    783     { "Close", (PyCFunction)view_close, METH_NOARGS,
    784         PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
    785     { NULL, NULL }
    786 };
    787 
    788 static PyTypeObject msiview_Type = {
    789         PyVarObject_HEAD_INIT(NULL, 0)
    790         "_msi.View",            /*tp_name*/
    791         sizeof(msiobj), /*tp_basicsize*/
    792         0,                      /*tp_itemsize*/
    793         /* methods */
    794         (destructor)msiobj_dealloc, /*tp_dealloc*/
    795         0,                      /*tp_print*/
    796         0,                      /*tp_getattr*/
    797         0,                      /*tp_setattr*/
    798         0,                      /*tp_compare*/
    799         0,                      /*tp_repr*/
    800         0,                      /*tp_as_number*/
    801         0,                      /*tp_as_sequence*/
    802         0,                      /*tp_as_mapping*/
    803         0,                      /*tp_hash*/
    804         0,                      /*tp_call*/
    805         0,                      /*tp_str*/
    806         PyObject_GenericGetAttr,/*tp_getattro*/
    807         PyObject_GenericSetAttr,/*tp_setattro*/
    808         0,                      /*tp_as_buffer*/
    809         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    810         0,                      /*tp_doc*/
    811         0,                      /*tp_traverse*/
    812         0,                      /*tp_clear*/
    813         0,                      /*tp_richcompare*/
    814         0,                      /*tp_weaklistoffset*/
    815         0,                      /*tp_iter*/
    816         0,                      /*tp_iternext*/
    817         view_methods,           /*tp_methods*/
    818         0,                      /*tp_members*/
    819         0,                      /*tp_getset*/
    820         0,                      /*tp_base*/
    821         0,                      /*tp_dict*/
    822         0,                      /*tp_descr_get*/
    823         0,                      /*tp_descr_set*/
    824         0,                      /*tp_dictoffset*/
    825         0,                      /*tp_init*/
    826         0,                      /*tp_alloc*/
    827         0,                      /*tp_new*/
    828         0,                      /*tp_free*/
    829         0,                      /*tp_is_gc*/
    830 };
    831 
    832 /*************************** Database objects **************/
    833 
    834 static PyObject*
    835 msidb_openview(msiobj *msidb, PyObject *args)
    836 {
    837     int status;
    838     char *sql;
    839     MSIHANDLE hView;
    840     msiobj *result;
    841 
    842     if (!PyArg_ParseTuple(args, "s:OpenView", &sql))
    843         return NULL;
    844 
    845     if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS)
    846         return msierror(status);
    847 
    848     result = PyObject_NEW(struct msiobj, &msiview_Type);
    849     if (!result) {
    850         MsiCloseHandle(hView);
    851         return NULL;
    852     }
    853 
    854     result->h = hView;
    855     return (PyObject*)result;
    856 }
    857 
    858 static PyObject*
    859 msidb_commit(msiobj *msidb, PyObject *args)
    860 {
    861     int status;
    862 
    863     if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
    864         return msierror(status);
    865 
    866     Py_INCREF(Py_None);
    867     return Py_None;
    868 }
    869 
    870 static PyObject*
    871 msidb_getsummaryinformation(msiobj *db, PyObject *args)
    872 {
    873     int status;
    874     int count;
    875     MSIHANDLE result;
    876     msiobj *oresult;
    877 
    878     if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
    879         return NULL;
    880 
    881     status = MsiGetSummaryInformation(db->h, NULL, count, &result);
    882     if (status != ERROR_SUCCESS)
    883         return msierror(status);
    884 
    885     oresult = PyObject_NEW(struct msiobj, &summary_Type);
    886     if (!result) {
    887         MsiCloseHandle(result);
    888         return NULL;
    889     }
    890 
    891     oresult->h = result;
    892     return (PyObject*)oresult;
    893 }
    894 
    895 static PyMethodDef db_methods[] = {
    896     { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
    897         PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
    898     { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
    899         PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
    900     { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
    901         PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
    902     { NULL, NULL }
    903 };
    904 
    905 static PyTypeObject msidb_Type = {
    906         PyVarObject_HEAD_INIT(NULL, 0)
    907         "_msi.Database",                /*tp_name*/
    908         sizeof(msiobj), /*tp_basicsize*/
    909         0,                      /*tp_itemsize*/
    910         /* methods */
    911         (destructor)msiobj_dealloc, /*tp_dealloc*/
    912         0,                      /*tp_print*/
    913         0,                      /*tp_getattr*/
    914         0,                      /*tp_setattr*/
    915         0,                      /*tp_compare*/
    916         0,                      /*tp_repr*/
    917         0,                      /*tp_as_number*/
    918         0,                      /*tp_as_sequence*/
    919         0,                      /*tp_as_mapping*/
    920         0,                      /*tp_hash*/
    921         0,                      /*tp_call*/
    922         0,                      /*tp_str*/
    923         PyObject_GenericGetAttr,/*tp_getattro*/
    924         PyObject_GenericSetAttr,/*tp_setattro*/
    925         0,                      /*tp_as_buffer*/
    926         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
    927         0,                      /*tp_doc*/
    928         0,                      /*tp_traverse*/
    929         0,                      /*tp_clear*/
    930         0,                      /*tp_richcompare*/
    931         0,                      /*tp_weaklistoffset*/
    932         0,                      /*tp_iter*/
    933         0,                      /*tp_iternext*/
    934         db_methods,             /*tp_methods*/
    935         0,                      /*tp_members*/
    936         0,                      /*tp_getset*/
    937         0,                      /*tp_base*/
    938         0,                      /*tp_dict*/
    939         0,                      /*tp_descr_get*/
    940         0,                      /*tp_descr_set*/
    941         0,                      /*tp_dictoffset*/
    942         0,                      /*tp_init*/
    943         0,                      /*tp_alloc*/
    944         0,                      /*tp_new*/
    945         0,                      /*tp_free*/
    946         0,                      /*tp_is_gc*/
    947 };
    948 
    949 #define Py_NOT_PERSIST(x, flag)                        \
    950     (x != (int)(flag) &&                      \
    951     x != ((int)(flag) | MSIDBOPEN_PATCHFILE))
    952 
    953 #define Py_INVALID_PERSIST(x)                \
    954     (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) &&  \
    955     Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) &&   \
    956     Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) &&     \
    957     Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) &&     \
    958     Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
    959 
    960 static PyObject* msiopendb(PyObject *obj, PyObject *args)
    961 {
    962     int status;
    963     char *path;
    964     int persist;
    965     MSIHANDLE h;
    966     msiobj *result;
    967     if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist))
    968         return NULL;
    969     /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
    970        MsiOpenDatabase may treat the value as a pointer, leading to unexpected
    971        behavior. */
    972     if (Py_INVALID_PERSIST(persist))
    973         return msierror(ERROR_INVALID_PARAMETER);
    974     status = MsiOpenDatabase(path, (LPCSTR)persist, &h);
    975     if (status != ERROR_SUCCESS)
    976         return msierror(status);
    977 
    978     result = PyObject_NEW(struct msiobj, &msidb_Type);
    979     if (!result) {
    980         MsiCloseHandle(h);
    981         return NULL;
    982     }
    983     result->h = h;
    984     return (PyObject*)result;
    985 }
    986 
    987 static PyObject*
    988 createrecord(PyObject *o, PyObject *args)
    989 {
    990     int count;
    991     MSIHANDLE h;
    992 
    993     if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
    994         return NULL;
    995 
    996     h = MsiCreateRecord(count);
    997     if (h == 0)
    998         return msierror(0);
    999 
   1000     return record_new(h);
   1001 }
   1002 
   1003 
   1004 static PyMethodDef msi_methods[] = {
   1005         {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
   1006                 PyDoc_STR("UuidCreate() -> string")},
   1007         {"FCICreate",   (PyCFunction)fcicreate, METH_VARARGS,
   1008                 PyDoc_STR("fcicreate(cabname,files) -> None")},
   1009         {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
   1010         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
   1011         {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
   1012         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
   1013         {NULL,          NULL}           /* sentinel */
   1014 };
   1015 
   1016 static char msi_doc[] = "Documentation";
   1017 
   1018 PyMODINIT_FUNC
   1019 init_msi(void)
   1020 {
   1021     PyObject *m;
   1022 
   1023     m = Py_InitModule3("_msi", msi_methods, msi_doc);
   1024     if (m == NULL)
   1025         return;
   1026 
   1027     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (int)MSIDBOPEN_CREATEDIRECT);
   1028     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (int)MSIDBOPEN_CREATE);
   1029     PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (int)MSIDBOPEN_DIRECT);
   1030     PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (int)MSIDBOPEN_READONLY);
   1031     PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (int)MSIDBOPEN_TRANSACT);
   1032     PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (int)MSIDBOPEN_PATCHFILE);
   1033 
   1034     PyModule_AddIntConstant(m, "MSICOLINFO_NAMES", MSICOLINFO_NAMES);
   1035     PyModule_AddIntConstant(m, "MSICOLINFO_TYPES", MSICOLINFO_TYPES);
   1036 
   1037     PyModule_AddIntConstant(m, "MSIMODIFY_SEEK", MSIMODIFY_SEEK);
   1038     PyModule_AddIntConstant(m, "MSIMODIFY_REFRESH", MSIMODIFY_REFRESH);
   1039     PyModule_AddIntConstant(m, "MSIMODIFY_INSERT", MSIMODIFY_INSERT);
   1040     PyModule_AddIntConstant(m, "MSIMODIFY_UPDATE", MSIMODIFY_UPDATE);
   1041     PyModule_AddIntConstant(m, "MSIMODIFY_ASSIGN", MSIMODIFY_ASSIGN);
   1042     PyModule_AddIntConstant(m, "MSIMODIFY_REPLACE", MSIMODIFY_REPLACE);
   1043     PyModule_AddIntConstant(m, "MSIMODIFY_MERGE", MSIMODIFY_MERGE);
   1044     PyModule_AddIntConstant(m, "MSIMODIFY_DELETE", MSIMODIFY_DELETE);
   1045     PyModule_AddIntConstant(m, "MSIMODIFY_INSERT_TEMPORARY", MSIMODIFY_INSERT_TEMPORARY);
   1046     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE", MSIMODIFY_VALIDATE);
   1047     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_NEW", MSIMODIFY_VALIDATE_NEW);
   1048     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_FIELD", MSIMODIFY_VALIDATE_FIELD);
   1049     PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_DELETE", MSIMODIFY_VALIDATE_DELETE);
   1050 
   1051     PyModule_AddIntConstant(m, "PID_CODEPAGE", PID_CODEPAGE);
   1052     PyModule_AddIntConstant(m, "PID_TITLE", PID_TITLE);
   1053     PyModule_AddIntConstant(m, "PID_SUBJECT", PID_SUBJECT);
   1054     PyModule_AddIntConstant(m, "PID_AUTHOR", PID_AUTHOR);
   1055     PyModule_AddIntConstant(m, "PID_KEYWORDS", PID_KEYWORDS);
   1056     PyModule_AddIntConstant(m, "PID_COMMENTS", PID_COMMENTS);
   1057     PyModule_AddIntConstant(m, "PID_TEMPLATE", PID_TEMPLATE);
   1058     PyModule_AddIntConstant(m, "PID_LASTAUTHOR", PID_LASTAUTHOR);
   1059     PyModule_AddIntConstant(m, "PID_REVNUMBER", PID_REVNUMBER);
   1060     PyModule_AddIntConstant(m, "PID_LASTPRINTED", PID_LASTPRINTED);
   1061     PyModule_AddIntConstant(m, "PID_CREATE_DTM", PID_CREATE_DTM);
   1062     PyModule_AddIntConstant(m, "PID_LASTSAVE_DTM", PID_LASTSAVE_DTM);
   1063     PyModule_AddIntConstant(m, "PID_PAGECOUNT", PID_PAGECOUNT);
   1064     PyModule_AddIntConstant(m, "PID_WORDCOUNT", PID_WORDCOUNT);
   1065     PyModule_AddIntConstant(m, "PID_CHARCOUNT", PID_CHARCOUNT);
   1066     PyModule_AddIntConstant(m, "PID_APPNAME", PID_APPNAME);
   1067     PyModule_AddIntConstant(m, "PID_SECURITY", PID_SECURITY);
   1068 
   1069     MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
   1070     if (!MSIError)
   1071         return;
   1072     PyModule_AddObject(m, "MSIError", MSIError);
   1073 }
   1074