• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Helper library for MSI creation with Python.
2  * Copyright (C) 2005 Martin v. L�wis
3  * Licensed to PSF under a contributor agreement.
4  */
5 
6 #include <Python.h>
7 #include <fci.h>
8 #include <fcntl.h>
9 #include <windows.h>
10 #include <msi.h>
11 #include <msiquery.h>
12 #include <msidefs.h>
13 #include <rpc.h>
14 
15 static PyObject *MSIError;
16 
17 static PyObject*
uuidcreate(PyObject * obj,PyObject * args)18 uuidcreate(PyObject* obj, PyObject*args)
19 {
20     UUID result;
21     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