• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         const WCHAR *value = _PyUnicode_AsUnicode(data);
629         if (value == NULL) {
630             return NULL;
631         }
632         status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR,
633             0, NULL, value);
634     } else if (PyLong_CheckExact(data)) {
635         long value = PyLong_AsLong(data);
636         if (value == -1 && PyErr_Occurred()) {
637             return NULL;
638         }
639         status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
640             value, NULL, NULL);
641     } else {
642         PyErr_SetString(PyExc_TypeError, "unsupported type");
643         return NULL;
644     }
645 
646     if (status != ERROR_SUCCESS)
647         return msierror(status);
648 
649     Py_RETURN_NONE;
650 }
651 
652 
653 static PyObject*
summary_persist(msiobj * si,PyObject * args)654 summary_persist(msiobj* si, PyObject *args)
655 {
656     int status;
657 
658     status = MsiSummaryInfoPersist(si->h);
659     if (status != ERROR_SUCCESS)
660         return msierror(status);
661     Py_RETURN_NONE;
662 }
663 
664 static PyMethodDef summary_methods[] = {
665     { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
666         PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
667     { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
668         PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
669     { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
670         PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
671     { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
672         PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
673     { NULL, NULL }
674 };
675 
676 static PyTypeObject summary_Type = {
677         PyVarObject_HEAD_INIT(NULL, 0)
678         "_msi.SummaryInformation",              /*tp_name*/
679         sizeof(msiobj), /*tp_basicsize*/
680         0,                      /*tp_itemsize*/
681         /* methods */
682         (destructor)msiobj_dealloc, /*tp_dealloc*/
683         0,                      /*tp_vectorcall_offset*/
684         0,                      /*tp_getattr*/
685         0,                      /*tp_setattr*/
686         0,                      /*tp_as_async*/
687         0,                      /*tp_repr*/
688         0,                      /*tp_as_number*/
689         0,                      /*tp_as_sequence*/
690         0,                      /*tp_as_mapping*/
691         0,                      /*tp_hash*/
692         0,                      /*tp_call*/
693         0,                      /*tp_str*/
694         PyObject_GenericGetAttr,/*tp_getattro*/
695         PyObject_GenericSetAttr,/*tp_setattro*/
696         0,                      /*tp_as_buffer*/
697         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
698         0,                      /*tp_doc*/
699         0,                      /*tp_traverse*/
700         0,                      /*tp_clear*/
701         0,                      /*tp_richcompare*/
702         0,                      /*tp_weaklistoffset*/
703         0,                      /*tp_iter*/
704         0,                      /*tp_iternext*/
705         summary_methods,        /*tp_methods*/
706         0,                      /*tp_members*/
707         0,                      /*tp_getset*/
708         0,                      /*tp_base*/
709         0,                      /*tp_dict*/
710         0,                      /*tp_descr_get*/
711         0,                      /*tp_descr_set*/
712         0,                      /*tp_dictoffset*/
713         0,                      /*tp_init*/
714         0,                      /*tp_alloc*/
715         0,                      /*tp_new*/
716         0,                      /*tp_free*/
717         0,                      /*tp_is_gc*/
718 };
719 
720 /*************************** View objects **************/
721 
722 static PyObject*
view_execute(msiobj * view,PyObject * args)723 view_execute(msiobj *view, PyObject*args)
724 {
725     int status;
726     MSIHANDLE params = 0;
727     PyObject *oparams = Py_None;
728 
729     if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
730         return NULL;
731 
732     if (oparams != Py_None) {
733         if (oparams->ob_type != &record_Type) {
734             PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
735             return NULL;
736         }
737         params = ((msiobj*)oparams)->h;
738     }
739 
740     status = MsiViewExecute(view->h, params);
741     if (status != ERROR_SUCCESS)
742         return msierror(status);
743 
744     Py_RETURN_NONE;
745 }
746 
747 static PyObject*
view_fetch(msiobj * view,PyObject * args)748 view_fetch(msiobj *view, PyObject*args)
749 {
750     int status;
751     MSIHANDLE result;
752 
753     status = MsiViewFetch(view->h, &result);
754     if (status == ERROR_NO_MORE_ITEMS) {
755         Py_RETURN_NONE;
756     } else if (status != ERROR_SUCCESS) {
757         return msierror(status);
758     }
759 
760     return record_new(result);
761 }
762 
763 static PyObject*
view_getcolumninfo(msiobj * view,PyObject * args)764 view_getcolumninfo(msiobj *view, PyObject *args)
765 {
766     int status;
767     int kind;
768     MSIHANDLE result;
769 
770     if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
771         return NULL;
772 
773     if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
774         return msierror(status);
775 
776     return record_new(result);
777 }
778 
779 static PyObject*
view_modify(msiobj * view,PyObject * args)780 view_modify(msiobj *view, PyObject *args)
781 {
782     int kind;
783     PyObject *data;
784     int status;
785 
786     if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
787         return NULL;
788 
789     if (data->ob_type != &record_Type) {
790         PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
791         return NULL;
792     }
793 
794     if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
795         return msierror(status);
796 
797     Py_RETURN_NONE;
798 }
799 
800 static PyObject*
view_close(msiobj * view,PyObject * args)801 view_close(msiobj *view, PyObject*args)
802 {
803     int status;
804 
805     if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
806         return msierror(status);
807 
808     Py_RETURN_NONE;
809 }
810 
811 static PyMethodDef view_methods[] = {
812     { "Execute", (PyCFunction)view_execute, METH_VARARGS,
813         PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
814     { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
815         PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
816     { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
817         PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
818     { "Modify", (PyCFunction)view_modify, METH_VARARGS,
819         PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
820     { "Close", (PyCFunction)view_close, METH_NOARGS,
821         PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
822     { NULL, NULL }
823 };
824 
825 static PyTypeObject msiview_Type = {
826         PyVarObject_HEAD_INIT(NULL, 0)
827         "_msi.View",            /*tp_name*/
828         sizeof(msiobj), /*tp_basicsize*/
829         0,                      /*tp_itemsize*/
830         /* methods */
831         (destructor)msiobj_dealloc, /*tp_dealloc*/
832         0,                      /*tp_vectorcall_offset*/
833         0,                      /*tp_getattr*/
834         0,                      /*tp_setattr*/
835         0,                      /*tp_as_async*/
836         0,                      /*tp_repr*/
837         0,                      /*tp_as_number*/
838         0,                      /*tp_as_sequence*/
839         0,                      /*tp_as_mapping*/
840         0,                      /*tp_hash*/
841         0,                      /*tp_call*/
842         0,                      /*tp_str*/
843         PyObject_GenericGetAttr,/*tp_getattro*/
844         PyObject_GenericSetAttr,/*tp_setattro*/
845         0,                      /*tp_as_buffer*/
846         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
847         0,                      /*tp_doc*/
848         0,                      /*tp_traverse*/
849         0,                      /*tp_clear*/
850         0,                      /*tp_richcompare*/
851         0,                      /*tp_weaklistoffset*/
852         0,                      /*tp_iter*/
853         0,                      /*tp_iternext*/
854         view_methods,           /*tp_methods*/
855         0,                      /*tp_members*/
856         0,                      /*tp_getset*/
857         0,                      /*tp_base*/
858         0,                      /*tp_dict*/
859         0,                      /*tp_descr_get*/
860         0,                      /*tp_descr_set*/
861         0,                      /*tp_dictoffset*/
862         0,                      /*tp_init*/
863         0,                      /*tp_alloc*/
864         0,                      /*tp_new*/
865         0,                      /*tp_free*/
866         0,                      /*tp_is_gc*/
867 };
868 
869 /*************************** Database objects **************/
870 
871 static PyObject*
msidb_openview(msiobj * msidb,PyObject * args)872 msidb_openview(msiobj *msidb, PyObject *args)
873 {
874     int status;
875     char *sql;
876     MSIHANDLE hView;
877     msiobj *result;
878 
879     if (!PyArg_ParseTuple(args, "s:OpenView", &sql))
880         return NULL;
881 
882     if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS)
883         return msierror(status);
884 
885     result = PyObject_NEW(struct msiobj, &msiview_Type);
886     if (!result) {
887         MsiCloseHandle(hView);
888         return NULL;
889     }
890 
891     result->h = hView;
892     return (PyObject*)result;
893 }
894 
895 static PyObject*
msidb_commit(msiobj * msidb,PyObject * args)896 msidb_commit(msiobj *msidb, PyObject *args)
897 {
898     int status;
899 
900     if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
901         return msierror(status);
902 
903     Py_RETURN_NONE;
904 }
905 
906 static PyObject*
msidb_getsummaryinformation(msiobj * db,PyObject * args)907 msidb_getsummaryinformation(msiobj *db, PyObject *args)
908 {
909     int status;
910     int count;
911     MSIHANDLE result;
912     msiobj *oresult;
913 
914     if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
915         return NULL;
916 
917     status = MsiGetSummaryInformation(db->h, NULL, count, &result);
918     if (status != ERROR_SUCCESS)
919         return msierror(status);
920 
921     oresult = PyObject_NEW(struct msiobj, &summary_Type);
922     if (!oresult) {
923         MsiCloseHandle(result);
924         return NULL;
925     }
926 
927     oresult->h = result;
928     return (PyObject*)oresult;
929 }
930 
931 static PyMethodDef db_methods[] = {
932     { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
933         PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
934     { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
935         PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
936     { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
937         PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
938     { "Close", (PyCFunction)msidb_close, METH_NOARGS,
939         PyDoc_STR("Close() -> None\nWraps MsiCloseHandle")},
940     { NULL, NULL }
941 };
942 
943 static PyTypeObject msidb_Type = {
944         PyVarObject_HEAD_INIT(NULL, 0)
945         "_msi.Database",                /*tp_name*/
946         sizeof(msiobj), /*tp_basicsize*/
947         0,                      /*tp_itemsize*/
948         /* methods */
949         (destructor)msiobj_dealloc, /*tp_dealloc*/
950         0,                      /*tp_vectorcall_offset*/
951         0,                      /*tp_getattr*/
952         0,                      /*tp_setattr*/
953         0,                      /*tp_as_async*/
954         0,                      /*tp_repr*/
955         0,                      /*tp_as_number*/
956         0,                      /*tp_as_sequence*/
957         0,                      /*tp_as_mapping*/
958         0,                      /*tp_hash*/
959         0,                      /*tp_call*/
960         0,                      /*tp_str*/
961         PyObject_GenericGetAttr,/*tp_getattro*/
962         PyObject_GenericSetAttr,/*tp_setattro*/
963         0,                      /*tp_as_buffer*/
964         Py_TPFLAGS_DEFAULT,     /*tp_flags*/
965         0,                      /*tp_doc*/
966         0,                      /*tp_traverse*/
967         0,                      /*tp_clear*/
968         0,                      /*tp_richcompare*/
969         0,                      /*tp_weaklistoffset*/
970         0,                      /*tp_iter*/
971         0,                      /*tp_iternext*/
972         db_methods,             /*tp_methods*/
973         0,                      /*tp_members*/
974         0,                      /*tp_getset*/
975         0,                      /*tp_base*/
976         0,                      /*tp_dict*/
977         0,                      /*tp_descr_get*/
978         0,                      /*tp_descr_set*/
979         0,                      /*tp_dictoffset*/
980         0,                      /*tp_init*/
981         0,                      /*tp_alloc*/
982         0,                      /*tp_new*/
983         0,                      /*tp_free*/
984         0,                      /*tp_is_gc*/
985 };
986 
987 #define Py_NOT_PERSIST(x, flag)                        \
988     (x != (SIZE_T)(flag) &&                      \
989     x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
990 
991 #define Py_INVALID_PERSIST(x)                \
992     (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) &&  \
993     Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) &&   \
994     Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) &&     \
995     Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) &&     \
996     Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
997 
msiopendb(PyObject * obj,PyObject * args)998 static PyObject* msiopendb(PyObject *obj, PyObject *args)
999 {
1000     int status;
1001     char *path;
1002     int persist;
1003     MSIHANDLE h;
1004     msiobj *result;
1005     if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist))
1006         return NULL;
1007     /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
1008        MsiOpenDatabase may treat the value as a pointer, leading to unexpected
1009        behavior. */
1010     if (Py_INVALID_PERSIST(persist))
1011         return msierror(ERROR_INVALID_PARAMETER);
1012     status = MsiOpenDatabase(path, (LPCSTR)(SIZE_T)persist, &h);
1013     if (status != ERROR_SUCCESS)
1014         return msierror(status);
1015 
1016     result = PyObject_NEW(struct msiobj, &msidb_Type);
1017     if (!result) {
1018         MsiCloseHandle(h);
1019         return NULL;
1020     }
1021     result->h = h;
1022     return (PyObject*)result;
1023 }
1024 
1025 static PyObject*
createrecord(PyObject * o,PyObject * args)1026 createrecord(PyObject *o, PyObject *args)
1027 {
1028     int count;
1029     MSIHANDLE h;
1030 
1031     if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
1032         return NULL;
1033 
1034     h = MsiCreateRecord(count);
1035     if (h == 0)
1036         return msierror(0);
1037 
1038     return record_new(h);
1039 }
1040 
1041 
1042 static PyMethodDef msi_methods[] = {
1043         {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
1044                 PyDoc_STR("UuidCreate() -> string")},
1045         {"FCICreate",   (PyCFunction)fcicreate, METH_VARARGS,
1046                 PyDoc_STR("fcicreate(cabname,files) -> None")},
1047         {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
1048         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
1049         {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
1050         PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
1051         {NULL,          NULL}           /* sentinel */
1052 };
1053 
1054 static char msi_doc[] = "Documentation";
1055 
1056 
1057 static struct PyModuleDef _msimodule = {
1058         PyModuleDef_HEAD_INIT,
1059         "_msi",
1060         msi_doc,
1061         -1,
1062         msi_methods,
1063         NULL,
1064         NULL,
1065         NULL,
1066         NULL
1067 };
1068 
1069 PyMODINIT_FUNC
PyInit__msi(void)1070 PyInit__msi(void)
1071 {
1072     PyObject *m;
1073 
1074     m = PyModule_Create(&_msimodule);
1075     if (m == NULL)
1076         return NULL;
1077 
1078     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (long)(SIZE_T)MSIDBOPEN_CREATEDIRECT);
1079     PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (long)(SIZE_T)MSIDBOPEN_CREATE);
1080     PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (long)(SIZE_T)MSIDBOPEN_DIRECT);
1081     PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (long)(SIZE_T)MSIDBOPEN_READONLY);
1082     PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (long)(SIZE_T)MSIDBOPEN_TRANSACT);
1083     PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (long)(SIZE_T)MSIDBOPEN_PATCHFILE);
1084 
1085     PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1086     PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
1087 
1088     PyModule_AddIntMacro(m, MSIMODIFY_SEEK);
1089     PyModule_AddIntMacro(m, MSIMODIFY_REFRESH);
1090     PyModule_AddIntMacro(m, MSIMODIFY_INSERT);
1091     PyModule_AddIntMacro(m, MSIMODIFY_UPDATE);
1092     PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN);
1093     PyModule_AddIntMacro(m, MSIMODIFY_REPLACE);
1094     PyModule_AddIntMacro(m, MSIMODIFY_MERGE);
1095     PyModule_AddIntMacro(m, MSIMODIFY_DELETE);
1096     PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY);
1097     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE);
1098     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW);
1099     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD);
1100     PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE);
1101 
1102     PyModule_AddIntMacro(m, PID_CODEPAGE);
1103     PyModule_AddIntMacro(m, PID_TITLE);
1104     PyModule_AddIntMacro(m, PID_SUBJECT);
1105     PyModule_AddIntMacro(m, PID_AUTHOR);
1106     PyModule_AddIntMacro(m, PID_KEYWORDS);
1107     PyModule_AddIntMacro(m, PID_COMMENTS);
1108     PyModule_AddIntMacro(m, PID_TEMPLATE);
1109     PyModule_AddIntMacro(m, PID_LASTAUTHOR);
1110     PyModule_AddIntMacro(m, PID_REVNUMBER);
1111     PyModule_AddIntMacro(m, PID_LASTPRINTED);
1112     PyModule_AddIntMacro(m, PID_CREATE_DTM);
1113     PyModule_AddIntMacro(m, PID_LASTSAVE_DTM);
1114     PyModule_AddIntMacro(m, PID_PAGECOUNT);
1115     PyModule_AddIntMacro(m, PID_WORDCOUNT);
1116     PyModule_AddIntMacro(m, PID_CHARCOUNT);
1117     PyModule_AddIntMacro(m, PID_APPNAME);
1118     PyModule_AddIntMacro(m, PID_SECURITY);
1119 
1120     MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1121     if (!MSIError)
1122         return NULL;
1123     PyModule_AddObject(m, "MSIError", MSIError);
1124     return m;
1125 }
1126