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(*sv->storages)); 65 if (!sv->storages) 66 return FALSE; 67 } 68 69 return TRUE; 70 } 71 72 static UINT STORAGES_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val) 73 { 74 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 75 76 TRACE("(%p, %d, %d, %p)\n", view, row, col, val); 77 78 if (col != 1) 79 return ERROR_INVALID_PARAMETER; 80 81 if (row >= sv->num_rows) 82 return ERROR_NO_MORE_ITEMS; 83 84 *val = sv->storages[row].str_index; 85 86 return ERROR_SUCCESS; 87 } 88 89 static UINT STORAGES_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) 90 { 91 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 92 93 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm); 94 95 if (row >= sv->num_rows) 96 return ERROR_FUNCTION_FAILED; 97 98 return ERROR_INVALID_DATA; 99 } 100 101 static UINT STORAGES_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len ) 102 { 103 ERR("Cannot modify primary key.\n"); 104 return ERROR_FUNCTION_FAILED; 105 } 106 107 static HRESULT stream_to_storage(IStream *stm, IStorage **stg) 108 { 109 ILockBytes *lockbytes = NULL; 110 STATSTG stat; 111 LPVOID data; 112 HRESULT hr; 113 DWORD size, read; 114 ULARGE_INTEGER offset; 115 116 hr = IStream_Stat(stm, &stat, STATFLAG_NONAME); 117 if (FAILED(hr)) 118 return hr; 119 120 if (stat.cbSize.QuadPart >> 32) 121 { 122 ERR("Storage is too large\n"); 123 return E_FAIL; 124 } 125 126 size = stat.cbSize.QuadPart; 127 data = msi_alloc(size); 128 if (!data) 129 return E_OUTOFMEMORY; 130 131 hr = IStream_Read(stm, data, size, &read); 132 if (FAILED(hr) || read != size) 133 goto done; 134 135 hr = CreateILockBytesOnHGlobal(NULL, TRUE, &lockbytes); 136 if (FAILED(hr)) 137 goto done; 138 139 ZeroMemory(&offset, sizeof(ULARGE_INTEGER)); 140 hr = ILockBytes_WriteAt(lockbytes, offset, data, size, &read); 141 if (FAILED(hr) || read != size) 142 goto done; 143 144 hr = StgOpenStorageOnILockBytes(lockbytes, NULL, 145 STGM_READWRITE | STGM_SHARE_DENY_NONE, 146 NULL, 0, stg); 147 if (FAILED(hr)) 148 goto done; 149 150 done: 151 msi_free(data); 152 if (lockbytes) ILockBytes_Release(lockbytes); 153 return hr; 154 } 155 156 static UINT STORAGES_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream ) 157 { 158 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 159 IStorage *stg, *substg, *prev; 160 const WCHAR *name; 161 HRESULT hr; 162 UINT r; 163 164 TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream); 165 166 if ((r = stream_to_storage(stream, &stg))) 167 return r; 168 169 name = msi_string_lookup(sv->db->strings, sv->storages[row].str_index, NULL); 170 171 hr = IStorage_CreateStorage(sv->db->storage, name, 172 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 173 0, 0, &substg); 174 if (FAILED(hr)) 175 { 176 IStorage_Release(stg); 177 return ERROR_FUNCTION_FAILED; 178 } 179 180 hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg); 181 if (FAILED(hr)) 182 { 183 IStorage_Release(substg); 184 IStorage_Release(stg); 185 return ERROR_FUNCTION_FAILED; 186 } 187 IStorage_Release(substg); 188 189 prev = sv->storages[row].storage; 190 sv->storages[row].storage = stg; 191 if (prev) IStorage_Release(prev); 192 193 return ERROR_SUCCESS; 194 } 195 196 static UINT STORAGES_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask) 197 { 198 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 199 IStorage *stg, *substg = NULL, *prev; 200 IStream *stm; 201 LPWSTR name = NULL; 202 HRESULT hr; 203 UINT r = ERROR_FUNCTION_FAILED; 204 205 TRACE("(%p, %p)\n", view, rec); 206 207 if (row >= sv->num_rows) 208 return ERROR_FUNCTION_FAILED; 209 210 r = MSI_RecordGetIStream(rec, 2, &stm); 211 if (r != ERROR_SUCCESS) 212 return r; 213 214 r = stream_to_storage(stm, &stg); 215 if (r != ERROR_SUCCESS) 216 { 217 IStream_Release(stm); 218 return r; 219 } 220 221 name = strdupW(MSI_RecordGetString(rec, 1)); 222 if (!name) 223 { 224 r = ERROR_OUTOFMEMORY; 225 goto done; 226 } 227 228 hr = IStorage_CreateStorage(sv->db->storage, name, 229 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 230 0, 0, &substg); 231 if (FAILED(hr)) 232 { 233 r = ERROR_FUNCTION_FAILED; 234 goto done; 235 } 236 237 hr = IStorage_CopyTo(stg, 0, NULL, NULL, substg); 238 if (FAILED(hr)) 239 { 240 r = ERROR_FUNCTION_FAILED; 241 goto done; 242 } 243 244 prev = sv->storages[row].storage; 245 sv->storages[row].str_index = msi_add_string(sv->db->strings, name, -1, FALSE); 246 IStorage_AddRef(stg); 247 sv->storages[row].storage = stg; 248 if (prev) IStorage_Release(prev); 249 250 done: 251 msi_free(name); 252 253 if (substg) IStorage_Release(substg); 254 IStorage_Release(stg); 255 IStream_Release(stm); 256 257 return r; 258 } 259 260 static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) 261 { 262 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 263 264 if (!storages_set_table_size(sv, ++sv->num_rows)) 265 return ERROR_FUNCTION_FAILED; 266 267 if (row == -1) 268 row = sv->num_rows - 1; 269 270 memset(&sv->storages[row], 0, sizeof(sv->storages[row])); 271 272 /* FIXME have to readjust rows */ 273 274 return STORAGES_set_row(view, row, rec, 0); 275 } 276 277 static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row) 278 { 279 FIXME("(%p %d): stub!\n", view, row); 280 return ERROR_SUCCESS; 281 } 282 283 static UINT STORAGES_execute(struct tagMSIVIEW *view, MSIRECORD *record) 284 { 285 TRACE("(%p, %p)\n", view, record); 286 return ERROR_SUCCESS; 287 } 288 289 static UINT STORAGES_close(struct tagMSIVIEW *view) 290 { 291 TRACE("(%p)\n", view); 292 return ERROR_SUCCESS; 293 } 294 295 static UINT STORAGES_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) 296 { 297 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 298 299 TRACE("(%p, %p, %p)\n", view, rows, cols); 300 301 if (cols) *cols = NUM_STORAGES_COLS; 302 if (rows) *rows = sv->num_rows; 303 304 return ERROR_SUCCESS; 305 } 306 307 static UINT STORAGES_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, 308 UINT *type, BOOL *temporary, LPCWSTR *table_name ) 309 { 310 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, 311 table_name); 312 313 if (n == 0 || n > NUM_STORAGES_COLS) 314 return ERROR_INVALID_PARAMETER; 315 316 switch (n) 317 { 318 case 1: 319 if (name) *name = L"Name"; 320 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STORAGES_NAME_LEN; 321 break; 322 323 case 2: 324 if (name) *name = L"Data"; 325 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE; 326 break; 327 } 328 if (table_name) *table_name = L"_Storages"; 329 if (temporary) *temporary = FALSE; 330 return ERROR_SUCCESS; 331 } 332 333 static UINT storages_find_row(MSISTORAGESVIEW *sv, MSIRECORD *rec, UINT *row) 334 { 335 LPCWSTR str; 336 UINT r, i, id, data; 337 338 str = MSI_RecordGetString(rec, 1); 339 r = msi_string2id(sv->db->strings, str, -1, &id); 340 if (r != ERROR_SUCCESS) 341 return r; 342 343 for (i = 0; i < sv->num_rows; i++) 344 { 345 STORAGES_fetch_int(&sv->view, i, 1, &data); 346 347 if (data == id) 348 { 349 *row = i; 350 return ERROR_SUCCESS; 351 } 352 } 353 354 return ERROR_FUNCTION_FAILED; 355 } 356 357 static UINT storages_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec) 358 { 359 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 360 UINT r, row; 361 362 r = storages_find_row(sv, rec, &row); 363 if (r != ERROR_SUCCESS) 364 return ERROR_FUNCTION_FAILED; 365 366 return STORAGES_set_row(view, row, rec, 0); 367 } 368 369 static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) 370 { 371 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 372 UINT r, row; 373 374 r = storages_find_row(sv, rec, &row); 375 if (r == ERROR_SUCCESS) 376 return storages_modify_update(view, rec); 377 378 return STORAGES_insert_row(view, rec, -1, FALSE); 379 } 380 381 static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) 382 { 383 UINT r; 384 385 TRACE("%p %d %p\n", view, eModifyMode, rec); 386 387 switch (eModifyMode) 388 { 389 case MSIMODIFY_ASSIGN: 390 r = storages_modify_assign(view, rec); 391 break; 392 393 case MSIMODIFY_INSERT: 394 r = STORAGES_insert_row(view, rec, -1, FALSE); 395 break; 396 397 case MSIMODIFY_UPDATE: 398 r = storages_modify_update(view, rec); 399 break; 400 401 case MSIMODIFY_VALIDATE_NEW: 402 case MSIMODIFY_INSERT_TEMPORARY: 403 case MSIMODIFY_REFRESH: 404 case MSIMODIFY_REPLACE: 405 case MSIMODIFY_MERGE: 406 case MSIMODIFY_DELETE: 407 case MSIMODIFY_VALIDATE: 408 case MSIMODIFY_VALIDATE_FIELD: 409 case MSIMODIFY_VALIDATE_DELETE: 410 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); 411 r = ERROR_CALL_NOT_IMPLEMENTED; 412 break; 413 414 default: 415 r = ERROR_INVALID_DATA; 416 } 417 418 return r; 419 } 420 421 static UINT STORAGES_delete(struct tagMSIVIEW *view) 422 { 423 MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; 424 UINT i; 425 426 TRACE("(%p)\n", view); 427 428 for (i = 0; i < sv->num_rows; i++) 429 { 430 if (sv->storages[i].storage) 431 IStorage_Release(sv->storages[i].storage); 432 } 433 434 msi_free(sv->storages); 435 sv->storages = NULL; 436 msi_free(sv); 437 438 return ERROR_SUCCESS; 439 } 440 441 static const MSIVIEWOPS storages_ops = 442 { 443 STORAGES_fetch_int, 444 STORAGES_fetch_stream, 445 NULL, 446 STORAGES_set_string, 447 STORAGES_set_stream, 448 STORAGES_set_row, 449 STORAGES_insert_row, 450 STORAGES_delete_row, 451 STORAGES_execute, 452 STORAGES_close, 453 STORAGES_get_dimensions, 454 STORAGES_get_column_info, 455 STORAGES_modify, 456 STORAGES_delete, 457 NULL, 458 NULL, 459 NULL, 460 NULL, 461 NULL, 462 }; 463 464 static INT add_storages_to_table(MSISTORAGESVIEW *sv) 465 { 466 IEnumSTATSTG *stgenum = NULL; 467 STATSTG stat; 468 HRESULT hr; 469 UINT count = 0; 470 ULONG size; 471 472 hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum); 473 if (FAILED(hr)) 474 return -1; 475 476 sv->max_storages = 1; 477 sv->storages = msi_alloc(sizeof(*sv->storages)); 478 if (!sv->storages) 479 return -1; 480 481 while (TRUE) 482 { 483 size = 0; 484 hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size); 485 if (FAILED(hr) || !size) 486 break; 487 488 if (stat.type != STGTY_STORAGE) 489 { 490 CoTaskMemFree(stat.pwcsName); 491 continue; 492 } 493 494 TRACE("enumerated storage %s\n", debugstr_w(stat.pwcsName)); 495 496 if (!storages_set_table_size(sv, ++count)) 497 { 498 count = -1; 499 break; 500 } 501 502 sv->storages[count - 1].str_index = msi_add_string(sv->db->strings, stat.pwcsName, -1, FALSE); 503 sv->storages[count - 1].storage = NULL; 504 505 IStorage_OpenStorage(sv->db->storage, stat.pwcsName, NULL, 506 STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, 507 &sv->storages[count - 1].storage); 508 CoTaskMemFree(stat.pwcsName); 509 } 510 511 IEnumSTATSTG_Release(stgenum); 512 return count; 513 } 514 515 UINT STORAGES_CreateView(MSIDATABASE *db, MSIVIEW **view) 516 { 517 MSISTORAGESVIEW *sv; 518 INT rows; 519 520 TRACE("(%p, %p)\n", db, view); 521 522 sv = msi_alloc_zero( sizeof(MSISTORAGESVIEW) ); 523 if (!sv) 524 return ERROR_FUNCTION_FAILED; 525 526 sv->view.ops = &storages_ops; 527 sv->db = db; 528 529 rows = add_storages_to_table(sv); 530 if (rows < 0) 531 { 532 msi_free( sv ); 533 return ERROR_FUNCTION_FAILED; 534 } 535 sv->num_rows = rows; 536 537 *view = (MSIVIEW *)sv; 538 539 return ERROR_SUCCESS; 540 } 541