blob: 9f1015845acff2f25aedf49e0fcc45f2b21abeab [file] [log] [blame]
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001/* Helper library for MSI creation with Python.
Florent Xiclunac934f322010-09-03 23:47:32 +00002 * Copyright (C) 2005 Martin v. Löwis
Martin v. Löwisdd860ca2006-03-05 13:39:10 +00003 * Licensed to PSF under a contributor agreement.
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00004 */
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
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +030015/*[clinic input]
16module _msi
17class _msi.Record "msiobj *" "&record_Type"
18class _msi.SummaryInformation "msiobj *" "&summary_Type"
19class _msi.View "msiobj *" "&msiview_Type"
20class _msi.Database "msiobj *" "&msidb_Type"
21[clinic start generated code]*/
22/*[clinic end generated code: output=da39a3ee5e6b4b0d input=89a3605762cf4bdc]*/
23
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000024static PyObject *MSIError;
25
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +030026/*[clinic input]
27_msi.UuidCreate
28
29Return the string representation of a new unique identifier.
30[clinic start generated code]*/
31
32static PyObject *
33_msi_UuidCreate_impl(PyObject *module)
34/*[clinic end generated code: output=534ecf36f10af98e input=168024ab4b3e832b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000035{
36 UUID result;
Victor Stinner9d3b93b2011-11-22 02:27:30 +010037 wchar_t *cresult;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000038 PyObject *oresult;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000039
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000040 /* 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) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000045 PyErr_SetString(PyExc_NotImplementedError, "processing 'no address' result");
46 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000047 }
48
Martin v. Löwis371bb502008-08-16 13:02:57 +000049 if (UuidToStringW(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +000050 PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
51 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000052 }
53
Victor Stinner9d3b93b2011-11-22 02:27:30 +010054 oresult = PyUnicode_FromWideChar(cresult, wcslen(cresult));
Martin v. Löwis371bb502008-08-16 13:02:57 +000055 RpcStringFreeW(&cresult);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000056 return oresult;
57
58}
59
Serhiy Storchakaba67d732020-06-30 11:56:03 +030060/* Helper for converting file names from UTF-8 to wchat_t*. */
61static wchar_t *
62utf8_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
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000084/* FCI callback functions */
85
86static FNFCIALLOC(cb_alloc)
87{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030088 return PyMem_RawMalloc(cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000089}
90
91static FNFCIFREE(cb_free)
92{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030093 PyMem_RawFree(memory);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +000094}
95
96static FNFCIOPEN(cb_open)
97{
Serhiy Storchakaba67d732020-06-30 11:56:03 +030098 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);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000104 if (result == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000105 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000106 return result;
107}
108
109static FNFCIREAD(cb_read)
110{
Victor Stinner67158282013-11-20 00:14:49 +0100111 UINT result = (UINT)_read((int)hf, memory, cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000112 if (result != cb)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000113 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000114 return result;
115}
116
117static FNFCIWRITE(cb_write)
118{
Victor Stinner67158282013-11-20 00:14:49 +0100119 UINT result = (UINT)_write((int)hf, memory, cb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000120 if (result != cb)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000121 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000122 return result;
123}
124
125static FNFCICLOSE(cb_close)
126{
Victor Stinner67158282013-11-20 00:14:49 +0100127 int result = _close((int)hf);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000128 if (result != 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000129 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000130 return result;
131}
132
133static FNFCISEEK(cb_seek)
134{
Victor Stinner67158282013-11-20 00:14:49 +0100135 long result = (long)_lseek((int)hf, dist, seektype);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000136 if (result == -1)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000137 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000138 return result;
139}
140
141static FNFCIDELETE(cb_delete)
142{
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300143 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);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000149 if (result != 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000150 *err = errno;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000151 return result;
152}
153
154static FNFCIFILEPLACED(cb_fileplaced)
155{
156 return 0;
157}
158
159static FNFCIGETTEMPFILE(cb_gettempfile)
160{
161 char *name = _tempnam("", "tmp");
162 if ((name != NULL) && ((int)strlen(name) < cbTempName)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000163 strcpy(pszTempName, name);
164 free(name);
165 return TRUE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000166 }
167
168 if (name) free(name);
169 return FALSE;
170}
171
172static FNFCISTATUS(cb_status)
173{
174 if (pv) {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200175 _Py_IDENTIFIER(status);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200176
177 PyObject *result = _PyObject_CallMethodId(pv, &PyId_status, "iii", typeStatus, cb1, cb2);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000178 if (result == NULL)
179 return -1;
180 Py_DECREF(result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000181 }
182 return 0;
183}
184
185static FNFCIGETNEXTCABINET(cb_getnextcabinet)
186{
187 if (pv) {
Martin v. Löwisbd928fe2011-10-14 10:20:37 +0200188 _Py_IDENTIFIER(getnextcabinet);
Martin v. Löwisafe55bb2011-10-09 10:38:36 +0200189
190 PyObject *result = _PyObject_CallMethodId(pv, &PyId_getnextcabinet, "i", pccab->iCab);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000191 if (result == NULL)
192 return -1;
193 if (!PyBytes_Check(result)) {
194 PyErr_Format(PyExc_TypeError,
195 "Incorrect return type %s from getnextcabinet",
196 result->ob_type->tp_name);
197 Py_DECREF(result);
198 return FALSE;
199 }
200 strncpy(pccab->szCab, PyBytes_AsString(result), sizeof(pccab->szCab));
201 return TRUE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000202 }
203 return FALSE;
204}
205
206static FNFCIGETOPENINFO(cb_getopeninfo)
207{
208 BY_HANDLE_FILE_INFORMATION bhfi;
209 FILETIME filetime;
210 HANDLE handle;
211
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300212 wchar_t *ws = utf8_to_wchar(pszName, err);
213 if (ws == NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000214 return -1;
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300215 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000216
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300217 /* 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) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000226 CloseHandle(handle);
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300227 PyMem_Free(ws);
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000228 return -1;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000229 }
230
231 FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
232 FileTimeToDosDateTime(&filetime, pdate, ptime);
233
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000234 *pattribs = (int)(bhfi.dwFileAttributes &
235 (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000236
237 CloseHandle(handle);
238
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300239 int result = _wopen(ws, _O_RDONLY | _O_BINARY | O_NOINHERIT);
240 PyMem_Free(ws);
241 return result;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000242}
243
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300244/*[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
253Create a new CAB file.
254[clinic start generated code]*/
255
256static PyObject *
257_msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files)
258/*[clinic end generated code: output=55dc05728361b799 input=1d2d75fdc8b44b71]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000259{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300260 const char *p;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000261 CCAB ccab;
262 HFCI hfci;
263 ERF erf;
Christian Heimes0bd4e112008-02-12 22:59:25 +0000264 Py_ssize_t i;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000265
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000266 if (!PyList_Check(files)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000267 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
268 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000269 }
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
Serhiy Storchakaba67d732020-06-30 11:56:03 +0300283 for (i = 0, p = cabname; *p; p++)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000284 if (*p == '\\' || *p == '/')
285 i = p - cabname + 1;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000286
Christian Heimes0bd4e112008-02-12 22:59:25 +0000287 if (i >= sizeof(ccab.szCabPath) ||
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000288 strlen(cabname+i) >= sizeof(ccab.szCab)) {
289 PyErr_SetString(PyExc_ValueError, "path name too long");
290 return 0;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000291 }
292
Christian Heimes0bd4e112008-02-12 22:59:25 +0000293 if (i > 0) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000294 memcpy(ccab.szCabPath, cabname, i);
295 ccab.szCabPath[i] = '\0';
296 strcpy(ccab.szCab, cabname+i);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000297 } else {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000298 strcpy(ccab.szCabPath, ".\\");
299 strcpy(ccab.szCab, cabname);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000300 }
301
302 hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000303 cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
304 cb_gettempfile, &ccab, NULL);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000305
306 if (hfci == NULL) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000307 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
308 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000309 }
310
311 for (i=0; i < PyList_GET_SIZE(files); i++) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000312 PyObject *item = PyList_GET_ITEM(files, i);
313 char *filename, *cabname;
Zachary Ware0a29e892015-05-18 00:47:15 -0500314
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
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000321 if (!FCIAddFile(hfci, filename, cabname, FALSE,
322 cb_getnextcabinet, cb_status, cb_getopeninfo,
323 tcompTYPE_MSZIP))
324 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000325 }
326
327 if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000328 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000329
330 if (!FCIDestroy(hfci))
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000331 goto err;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000332
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200333 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000334err:
Zachary Ware0a29e892015-05-18 00:47:15 -0500335 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
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000340 FCIDestroy(hfci);
341 return NULL;
342}
343
344typedef struct msiobj{
345 PyObject_HEAD
346 MSIHANDLE h;
347}msiobj;
348
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000349static void
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000350msiobj_dealloc(msiobj* msidb)
351{
352 MsiCloseHandle(msidb->h);
353 msidb->h = 0;
Zackery Spytzcb04f752017-11-07 03:03:09 -0700354 PyObject_Del(msidb);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000355}
356
357static PyObject*
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000358msierror(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) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000367 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;
Berker Peksag4864a612017-11-24 12:53:58 +0300386 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;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000392 default:
393 PyErr_Format(MSIError, "unknown error %x", status);
394 return NULL;
395 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000396 }
397
398 code = MsiRecordGetInteger(err, 1); /* XXX code */
399 if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000400 res = malloc(size+1);
Zackery Spytz4e519372018-09-07 16:02:56 -0600401 if (res == NULL) {
402 MsiCloseHandle(err);
403 return PyErr_NoMemory();
404 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000405 MsiFormatRecord(0, err, res, &size);
406 res[size]='\0';
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000407 }
408 MsiCloseHandle(err);
409 PyErr_SetString(MSIError, res);
410 if (res != buf)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000411 free(res);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000412 return NULL;
413}
414
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300415#include "clinic/_msi.c.h"
416
417/*[clinic input]
418_msi.Database.Close
419
420Close the database object.
421[clinic start generated code]*/
422
423static PyObject *
424_msi_Database_Close_impl(msiobj *self)
425/*[clinic end generated code: output=ddf2d7712ea804f1 input=104330ce4a486187]*/
Berker Peksaga9356542017-11-07 15:58:53 +0300426{
427 int status;
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300428 if ((status = MsiCloseHandle(self->h)) != ERROR_SUCCESS) {
Berker Peksaga9356542017-11-07 15:58:53 +0300429 return msierror(status);
430 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300431 self->h = 0;
Berker Peksaga9356542017-11-07 15:58:53 +0300432 Py_RETURN_NONE;
433}
434
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000435/*************************** Record objects **********************/
436
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300437/*[clinic input]
438_msi.Record.GetFieldCount
439
440Return the number of fields of the record.
441[clinic start generated code]*/
442
443static PyObject *
444_msi_Record_GetFieldCount_impl(msiobj *self)
445/*[clinic end generated code: output=112795079c904398 input=5fb9d4071b28897b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000446{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300447 return PyLong_FromLong(MsiRecordGetFieldCount(self->h));
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000448}
449
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300450/*[clinic input]
451_msi.Record.GetInteger
452 field: unsigned_int(bitwise=True)
453 /
454
455Return the value of field as an integer where possible.
456[clinic start generated code]*/
457
458static PyObject *
459_msi_Record_GetInteger_impl(msiobj *self, unsigned int field)
460/*[clinic end generated code: output=7174ebb6e8ed1c79 input=d19209947e2bfe61]*/
Martin v. Löwise95593e2008-06-02 10:08:54 +0000461{
Martin v. Löwise95593e2008-06-02 10:08:54 +0000462 int status;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000463
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300464 status = MsiRecordGetInteger(self->h, field);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000465 if (status == MSI_NULL_INTEGER){
466 PyErr_SetString(MSIError, "could not convert record field to integer");
467 return NULL;
468 }
Martin v. Löwis704d8b12008-06-02 11:32:23 +0000469 return PyLong_FromLong((long) status);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000470}
471
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300472/*[clinic input]
473_msi.Record.GetString
474 field: unsigned_int(bitwise=True)
475 /
476
477Return the value of field as a string where possible.
478[clinic start generated code]*/
479
480static PyObject *
481_msi_Record_GetString_impl(msiobj *self, unsigned int field)
482/*[clinic end generated code: output=f670d1b484cfa47c input=ffa11f21450b77d8]*/
Martin v. Löwise95593e2008-06-02 10:08:54 +0000483{
Martin v. Löwise95593e2008-06-02 10:08:54 +0000484 unsigned int status;
Martin v. Löwis371bb502008-08-16 13:02:57 +0000485 WCHAR buf[2000];
486 WCHAR *res = buf;
Martin v. Löwise95593e2008-06-02 10:08:54 +0000487 DWORD size = sizeof(buf);
488 PyObject* string;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000489
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300490 status = MsiRecordGetStringW(self->h, field, res, &size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000491 if (status == ERROR_MORE_DATA) {
Martin v. Löwis371bb502008-08-16 13:02:57 +0000492 res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR));
Martin v. Löwise95593e2008-06-02 10:08:54 +0000493 if (res == NULL)
494 return PyErr_NoMemory();
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300495 status = MsiRecordGetStringW(self->h, field, res, &size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000496 }
497 if (status != ERROR_SUCCESS)
498 return msierror((int) status);
Victor Stinner9d3b93b2011-11-22 02:27:30 +0100499 string = PyUnicode_FromWideChar(res, size);
Martin v. Löwise95593e2008-06-02 10:08:54 +0000500 if (buf != res)
501 free(res);
502 return string;
503}
504
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300505/*[clinic input]
506_msi.Record.ClearData
507
508Set all fields of the record to 0.
509[clinic start generated code]*/
510
511static PyObject *
512_msi_Record_ClearData_impl(msiobj *self)
513/*[clinic end generated code: output=1891467214b977f4 input=2a911c95aaded102]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000514{
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300515 int status = MsiRecordClearData(self->h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000516 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000517 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000518
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200519 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000520}
521
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300522/*[clinic input]
523_msi.Record.SetString
524 field: int
525 value: Py_UNICODE
526 /
527
528Set field to a string value.
529[clinic start generated code]*/
530
531static PyObject *
532_msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value)
533/*[clinic end generated code: output=2e37505b0f11f985 input=fb8ec70a2a6148e0]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000534{
535 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000536
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300537 if ((status = MsiRecordSetStringW(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000538 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000539
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200540 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000541}
542
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300543/*[clinic input]
544_msi.Record.SetStream
545 field: int
546 value: Py_UNICODE
547 /
548
549Set field to the contents of the file named value.
550[clinic start generated code]*/
551
552static PyObject *
553_msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value)
554/*[clinic end generated code: output=442facac16913b48 input=a07aa19b865e8292]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000555{
556 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000557
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300558 if ((status = MsiRecordSetStreamW(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000559 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000560
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200561 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000562}
563
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300564/*[clinic input]
565_msi.Record.SetInteger
566 field: int
567 value: int
568 /
569
570Set field to an integer value.
571[clinic start generated code]*/
572
573static PyObject *
574_msi_Record_SetInteger_impl(msiobj *self, int field, int value)
575/*[clinic end generated code: output=669e8647775d0ce7 input=c571aa775e7e451b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000576{
577 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000578
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300579 if ((status = MsiRecordSetInteger(self->h, field, value)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000580 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000581
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200582 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000583}
584
585
586
587static PyMethodDef record_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300588 _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
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000595 { NULL, NULL }
596};
597
598static PyTypeObject record_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000599 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*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200605 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000606 0, /*tp_getattr*/
607 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200608 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000609 0, /*tp_repr*/
610 0, /*tp_as_number*/
611 0, /*tp_as_sequence*/
612 0, /*tp_as_mapping*/
613 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000614 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
642static PyObject*
643record_new(MSIHANDLE h)
644{
Victor Stinner92055202020-04-08 00:38:15 +0200645 msiobj *result = PyObject_New(struct msiobj, &record_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000646
647 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000648 MsiCloseHandle(h);
649 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000650 }
651
652 result->h = h;
653 return (PyObject*)result;
654}
655
656/*************************** SummaryInformation objects **************/
657
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300658/*[clinic input]
659_msi.SummaryInformation.GetProperty
660 field: int
661 the name of the property, one of the PID_* constants
662 /
663
664Return a property of the summary.
665[clinic start generated code]*/
666
667static PyObject *
668_msi_SummaryInformation_GetProperty_impl(msiobj *self, int field)
669/*[clinic end generated code: output=f8946a33ee14f6ef input=f8dfe2c890d6cb8b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000670{
671 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000672 PyObject *result;
673 UINT type;
674 INT ival;
675 FILETIME fval;
676 char sbuf[1000];
677 char *sval = sbuf;
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800678 DWORD ssize = sizeof(sbuf);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000679
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300680 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000681 &fval, sval, &ssize);
Thomas Wouters89f507f2006-12-13 04:49:30 +0000682 if (status == ERROR_MORE_DATA) {
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800683 ssize++;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000684 sval = malloc(ssize);
Zackery Spytz4e519372018-09-07 16:02:56 -0600685 if (sval == NULL) {
686 return PyErr_NoMemory();
687 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300688 status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000689 &fval, sval, &ssize);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000690 }
Zackery Spytz549e55a2019-05-31 18:16:20 -0600691 if (status != ERROR_SUCCESS) {
692 return msierror(status);
693 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000694
695 switch(type) {
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800696 case VT_I2:
697 case VT_I4:
698 result = PyLong_FromLong(ival);
699 break;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000700 case VT_FILETIME:
701 PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800702 result = NULL;
703 break;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000704 case VT_LPSTR:
705 result = PyBytes_FromStringAndSize(sval, ssize);
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800706 break;
Berker Peksag19fb1342017-11-24 18:11:18 +0300707 case VT_EMPTY:
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800708 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;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000715 }
Tzu-ping Chung2de576e2019-02-03 01:13:23 +0800716 if (sval != sbuf)
717 free(sval);
718 return result;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000719}
720
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300721/*[clinic input]
722_msi.SummaryInformation.GetPropertyCount
723
724Return the number of summary properties.
725[clinic start generated code]*/
726
727static PyObject *
728_msi_SummaryInformation_GetPropertyCount_impl(msiobj *self)
729/*[clinic end generated code: output=68e94b2aeee92b3d input=2e71e985586d82dc]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000730{
731 int status;
732 UINT result;
733
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300734 status = MsiSummaryInfoGetPropertyCount(self->h, &result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000735 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000736 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000737
Christian Heimes217cfd12007-12-02 14:31:20 +0000738 return PyLong_FromLong(result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000739}
740
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300741/*[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
749Set a property.
750[clinic start generated code]*/
751
752static PyObject *
753_msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
754 PyObject *data)
755/*[clinic end generated code: output=3d4692c8984bb675 input=f2a7811b905abbed]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000756{
757 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000758
Martin v. Löwis371bb502008-08-16 13:02:57 +0000759 if (PyUnicode_Check(data)) {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300760#if USE_UNICODE_WCHAR_CACHE
761_Py_COMP_DIAG_PUSH
762_Py_COMP_DIAG_IGNORE_DEPR_DECLS
Serhiy Storchakaccdc09e2017-06-28 09:55:22 +0300763 const WCHAR *value = _PyUnicode_AsUnicode(data);
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300764_Py_COMP_DIAG_POP
765#else /* USE_UNICODE_WCHAR_CACHE */
766 WCHAR *value = PyUnicode_AsWideCharString(data, NULL);
767#endif /* USE_UNICODE_WCHAR_CACHE */
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300768 if (value == NULL) {
769 return NULL;
770 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300771 status = MsiSummaryInfoSetPropertyW(self->h, field, VT_LPSTR,
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300772 0, NULL, value);
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300773#if !USE_UNICODE_WCHAR_CACHE
774 PyMem_Free(value);
775#endif /* USE_UNICODE_WCHAR_CACHE */
Martin v. Löwisd1a1d1e2007-12-04 22:10:37 +0000776 } else if (PyLong_CheckExact(data)) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000777 long value = PyLong_AsLong(data);
778 if (value == -1 && PyErr_Occurred()) {
779 return NULL;
780 }
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300781 status = MsiSummaryInfoSetProperty(self->h, field, VT_I4,
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000782 value, NULL, NULL);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000783 } else {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000784 PyErr_SetString(PyExc_TypeError, "unsupported type");
785 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000786 }
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000787
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000788 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000789 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000790
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200791 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000792}
793
794
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300795/*[clinic input]
796_msi.SummaryInformation.Persist
797
798Write the modified properties to the summary information stream.
799[clinic start generated code]*/
800
801static PyObject *
802_msi_SummaryInformation_Persist_impl(msiobj *self)
803/*[clinic end generated code: output=c564bd17f5e122c9 input=e3dda9d530095ef7]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000804{
805 int status;
806
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300807 status = MsiSummaryInfoPersist(self->h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000808 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000809 return msierror(status);
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200810 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000811}
812
813static PyMethodDef summary_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300814 _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF
815 _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF
816 _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF
817 _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000818 { NULL, NULL }
819};
820
821static PyTypeObject summary_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000822 PyVarObject_HEAD_INIT(NULL, 0)
823 "_msi.SummaryInformation", /*tp_name*/
824 sizeof(msiobj), /*tp_basicsize*/
825 0, /*tp_itemsize*/
826 /* methods */
827 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200828 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000829 0, /*tp_getattr*/
830 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +0200831 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000832 0, /*tp_repr*/
833 0, /*tp_as_number*/
834 0, /*tp_as_sequence*/
835 0, /*tp_as_mapping*/
836 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000837 0, /*tp_call*/
838 0, /*tp_str*/
839 PyObject_GenericGetAttr,/*tp_getattro*/
840 PyObject_GenericSetAttr,/*tp_setattro*/
841 0, /*tp_as_buffer*/
842 Py_TPFLAGS_DEFAULT, /*tp_flags*/
843 0, /*tp_doc*/
844 0, /*tp_traverse*/
845 0, /*tp_clear*/
846 0, /*tp_richcompare*/
847 0, /*tp_weaklistoffset*/
848 0, /*tp_iter*/
849 0, /*tp_iternext*/
850 summary_methods, /*tp_methods*/
851 0, /*tp_members*/
852 0, /*tp_getset*/
853 0, /*tp_base*/
854 0, /*tp_dict*/
855 0, /*tp_descr_get*/
856 0, /*tp_descr_set*/
857 0, /*tp_dictoffset*/
858 0, /*tp_init*/
859 0, /*tp_alloc*/
860 0, /*tp_new*/
861 0, /*tp_free*/
862 0, /*tp_is_gc*/
863};
864
865/*************************** View objects **************/
866
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300867/*[clinic input]
868_msi.View.Execute
869 params as oparams: object
870 a record describing actual values of the parameter tokens
871 in the query or None
872 /
873
874Execute the SQL query of the view.
875[clinic start generated code]*/
876
877static PyObject *
878_msi_View_Execute(msiobj *self, PyObject *oparams)
879/*[clinic end generated code: output=f0f65fd2900bcb4e input=cb163a15d453348e]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000880{
881 int status;
882 MSIHANDLE params = 0;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000883
884 if (oparams != Py_None) {
885 if (oparams->ob_type != &record_Type) {
886 PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
887 return NULL;
888 }
889 params = ((msiobj*)oparams)->h;
890 }
891
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300892 status = MsiViewExecute(self->h, params);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000893 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000894 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000895
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200896 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000897}
898
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300899/*[clinic input]
900_msi.View.Fetch
901
902Return a result record of the query.
903[clinic start generated code]*/
904
905static PyObject *
906_msi_View_Fetch_impl(msiobj *self)
907/*[clinic end generated code: output=ba154a3794537d4e input=7f3e3d06c449001c]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000908{
909 int status;
910 MSIHANDLE result;
911
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300912 status = MsiViewFetch(self->h, &result);
Berker Peksagbdb83152017-11-23 15:47:30 +0300913 if (status == ERROR_NO_MORE_ITEMS) {
914 Py_RETURN_NONE;
915 } else if (status != ERROR_SUCCESS) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000916 return msierror(status);
Berker Peksagbdb83152017-11-23 15:47:30 +0300917 }
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000918
919 return record_new(result);
920}
921
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300922/*[clinic input]
923_msi.View.GetColumnInfo
924 kind: int
925 MSICOLINFO_NAMES or MSICOLINFO_TYPES
926 /
927
928Return a record describing the columns of the view.
929[clinic start generated code]*/
930
931static PyObject *
932_msi_View_GetColumnInfo_impl(msiobj *self, int kind)
933/*[clinic end generated code: output=e7c1697db9403660 input=afedb892bf564a3b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000934{
935 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000936 MSIHANDLE result;
937
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300938 if ((status = MsiViewGetColumnInfo(self->h, kind, &result)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000939 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000940
941 return record_new(result);
942}
943
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300944/*[clinic input]
945_msi.View.Modify
946 kind: int
947 one of the MSIMODIFY_* constants
948 data: object
949 a record describing the new data
950 /
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000951
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300952Modify the view.
953[clinic start generated code]*/
954
955static PyObject *
956_msi_View_Modify_impl(msiobj *self, int kind, PyObject *data)
957/*[clinic end generated code: output=69aaf3ce8ddac0ba input=2828de22de0d47b4]*/
958{
959 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000960
961 if (data->ob_type != &record_Type) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000962 PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
963 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000964 }
965
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300966 if ((status = MsiViewModify(self->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000967 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000968
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200969 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000970}
971
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300972/*[clinic input]
973_msi.View.Close
974
975Close the view.
976[clinic start generated code]*/
977
978static PyObject *
979_msi_View_Close_impl(msiobj *self)
980/*[clinic end generated code: output=488f7b8645ca104a input=de6927d1308c401c]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000981{
982 int status;
983
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300984 if ((status = MsiViewClose(self->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +0000985 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000986
Serhiy Storchaka228b12e2017-01-23 09:47:21 +0200987 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000988}
989
990static PyMethodDef view_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +0300991 _MSI_VIEW_EXECUTE_METHODDEF
992 _MSI_VIEW_GETCOLUMNINFO_METHODDEF
993 _MSI_VIEW_FETCH_METHODDEF
994 _MSI_VIEW_MODIFY_METHODDEF
995 _MSI_VIEW_CLOSE_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +0000996 { NULL, NULL }
997};
998
999static PyTypeObject msiview_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001000 PyVarObject_HEAD_INIT(NULL, 0)
1001 "_msi.View", /*tp_name*/
1002 sizeof(msiobj), /*tp_basicsize*/
1003 0, /*tp_itemsize*/
1004 /* methods */
1005 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001006 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001007 0, /*tp_getattr*/
1008 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001009 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001010 0, /*tp_repr*/
1011 0, /*tp_as_number*/
1012 0, /*tp_as_sequence*/
1013 0, /*tp_as_mapping*/
1014 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001015 0, /*tp_call*/
1016 0, /*tp_str*/
1017 PyObject_GenericGetAttr,/*tp_getattro*/
1018 PyObject_GenericSetAttr,/*tp_setattro*/
1019 0, /*tp_as_buffer*/
1020 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1021 0, /*tp_doc*/
1022 0, /*tp_traverse*/
1023 0, /*tp_clear*/
1024 0, /*tp_richcompare*/
1025 0, /*tp_weaklistoffset*/
1026 0, /*tp_iter*/
1027 0, /*tp_iternext*/
1028 view_methods, /*tp_methods*/
1029 0, /*tp_members*/
1030 0, /*tp_getset*/
1031 0, /*tp_base*/
1032 0, /*tp_dict*/
1033 0, /*tp_descr_get*/
1034 0, /*tp_descr_set*/
1035 0, /*tp_dictoffset*/
1036 0, /*tp_init*/
1037 0, /*tp_alloc*/
1038 0, /*tp_new*/
1039 0, /*tp_free*/
1040 0, /*tp_is_gc*/
1041};
1042
1043/*************************** Database objects **************/
1044
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001045/*[clinic input]
1046_msi.Database.OpenView
1047 sql: Py_UNICODE
1048 the SQL statement to execute
1049 /
1050
1051Return a view object.
1052[clinic start generated code]*/
1053
1054static PyObject *
1055_msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql)
1056/*[clinic end generated code: output=e712e6a11229abfd input=50f1771f37e500df]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001057{
1058 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001059 MSIHANDLE hView;
1060 msiobj *result;
1061
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001062 if ((status = MsiDatabaseOpenViewW(self->h, sql, &hView)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001063 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001064
Victor Stinner92055202020-04-08 00:38:15 +02001065 result = PyObject_New(struct msiobj, &msiview_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001066 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001067 MsiCloseHandle(hView);
1068 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001069 }
1070
1071 result->h = hView;
1072 return (PyObject*)result;
1073}
1074
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001075/*[clinic input]
1076_msi.Database.Commit
1077
1078Commit the changes pending in the current transaction.
1079[clinic start generated code]*/
1080
1081static PyObject *
1082_msi_Database_Commit_impl(msiobj *self)
1083/*[clinic end generated code: output=f33021feb8b0cdd8 input=375bb120d402266d]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001084{
1085 int status;
1086
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001087 if ((status = MsiDatabaseCommit(self->h)) != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001088 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001089
Serhiy Storchaka228b12e2017-01-23 09:47:21 +02001090 Py_RETURN_NONE;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001091}
1092
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001093/*[clinic input]
1094_msi.Database.GetSummaryInformation
1095 count: int
1096 the maximum number of updated values
1097 /
1098
1099Return a new summary information object.
1100[clinic start generated code]*/
1101
1102static PyObject *
1103_msi_Database_GetSummaryInformation_impl(msiobj *self, int count)
1104/*[clinic end generated code: output=781e51a4ea4da847 input=18a899ead6521735]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001105{
1106 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001107 MSIHANDLE result;
1108 msiobj *oresult;
1109
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001110 status = MsiGetSummaryInformation(self->h, NULL, count, &result);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001111 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001112 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001113
Victor Stinner92055202020-04-08 00:38:15 +02001114 oresult = PyObject_New(struct msiobj, &summary_Type);
Zackery Spytzbf94cc72019-03-07 11:20:13 -07001115 if (!oresult) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001116 MsiCloseHandle(result);
1117 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001118 }
1119
1120 oresult->h = result;
1121 return (PyObject*)oresult;
1122}
1123
1124static PyMethodDef db_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001125 _MSI_DATABASE_OPENVIEW_METHODDEF
1126 _MSI_DATABASE_COMMIT_METHODDEF
1127 _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF
1128 _MSI_DATABASE_CLOSE_METHODDEF
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001129 { NULL, NULL }
1130};
1131
1132static PyTypeObject msidb_Type = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001133 PyVarObject_HEAD_INIT(NULL, 0)
1134 "_msi.Database", /*tp_name*/
1135 sizeof(msiobj), /*tp_basicsize*/
1136 0, /*tp_itemsize*/
1137 /* methods */
1138 (destructor)msiobj_dealloc, /*tp_dealloc*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001139 0, /*tp_vectorcall_offset*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001140 0, /*tp_getattr*/
1141 0, /*tp_setattr*/
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001142 0, /*tp_as_async*/
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001143 0, /*tp_repr*/
1144 0, /*tp_as_number*/
1145 0, /*tp_as_sequence*/
1146 0, /*tp_as_mapping*/
1147 0, /*tp_hash*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001148 0, /*tp_call*/
1149 0, /*tp_str*/
1150 PyObject_GenericGetAttr,/*tp_getattro*/
1151 PyObject_GenericSetAttr,/*tp_setattro*/
1152 0, /*tp_as_buffer*/
1153 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1154 0, /*tp_doc*/
1155 0, /*tp_traverse*/
1156 0, /*tp_clear*/
1157 0, /*tp_richcompare*/
1158 0, /*tp_weaklistoffset*/
1159 0, /*tp_iter*/
1160 0, /*tp_iternext*/
1161 db_methods, /*tp_methods*/
1162 0, /*tp_members*/
1163 0, /*tp_getset*/
1164 0, /*tp_base*/
1165 0, /*tp_dict*/
1166 0, /*tp_descr_get*/
1167 0, /*tp_descr_set*/
1168 0, /*tp_dictoffset*/
1169 0, /*tp_init*/
1170 0, /*tp_alloc*/
1171 0, /*tp_new*/
1172 0, /*tp_free*/
1173 0, /*tp_is_gc*/
1174};
1175
Steve Dower6ceda632016-09-09 11:56:34 -07001176#define Py_NOT_PERSIST(x, flag) \
Segev Finer679b5662017-07-27 01:17:57 +03001177 (x != (SIZE_T)(flag) && \
1178 x != ((SIZE_T)(flag) | MSIDBOPEN_PATCHFILE))
Steve Dower6ceda632016-09-09 11:56:34 -07001179
1180#define Py_INVALID_PERSIST(x) \
1181 (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) && \
1182 Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) && \
1183 Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) && \
1184 Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
1185 Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
1186
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001187/*[clinic input]
1188_msi.OpenDatabase
1189 path: Py_UNICODE
1190 the file name of the MSI file
1191 persist: int
1192 the persistence mode
1193 /
1194
1195Return a new database object.
1196[clinic start generated code]*/
1197
1198static PyObject *
1199_msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist)
1200/*[clinic end generated code: output=d34b7202b745de05 input=1300f3b97659559b]*/
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001201{
1202 int status;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001203 MSIHANDLE h;
1204 msiobj *result;
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001205
Steve Dower6ceda632016-09-09 11:56:34 -07001206 /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
1207 MsiOpenDatabase may treat the value as a pointer, leading to unexpected
1208 behavior. */
1209 if (Py_INVALID_PERSIST(persist))
1210 return msierror(ERROR_INVALID_PARAMETER);
Serhiy Storchaka55939b12020-06-25 11:37:12 +03001211 status = MsiOpenDatabaseW(path, (LPCWSTR)(SIZE_T)persist, &h);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001212 if (status != ERROR_SUCCESS)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001213 return msierror(status);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001214
Victor Stinner92055202020-04-08 00:38:15 +02001215 result = PyObject_New(struct msiobj, &msidb_Type);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001216 if (!result) {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001217 MsiCloseHandle(h);
1218 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001219 }
1220 result->h = h;
1221 return (PyObject*)result;
1222}
1223
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001224/*[clinic input]
1225_msi.CreateRecord
1226 count: int
1227 the number of fields of the record
1228 /
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001229
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001230Return a new record object.
1231[clinic start generated code]*/
1232
1233static PyObject *
1234_msi_CreateRecord_impl(PyObject *module, int count)
1235/*[clinic end generated code: output=0ba0a00beea3e99e input=53f17d5b5d9b077d]*/
1236{
1237 MSIHANDLE h;
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001238
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001239 h = MsiCreateRecord(count);
1240 if (h == 0)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001241 return msierror(0);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001242
1243 return record_new(h);
1244}
1245
1246
1247static PyMethodDef msi_methods[] = {
Serhiy Storchaka5d5c84e2020-07-01 21:53:07 +03001248 _MSI_UUIDCREATE_METHODDEF
1249 _MSI_FCICREATE_METHODDEF
1250 _MSI_OPENDATABASE_METHODDEF
1251 _MSI_CREATERECORD_METHODDEF
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001252 {NULL, NULL} /* sentinel */
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001253};
1254
1255static char msi_doc[] = "Documentation";
1256
Martin v. Löwis1a214512008-06-11 05:26:20 +00001257
1258static struct PyModuleDef _msimodule = {
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001259 PyModuleDef_HEAD_INIT,
1260 "_msi",
1261 msi_doc,
1262 -1,
1263 msi_methods,
1264 NULL,
1265 NULL,
1266 NULL,
1267 NULL
Martin v. Löwis1a214512008-06-11 05:26:20 +00001268};
1269
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001270PyMODINIT_FUNC
Martin v. Löwis1a214512008-06-11 05:26:20 +00001271PyInit__msi(void)
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001272{
1273 PyObject *m;
1274
Martin v. Löwis1a214512008-06-11 05:26:20 +00001275 m = PyModule_Create(&_msimodule);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001276 if (m == NULL)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001277 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001278
Segev Finer679b5662017-07-27 01:17:57 +03001279 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (long)(SIZE_T)MSIDBOPEN_CREATEDIRECT);
1280 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (long)(SIZE_T)MSIDBOPEN_CREATE);
1281 PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (long)(SIZE_T)MSIDBOPEN_DIRECT);
1282 PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (long)(SIZE_T)MSIDBOPEN_READONLY);
1283 PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (long)(SIZE_T)MSIDBOPEN_TRANSACT);
1284 PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (long)(SIZE_T)MSIDBOPEN_PATCHFILE);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001285
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001286 PyModule_AddIntMacro(m, MSICOLINFO_NAMES);
1287 PyModule_AddIntMacro(m, MSICOLINFO_TYPES);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001288
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001289 PyModule_AddIntMacro(m, MSIMODIFY_SEEK);
1290 PyModule_AddIntMacro(m, MSIMODIFY_REFRESH);
1291 PyModule_AddIntMacro(m, MSIMODIFY_INSERT);
1292 PyModule_AddIntMacro(m, MSIMODIFY_UPDATE);
1293 PyModule_AddIntMacro(m, MSIMODIFY_ASSIGN);
1294 PyModule_AddIntMacro(m, MSIMODIFY_REPLACE);
1295 PyModule_AddIntMacro(m, MSIMODIFY_MERGE);
1296 PyModule_AddIntMacro(m, MSIMODIFY_DELETE);
1297 PyModule_AddIntMacro(m, MSIMODIFY_INSERT_TEMPORARY);
1298 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE);
1299 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_NEW);
1300 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_FIELD);
1301 PyModule_AddIntMacro(m, MSIMODIFY_VALIDATE_DELETE);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001302
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02001303 PyModule_AddIntMacro(m, PID_CODEPAGE);
1304 PyModule_AddIntMacro(m, PID_TITLE);
1305 PyModule_AddIntMacro(m, PID_SUBJECT);
1306 PyModule_AddIntMacro(m, PID_AUTHOR);
1307 PyModule_AddIntMacro(m, PID_KEYWORDS);
1308 PyModule_AddIntMacro(m, PID_COMMENTS);
1309 PyModule_AddIntMacro(m, PID_TEMPLATE);
1310 PyModule_AddIntMacro(m, PID_LASTAUTHOR);
1311 PyModule_AddIntMacro(m, PID_REVNUMBER);
1312 PyModule_AddIntMacro(m, PID_LASTPRINTED);
1313 PyModule_AddIntMacro(m, PID_CREATE_DTM);
1314 PyModule_AddIntMacro(m, PID_LASTSAVE_DTM);
1315 PyModule_AddIntMacro(m, PID_PAGECOUNT);
1316 PyModule_AddIntMacro(m, PID_WORDCOUNT);
1317 PyModule_AddIntMacro(m, PID_CHARCOUNT);
1318 PyModule_AddIntMacro(m, PID_APPNAME);
1319 PyModule_AddIntMacro(m, PID_SECURITY);
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001320
1321 MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1322 if (!MSIError)
Antoine Pitrouf95a1b32010-05-09 15:52:27 +00001323 return NULL;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001324 PyModule_AddObject(m, "MSIError", MSIError);
Amaury Forgeot d'Arcbf9e9662008-06-17 21:39:46 +00001325 return m;
Martin v. Löwisfbab90e2006-03-05 13:36:04 +00001326}