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