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