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