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