1 /* Helper library for MSI creation with Python.
2  * Copyright (C) 2005 Martin v. Löwis
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*
uuidcreate(PyObject * obj,PyObject * args)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 
FNFCIALLOC(cb_alloc)46 static FNFCIALLOC(cb_alloc)
47 {
48     return malloc(cb);
49 }
50 
FNFCIFREE(cb_free)51 static FNFCIFREE(cb_free)
52 {
53     free(memory);
54 }
55 
FNFCIOPEN(cb_open)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 
FNFCIREAD(cb_read)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 
FNFCIWRITE(cb_write)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 
FNFCICLOSE(cb_close)80 static FNFCICLOSE(cb_close)
81 {
82     int result = _close((int)hf);
83     if (result != 0)
84         *err = errno;
85     return result;
86 }
87 
FNFCISEEK(cb_seek)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 
FNFCIDELETE(cb_delete)96 static FNFCIDELETE(cb_delete)
97 {
98     int result = remove(pszFile);
99     if (result != 0)
100         *err = errno;
101     return result;
102 }
103 
FNFCIFILEPLACED(cb_fileplaced)104 static FNFCIFILEPLACED(cb_fileplaced)
105 {
106     return 0;
107 }
108 
FNFCIGETTEMPFILE(cb_gettempfile)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 
FNFCISTATUS(cb_status)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 
FNFCIGETNEXTCABINET(cb_getnextcabinet)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 
FNFCIGETOPENINFO(cb_getopeninfo)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 
fcicreate(PyObject * obj,PyObject * args)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
msiobj_dealloc(msiobj * msidb)282 msiobj_dealloc(msiobj* msidb)
283 {
284     MsiCloseHandle(msidb->h);
285     msidb->h = 0;
286     PyObject_Del(msidb);
287 }
288 
289 static PyObject*
msierror(int status)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*
msidb_close(msiobj * msidb,PyObject * args)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*
record_getfieldcount(msiobj * record,PyObject * args)361 record_getfieldcount(msiobj* record, PyObject* args)
362 {
363     return PyLong_FromLong(MsiRecordGetFieldCount(record->h));
364 }
365 
366 static PyObject*
record_getinteger(msiobj * record,PyObject * args)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*
record_getstring(msiobj * record,PyObject * args)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*
record_cleardata(msiobj * record,PyObject * args)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*
record_setstring(msiobj * record,PyObject * args)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*
record_setstream(msiobj * record,PyObject * args)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*
record_setinteger(msiobj * record,PyObject * args)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_vectorcall_offset*/
495         0,                      /*tp_getattr*/
496         0,                      /*tp_setattr*/
497         0,                      /*tp_as_async*/
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*
record_new(MSIHANDLE h)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*
summary_getproperty(msiobj * si,PyObject * args)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     if (status != ERROR_SUCCESS) {
575         return msierror(status);
576     }
577 
578     switch(type) {
579         case VT_I2:
580         case VT_I4:
581             result = PyLong_FromLong(ival);
582             break;
583         case VT_FILETIME:
584             PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
585             result = NULL;
586             break;
587         case VT_LPSTR:
588             result = PyBytes_FromStringAndSize(sval, ssize);
589             break;
590         case VT_EMPTY:
591             Py_INCREF(Py_None);
592             result = Py_None;
593             break;
594         default:
595             PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
596             result = NULL;
597             break;
598     }
599     if (sval != sbuf)
600         free(sval);
601     return result;
602 }
603 
604 static PyObject*
summary_getpropertycount(msiobj * si,PyObject * args)605 summary_getpropertycount(msiobj* si, PyObject *args)
606 {
607     int status;
608     UINT result;
609 
610     status = MsiSummaryInfoGetPropertyCount(si->h, &result);
611     if (status != ERROR_SUCCESS)
612         return msierror(status);
613 
614     return PyLong_FromLong(result);
615 }
616 
617 static PyObject*
summary_setproperty(msiobj * si,PyObject * args)618 summary_setproperty(msiobj* si, PyObject *args)
619 {
620     int status;
621     int field;
622     PyObject* data;
623 
624     if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data))
625         return NULL;
626 
627     if (PyUnicode_Check(data)) {
628 _Py_COMP_DIAG_PUSH
629 _Py_COMP_DIAG_IGNORE_DEPR_DECLS
630         const WCHAR *value = _PyUnicode_AsUnicode(data);
631 _Py_COMP_DIAG_POP
632         if (value == NULL) {
633             return NULL;
634         }
635         status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR,
636             0, NULL, value);
637     } else if (PyLong_CheckExact(data)) {
638         long value = PyLong_AsLong(data);
639         if (value == -1 && PyErr_Occurred()) {
640             return NULL;
641         }
642         status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
643             value, NULL, NULL);
644     } else {
645         PyErr_SetString(PyExc_TypeError, "unsupported type");
646         return NULL;
647     }
648 
649     if (status != ERROR_SUCCESS)
650         return msierror(status);
651 
652     Py_RETURN_NONE;
653 }
654 
655 
656 static PyObject*
summary_persist(msiobj * si,PyObject * args)657 summary_persist(msiobj* si, PyObject *args)
658 {
659     int status;
660 
661     status = MsiSummaryInfoPersist(si->h);
662     if (status != ERROR_SUCCESS)
663         return msierror(status);
664     Py_RETURN_NONE;
665 }
666 
667 static PyMethodDef summary_methods[] = {
668     { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
669         PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
670     { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
671         PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
672     { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
673         PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
674     { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
675         PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
676     { NULL, NULL }
677 };
678 
679 static PyTypeObject summary_Type = {
680         PyVarObject_HEAD_INIT(NULL, 0)
681         "_msi.SummaryInformation",              /*tp_name*/
682         sizeof(msiobj), /*tp_basicsize*/
683         0,                      /*tp_itemsize*/
684         /* methods */
685         (destructor)msiobj_dealloc, /*tp_dealloc*/
686         0,                      /*tp_vectorcall_offset*/
687         0,                      /*tp_getattr*/
688         0,                      /*tp_setattr*/
689         0,                      /*tp_as_async*/
690         0,                      /*tp_repr*/
691         0,                      /*tp_as_number*/
692         0,                      /*tp_as_sequence*/
693         0,                      /*tp_as_mapping*/
694         0,                      /*tp_hash*/
695         0,                      /*tp_call*/
696         0,                      /*tp_str*/
697         PyObject_GenericGetAttr,/*tp_getattro*/
698         PyObject_GenericSetAttr,/*tp_setattro*/
699         0,                      /*tp_as_buffer*/
700         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
701         0,                      /*tp_doc*/
702         0,                      /*tp_traverse*/
703         0,                      /*tp_clear*/
704         0,                      /*tp_richcompare*/
705         0,                      /*tp_weaklistoffset*/
706         0,                      /*tp_iter*/
707         0,                      /*tp_iternext*/
708         summary_methods,        /*tp_methods*/
709         0,                      /*tp_members*/
710         0,                      /*tp_getset*/
711         0,                      /*tp_base*/
712         0,                      /*tp_dict*/
713         0,                      /*tp_descr_get*/
714         0,                      /*tp_descr_set*/
715         0,                      /*tp_dictoffset*/
716         0,                      /*tp_init*/
717         0,                      /*tp_alloc*/
718         0,                      /*tp_new*/
719         0,                      /*tp_free*/
720         0,                      /*tp_is_gc*/
721 };
722 
723 /*************************** View objects **************/
724 
725 static PyObject*
view_execute(msiobj * view,PyObject * args)726 view_execute(msiobj *view, PyObject*args)
727 {
728     int status;
729     MSIHANDLE params = 0;
730     PyObject *oparams = Py_None;
731 
732     if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
733         return NULL;
734 
735     if (oparams != Py_None) {
736         if (oparams->ob_type != &record_Type) {
737             PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
738             return NULL;
739         }
740         params = ((msiobj*)oparams)->h;
741     }
742 
743     status = MsiViewExecute(view->h, params);
744     if (status != ERROR_SUCCESS)
745         return msierror(status);
746 
747     Py_RETURN_NONE;
748 }
749 
750 static PyObject*
view_fetch(msiobj * view,PyObject * args)751 view_fetch(msiobj *view, PyObject*args)
752 {
753     int status;
754     MSIHANDLE result;
755 
756     status = MsiViewFetch(view->h, &result);
757     if (status == ERROR_NO_MORE_ITEMS) {
758         Py_RETURN_NONE;
759     } else if (status != ERROR_SUCCESS) {
760         return msierror(status);
761     }
762 
763     return record_new(result);
764 }
765 
766 static PyObject*
view_getcolumninfo(msiobj * view,PyObject * args)767 view_getcolumninfo(msiobj *view, PyObject *args)
768 {
769     int status;
770     int kind;
771     MSIHANDLE result;
772 
773     if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
774         return NULL;
775 
776     if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
777         return msierror(status);
778 
779     return record_new(result);
780 }
781 
782 static PyObject*
view_modify(msiobj * view,PyObject * args)783 view_modify(msiobj *view, PyObject *args)
784 {
785     int kind;
786     PyObject *data;
787     int status;
788 
789     if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
790         return NULL;
791 
792     if (data->ob_type != &record_Type) {
793         PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
794         return NULL;
795     }
796 
797     if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
798         return msierror(status);
799 
800     Py_RETURN_NONE;
801 }
802 
803 static PyObject*
view_close(msiobj * view,PyObject * args)804 view_close(msiobj *view, PyObject*args)
805 {
806     int status;
807 
808     if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
809         return msierror(status);
810 
811     Py_RETURN_NONE;
812 }
813 
814 static PyMethodDef view_methods[] = {
815     { "Execute", (PyCFunction)view_execute, METH_VARARGS,
816         PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
817     { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
818         PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
819     { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
820         PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
821     { "Modify", (PyCFunction)view_modify, METH_VARARGS,
822         PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
823     { "Close", (PyCFunction)view_close, METH_NOARGS,
824         PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
825     { NULL, NULL }
826 };
827 
828 static PyTypeObject msiview_Type = {
829         PyVarObject_HEAD_INIT(NULL, 0)
830         "_msi.View",            /*tp_name*/
831         sizeof(msiobj), /*tp_basicsize*/
832         0,                      /*tp_itemsize*/
833         /* methods */
834         (destructor)msiobj_dealloc, /*tp_dealloc*/
835         0,                      /*tp_vectorcall_offset*/
836         0,                      /*tp_getattr*/
837         0,                      /*tp_setattr*/
838         0,                      /*tp_as_async*/
839         0,                      /*tp_repr*/
840         0,                      /*tp_as_number*/
841         0,                      /*tp_as_sequence*/
842         0,                      /*tp_as_mapping*/
843         0,                      /*tp_hash*/
844         0,                      /*tp_call*/
845         0,                      /*tp_str*/
846         PyObject_GenericGetAttr,/*tp_getattro*/
847         PyObject_GenericSetAttr,/*tp_setattro*/
848         0,                      /*tp_as_buffer*/
849         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
850         0,                      /*tp_doc*/
851         0,                      /*tp_traverse*/
852         0,                      /*tp_clear*/
853         0,                      /*tp_richcompare*/
854         0,                      /*tp_weaklistoffset*/
855         0,                      /*tp_iter*/
856         0,                      /*tp_iternext*/
857         view_methods,           /*tp_methods*/
858         0,                      /*tp_members*/
859         0,                      /*tp_getset*/
860         0,                      /*tp_base*/
861         0,                      /*tp_dict*/
862         0,                      /*tp_descr_get*/
863         0,                      /*tp_descr_set*/
864         0,                      /*tp_dictoffset*/
865         0,                      /*tp_init*/
866         0,                      /*tp_alloc*/
867         0,                      /*tp_new*/
868         0,                      /*tp_free*/
869         0,                      /*tp_is_gc*/
870 };
871 
872 /*************************** Database objects **************/
873 
874 static PyObject*
msidb_openview(msiobj * msidb,PyObject * args)875 msidb_openview(msiobj *msidb, PyObject *args)
876 {
877     int status;
878     const wchar_t *sql;
879     MSIHANDLE hView;
880     msiobj *result;
881 
882     if (!PyArg_ParseTuple(args, "u:OpenView", &sql))
883         return NULL;
884 
885     if ((status = MsiDatabaseOpenViewW(msidb->h, sql, &hView)) != ERROR_SUCCESS)
886         return msierror(status);
887 
888     result = PyObject_New(struct msiobj, &msiview_Type);
889     if (!result) {
890         MsiCloseHandle(hView);
891         return NULL;
892     }
893 
894     result->h = hView;
895     return (PyObject*)result;
896 }
897 
898 static PyObject*
msidb_commit(msiobj * msidb,PyObject * args)899 msidb_commit(msiobj *msidb, PyObject *args)
900 {
901     int status;
902 
903     if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
904         return msierror(status);
905 
906     Py_RETURN_NONE;
907 }
908 
909 static PyObject*
msidb_getsummaryinformation(msiobj * db,PyObject * args)910 msidb_getsummaryinformation(msiobj *db, PyObject *args)
911 {
912     int status;
913     int count;
914     MSIHANDLE result;
915     msiobj *oresult;
916 
917     if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
918         return NULL;
919 
920     status = MsiGetSummaryInformation(db->h, NULL, count, &result);
921     if (status != ERROR_SUCCESS)
922         return msierror(status);
923 
924     oresult = PyObject_New(struct msiobj, &summary_Type);
925     if (!oresult) {
926         MsiCloseHandle(result);
927         return NULL;
928     }
929 
930     oresult->h = result;
931     return (PyObject*)oresult;
932 }
933 
934 static PyMethodDef db_methods[] = {
935     { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
936         PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
937     { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
938         PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
939     { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
940         PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
941     { "Close", (PyCFunction)msidb_close, METH_NOARGS,
942         PyDoc_STR("Close() -> None\nWraps MsiCloseHandle")},
943     { NULL, NULL }
944 };
945 
946 static PyTypeObject msidb_Type = {
947         PyVarObject_HEAD_INIT(NULL, 0)
948         "_msi.Database",                /*tp_name*/
949         sizeof(msiobj), /*tp_basicsize*/
950         0,                      /*tp_itemsize*/
951         /* methods */
952         (destructor)msiobj_dealloc, /*tp_dealloc*/
953         0,                      /*tp_vectorcall_offset*/
954         0,                      /*tp_getattr*/
955         0,                      /*tp_setattr*/
956         0,                      /*tp_as_async*/
957         0,                      /*tp_repr*/
958         0,                      /*tp_as_number*/
959         0,                      /*tp_as_sequence*/
960         0,                      /*tp_as_mapping*/
961         0,                      /*tp_hash*/
962         0,                      /*tp_call*/
963         0,                      /*tp_str*/
964         PyObject_GenericGetAttr,/*tp_getattro*/
965         PyObject_GenericSetAttr,/*tp_setattro*/
966         0,                      /*tp_as_buffer*/
967         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
968         0,                      /*tp_doc*/
969         0,                      /*tp_traverse*/
970         0,                      /*tp_clear*/
971         0,                      /*tp_richcompare*/
972         0,                      /*tp_weaklistoffset*/
973         0,                      /*tp_iter*/
974         0,                      /*tp_iternext*/
975         db_methods,             /*tp_methods*/
976         0,                      /*tp_members*/
977         0,                      /*tp_getset*/
978         0,                      /*tp_base*/
979         0,                      /*tp_dict*/
980         0,                      /*tp_descr_get*/
981         0,                      /*tp_descr_set*/
982         0,                      /*tp_dictoffset*/
983         0,                      /*tp_init*/
984         0,                      /*tp_alloc*/
985         0,                      /*tp_new*/
986         0,                      /*tp_free*/
987         0,                      /*tp_is_gc*/
988 };
989 
990 #define Py_NOT_PERSIST(x, flag)                        \
991     (x != (SIZE_T)(flag) &&                      \
992     x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
993 
994 #define Py_INVALID_PERSIST(x)                \
995     (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) &&  \
996     Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) &&   \
997     Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) &&     \
998     Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) &&     \
999     Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
1000 
msiopendb(PyObject * obj,PyObject * args)1001 static PyObject* msiopendb(PyObject *obj, PyObject *args)
1002 {
1003     int status;
1004     const wchar_t *path;
1005     int persist;
1006     MSIHANDLE h;
1007     msiobj *result;
1008     if (!PyArg_ParseTuple(args, "ui:MSIOpenDatabase", &path, &persist))
1009         return NULL;
1010     /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
1011        MsiOpenDatabase may treat the value as a pointer, leading to unexpected
1012        behavior. */
1013     if (Py_INVALID_PERSIST(persist))
1014         return msierror(ERROR_INVALID_PARAMETER);
1015     status = MsiOpenDatabaseW(path, (LPCWSTR)(SIZE_T)persist, &h);
1016     if (status != ERROR_SUCCESS)
1017         return msierror(status);
1018 
1019     result = PyObject_New(struct msiobj, &msidb_Type);
1020     if (!result) {
1021         MsiCloseHandle(h);
1022         return NULL;
1023     }
1024     result->h = h;
1025     return (PyObject*)result;
1026 }
1027 
1028 static PyObject*
createrecord(PyObject * o,PyObject * args)1029 createrecord(PyObject *o, PyObject *args)
1030 {
1031     int count;
1032     MSIHANDLE h;
1033 
1034     if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
1035         return NULL;
1036 
1037     h = MsiCreateRecord(count);
1038     if (h == 0)
1039         return msierror(0);
1040 
1041     return record_new(h);
1042 }
1043 
1044 
1045 static PyMethodDef msi_methods[] = {
1046         {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
1047                 PyDoc_STR("UuidCreate() -> string")},
1048         {"FCICreate",   (PyCFunction)fcicreate, METH_VARARGS,
1049                 PyDoc_STR("fcicreate(cabname,files) -> None")},
1050         {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
1051         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
1052         {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
1053         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
1054         {NULL,          NULL}           /* sentinel */
1055 };
1056 
1057 static char msi_doc[] = "Documentation";
1058 
1059 
1060 static struct PyModuleDef _msimodule = {
1061         PyModuleDef_HEAD_INIT,
1062         "_msi",
1063         msi_doc,
1064         -1,
1065         msi_methods,
1066         NULL,
1067         NULL,
1068         NULL,
1069         NULL
1070 };
1071 
1072 PyMODINIT_FUNC
PyInit__msi(void)1073 PyInit__msi(void)
1074 {
1075     PyObject *m;
1076 
1077     m = PyModule_Create(&_msimodule);
1078     if (m == NULL)
1079         return NULL;
1080 
1081     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (long)(SIZE_T)MSIDBOPEN_CREATEDIRECT);
1082     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (long)(SIZE_T)MSIDBOPEN_CREATE);
1083     PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (long)(SIZE_T)MSIDBOPEN_DIRECT);
1084     PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (long)(SIZE_T)MSIDBOPEN_READONLY);
1085     PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (long)(SIZE_T)MSIDBOPEN_TRANSACT);
1086     PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (long)(SIZE_T)MSIDBOPEN_PATCHFILE);
1087 
1088     PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1089     PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
1090 
1091     PyModule_AddIntMacro(m, MSIMODIFY_SEEK);
1092     PyModule_AddIntMacro(m, MSIMODIFY_REFRESH);
1093     PyModule_AddIntMacro(m, MSIMODIFY_INSERT);
1094     PyModule_AddIntMacro(m, MSIMODIFY_UPDATE);
1095     PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN);
1096     PyModule_AddIntMacro(m, MSIMODIFY_REPLACE);
1097     PyModule_AddIntMacro(m, MSIMODIFY_MERGE);
1098     PyModule_AddIntMacro(m, MSIMODIFY_DELETE);
1099     PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY);
1100     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE);
1101     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW);
1102     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD);
1103     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE);
1104 
1105     PyModule_AddIntMacro(m, PID_CODEPAGE);
1106     PyModule_AddIntMacro(m, PID_TITLE);
1107     PyModule_AddIntMacro(m, PID_SUBJECT);
1108     PyModule_AddIntMacro(m, PID_AUTHOR);
1109     PyModule_AddIntMacro(m, PID_KEYWORDS);
1110     PyModule_AddIntMacro(m, PID_COMMENTS);
1111     PyModule_AddIntMacro(m, PID_TEMPLATE);
1112     PyModule_AddIntMacro(m, PID_LASTAUTHOR);
1113     PyModule_AddIntMacro(m, PID_REVNUMBER);
1114     PyModule_AddIntMacro(m, PID_LASTPRINTED);
1115     PyModule_AddIntMacro(m, PID_CREATE_DTM);
1116     PyModule_AddIntMacro(m, PID_LASTSAVE_DTM);
1117     PyModule_AddIntMacro(m, PID_PAGECOUNT);
1118     PyModule_AddIntMacro(m, PID_WORDCOUNT);
1119     PyModule_AddIntMacro(m, PID_CHARCOUNT);
1120     PyModule_AddIntMacro(m, PID_APPNAME);
1121     PyModule_AddIntMacro(m, PID_SECURITY);
1122 
1123     MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1124     if (!MSIError)
1125         return NULL;
1126     PyModule_AddObject(m, "MSIError", MSIError);
1127     return m;
1128 }
1129