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