1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2008 James Hawkins 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <stdarg.h> 22 23 #define COBJMACROS 24 25 #include "windef.h" 26 #include "winbase.h" 27 #include "winuser.h" 28 #include "winerror.h" 29 #include "ole2.h" 30 #include "msi.h" 31 #include "msiquery.h" 32 #include "objbase.h" 33 #include "msipriv.h" 34 #include "query.h" 35 36 #include "wine/debug.h" 37 38 WINE_DEFAULT_DEBUG_CHANNEL(msidb); 39 40 #define NUM_STORAGES_COLS 2 41 #define MAX_STORAGES_NAME_LEN 62 42 43 typedef struct tabSTORAGE 44 { 45 UINT str_index; 46 IStorage *storage; 47 } STORAGE; 48 49 typedef struct tagMSISTORAGESVIEW 50 { 51 MSIVIEW view; 52 MSIDATABASE *db; 53 STORAGE **storages; 54 UINT max_storages; 55 UINT num_rows; 56 UINT row_size; 57 } MSISTORAGESVIEW; 58 59 static BOOL storages_set_table_size(MSISTORAGESVIEW *sv, UINT size) 60 { 61 if (size >= sv->max_storages) 62 { 63 sv->max_storages *= 2; 64 sv->storages = msi_realloc(sv->storages, sv->max_storages * sizeof(STORAGE *)); 65 if (!sv->storages) 66 return FALSE; 67 } 68 69 return TRUE; 70 } 71 72 static STORAGE *create_storage(MSISTORAGESVIEW *sv, LPCWSTR name, IStorage *stg) 73 { 74 STORAGE *storage; 75 76 storage = msi_alloc(sizeof(STORAGE)); 77 if (!storage) 78 return NULL; 79 80 storage->str_index = msi_add_string(sv->db->strings, name, -1, StringNonPersistent); 81 storage->storage = stg; 82 83 if (storage->storage) 84 IStorage_AddRef(storage->storage); 85 86 return storage; 87 } 88 89 static UINT STORAGES_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val) 90 { 91 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 92 93 TRACE("(%p, %d, %d, %p)\n", view, row, col, val); 94 95 if (col != 1) 96 return ERROR_INVALID_PARAMETER; 97 98 if (row >= sv->num_rows) 99 return ERROR_NO_MORE_ITEMS; 100 101 *val = sv->storages[row]->str_index; 102 103 return ERROR_SUCCESS; 104 } 105 106 static UINT STORAGES_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) 107 { 108 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 109 110 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm); 111 112 if (row >= sv->num_rows) 113 return ERROR_FUNCTION_FAILED; 114 115 return ERROR_INVALID_DATA; 116 } 117 118 static UINT STORAGES_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) 119 { 120 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 121 122 FIXME("%p %d %p\n", sv, row, rec); 123 124 return ERROR_CALL_NOT_IMPLEMENTED; 125 } 126 127 static HRESULT stream_to_storage(IStream *stm, IStorage **stg) 128 { 129 ILockBytes *lockbytes = NULL; 130 STATSTG stat; 131 LPVOID data; 132 HRESULT hr; 133 DWORD size, read; 134 ULARGE_INTEGER offset; 135 136 hr = IStream_Stat(stm, &stat, STATFLAG_NONAME); 137 if (FAILED(hr)) 138 return hr; 139 140 if (stat.cbSize.QuadPart >> 32) 141 { 142 ERR("Storage is too large\n"); 143 return E_FAIL; 144 } 145 146 size = stat.cbSize.QuadPart; 147 data = msi_alloc(size); 148 if (!data) 149 return E_OUTOFMEMORY; 150 151 hr = IStream_Read(stm, data, size, &read); 152 if (FAILED(hr) || read != size) 153 goto done; 154 155 hr = CreateILockBytesOnHGlobal(NULL, TRUE, &lockbytes); 156 if (FAILED(hr)) 157 goto done; 158 159 ZeroMemory(&offset, sizeof(ULARGE_INTEGER)); 160 hr = ILockBytes_WriteAt(lockbytes, offset, data, size, &read); 161 if (FAILED(hr) || read != size) 162 goto done; 163 164 hr = StgOpenStorageOnILockBytes(lockbytes, NULL, 165 STGM_READWRITE | STGM_SHARE_DENY_NONE, 166 NULL, 0, stg); 167 if (FAILED(hr)) 168 goto done; 169 170 done: 171 msi_free(data); 172 if (lockbytes) ILockBytes_Release(lockbytes); 173 return hr; 174 } 175 176 static UINT STORAGES_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask) 177 { 178 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 179 IStorage *stg, *substg = NULL; 180 IStream *stm; 181 LPWSTR name = NULL; 182 HRESULT hr; 183 UINT r = ERROR_FUNCTION_FAILED; 184 185 TRACE("(%p, %p)\n", view, rec); 186 187 if (row > sv->num_rows) 188 return ERROR_FUNCTION_FAILED; 189 190 r = MSI_RecordGetIStream(rec, 2, &stm); 191 if (r != ERROR_SUCCESS) 192 return r; 193 194 r = stream_to_storage(stm, &stg); 195 if (r != ERROR_SUCCESS) 196 { 197 IStream_Release(stm); 198 return r; 199 } 200 201 name = strdupW(MSI_RecordGetString(rec, 1)); 202 if (!name) 203 { 204 r = ERROR_OUTOFMEMORY; 205 goto done; 206 } 207 208 hr = IStorage_CreateStorage(sv->db->storage, name, 209 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 210 0, 0, &substg); 211 if (FAILED(hr)) 212 { 213 r = ERROR_FUNCTION_FAILED; 214 goto done; 215 } 216 217 hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg); 218 if (FAILED(hr)) 219 { 220 r = ERROR_FUNCTION_FAILED; 221 goto done; 222 } 223 224 sv->storages[row] = create_storage(sv, name, stg); 225 if (!sv->storages[row]) 226 r = ERROR_FUNCTION_FAILED; 227 228 done: 229 msi_free(name); 230 231 if (substg) IStorage_Release(substg); 232 IStorage_Release(stg); 233 IStream_Release(stm); 234 235 return r; 236 } 237 238 static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) 239 { 240 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 241 242 if (!storages_set_table_size(sv, ++sv->num_rows)) 243 return ERROR_FUNCTION_FAILED; 244 245 if (row == -1) 246 row = sv->num_rows - 1; 247 248 /* FIXME have to readjust rows */ 249 250 return STORAGES_set_row(view, row, rec, 0); 251 } 252 253 static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row) 254 { 255 FIXME("(%p %d): stub!\n", view, row); 256 return ERROR_SUCCESS; 257 } 258 259 static UINT STORAGES_execute(struct tagMSIVIEW *view, MSIRECORD *record) 260 { 261 TRACE("(%p, %p)\n", view, record); 262 return ERROR_SUCCESS; 263 } 264 265 static UINT STORAGES_close(struct tagMSIVIEW *view) 266 { 267 TRACE("(%p)\n", view); 268 return ERROR_SUCCESS; 269 } 270 271 static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) 272 { 273 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 274 275 TRACE("(%p, %p, %p)\n", view, rows, cols); 276 277 if (cols) *cols = NUM_STORAGES_COLS; 278 if (rows) *rows = sv->num_rows; 279 280 return ERROR_SUCCESS; 281 } 282 283 static UINT STORAGES_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, 284 UINT *type, BOOL *temporary, LPCWSTR *table_name ) 285 { 286 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, 287 table_name); 288 289 if (n == 0 || n > NUM_STORAGES_COLS) 290 return ERROR_INVALID_PARAMETER; 291 292 switch (n) 293 { 294 case 1: 295 if (name) *name = szName; 296 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STORAGES_NAME_LEN; 297 break; 298 299 case 2: 300 if (name) *name = szData; 301 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE; 302 break; 303 } 304 if (table_name) *table_name = szStorages; 305 if (temporary) *temporary = FALSE; 306 return ERROR_SUCCESS; 307 } 308 309 static UINT storages_find_row(MSISTORAGESVIEW *sv, MSIRECORD *rec, UINT *row) 310 { 311 LPCWSTR str; 312 UINT r, i, id, data; 313 314 str = MSI_RecordGetString(rec, 1); 315 r = msi_string2id(sv->db->strings, str, -1, &id); 316 if (r != ERROR_SUCCESS) 317 return r; 318 319 for (i = 0; i < sv->num_rows; i++) 320 { 321 STORAGES_fetch_int(&sv->view, i, 1, &data); 322 323 if (data == id) 324 { 325 *row = i; 326 return ERROR_SUCCESS; 327 } 328 } 329 330 return ERROR_FUNCTION_FAILED; 331 } 332 333 static UINT storages_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec) 334 { 335 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 336 UINT r, row; 337 338 r = storages_find_row(sv, rec, &row); 339 if (r != ERROR_SUCCESS) 340 return ERROR_FUNCTION_FAILED; 341 342 return STORAGES_set_row(view, row, rec, 0); 343 } 344 345 static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) 346 { 347 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 348 UINT r, row; 349 350 r = storages_find_row(sv, rec, &row); 351 if (r == ERROR_SUCCESS) 352 return storages_modify_update(view, rec); 353 354 return STORAGES_insert_row(view, rec, -1, FALSE); 355 } 356 357 static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) 358 { 359 UINT r; 360 361 TRACE("%p %d %p\n", view, eModifyMode, rec); 362 363 switch (eModifyMode) 364 { 365 case MSIMODIFY_ASSIGN: 366 r = storages_modify_assign(view, rec); 367 break; 368 369 case MSIMODIFY_INSERT: 370 r = STORAGES_insert_row(view, rec, -1, FALSE); 371 break; 372 373 case MSIMODIFY_UPDATE: 374 r = storages_modify_update(view, rec); 375 break; 376 377 case MSIMODIFY_VALIDATE_NEW: 378 case MSIMODIFY_INSERT_TEMPORARY: 379 case MSIMODIFY_REFRESH: 380 case MSIMODIFY_REPLACE: 381 case MSIMODIFY_MERGE: 382 case MSIMODIFY_DELETE: 383 case MSIMODIFY_VALIDATE: 384 case MSIMODIFY_VALIDATE_FIELD: 385 case MSIMODIFY_VALIDATE_DELETE: 386 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); 387 r = ERROR_CALL_NOT_IMPLEMENTED; 388 break; 389 390 default: 391 r = ERROR_INVALID_DATA; 392 } 393 394 return r; 395 } 396 397 static UINT STORAGES_delete(struct tagMSIVIEW *view) 398 { 399 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 400 UINT i; 401 402 TRACE("(%p)\n", view); 403 404 for (i = 0; i < sv->num_rows; i++) 405 { 406 if (sv->storages[i]->storage) 407 IStorage_Release(sv->storages[i]->storage); 408 msi_free(sv->storages[i]); 409 } 410 411 msi_free(sv->storages); 412 sv->storages = NULL; 413 msi_free(sv); 414 415 return ERROR_SUCCESS; 416 } 417 418 static UINT STORAGES_find_matching_rows(struct tagMSIVIEW *view, UINT col, 419 UINT val, UINT *row, MSIITERHANDLE *handle) 420 { 421 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 422 UINT index = PtrToUlong(*handle); 423 424 TRACE("(%d, %d): %d\n", *row, col, val); 425 426 if (col == 0 || col > NUM_STORAGES_COLS) 427 return ERROR_INVALID_PARAMETER; 428 429 while (index < sv->num_rows) 430 { 431 if (sv->storages[index]->str_index == val) 432 { 433 *row = index; 434 break; 435 } 436 437 index++; 438 } 439 440 *handle = UlongToPtr(++index); 441 if (index >= sv->num_rows) 442 return ERROR_NO_MORE_ITEMS; 443 444 return ERROR_SUCCESS; 445 } 446 447 static const MSIVIEWOPS storages_ops = 448 { 449 STORAGES_fetch_int, 450 STORAGES_fetch_stream, 451 STORAGES_get_row, 452 STORAGES_set_row, 453 STORAGES_insert_row, 454 STORAGES_delete_row, 455 STORAGES_execute, 456 STORAGES_close, 457 STORAGES_get_dimensions, 458 STORAGES_get_column_info, 459 STORAGES_modify, 460 STORAGES_delete, 461 STORAGES_find_matching_rows, 462 NULL, 463 NULL, 464 NULL, 465 NULL, 466 NULL, 467 NULL, 468 }; 469 470 static INT add_storages_to_table(MSISTORAGESVIEW *sv) 471 { 472 STORAGE *storage = NULL; 473 IEnumSTATSTG *stgenum = NULL; 474 STATSTG stat; 475 HRESULT hr; 476 UINT count = 0, size; 477 478 hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum); 479 if (FAILED(hr)) 480 return -1; 481 482 sv->max_storages = 1; 483 sv->storages = msi_alloc(sizeof(STORAGE *)); 484 if (!sv->storages) 485 return -1; 486 487 while (TRUE) 488 { 489 size = 0; 490 hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size); 491 if (FAILED(hr) || !size) 492 break; 493 494 if (stat.type != STGTY_STORAGE) 495 { 496 CoTaskMemFree(stat.pwcsName); 497 continue; 498 } 499 500 TRACE("enumerated storage %s\n", debugstr_w(stat.pwcsName)); 501 502 storage = create_storage(sv, stat.pwcsName, NULL); 503 if (!storage) 504 { 505 count = -1; 506 CoTaskMemFree(stat.pwcsName); 507 break; 508 } 509 510 IStorage_OpenStorage(sv->db->storage, stat.pwcsName, NULL, 511 STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, 512 &storage->storage); 513 CoTaskMemFree(stat.pwcsName); 514 515 if (!storages_set_table_size(sv, ++count)) 516 { 517 count = -1; 518 break; 519 } 520 521 sv->storages[count - 1] = storage; 522 } 523 524 IEnumSTATSTG_Release(stgenum); 525 return count; 526 } 527 528 UINT STORAGES_CreateView(MSIDATABASE *db, MSIVIEW **view) 529 { 530 MSISTORAGESVIEW *sv; 531 INT rows; 532 533 TRACE("(%p, %p)\n", db, view); 534 535 sv = msi_alloc_zero( sizeof(MSISTORAGESVIEW) ); 536 if (!sv) 537 return ERROR_FUNCTION_FAILED; 538 539 sv->view.ops = &storages_ops; 540 sv->db = db; 541 542 rows = add_storages_to_table(sv); 543 if (rows < 0) 544 { 545 msi_free( sv ); 546 return ERROR_FUNCTION_FAILED; 547 } 548 sv->num_rows = rows; 549 550 *view = (MSIVIEW *)sv; 551 552 return ERROR_SUCCESS; 553 } 554