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 /*[clinic input]
16 module _msi
17 class _msi.Record "msiobj *" "&record_Type"
18 class _msi.SummaryInformation "msiobj *" "&summary_Type"
19 class _msi.View "msiobj *" "&msiview_Type"
20 class _msi.Database "msiobj *" "&msidb_Type"
21 [clinic start generated code]*/
22 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=89a3605762cf4bdc]*/
23
24 static PyObject *MSIError;
25
26 /*[clinic input]
27 _msi.UuidCreate
28
29 Return the string representation of a new unique identifier.
30 [clinic start generated code]*/
31
32 static PyObject *
_msi_UuidCreate_impl(PyObject * module)33 _msi_UuidCreate_impl(PyObject *module)
34 /*[clinic end generated code: output=534ecf36f10af98e input=168024ab4b3e832b]*/
35 {
36 UUID result;
37 wchar_t *cresult;
38 PyObject *oresult;
39
40 /* May return ok, local only, and no address.
41 For local only, the documentation says we still get a uuid.
42 For RPC_S_UUID_NO_ADDRESS, it's not clear whether we can
43 use the result. */
44 if (UuidCreate(&result) == RPC_S_UUID_NO_ADDRESS) {
45 PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result");
46 return NULL;
47 }
48
49 if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
50 PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
51 return NULL;
52 }
53
54 oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult));
55 RpcStringFreeW(&cresult);
56 return oresult;
57
58 }
59
60 /* Helper for converting file names from UTF-8 to wchat_t*. */
61 static wchar_t *
utf8_to_wchar(const char * s,int * err)62 utf8_to_wchar(const char *s, int *err)
63 {
64 PyObject *obj = PyUnicode_FromString(s);
65 if (obj == NULL) {
66 if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
67 *err = ENOMEM;
68 }
69 else {
70 *err = EINVAL;
71 }
72 PyErr_Clear();
73 return NULL;
74 }
75 wchar_t *ws = PyUnicode_AsWideCharString(obj, NULL);
76 if (ws == NULL) {
77 *err = ENOMEM;
78 PyErr_Clear();
79 }
80 Py_DECREF(obj);
81 return ws;
82 }
83
84 /* FCI callback functions */
85
FNFCIALLOC(cb_alloc)86 static FNFCIALLOC(cb_alloc)
87 {
88 return PyMem_RawMalloc(cb);
89 }
90
FNFCIFREE(cb_free)91 static FNFCIFREE(cb_free)
92 {
93 PyMem_RawFree(memory);
94 }
95
FNFCIOPEN(cb_open)96 static FNFCIOPEN(cb_open)
97 {
98 wchar_t *ws = utf8_to_wchar(pszFile, err);
99 if (ws == NULL) {
100 return -1;
101 }
102 int result = _wopen(ws, oflag | O_NOINHERIT, pmode);
103 PyMem_Free(ws);
104 if (result == -1)
105 *err = errno;
106 return result;
107 }
108
FNFCIREAD(cb_read)109 static FNFCIREAD(cb_read)
110 {
111 UINT result = (UINT)_read((int)hf, memory, cb);
112 if (result != cb)
113 *err = errno;
114 return result;
115 }
116
FNFCIWRITE(cb_write)117 static FNFCIWRITE(cb_write)
118 {
119 UINT result = (UINT)_write((int)hf, memory, cb);
120 if (result != cb)
121 *err = errno;
122 return result;
123 }
124
FNFCICLOSE(cb_close)125 static FNFCICLOSE(cb_close)
126 {
127 int result = _close((int)hf);
128 if (result != 0)
129 *err = errno;
130 return result;
131 }
132
FNFCISEEK(cb_seek)133 static FNFCISEEK(cb_seek)
134 {
135 long result = (long)_lseek((int)hf, dist, seektype);
136 if (result == -1)
137 *err = errno;
138 return result;
139 }
140
FNFCIDELETE(cb_delete)141 static FNFCIDELETE(cb_delete)
142 {
143 wchar_t *ws = utf8_to_wchar(pszFile, err);
144 if (ws == NULL) {
145 return -1;
146 }
147 int result = _wremove(ws);
148 PyMem_Free(ws);
149 if (result != 0)
150 *err = errno;
151 return result;
152 }
153
FNFCIFILEPLACED(cb_fileplaced)154 static FNFCIFILEPLACED(cb_fileplaced)
155 {
156 return 0;
157 }
158
FNFCIGETTEMPFILE(cb_gettempfile)159 static FNFCIGETTEMPFILE(cb_gettempfile)
160 {
161 char *name = _tempnam("", "tmp");
162 if ((name != NULL) && ((int)strlen(name) < cbTempName)) {
163 strcpy(pszTempName, name);
164 free(name);
165 return TRUE;
166 }
167
168 if (name) free(name);
169 return FALSE;
170 }
171
FNFCISTATUS(cb_status)172 static FNFCISTATUS(cb_status)
173 {
174 if (pv) {
175 _Py_IDENTIFIER(status);
176
177 PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2);
178 if (result == NULL)
179 return -1;
180 Py_DECREF(result);
181 }
182 return 0;
183 }
184
FNFCIGETNEXTCABINET(cb_getnextcabinet)185 static FNFCIGETNEXTCABINET(cb_getnextcabinet)
186 {
187 if (pv) {
188 _Py_IDENTIFIER(getnextcabinet);
189
190 PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab);
191 if (result == NULL)
192 return -1;
193 if (!PyBytes_Check(result)) {
194 PyErr_Format(PyExc_TypeError,
195 "Incorrect return type %s from getnextcabinet",
196 Py_TYPE(result)->tp_name);
197 Py_DECREF(result);
198 return FALSE;
199 }
200 strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab));
201 return TRUE;
202 }
203 return FALSE;
204 }
205
FNFCIGETOPENINFO(cb_getopeninfo)206 static FNFCIGETOPENINFO(cb_getopeninfo)
207 {
208 BY_HANDLE_FILE_INFORMATION bhfi;
209 FILETIME filetime;
210 HANDLE handle;
211
212 wchar_t *ws = utf8_to_wchar(pszName, err);
213 if (ws == NULL) {
214 return -1;
215 }
216
217 /* Need Win32 handle to get time stamps */
218 handle = CreateFileW(ws, GENERIC_READ, FILE_SHARE_READ, NULL,
219 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
220 if (handle == INVALID_HANDLE_VALUE) {
221 PyMem_Free(ws);
222 return -1;
223 }
224
225 if (GetFileInformationByHandle(handle, &bhfi) == FALSE) {
226 CloseHandle(handle);
227 PyMem_Free(ws);
228 return -1;
229 }
230
231 FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
232 FileTimeToDosDateTime(&filetime, pdate, ptime);
233
234 *pattribs = (int)(bhfi.dwFileAttributes &
235 (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
236
237 CloseHandle(handle);
238
239 int result = _wopen(ws, _O_RDONLY | _O_BINARY | O_NOINHERIT);
240 PyMem_Free(ws);
241 return result;
242 }
243
244 /*[clinic input]
245 _msi.FCICreate
246 cabname: str
247 the name of the CAB file
248 files: object
249 a list of tuples, each containing the name of the file on disk,
250 and the name of the file inside the CAB file
251 /
252
253 Create a new CAB file.
254 [clinic start generated code]*/
255
256 static PyObject *
_msi_FCICreate_impl(PyObject * module,const char * cabname,PyObject * files)257 _msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files)
258 /*[clinic end generated code: output=55dc05728361b799 input=1d2d75fdc8b44b71]*/
259 {
260 const char *p;
261 CCAB ccab;
262 HFCI hfci;
263 ERF erf;
264 Py_ssize_t i;
265
266 if (!PyList_Check(files)) {
267 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
268 return NULL;
269 }
270
271 ccab.cb = INT_MAX; /* no need to split CAB into multiple media */
272 ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */
273 ccab.cbReserveCFData = 0;
274 ccab.cbReserveCFFolder = 0;
275 ccab.cbReserveCFHeader = 0;
276
277 ccab.iCab = 1;
278 ccab.iDisk = 1;
279
280 ccab.setID = 0;
281 ccab.szDisk[0] = '\0';
282
283 for (i = 0, p = cabname; *p; p++)
284 if (*p == '\\' || *p == '/')
285 i = p - cabname + 1;
286
287 if (i >= sizeof(ccab.szCabPath) ||
288 strlen(cabname+i) >= sizeof(ccab.szCab)) {
289 PyErr_SetString(PyExc_ValueError, "path name too long");
290 return 0;
291 }
292
293 if (i > 0) {
294 memcpy(ccab.szCabPath, cabname, i);
295 ccab.szCabPath[i] = '\0';
296 strcpy(ccab.szCab, cabname+i);
297 } else {
298 strcpy(ccab.szCabPath, ".\\");
299 strcpy(ccab.szCab, cabname);
300 }
301
302 hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
303 cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
304 cb_gettempfile, &ccab, NULL);
305
306 if (hfci == NULL) {
307 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
308 return NULL;
309 }
310
311 for (i=0; i < PyList_GET_SIZE(files); i++) {
312 PyObject *item = PyList_GET_ITEM(files, i);
313 char *filename, *cabname;
314
315 if (!PyArg_ParseTuple(item, "ss", &filename, &cabname)) {
316 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list of tuples containing two strings");
317 FCIDestroy(hfci);
318 return NULL;
319 }
320
321 if (!FCIAddFile(hfci, filename, cabname, FALSE,
322 cb_getnextcabinet, cb_status, cb_getopeninfo,
323 tcompTYPE_MSZIP))
324 goto err;
325 }
326
327 if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
328 goto err;
329
330 if (!FCIDestroy(hfci))
331 goto err;
332
333 Py_RETURN_NONE;
334 err:
335 if(erf.fError)
336 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */
337 else
338 PyErr_SetString(PyExc_ValueError, "FCI general error");
339
340 FCIDestroy(hfci);
341 return NULL;
342 }
343
344 typedef struct msiobj{
345 PyObject_HEAD
346 MSIHANDLE h;
347 }msiobj;
348
349 static void
msiobj_dealloc(msiobj * msidb)350 msiobj_dealloc(msiobj* msidb)
351 {
352 MsiCloseHandle(msidb->h);
353 msidb->h = 0;
354 PyObject_Free(msidb);
355 }
356
357 static PyObject*
msierror(int status)358 msierror(int status)
359 {
360 int code;
361 char buf[2000];
362 char *res = buf;
363 DWORD size = sizeof(buf);
364 MSIHANDLE err = MsiGetLastErrorRecord();
365
366 if (err == 0) {
367 switch(status) {
368 case ERROR_ACCESS_DENIED:
369 PyErr_SetString(MSIError, "access denied");
370 return NULL;
371 case ERROR_FUNCTION_FAILED:
372 PyErr_SetString(MSIError, "function failed");
373 return NULL;
374 case ERROR_INVALID_DATA:
375 PyErr_SetString(MSIError, "invalid data");
376 return NULL;
377 case ERROR_INVALID_HANDLE:
378 PyErr_SetString(MSIError, "invalid handle");
379 return NULL;
380 case ERROR_INVALID_STATE:
381 PyErr_SetString(MSIError, "invalid state");
382 return NULL;
383 case ERROR_INVALID_PARAMETER:
384 PyErr_SetString(MSIError, "invalid parameter");
385 return NULL;
386 case ERROR_OPEN_FAILED:
387 PyErr_SetString(MSIError, "open failed");
388 return NULL;
389 case ERROR_CREATE_FAILED:
390 PyErr_SetString(MSIError, "create failed");
391 return NULL;
392 default:
393 PyErr_Format(MSIError, "unknown error %x", status);
394 return NULL;
395 }
396 }
397
398 code = MsiRecordGetInteger(err, 1); /* XXX code */
399 if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
400 res = malloc(size+1);
401 if (res == NULL) {
402 MsiCloseHandle(err);
403 return PyErr_NoMemory();
404 }
405 MsiFormatRecord(0, err, res, &size);
406 res[size]='\0';
407 }
408 MsiCloseHandle(err);
409 PyErr_SetString(MSIError, res);
410 if (res != buf)
411 free(res);
412 return NULL;
413 }
414
415 #include "clinic/_msi.c.h"
416
417 /*[clinic input]
418 _msi.Database.Close
419
420 Close the database object.
421 [clinic start generated code]*/
422
423 static PyObject *
_msi_Database_Close_impl(msiobj * self)424 _msi_Database_Close_impl(msiobj *self)
425 /*[clinic end generated code: output=ddf2d7712ea804f1 input=104330ce4a486187]*/
426 {
427 int status;
428 if ((status = MsiCloseHandle(self->h)) != ERROR_SUCCESS) {
429 return msierror(status);
430 }
431 self->h = 0;
432 Py_RETURN_NONE;
433 }
434
435 /*************************** Record objects **********************/
436
437 /*[clinic input]
438 _msi.Record.GetFieldCount
439
440 Return the number of fields of the record.
441 [clinic start generated code]*/
442
443 static PyObject *
_msi_Record_GetFieldCount_impl(msiobj * self)444 _msi_Record_GetFieldCount_impl(msiobj *self)
445 /*[clinic end generated code: output=112795079c904398 input=5fb9d4071b28897b]*/
446 {
447 return PyLong_FromLong(MsiRecordGetFieldCount(self->h));
448 }
449
450 /*[clinic input]
451 _msi.Record.GetInteger
452 field: unsigned_int(bitwise=True)
453 /
454
455 Return the value of field as an integer where possible.
456 [clinic start generated code]*/
457
458 static PyObject *
_msi_Record_GetInteger_impl(msiobj * self,unsigned int field)459 _msi_Record_GetInteger_impl(msiobj *self, unsigned int field)
460 /*[clinic end generated code: output=7174ebb6e8ed1c79 input=d19209947e2bfe61]*/
461 {
462 int status;
463
464 status = MsiRecordGetInteger(self->h, field);
465 if (status == MSI_NULL_INTEGER){
466 PyErr_SetString(MSIError, "could not convert record field to integer");
467 return NULL;
468 }
469 return PyLong_FromLong((long) status);
470 }
471
472 /*[clinic input]
473 _msi.Record.GetString
474 field: unsigned_int(bitwise=True)
475 /
476
477 Return the value of field as a string where possible.
478 [clinic start generated code]*/
479
480 static PyObject *
_msi_Record_GetString_impl(msiobj * self,unsigned int field)481 _msi_Record_GetString_impl(msiobj *self, unsigned int field)
482 /*[clinic end generated code: output=f670d1b484cfa47c input=ffa11f21450b77d8]*/
483 {
484 unsigned int status;
485 WCHAR buf[2000];
486 WCHAR *res = buf;
487 DWORD size = sizeof(buf);
488 PyObject* string;
489
490 status = MsiRecordGetStringW(self->h, field, res, &size);
491 if (status == ERROR_MORE_DATA) {
492 res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR));
493 if (res == NULL)
494 return PyErr_NoMemory();
495 status = MsiRecordGetStringW(self->h, field, res, &size);
496 }
497 if (status != ERROR_SUCCESS)
498 return msierror((int) status);
499 string = PyUnicode_FromWideChar(res, size);
500 if (buf != res)
501 free(res);
502 return string;
503 }
504
505 /*[clinic input]
506 _msi.Record.ClearData
507
508 Set all fields of the record to 0.
509 [clinic start generated code]*/
510
511 static PyObject *
_msi_Record_ClearData_impl(msiobj * self)512 _msi_Record_ClearData_impl(msiobj *self)
513 /*[clinic end generated code: output=1891467214b977f4 input=2a911c95aaded102]*/
514 {
515 int status = MsiRecordClearData(self->h);
516 if (status != ERROR_SUCCESS)
517 return msierror(status);
518
519 Py_RETURN_NONE;
520 }
521
522 /*[clinic input]
523 _msi.Record.SetString
524 field: int
525 value: Py_UNICODE
526 /
527
528 Set field to a string value.
529 [clinic start generated code]*/
530
531 static PyObject *
_msi_Record_SetString_impl(msiobj * self,int field,const Py_UNICODE * value)532 _msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value)
533 /*[clinic end generated code: output=2e37505b0f11f985 input=fb8ec70a2a6148e0]*/
534 {
535 int status;
536
537 if ((status = MsiRecordSetStringW(self->h, field, value)) != ERROR_SUCCESS)
538 return msierror(status);
539
540 Py_RETURN_NONE;
541 }
542
543 /*[clinic input]
544 _msi.Record.SetStream
545 field: int
546 value: Py_UNICODE
547 /
548
549 Set field to the contents of the file named value.
550 [clinic start generated code]*/
551
552 static PyObject *
_msi_Record_SetStream_impl(msiobj * self,int field,const Py_UNICODE * value)553 _msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value)
554 /*[clinic end generated code: output=442facac16913b48 input=a07aa19b865e8292]*/
555 {
556 int status;
557
558 if ((status = MsiRecordSetStreamW(self->h, field, value)) != ERROR_SUCCESS)
559 return msierror(status);
560
561 Py_RETURN_NONE;
562 }
563
564 /*[clinic input]
565 _msi.Record.SetInteger
566 field: int
567 value: int
568 /
569
570 Set field to an integer value.
571 [clinic start generated code]*/
572
573 static PyObject *
_msi_Record_SetInteger_impl(msiobj * self,int field,int value)574 _msi_Record_SetInteger_impl(msiobj *self, int field, int value)
575 /*[clinic end generated code: output=669e8647775d0ce7 input=c571aa775e7e451b]*/
576 {
577 int status;
578
579 if ((status = MsiRecordSetInteger(self->h, field, value)) != ERROR_SUCCESS)
580 return msierror(status);
581
582 Py_RETURN_NONE;
583 }
584
585
586
587 static PyMethodDef record_methods[] = {
588 _MSI_RECORD_GETFIELDCOUNT_METHODDEF
589 _MSI_RECORD_GETINTEGER_METHODDEF
590 _MSI_RECORD_GETSTRING_METHODDEF
591 _MSI_RECORD_SETSTRING_METHODDEF
592 _MSI_RECORD_SETSTREAM_METHODDEF
593 _MSI_RECORD_SETINTEGER_METHODDEF
594 _MSI_RECORD_CLEARDATA_METHODDEF
595 { NULL, NULL }
596 };
597
598 static PyTypeObject record_Type = {
599 PyVarObject_HEAD_INIT(NULL, 0)
600 "_msi.Record", /*tp_name*/
601 sizeof(msiobj), /*tp_basicsize*/
602 0, /*tp_itemsize*/
603 /* methods */
604 (destructor)msiobj_dealloc, /*tp_dealloc*/
605 0, /*tp_vectorcall_offset*/
606 0, /*tp_getattr*/
607 0, /*tp_setattr*/
608 0, /*tp_as_async*/
609 0, /*tp_repr*/
610 0, /*tp_as_number*/
611 0, /*tp_as_sequence*/
612 0, /*tp_as_mapping*/
613 0, /*tp_hash*/
614 0, /*tp_call*/
615 0, /*tp_str*/
616 PyObject_GenericGetAttr,/*tp_getattro*/
617 PyObject_GenericSetAttr,/*tp_setattro*/
618 0, /*tp_as_buffer*/
619 Py_TPFLAGS_DEFAULT, /*tp_flags*/
620 0, /*tp_doc*/
621 0, /*tp_traverse*/
622 0, /*tp_clear*/
623 0, /*tp_richcompare*/
624 0, /*tp_weaklistoffset*/
625 0, /*tp_iter*/
626 0, /*tp_iternext*/
627 record_methods, /*tp_methods*/
628 0, /*tp_members*/
629 0, /*tp_getset*/
630 0, /*tp_base*/
631 0, /*tp_dict*/
632 0, /*tp_descr_get*/
633 0, /*tp_descr_set*/
634 0, /*tp_dictoffset*/
635 0, /*tp_init*/
636 0, /*tp_alloc*/
637 0, /*tp_new*/
638 0, /*tp_free*/
639 0, /*tp_is_gc*/
640 };
641
642 static PyObject*
record_new(MSIHANDLE h)643 record_new(MSIHANDLE h)
644 {
645 msiobj *result = PyObject_New(struct msiobj, &record_Type);
646
647 if (!result) {
648 MsiCloseHandle(h);
649 return NULL;
650 }
651
652 result->h = h;
653 return (PyObject*)result;
654 }
655
656 /*************************** SummaryInformation objects **************/
657
658 /*[clinic input]
659 _msi.SummaryInformation.GetProperty
660 field: int
661 the name of the property, one of the PID_* constants
662 /
663
664 Return a property of the summary.
665 [clinic start generated code]*/
666
667 static PyObject *
_msi_SummaryInformation_GetProperty_impl(msiobj * self,int field)668 _msi_SummaryInformation_GetProperty_impl(msiobj *self, int field)
669 /*[clinic end generated code: output=f8946a33ee14f6ef input=f8dfe2c890d6cb8b]*/
670 {
671 int status;
672 PyObject *result;
673 UINT type;
674 INT ival;
675 FILETIME fval;
676 char sbuf[1000];
677 char *sval = sbuf;
678 DWORD ssize = sizeof(sbuf);
679
680 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
681 &fval, sval, &ssize);
682 if (status == ERROR_MORE_DATA) {
683 ssize++;
684 sval = malloc(ssize);
685 if (sval == NULL) {
686 return PyErr_NoMemory();
687 }
688 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
689 &fval, sval, &ssize);
690 }
691 if (status != ERROR_SUCCESS) {
692 return msierror(status);
693 }
694
695 switch(type) {
696 case VT_I2:
697 case VT_I4:
698 result = PyLong_FromLong(ival);
699 break;
700 case VT_FILETIME:
701 PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
702 result = NULL;
703 break;
704 case VT_LPSTR:
705 result = PyBytes_FromStringAndSize(sval, ssize);
706 break;
707 case VT_EMPTY:
708 Py_INCREF(Py_None);
709 result = Py_None;
710 break;
711 default:
712 PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
713 result = NULL;
714 break;
715 }
716 if (sval != sbuf)
717 free(sval);
718 return result;
719 }
720
721 /*[clinic input]
722 _msi.SummaryInformation.GetPropertyCount
723
724 Return the number of summary properties.
725 [clinic start generated code]*/
726
727 static PyObject *
_msi_SummaryInformation_GetPropertyCount_impl(msiobj * self)728 _msi_SummaryInformation_GetPropertyCount_impl(msiobj *self)
729 /*[clinic end generated code: output=68e94b2aeee92b3d input=2e71e985586d82dc]*/
730 {
731 int status;
732 UINT result;
733
734 status = MsiSummaryInfoGetPropertyCount(self->h, &result);
735 if (status != ERROR_SUCCESS)
736 return msierror(status);
737
738 return PyLong_FromLong(result);
739 }
740
741 /*[clinic input]
742 _msi.SummaryInformation.SetProperty
743 field: int
744 the name of the property, one of the PID_* constants
745 value as data: object
746 the new value of the property (integer or string)
747 /
748
749 Set a property.
750 [clinic start generated code]*/
751
752 static PyObject *
_msi_SummaryInformation_SetProperty_impl(msiobj * self,int field,PyObject * data)753 _msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
754 PyObject *data)
755 /*[clinic end generated code: output=3d4692c8984bb675 input=f2a7811b905abbed]*/
756 {
757 int status;
758
759 if (PyUnicode_Check(data)) {
760 #if USE_UNICODE_WCHAR_CACHE
761 const WCHAR *value = _PyUnicode_AsUnicode(data);
762 #else /* USE_UNICODE_WCHAR_CACHE */
763 WCHAR *value = PyUnicode_AsWideCharString(data, NULL);
764 #endif /* USE_UNICODE_WCHAR_CACHE */
765 if (value == NULL) {
766 return NULL;
767 }
768 status = MsiSummaryInfoSetPropertyW(self->h, field, VT_LPSTR,
769 0, NULL, value);
770 #if !USE_UNICODE_WCHAR_CACHE
771 PyMem_Free(value);
772 #endif /* USE_UNICODE_WCHAR_CACHE */
773 } else if (PyLong_CheckExact(data)) {
774 long value = PyLong_AsLong(data);
775 if (value == -1 && PyErr_Occurred()) {
776 return NULL;
777 }
778 status = MsiSummaryInfoSetProperty(self->h, field, VT_I4,
779 value, NULL, NULL);
780 } else {
781 PyErr_SetString(PyExc_TypeError, "unsupported type");
782 return NULL;
783 }
784
785 if (status != ERROR_SUCCESS)
786 return msierror(status);
787
788 Py_RETURN_NONE;
789 }
790
791
792 /*[clinic input]
793 _msi.SummaryInformation.Persist
794
795 Write the modified properties to the summary information stream.
796 [clinic start generated code]*/
797
798 static PyObject *
_msi_SummaryInformation_Persist_impl(msiobj * self)799 _msi_SummaryInformation_Persist_impl(msiobj *self)
800 /*[clinic end generated code: output=c564bd17f5e122c9 input=e3dda9d530095ef7]*/
801 {
802 int status;
803
804 status = MsiSummaryInfoPersist(self->h);
805 if (status != ERROR_SUCCESS)
806 return msierror(status);
807 Py_RETURN_NONE;
808 }
809
810 static PyMethodDef summary_methods[] = {
811 _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF
812 _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF
813 _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF
814 _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF
815 { NULL, NULL }
816 };
817
818 static PyTypeObject summary_Type = {
819 PyVarObject_HEAD_INIT(NULL, 0)
820 "_msi.SummaryInformation", /*tp_name*/
821 sizeof(msiobj), /*tp_basicsize*/
822 0, /*tp_itemsize*/
823 /* methods */
824 (destructor)msiobj_dealloc, /*tp_dealloc*/
825 0, /*tp_vectorcall_offset*/
826 0, /*tp_getattr*/
827 0, /*tp_setattr*/
828 0, /*tp_as_async*/
829 0, /*tp_repr*/
830 0, /*tp_as_number*/
831 0, /*tp_as_sequence*/
832 0, /*tp_as_mapping*/
833 0, /*tp_hash*/
834 0, /*tp_call*/
835 0, /*tp_str*/
836 PyObject_GenericGetAttr,/*tp_getattro*/
837 PyObject_GenericSetAttr,/*tp_setattro*/
838 0, /*tp_as_buffer*/
839 Py_TPFLAGS_DEFAULT, /*tp_flags*/
840 0, /*tp_doc*/
841 0, /*tp_traverse*/
842 0, /*tp_clear*/
843 0, /*tp_richcompare*/
844 0, /*tp_weaklistoffset*/
845 0, /*tp_iter*/
846 0, /*tp_iternext*/
847 summary_methods, /*tp_methods*/
848 0, /*tp_members*/
849 0, /*tp_getset*/
850 0, /*tp_base*/
851 0, /*tp_dict*/
852 0, /*tp_descr_get*/
853 0, /*tp_descr_set*/
854 0, /*tp_dictoffset*/
855 0, /*tp_init*/
856 0, /*tp_alloc*/
857 0, /*tp_new*/
858 0, /*tp_free*/
859 0, /*tp_is_gc*/
860 };
861
862 /*************************** View objects **************/
863
864 /*[clinic input]
865 _msi.View.Execute
866 params as oparams: object
867 a record describing actual values of the parameter tokens
868 in the query or None
869 /
870
871 Execute the SQL query of the view.
872 [clinic start generated code]*/
873
874 static PyObject *
_msi_View_Execute(msiobj * self,PyObject * oparams)875 _msi_View_Execute(msiobj *self, PyObject *oparams)
876 /*[clinic end generated code: output=f0f65fd2900bcb4e input=cb163a15d453348e]*/
877 {
878 int status;
879 MSIHANDLE params = 0;
880
881 if (oparams != Py_None) {
882 if (!Py_IS_TYPE(oparams, &record_Type)) {
883 PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
884 return NULL;
885 }
886 params = ((msiobj*)oparams)->h;
887 }
888
889 status = MsiViewExecute(self->h, params);
890 if (status != ERROR_SUCCESS)
891 return msierror(status);
892
893 Py_RETURN_NONE;
894 }
895
896 /*[clinic input]
897 _msi.View.Fetch
898
899 Return a result record of the query.
900 [clinic start generated code]*/
901
902 static PyObject *
_msi_View_Fetch_impl(msiobj * self)903 _msi_View_Fetch_impl(msiobj *self)
904 /*[clinic end generated code: output=ba154a3794537d4e input=7f3e3d06c449001c]*/
905 {
906 int status;
907 MSIHANDLE result;
908
909 status = MsiViewFetch(self->h, &result);
910 if (status == ERROR_NO_MORE_ITEMS) {
911 Py_RETURN_NONE;
912 } else if (status != ERROR_SUCCESS) {
913 return msierror(status);
914 }
915
916 return record_new(result);
917 }
918
919 /*[clinic input]
920 _msi.View.GetColumnInfo
921 kind: int
922 MSICOLINFO_NAMES or MSICOLINFO_TYPES
923 /
924
925 Return a record describing the columns of the view.
926 [clinic start generated code]*/
927
928 static PyObject *
_msi_View_GetColumnInfo_impl(msiobj * self,int kind)929 _msi_View_GetColumnInfo_impl(msiobj *self, int kind)
930 /*[clinic end generated code: output=e7c1697db9403660 input=afedb892bf564a3b]*/
931 {
932 int status;
933 MSIHANDLE result;
934
935 if ((status = MsiViewGetColumnInfo(self->h, kind, &result)) != ERROR_SUCCESS)
936 return msierror(status);
937
938 return record_new(result);
939 }
940
941 /*[clinic input]
942 _msi.View.Modify
943 kind: int
944 one of the MSIMODIFY_* constants
945 data: object
946 a record describing the new data
947 /
948
949 Modify the view.
950 [clinic start generated code]*/
951
952 static PyObject *
_msi_View_Modify_impl(msiobj * self,int kind,PyObject * data)953 _msi_View_Modify_impl(msiobj *self, int kind, PyObject *data)
954 /*[clinic end generated code: output=69aaf3ce8ddac0ba input=2828de22de0d47b4]*/
955 {
956 int status;
957
958 if (!Py_IS_TYPE(data, &record_Type)) {
959 PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
960 return NULL;
961 }
962
963 if ((status = MsiViewModify(self->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
964 return msierror(status);
965
966 Py_RETURN_NONE;
967 }
968
969 /*[clinic input]
970 _msi.View.Close
971
972 Close the view.
973 [clinic start generated code]*/
974
975 static PyObject *
_msi_View_Close_impl(msiobj * self)976 _msi_View_Close_impl(msiobj *self)
977 /*[clinic end generated code: output=488f7b8645ca104a input=de6927d1308c401c]*/
978 {
979 int status;
980
981 if ((status = MsiViewClose(self->h)) != ERROR_SUCCESS)
982 return msierror(status);
983
984 Py_RETURN_NONE;
985 }
986
987 static PyMethodDef view_methods[] = {
988 _MSI_VIEW_EXECUTE_METHODDEF
989 _MSI_VIEW_GETCOLUMNINFO_METHODDEF
990 _MSI_VIEW_FETCH_METHODDEF
991 _MSI_VIEW_MODIFY_METHODDEF
992 _MSI_VIEW_CLOSE_METHODDEF
993 { NULL, NULL }
994 };
995
996 static PyTypeObject msiview_Type = {
997 PyVarObject_HEAD_INIT(NULL, 0)
998 "_msi.View", /*tp_name*/
999 sizeof(msiobj), /*tp_basicsize*/
1000 0, /*tp_itemsize*/
1001 /* methods */
1002 (destructor)msiobj_dealloc, /*tp_dealloc*/
1003 0, /*tp_vectorcall_offset*/
1004 0, /*tp_getattr*/
1005 0, /*tp_setattr*/
1006 0, /*tp_as_async*/
1007 0, /*tp_repr*/
1008 0, /*tp_as_number*/
1009 0, /*tp_as_sequence*/
1010 0, /*tp_as_mapping*/
1011 0, /*tp_hash*/
1012 0, /*tp_call*/
1013 0, /*tp_str*/
1014 PyObject_GenericGetAttr,/*tp_getattro*/
1015 PyObject_GenericSetAttr,/*tp_setattro*/
1016 0, /*tp_as_buffer*/
1017 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1018 0, /*tp_doc*/
1019 0, /*tp_traverse*/
1020 0, /*tp_clear*/
1021 0, /*tp_richcompare*/
1022 0, /*tp_weaklistoffset*/
1023 0, /*tp_iter*/
1024 0, /*tp_iternext*/
1025 view_methods, /*tp_methods*/
1026 0, /*tp_members*/
1027 0, /*tp_getset*/
1028 0, /*tp_base*/
1029 0, /*tp_dict*/
1030 0, /*tp_descr_get*/
1031 0, /*tp_descr_set*/
1032 0, /*tp_dictoffset*/
1033 0, /*tp_init*/
1034 0, /*tp_alloc*/
1035 0, /*tp_new*/
1036 0, /*tp_free*/
1037 0, /*tp_is_gc*/
1038 };
1039
1040 /*************************** Database objects **************/
1041
1042 /*[clinic input]
1043 _msi.Database.OpenView
1044 sql: Py_UNICODE
1045 the SQL statement to execute
1046 /
1047
1048 Return a view object.
1049 [clinic start generated code]*/
1050
1051 static PyObject *
_msi_Database_OpenView_impl(msiobj * self,const Py_UNICODE * sql)1052 _msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql)
1053 /*[clinic end generated code: output=e712e6a11229abfd input=50f1771f37e500df]*/
1054 {
1055 int status;
1056 MSIHANDLE hView;
1057 msiobj *result;
1058
1059 if ((status = MsiDatabaseOpenViewW(self->h, sql, &hView)) != ERROR_SUCCESS)
1060 return msierror(status);
1061
1062 result = PyObject_New(struct msiobj, &msiview_Type);
1063 if (!result) {
1064 MsiCloseHandle(hView);
1065 return NULL;
1066 }
1067
1068 result->h = hView;
1069 return (PyObject*)result;
1070 }
1071
1072 /*[clinic input]
1073 _msi.Database.Commit
1074
1075 Commit the changes pending in the current transaction.
1076 [clinic start generated code]*/
1077
1078 static PyObject *
_msi_Database_Commit_impl(msiobj * self)1079 _msi_Database_Commit_impl(msiobj *self)
1080 /*[clinic end generated code: output=f33021feb8b0cdd8 input=375bb120d402266d]*/
1081 {
1082 int status;
1083
1084 if ((status = MsiDatabaseCommit(self->h)) != ERROR_SUCCESS)
1085 return msierror(status);
1086
1087 Py_RETURN_NONE;
1088 }
1089
1090 /*[clinic input]
1091 _msi.Database.GetSummaryInformation
1092 count: int
1093 the maximum number of updated values
1094 /
1095
1096 Return a new summary information object.
1097 [clinic start generated code]*/
1098
1099 static PyObject *
_msi_Database_GetSummaryInformation_impl(msiobj * self,int count)1100 _msi_Database_GetSummaryInformation_impl(msiobj *self, int count)
1101 /*[clinic end generated code: output=781e51a4ea4da847 input=18a899ead6521735]*/
1102 {
1103 int status;
1104 MSIHANDLE result;
1105 msiobj *oresult;
1106
1107 status = MsiGetSummaryInformation(self->h, NULL, count, &result);
1108 if (status != ERROR_SUCCESS)
1109 return msierror(status);
1110
1111 oresult = PyObject_New(struct msiobj, &summary_Type);
1112 if (!oresult) {
1113 MsiCloseHandle(result);
1114 return NULL;
1115 }
1116
1117 oresult->h = result;
1118 return (PyObject*)oresult;
1119 }
1120
1121 static PyMethodDef db_methods[] = {
1122 _MSI_DATABASE_OPENVIEW_METHODDEF
1123 _MSI_DATABASE_COMMIT_METHODDEF
1124 _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF
1125 _MSI_DATABASE_CLOSE_METHODDEF
1126 { NULL, NULL }
1127 };
1128
1129 static PyTypeObject msidb_Type = {
1130 PyVarObject_HEAD_INIT(NULL, 0)
1131 "_msi.Database", /*tp_name*/
1132 sizeof(msiobj), /*tp_basicsize*/
1133 0, /*tp_itemsize*/
1134 /* methods */
1135 (destructor)msiobj_dealloc, /*tp_dealloc*/
1136 0, /*tp_vectorcall_offset*/
1137 0, /*tp_getattr*/
1138 0, /*tp_setattr*/
1139 0, /*tp_as_async*/
1140 0, /*tp_repr*/
1141 0, /*tp_as_number*/
1142 0, /*tp_as_sequence*/
1143 0, /*tp_as_mapping*/
1144 0, /*tp_hash*/
1145 0, /*tp_call*/
1146 0, /*tp_str*/
1147 PyObject_GenericGetAttr,/*tp_getattro*/
1148 PyObject_GenericSetAttr,/*tp_setattro*/
1149 0, /*tp_as_buffer*/
1150 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1151 0, /*tp_doc*/
1152 0, /*tp_traverse*/
1153 0, /*tp_clear*/
1154 0, /*tp_richcompare*/
1155 0, /*tp_weaklistoffset*/
1156 0, /*tp_iter*/
1157 0, /*tp_iternext*/
1158 db_methods, /*tp_methods*/
1159 0, /*tp_members*/
1160 0, /*tp_getset*/
1161 0, /*tp_base*/
1162 0, /*tp_dict*/
1163 0, /*tp_descr_get*/
1164 0, /*tp_descr_set*/
1165 0, /*tp_dictoffset*/
1166 0, /*tp_init*/
1167 0, /*tp_alloc*/
1168 0, /*tp_new*/
1169 0, /*tp_free*/
1170 0, /*tp_is_gc*/
1171 };
1172
1173 #define Py_NOT_PERSIST(x, flag) \
1174 (x != (SIZE_T)(flag) && \
1175 x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
1176
1177 #define Py_INVALID_PERSIST(x) \
1178 (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) && \
1179 Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) && \
1180 Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) && \
1181 Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
1182 Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
1183
1184 /*[clinic input]
1185 _msi.OpenDatabase
1186 path: Py_UNICODE
1187 the file name of the MSI file
1188 persist: int
1189 the persistence mode
1190 /
1191
1192 Return a new database object.
1193 [clinic start generated code]*/
1194
1195 static PyObject *
_msi_OpenDatabase_impl(PyObject * module,const Py_UNICODE * path,int persist)1196 _msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist)
1197 /*[clinic end generated code: output=d34b7202b745de05 input=1300f3b97659559b]*/
1198 {
1199 int status;
1200 MSIHANDLE h;
1201 msiobj *result;
1202
1203 /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
1204 MsiOpenDatabase may treat the value as a pointer, leading to unexpected
1205 behavior. */
1206 if (Py_INVALID_PERSIST(persist))
1207 return msierror(ERROR_INVALID_PARAMETER);
1208 status = MsiOpenDatabaseW(path, (LPCWSTR)(SIZE_T)persist, &h);
1209 if (status != ERROR_SUCCESS)
1210 return msierror(status);
1211
1212 result = PyObject_New(struct msiobj, &msidb_Type);
1213 if (!result) {
1214 MsiCloseHandle(h);
1215 return NULL;
1216 }
1217 result->h = h;
1218 return (PyObject*)result;
1219 }
1220
1221 /*[clinic input]
1222 _msi.CreateRecord
1223 count: int
1224 the number of fields of the record
1225 /
1226
1227 Return a new record object.
1228 [clinic start generated code]*/
1229
1230 static PyObject *
_msi_CreateRecord_impl(PyObject * module,int count)1231 _msi_CreateRecord_impl(PyObject *module, int count)
1232 /*[clinic end generated code: output=0ba0a00beea3e99e input=53f17d5b5d9b077d]*/
1233 {
1234 MSIHANDLE h;
1235
1236 h = MsiCreateRecord(count);
1237 if (h == 0)
1238 return msierror(0);
1239
1240 return record_new(h);
1241 }
1242
1243
1244 static PyMethodDef msi_methods[] = {
1245 _MSI_UUIDCREATE_METHODDEF
1246 _MSI_FCICREATE_METHODDEF
1247 _MSI_OPENDATABASE_METHODDEF
1248 _MSI_CREATERECORD_METHODDEF
1249 {NULL, NULL} /* sentinel */
1250 };
1251
1252 static char msi_doc[] = "Documentation";
1253
1254
1255 static struct PyModuleDef _msimodule = {
1256 PyModuleDef_HEAD_INIT,
1257 "_msi",
1258 msi_doc,
1259 -1,
1260 msi_methods,
1261 NULL,
1262 NULL,
1263 NULL,
1264 NULL
1265 };
1266
1267 PyMODINIT_FUNC
PyInit__msi(void)1268 PyInit__msi(void)
1269 {
1270 PyObject *m;
1271
1272 m = PyModule_Create(&_msimodule);
1273 if (m == NULL)
1274 return NULL;
1275
1276 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (long)(SIZE_T)MSIDBOPEN_CREATEDIRECT);
1277 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (long)(SIZE_T)MSIDBOPEN_CREATE);
1278 PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (long)(SIZE_T)MSIDBOPEN_DIRECT);
1279 PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (long)(SIZE_T)MSIDBOPEN_READONLY);
1280 PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (long)(SIZE_T)MSIDBOPEN_TRANSACT);
1281 PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (long)(SIZE_T)MSIDBOPEN_PATCHFILE);
1282
1283 PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1284 PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
1285
1286 PyModule_AddIntMacro(m, MSIMODIFY_SEEK);
1287 PyModule_AddIntMacro(m, MSIMODIFY_REFRESH);
1288 PyModule_AddIntMacro(m, MSIMODIFY_INSERT);
1289 PyModule_AddIntMacro(m, MSIMODIFY_UPDATE);
1290 PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN);
1291 PyModule_AddIntMacro(m, MSIMODIFY_REPLACE);
1292 PyModule_AddIntMacro(m, MSIMODIFY_MERGE);
1293 PyModule_AddIntMacro(m, MSIMODIFY_DELETE);
1294 PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY);
1295 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE);
1296 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW);
1297 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD);
1298 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE);
1299
1300 PyModule_AddIntMacro(m, PID_CODEPAGE);
1301 PyModule_AddIntMacro(m, PID_TITLE);
1302 PyModule_AddIntMacro(m, PID_SUBJECT);
1303 PyModule_AddIntMacro(m, PID_AUTHOR);
1304 PyModule_AddIntMacro(m, PID_KEYWORDS);
1305 PyModule_AddIntMacro(m, PID_COMMENTS);
1306 PyModule_AddIntMacro(m, PID_TEMPLATE);
1307 PyModule_AddIntMacro(m, PID_LASTAUTHOR);
1308 PyModule_AddIntMacro(m, PID_REVNUMBER);
1309 PyModule_AddIntMacro(m, PID_LASTPRINTED);
1310 PyModule_AddIntMacro(m, PID_CREATE_DTM);
1311 PyModule_AddIntMacro(m, PID_LASTSAVE_DTM);
1312 PyModule_AddIntMacro(m, PID_PAGECOUNT);
1313 PyModule_AddIntMacro(m, PID_WORDCOUNT);
1314 PyModule_AddIntMacro(m, PID_CHARCOUNT);
1315 PyModule_AddIntMacro(m, PID_APPNAME);
1316 PyModule_AddIntMacro(m, PID_SECURITY);
1317
1318 MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1319 if (!MSIError)
1320 return NULL;
1321 PyModule_AddObject(m, "MSIError", MSIError);
1322 return m;
1323 }
1324