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 char *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 (UuidToString(&result, &cresult) == RPC_S_OUT_OF_MEMORY) {
34 PyErr_SetString(PyExc_MemoryError, "out of memory in uuidgen");
35 return NULL;
36 }
37
38 oresult = PyString_FromString(cresult);
39 RpcStringFree(&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, 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(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(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(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(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 PyObject *result = PyObject_CallMethod(pv, "status", "iii", typeStatus, cb1, cb2);
126 if (result == NULL)
127 return -1;
128 Py_DECREF(result);
129 }
130 return 0;
131 }
132
FNFCIGETNEXTCABINET(cb_getnextcabinet)133 static FNFCIGETNEXTCABINET(cb_getnextcabinet)
134 {
135 if (pv) {
136 PyObject *result = PyObject_CallMethod(pv, "getnextcabinet", "i", pccab->iCab);
137 if (result == NULL)
138 return -1;
139 if (!PyString_Check(result)) {
140 PyErr_Format(PyExc_TypeError,
141 "Incorrect return type %s from getnextcabinet",
142 result->ob_type->tp_name);
143 Py_DECREF(result);
144 return FALSE;
145 }
146 strncpy(pccab->szCab, PyString_AsString(result), sizeof(pccab->szCab));
147 return TRUE;
148 }
149 return FALSE;
150 }
151
FNFCIGETOPENINFO(cb_getopeninfo)152 static FNFCIGETOPENINFO(cb_getopeninfo)
153 {
154 BY_HANDLE_FILE_INFORMATION bhfi;
155 FILETIME filetime;
156 HANDLE handle;
157
158 /* Need Win32 handle to get time stamps */
159 handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
160 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
161 if (handle == INVALID_HANDLE_VALUE)
162 return -1;
163
164 if (GetFileInformationByHandle(handle, &bhfi) == FALSE)
165 {
166 CloseHandle(handle);
167 return -1;
168 }
169
170 FileTimeToLocalFileTime(&bhfi.ftLastWriteTime, &filetime);
171 FileTimeToDosDateTime(&filetime, pdate, ptime);
172
173 *pattribs = (int)(bhfi.dwFileAttributes &
174 (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
175
176 CloseHandle(handle);
177
178 return _open(pszName, _O_RDONLY | _O_BINARY);
179 }
180
fcicreate(PyObject * obj,PyObject * args)181 static PyObject* fcicreate(PyObject* obj, PyObject* args)
182 {
183 char *cabname, *p;
184 PyObject *files;
185 CCAB ccab;
186 HFCI hfci;
187 ERF erf;
188 Py_ssize_t i;
189
190
191 if (!PyArg_ParseTuple(args, "sO:FCICreate", &cabname, &files))
192 return NULL;
193
194 if (!PyList_Check(files)) {
195 PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
196 return NULL;
197 }
198
199 ccab.cb = INT_MAX; /* no need to split CAB into multiple media */
200 ccab.cbFolderThresh = 1000000; /* flush directory after this many bytes */
201 ccab.cbReserveCFData = 0;
202 ccab.cbReserveCFFolder = 0;
203 ccab.cbReserveCFHeader = 0;
204
205 ccab.iCab = 1;
206 ccab.iDisk = 1;
207
208 ccab.setID = 0;
209 ccab.szDisk[0] = '\0';
210
211 for (i = 0, p = cabname; *p; p = CharNext(p))
212 if (*p == '\\' || *p == '/')
213 i = p - cabname + 1;
214
215 if (i >= sizeof(ccab.szCabPath) ||
216 strlen(cabname+i) >= sizeof(ccab.szCab)) {
217 PyErr_SetString(PyExc_ValueError, "path name too long");
218 return 0;
219 }
220
221 if (i > 0) {
222 memcpy(ccab.szCabPath, cabname, i);
223 ccab.szCabPath[i] = '\0';
224 strcpy(ccab.szCab, cabname+i);
225 } else {
226 strcpy(ccab.szCabPath, ".\\");
227 strcpy(ccab.szCab, cabname);
228 }
229
230 hfci = FCICreate(&erf, cb_fileplaced, cb_alloc, cb_free,
231 cb_open, cb_read, cb_write, cb_close, cb_seek, cb_delete,
232 cb_gettempfile, &ccab, NULL);
233
234 if (hfci == NULL) {
235 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper);
236 return NULL;
237 }
238
239 for (i=0; i < PyList_GET_SIZE(files); i++) {
240 PyObject *item = PyList_GET_ITEM(files, i);
241 char *filename, *cabname;
242 if (!PyArg_ParseTuple(item, "ss", &filename, &cabname))
243 goto err;
244 if (!FCIAddFile(hfci, filename, cabname, FALSE,
245 cb_getnextcabinet, cb_status, cb_getopeninfo,
246 tcompTYPE_MSZIP))
247 goto err;
248 }
249
250 if (!FCIFlushCabinet(hfci, FALSE, cb_getnextcabinet, cb_status))
251 goto err;
252
253 if (!FCIDestroy(hfci))
254 goto err;
255
256 Py_INCREF(Py_None);
257 return Py_None;
258 err:
259 PyErr_Format(PyExc_ValueError, "FCI error %d", erf.erfOper); /* XXX better error type */
260 FCIDestroy(hfci);
261 return NULL;
262 }
263
264 typedef struct msiobj{
265 PyObject_HEAD
266 MSIHANDLE h;
267 }msiobj;
268
269 static void
msiobj_dealloc(msiobj * msidb)270 msiobj_dealloc(msiobj* msidb)
271 {
272 MsiCloseHandle(msidb->h);
273 msidb->h = 0;
274 PyObject_Del(msidb);
275 }
276
277 static PyObject*
msiobj_close(msiobj * msidb,PyObject * args)278 msiobj_close(msiobj* msidb, PyObject *args)
279 {
280 MsiCloseHandle(msidb->h);
281 msidb->h = 0;
282 Py_INCREF(Py_None);
283 return Py_None;
284 }
285
286 static PyObject*
msierror(int status)287 msierror(int status)
288 {
289 int code;
290 char buf[2000];
291 char *res = buf;
292 DWORD size = sizeof(buf);
293 MSIHANDLE err = MsiGetLastErrorRecord();
294
295 if (err == 0) {
296 switch(status) {
297 case ERROR_ACCESS_DENIED:
298 PyErr_SetString(MSIError, "access denied");
299 return NULL;
300 case ERROR_FUNCTION_FAILED:
301 PyErr_SetString(MSIError, "function failed");
302 return NULL;
303 case ERROR_INVALID_DATA:
304 PyErr_SetString(MSIError, "invalid data");
305 return NULL;
306 case ERROR_INVALID_HANDLE:
307 PyErr_SetString(MSIError, "invalid handle");
308 return NULL;
309 case ERROR_INVALID_STATE:
310 PyErr_SetString(MSIError, "invalid state");
311 return NULL;
312 case ERROR_INVALID_PARAMETER:
313 PyErr_SetString(MSIError, "invalid parameter");
314 return NULL;
315 default:
316 PyErr_Format(MSIError, "unknown error %x", status);
317 return NULL;
318 }
319 }
320
321 code = MsiRecordGetInteger(err, 1); /* XXX code */
322 if (MsiFormatRecord(0, err, res, &size) == ERROR_MORE_DATA) {
323 res = malloc(size+1);
324 if (res == NULL) {
325 MsiCloseHandle(err);
326 return PyErr_NoMemory();
327 }
328 MsiFormatRecord(0, err, res, &size);
329 res[size]='\0';
330 }
331 MsiCloseHandle(err);
332 PyErr_SetString(MSIError, res);
333 if (res != buf)
334 free(res);
335 return NULL;
336 }
337
338 /*************************** Record objects **********************/
339
340 static PyObject*
record_getfieldcount(msiobj * record,PyObject * args)341 record_getfieldcount(msiobj* record, PyObject* args)
342 {
343 return PyInt_FromLong(MsiRecordGetFieldCount(record->h));
344 }
345
346 static PyObject*
record_getinteger(msiobj * record,PyObject * args)347 record_getinteger(msiobj* record, PyObject* args)
348 {
349 unsigned int field;
350 int status;
351
352 if (!PyArg_ParseTuple(args, "I:GetInteger", &field))
353 return NULL;
354 status = MsiRecordGetInteger(record->h, field);
355 if (status == MSI_NULL_INTEGER){
356 PyErr_SetString(MSIError, "could not convert record field to integer");
357 return NULL;
358 }
359 return PyInt_FromLong((long) status);
360 }
361
362 static PyObject*
record_getstring(msiobj * record,PyObject * args)363 record_getstring(msiobj* record, PyObject* args)
364 {
365 unsigned int field;
366 unsigned int status;
367 char buf[2000];
368 char *res = buf;
369 DWORD size = sizeof(buf);
370 PyObject* string;
371
372 if (!PyArg_ParseTuple(args, "I:GetString", &field))
373 return NULL;
374 status = MsiRecordGetString(record->h, field, res, &size);
375 if (status == ERROR_MORE_DATA) {
376 res = (char*) malloc(size + 1);
377 if (res == NULL)
378 return PyErr_NoMemory();
379 status = MsiRecordGetString(record->h, field, res, &size);
380 }
381 if (status != ERROR_SUCCESS)
382 return msierror((int) status);
383 string = PyString_FromString(res);
384 if (buf != res)
385 free(res);
386 return string;
387 }
388
389 static PyObject*
record_cleardata(msiobj * record,PyObject * args)390 record_cleardata(msiobj* record, PyObject *args)
391 {
392 int status = MsiRecordClearData(record->h);
393 if (status != ERROR_SUCCESS)
394 return msierror(status);
395
396 Py_INCREF(Py_None);
397 return Py_None;
398 }
399
400 static PyObject*
record_setstring(msiobj * record,PyObject * args)401 record_setstring(msiobj* record, PyObject *args)
402 {
403 int status;
404 int field;
405 char *data;
406
407 if (!PyArg_ParseTuple(args, "is:SetString", &field, &data))
408 return NULL;
409
410 if ((status = MsiRecordSetString(record->h, field, data)) != ERROR_SUCCESS)
411 return msierror(status);
412
413 Py_INCREF(Py_None);
414 return Py_None;
415 }
416
417 static PyObject*
record_setstream(msiobj * record,PyObject * args)418 record_setstream(msiobj* record, PyObject *args)
419 {
420 int status;
421 int field;
422 char *data;
423
424 if (!PyArg_ParseTuple(args, "is:SetStream", &field, &data))
425 return NULL;
426
427 if ((status = MsiRecordSetStream(record->h, field, data)) != ERROR_SUCCESS)
428 return msierror(status);
429
430 Py_INCREF(Py_None);
431 return Py_None;
432 }
433
434 static PyObject*
record_setinteger(msiobj * record,PyObject * args)435 record_setinteger(msiobj* record, PyObject *args)
436 {
437 int status;
438 int field;
439 int data;
440
441 if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data))
442 return NULL;
443
444 if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS)
445 return msierror(status);
446
447 Py_INCREF(Py_None);
448 return Py_None;
449 }
450
451
452
453 static PyMethodDef record_methods[] = {
454 { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS,
455 PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")},
456 { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS,
457 PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")},
458 { "GetString", (PyCFunction)record_getstring, METH_VARARGS,
459 PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")},
460 { "SetString", (PyCFunction)record_setstring, METH_VARARGS,
461 PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")},
462 { "SetStream", (PyCFunction)record_setstream, METH_VARARGS,
463 PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")},
464 { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS,
465 PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")},
466 { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS,
467 PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")},
468 { NULL, NULL }
469 };
470
471 static PyTypeObject record_Type = {
472 PyVarObject_HEAD_INIT(NULL, 0)
473 "_msi.Record", /*tp_name*/
474 sizeof(msiobj), /*tp_basicsize*/
475 0, /*tp_itemsize*/
476 /* methods */
477 (destructor)msiobj_dealloc, /*tp_dealloc*/
478 0, /*tp_print*/
479 0, /*tp_getattr*/
480 0, /*tp_setattr*/
481 0, /*tp_compare*/
482 0, /*tp_repr*/
483 0, /*tp_as_number*/
484 0, /*tp_as_sequence*/
485 0, /*tp_as_mapping*/
486 0, /*tp_hash*/
487 0, /*tp_call*/
488 0, /*tp_str*/
489 PyObject_GenericGetAttr,/*tp_getattro*/
490 PyObject_GenericSetAttr,/*tp_setattro*/
491 0, /*tp_as_buffer*/
492 Py_TPFLAGS_DEFAULT, /*tp_flags*/
493 0, /*tp_doc*/
494 0, /*tp_traverse*/
495 0, /*tp_clear*/
496 0, /*tp_richcompare*/
497 0, /*tp_weaklistoffset*/
498 0, /*tp_iter*/
499 0, /*tp_iternext*/
500 record_methods, /*tp_methods*/
501 0, /*tp_members*/
502 0, /*tp_getset*/
503 0, /*tp_base*/
504 0, /*tp_dict*/
505 0, /*tp_descr_get*/
506 0, /*tp_descr_set*/
507 0, /*tp_dictoffset*/
508 0, /*tp_init*/
509 0, /*tp_alloc*/
510 0, /*tp_new*/
511 0, /*tp_free*/
512 0, /*tp_is_gc*/
513 };
514
515 static PyObject*
record_new(MSIHANDLE h)516 record_new(MSIHANDLE h)
517 {
518 msiobj *result = PyObject_NEW(struct msiobj, &record_Type);
519
520 if (!result) {
521 MsiCloseHandle(h);
522 return NULL;
523 }
524
525 result->h = h;
526 return (PyObject*)result;
527 }
528
529 /*************************** SummaryInformation objects **************/
530
531 static PyObject*
summary_getproperty(msiobj * si,PyObject * args)532 summary_getproperty(msiobj* si, PyObject *args)
533 {
534 int status;
535 int field;
536 PyObject *result;
537 UINT type;
538 INT ival;
539 FILETIME fval;
540 char sbuf[1000];
541 char *sval = sbuf;
542 DWORD ssize = sizeof(sval);
543
544 if (!PyArg_ParseTuple(args, "i:GetProperty", &field))
545 return NULL;
546
547 status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
548 &fval, sval, &ssize);
549 if (status == ERROR_MORE_DATA) {
550 sval = malloc(ssize);
551 if (sval == NULL) {
552 return PyErr_NoMemory();
553 }
554 status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
555 &fval, sval, &ssize);
556 }
557
558 switch(type) {
559 case VT_I2: case VT_I4:
560 return PyInt_FromLong(ival);
561 case VT_FILETIME:
562 PyErr_SetString(PyExc_NotImplementedError, "FILETIME result");
563 return NULL;
564 case VT_LPSTR:
565 result = PyString_FromStringAndSize(sval, ssize);
566 if (sval != sbuf)
567 free(sval);
568 return result;
569 }
570 PyErr_Format(PyExc_NotImplementedError, "result of type %d", type);
571 return NULL;
572 }
573
574 static PyObject*
summary_getpropertycount(msiobj * si,PyObject * args)575 summary_getpropertycount(msiobj* si, PyObject *args)
576 {
577 int status;
578 UINT result;
579
580 status = MsiSummaryInfoGetPropertyCount(si->h, &result);
581 if (status != ERROR_SUCCESS)
582 return msierror(status);
583
584 return PyInt_FromLong(result);
585 }
586
587 static PyObject*
summary_setproperty(msiobj * si,PyObject * args)588 summary_setproperty(msiobj* si, PyObject *args)
589 {
590 int status;
591 int field;
592 PyObject* data;
593
594 if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data))
595 return NULL;
596
597 if (PyString_Check(data)) {
598 status = MsiSummaryInfoSetProperty(si->h, field, VT_LPSTR,
599 0, NULL, PyString_AsString(data));
600 } else if (PyInt_Check(data)) {
601 status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
602 PyInt_AsLong(data), NULL, NULL);
603 } else {
604 PyErr_SetString(PyExc_TypeError, "unsupported type");
605 return NULL;
606 }
607
608 if (status != ERROR_SUCCESS)
609 return msierror(status);
610
611 Py_INCREF(Py_None);
612 return Py_None;
613 }
614
615
616 static PyObject*
summary_persist(msiobj * si,PyObject * args)617 summary_persist(msiobj* si, PyObject *args)
618 {
619 int status;
620
621 status = MsiSummaryInfoPersist(si->h);
622 if (status != ERROR_SUCCESS)
623 return msierror(status);
624 Py_INCREF(Py_None);
625 return Py_None;
626 }
627
628 static PyMethodDef summary_methods[] = {
629 { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
630 PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
631 { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
632 PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
633 { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
634 PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
635 { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
636 PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
637 { NULL, NULL }
638 };
639
640 static PyTypeObject summary_Type = {
641 PyVarObject_HEAD_INIT(NULL, 0)
642 "_msi.SummaryInformation", /*tp_name*/
643 sizeof(msiobj), /*tp_basicsize*/
644 0, /*tp_itemsize*/
645 /* methods */
646 (destructor)msiobj_dealloc, /*tp_dealloc*/
647 0, /*tp_print*/
648 0, /*tp_getattr*/
649 0, /*tp_setattr*/
650 0, /*tp_compare*/
651 0, /*tp_repr*/
652 0, /*tp_as_number*/
653 0, /*tp_as_sequence*/
654 0, /*tp_as_mapping*/
655 0, /*tp_hash*/
656 0, /*tp_call*/
657 0, /*tp_str*/
658 PyObject_GenericGetAttr,/*tp_getattro*/
659 PyObject_GenericSetAttr,/*tp_setattro*/
660 0, /*tp_as_buffer*/
661 Py_TPFLAGS_DEFAULT, /*tp_flags*/
662 0, /*tp_doc*/
663 0, /*tp_traverse*/
664 0, /*tp_clear*/
665 0, /*tp_richcompare*/
666 0, /*tp_weaklistoffset*/
667 0, /*tp_iter*/
668 0, /*tp_iternext*/
669 summary_methods, /*tp_methods*/
670 0, /*tp_members*/
671 0, /*tp_getset*/
672 0, /*tp_base*/
673 0, /*tp_dict*/
674 0, /*tp_descr_get*/
675 0, /*tp_descr_set*/
676 0, /*tp_dictoffset*/
677 0, /*tp_init*/
678 0, /*tp_alloc*/
679 0, /*tp_new*/
680 0, /*tp_free*/
681 0, /*tp_is_gc*/
682 };
683
684 /*************************** View objects **************/
685
686 static PyObject*
view_execute(msiobj * view,PyObject * args)687 view_execute(msiobj *view, PyObject*args)
688 {
689 int status;
690 MSIHANDLE params = 0;
691 PyObject *oparams = Py_None;
692
693 if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
694 return NULL;
695
696 if (oparams != Py_None) {
697 if (oparams->ob_type != &record_Type) {
698 PyErr_SetString(PyExc_TypeError, "Execute argument must be a record");
699 return NULL;
700 }
701 params = ((msiobj*)oparams)->h;
702 }
703
704 status = MsiViewExecute(view->h, params);
705 if (status != ERROR_SUCCESS)
706 return msierror(status);
707
708 Py_INCREF(Py_None);
709 return Py_None;
710 }
711
712 static PyObject*
view_fetch(msiobj * view,PyObject * args)713 view_fetch(msiobj *view, PyObject*args)
714 {
715 int status;
716 MSIHANDLE result;
717
718 if ((status = MsiViewFetch(view->h, &result)) != ERROR_SUCCESS)
719 return msierror(status);
720
721 return record_new(result);
722 }
723
724 static PyObject*
view_getcolumninfo(msiobj * view,PyObject * args)725 view_getcolumninfo(msiobj *view, PyObject *args)
726 {
727 int status;
728 int kind;
729 MSIHANDLE result;
730
731 if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
732 return NULL;
733
734 if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
735 return msierror(status);
736
737 return record_new(result);
738 }
739
740 static PyObject*
view_modify(msiobj * view,PyObject * args)741 view_modify(msiobj *view, PyObject *args)
742 {
743 int kind;
744 PyObject *data;
745 int status;
746
747 if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
748 return NULL;
749
750 if (data->ob_type != &record_Type) {
751 PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
752 return NULL;
753 }
754
755 if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
756 return msierror(status);
757
758 Py_INCREF(Py_None);
759 return Py_None;
760 }
761
762 static PyObject*
view_close(msiobj * view,PyObject * args)763 view_close(msiobj *view, PyObject*args)
764 {
765 int status;
766
767 if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
768 return msierror(status);
769
770 Py_INCREF(Py_None);
771 return Py_None;
772 }
773
774 static PyMethodDef view_methods[] = {
775 { "Execute", (PyCFunction)view_execute, METH_VARARGS,
776 PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
777 { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
778 PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
779 { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
780 PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
781 { "Modify", (PyCFunction)view_modify, METH_VARARGS,
782 PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
783 { "Close", (PyCFunction)view_close, METH_NOARGS,
784 PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
785 { NULL, NULL }
786 };
787
788 static PyTypeObject msiview_Type = {
789 PyVarObject_HEAD_INIT(NULL, 0)
790 "_msi.View", /*tp_name*/
791 sizeof(msiobj), /*tp_basicsize*/
792 0, /*tp_itemsize*/
793 /* methods */
794 (destructor)msiobj_dealloc, /*tp_dealloc*/
795 0, /*tp_print*/
796 0, /*tp_getattr*/
797 0, /*tp_setattr*/
798 0, /*tp_compare*/
799 0, /*tp_repr*/
800 0, /*tp_as_number*/
801 0, /*tp_as_sequence*/
802 0, /*tp_as_mapping*/
803 0, /*tp_hash*/
804 0, /*tp_call*/
805 0, /*tp_str*/
806 PyObject_GenericGetAttr,/*tp_getattro*/
807 PyObject_GenericSetAttr,/*tp_setattro*/
808 0, /*tp_as_buffer*/
809 Py_TPFLAGS_DEFAULT, /*tp_flags*/
810 0, /*tp_doc*/
811 0, /*tp_traverse*/
812 0, /*tp_clear*/
813 0, /*tp_richcompare*/
814 0, /*tp_weaklistoffset*/
815 0, /*tp_iter*/
816 0, /*tp_iternext*/
817 view_methods, /*tp_methods*/
818 0, /*tp_members*/
819 0, /*tp_getset*/
820 0, /*tp_base*/
821 0, /*tp_dict*/
822 0, /*tp_descr_get*/
823 0, /*tp_descr_set*/
824 0, /*tp_dictoffset*/
825 0, /*tp_init*/
826 0, /*tp_alloc*/
827 0, /*tp_new*/
828 0, /*tp_free*/
829 0, /*tp_is_gc*/
830 };
831
832 /*************************** Database objects **************/
833
834 static PyObject*
msidb_openview(msiobj * msidb,PyObject * args)835 msidb_openview(msiobj *msidb, PyObject *args)
836 {
837 int status;
838 char *sql;
839 MSIHANDLE hView;
840 msiobj *result;
841
842 if (!PyArg_ParseTuple(args, "s:OpenView", &sql))
843 return NULL;
844
845 if ((status = MsiDatabaseOpenView(msidb->h, sql, &hView)) != ERROR_SUCCESS)
846 return msierror(status);
847
848 result = PyObject_NEW(struct msiobj, &msiview_Type);
849 if (!result) {
850 MsiCloseHandle(hView);
851 return NULL;
852 }
853
854 result->h = hView;
855 return (PyObject*)result;
856 }
857
858 static PyObject*
msidb_commit(msiobj * msidb,PyObject * args)859 msidb_commit(msiobj *msidb, PyObject *args)
860 {
861 int status;
862
863 if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
864 return msierror(status);
865
866 Py_INCREF(Py_None);
867 return Py_None;
868 }
869
870 static PyObject*
msidb_getsummaryinformation(msiobj * db,PyObject * args)871 msidb_getsummaryinformation(msiobj *db, PyObject *args)
872 {
873 int status;
874 int count;
875 MSIHANDLE result;
876 msiobj *oresult;
877
878 if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
879 return NULL;
880
881 status = MsiGetSummaryInformation(db->h, NULL, count, &result);
882 if (status != ERROR_SUCCESS)
883 return msierror(status);
884
885 oresult = PyObject_NEW(struct msiobj, &summary_Type);
886 if (!result) {
887 MsiCloseHandle(result);
888 return NULL;
889 }
890
891 oresult->h = result;
892 return (PyObject*)oresult;
893 }
894
895 static PyMethodDef db_methods[] = {
896 { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
897 PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
898 { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
899 PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
900 { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
901 PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
902 { NULL, NULL }
903 };
904
905 static PyTypeObject msidb_Type = {
906 PyVarObject_HEAD_INIT(NULL, 0)
907 "_msi.Database", /*tp_name*/
908 sizeof(msiobj), /*tp_basicsize*/
909 0, /*tp_itemsize*/
910 /* methods */
911 (destructor)msiobj_dealloc, /*tp_dealloc*/
912 0, /*tp_print*/
913 0, /*tp_getattr*/
914 0, /*tp_setattr*/
915 0, /*tp_compare*/
916 0, /*tp_repr*/
917 0, /*tp_as_number*/
918 0, /*tp_as_sequence*/
919 0, /*tp_as_mapping*/
920 0, /*tp_hash*/
921 0, /*tp_call*/
922 0, /*tp_str*/
923 PyObject_GenericGetAttr,/*tp_getattro*/
924 PyObject_GenericSetAttr,/*tp_setattro*/
925 0, /*tp_as_buffer*/
926 Py_TPFLAGS_DEFAULT, /*tp_flags*/
927 0, /*tp_doc*/
928 0, /*tp_traverse*/
929 0, /*tp_clear*/
930 0, /*tp_richcompare*/
931 0, /*tp_weaklistoffset*/
932 0, /*tp_iter*/
933 0, /*tp_iternext*/
934 db_methods, /*tp_methods*/
935 0, /*tp_members*/
936 0, /*tp_getset*/
937 0, /*tp_base*/
938 0, /*tp_dict*/
939 0, /*tp_descr_get*/
940 0, /*tp_descr_set*/
941 0, /*tp_dictoffset*/
942 0, /*tp_init*/
943 0, /*tp_alloc*/
944 0, /*tp_new*/
945 0, /*tp_free*/
946 0, /*tp_is_gc*/
947 };
948
949 #define Py_NOT_PERSIST(x, flag) \
950 (x != (int)(flag) && \
951 x != ((int)(flag) | MSIDBOPEN_PATCHFILE))
952
953 #define Py_INVALID_PERSIST(x) \
954 (Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) && \
955 Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) && \
956 Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) && \
957 Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
958 Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
959
msiopendb(PyObject * obj,PyObject * args)960 static PyObject* msiopendb(PyObject *obj, PyObject *args)
961 {
962 int status;
963 char *path;
964 int persist;
965 MSIHANDLE h;
966 msiobj *result;
967 if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist))
968 return NULL;
969 /* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
970 MsiOpenDatabase may treat the value as a pointer, leading to unexpected
971 behavior. */
972 if (Py_INVALID_PERSIST(persist))
973 return msierror(ERROR_INVALID_PARAMETER);
974 status = MsiOpenDatabase(path, (LPCSTR)persist, &h);
975 if (status != ERROR_SUCCESS)
976 return msierror(status);
977
978 result = PyObject_NEW(struct msiobj, &msidb_Type);
979 if (!result) {
980 MsiCloseHandle(h);
981 return NULL;
982 }
983 result->h = h;
984 return (PyObject*)result;
985 }
986
987 static PyObject*
createrecord(PyObject * o,PyObject * args)988 createrecord(PyObject *o, PyObject *args)
989 {
990 int count;
991 MSIHANDLE h;
992
993 if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
994 return NULL;
995
996 h = MsiCreateRecord(count);
997 if (h == 0)
998 return msierror(0);
999
1000 return record_new(h);
1001 }
1002
1003
1004 static PyMethodDef msi_methods[] = {
1005 {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
1006 PyDoc_STR("UuidCreate() -> string")},
1007 {"FCICreate", (PyCFunction)fcicreate, METH_VARARGS,
1008 PyDoc_STR("fcicreate(cabname,files) -> None")},
1009 {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
1010 PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
1011 {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
1012 PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
1013 {NULL, NULL} /* sentinel */
1014 };
1015
1016 static char msi_doc[] = "Documentation";
1017
1018 PyMODINIT_FUNC
init_msi(void)1019 init_msi(void)
1020 {
1021 PyObject *m;
1022
1023 m = Py_InitModule3("_msi", msi_methods, msi_doc);
1024 if (m == NULL)
1025 return;
1026
1027 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATEDIRECT", (int)MSIDBOPEN_CREATEDIRECT);
1028 PyModule_AddIntConstant(m, "MSIDBOPEN_CREATE", (int)MSIDBOPEN_CREATE);
1029 PyModule_AddIntConstant(m, "MSIDBOPEN_DIRECT", (int)MSIDBOPEN_DIRECT);
1030 PyModule_AddIntConstant(m, "MSIDBOPEN_READONLY", (int)MSIDBOPEN_READONLY);
1031 PyModule_AddIntConstant(m, "MSIDBOPEN_TRANSACT", (int)MSIDBOPEN_TRANSACT);
1032 PyModule_AddIntConstant(m, "MSIDBOPEN_PATCHFILE", (int)MSIDBOPEN_PATCHFILE);
1033
1034 PyModule_AddIntConstant(m, "MSICOLINFO_NAMES", MSICOLINFO_NAMES);
1035 PyModule_AddIntConstant(m, "MSICOLINFO_TYPES", MSICOLINFO_TYPES);
1036
1037 PyModule_AddIntConstant(m, "MSIMODIFY_SEEK", MSIMODIFY_SEEK);
1038 PyModule_AddIntConstant(m, "MSIMODIFY_REFRESH", MSIMODIFY_REFRESH);
1039 PyModule_AddIntConstant(m, "MSIMODIFY_INSERT", MSIMODIFY_INSERT);
1040 PyModule_AddIntConstant(m, "MSIMODIFY_UPDATE", MSIMODIFY_UPDATE);
1041 PyModule_AddIntConstant(m, "MSIMODIFY_ASSIGN", MSIMODIFY_ASSIGN);
1042 PyModule_AddIntConstant(m, "MSIMODIFY_REPLACE", MSIMODIFY_REPLACE);
1043 PyModule_AddIntConstant(m, "MSIMODIFY_MERGE", MSIMODIFY_MERGE);
1044 PyModule_AddIntConstant(m, "MSIMODIFY_DELETE", MSIMODIFY_DELETE);
1045 PyModule_AddIntConstant(m, "MSIMODIFY_INSERT_TEMPORARY", MSIMODIFY_INSERT_TEMPORARY);
1046 PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE", MSIMODIFY_VALIDATE);
1047 PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_NEW", MSIMODIFY_VALIDATE_NEW);
1048 PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_FIELD", MSIMODIFY_VALIDATE_FIELD);
1049 PyModule_AddIntConstant(m, "MSIMODIFY_VALIDATE_DELETE", MSIMODIFY_VALIDATE_DELETE);
1050
1051 PyModule_AddIntConstant(m, "PID_CODEPAGE", PID_CODEPAGE);
1052 PyModule_AddIntConstant(m, "PID_TITLE", PID_TITLE);
1053 PyModule_AddIntConstant(m, "PID_SUBJECT", PID_SUBJECT);
1054 PyModule_AddIntConstant(m, "PID_AUTHOR", PID_AUTHOR);
1055 PyModule_AddIntConstant(m, "PID_KEYWORDS", PID_KEYWORDS);
1056 PyModule_AddIntConstant(m, "PID_COMMENTS", PID_COMMENTS);
1057 PyModule_AddIntConstant(m, "PID_TEMPLATE", PID_TEMPLATE);
1058 PyModule_AddIntConstant(m, "PID_LASTAUTHOR", PID_LASTAUTHOR);
1059 PyModule_AddIntConstant(m, "PID_REVNUMBER", PID_REVNUMBER);
1060 PyModule_AddIntConstant(m, "PID_LASTPRINTED", PID_LASTPRINTED);
1061 PyModule_AddIntConstant(m, "PID_CREATE_DTM", PID_CREATE_DTM);
1062 PyModule_AddIntConstant(m, "PID_LASTSAVE_DTM", PID_LASTSAVE_DTM);
1063 PyModule_AddIntConstant(m, "PID_PAGECOUNT", PID_PAGECOUNT);
1064 PyModule_AddIntConstant(m, "PID_WORDCOUNT", PID_WORDCOUNT);
1065 PyModule_AddIntConstant(m, "PID_CHARCOUNT", PID_CHARCOUNT);
1066 PyModule_AddIntConstant(m, "PID_APPNAME", PID_APPNAME);
1067 PyModule_AddIntConstant(m, "PID_SECURITY", PID_SECURITY);
1068
1069 MSIError = PyErr_NewException ("_msi.MSIError", NULL, NULL);
1070 if (!MSIError)
1071 return;
1072 PyModule_AddObject(m, "MSIError", MSIError);
1073 }
1074