51064ed2d5b440425a21ebff26fe3bd20b1b5360
[reactos.git] / reactos / dll / win32 / msi / streams.c
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2007 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 "winerror.h"
32 //#include "msi.h"
33 //#include "msiquery.h"
34 //#include "objbase.h"
35 #include "msipriv.h"
36 //#include "query.h"
37
38 #include <wine/debug.h>
39 //#include "wine/unicode.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
42
43 #define NUM_STREAMS_COLS 2
44
45 typedef struct tabSTREAM
46 {
47 UINT str_index;
48 IStream *stream;
49 } STREAM;
50
51 typedef struct tagMSISTREAMSVIEW
52 {
53 MSIVIEW view;
54 MSIDATABASE *db;
55 STREAM **streams;
56 UINT max_streams;
57 UINT num_rows;
58 UINT row_size;
59 } MSISTREAMSVIEW;
60
61 static BOOL streams_set_table_size(MSISTREAMSVIEW *sv, UINT size)
62 {
63 if (size >= sv->max_streams)
64 {
65 sv->max_streams *= 2;
66 sv->streams = msi_realloc_zero(sv->streams, sv->max_streams * sizeof(STREAM *));
67 if (!sv->streams)
68 return FALSE;
69 }
70
71 return TRUE;
72 }
73
74 static STREAM *create_stream(MSISTREAMSVIEW *sv, LPCWSTR name, BOOL encoded, IStream *stm)
75 {
76 STREAM *stream;
77 WCHAR decoded[MAX_STREAM_NAME_LEN];
78
79 stream = msi_alloc(sizeof(STREAM));
80 if (!stream)
81 return NULL;
82
83 if (encoded)
84 {
85 decode_streamname(name, decoded);
86 TRACE("stream -> %s %s\n", debugstr_w(name), debugstr_w(decoded));
87 name = decoded;
88 }
89
90 stream->str_index = msi_addstringW(sv->db->strings, name, -1, 1, StringNonPersistent);
91 stream->stream = stm;
92 return stream;
93 }
94
95 static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
96 {
97 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
98
99 TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
100
101 if (col != 1)
102 return ERROR_INVALID_PARAMETER;
103
104 if (row >= sv->num_rows)
105 return ERROR_NO_MORE_ITEMS;
106
107 *val = sv->streams[row]->str_index;
108
109 return ERROR_SUCCESS;
110 }
111
112 static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
113 {
114 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
115
116 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
117
118 if (row >= sv->num_rows)
119 return ERROR_FUNCTION_FAILED;
120
121 IStream_AddRef(sv->streams[row]->stream);
122 *stm = sv->streams[row]->stream;
123
124 return ERROR_SUCCESS;
125 }
126
127 static UINT STREAMS_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
128 {
129 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
130
131 TRACE("%p %d %p\n", sv, row, rec);
132
133 return msi_view_get_row( sv->db, view, row, rec );
134 }
135
136 static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
137 {
138 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
139 STREAM *stream;
140 IStream *stm;
141 STATSTG stat;
142 LPWSTR encname = NULL, name = NULL;
143 USHORT *data = NULL;
144 HRESULT hr;
145 ULONG count;
146 UINT r = ERROR_FUNCTION_FAILED;
147
148 TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
149
150 if (row > sv->num_rows)
151 return ERROR_FUNCTION_FAILED;
152
153 r = MSI_RecordGetIStream(rec, 2, &stm);
154 if (r != ERROR_SUCCESS)
155 return r;
156
157 hr = IStream_Stat(stm, &stat, STATFLAG_NONAME);
158 if (FAILED(hr))
159 {
160 WARN("failed to stat stream: %08x\n", hr);
161 goto done;
162 }
163
164 if (stat.cbSize.QuadPart >> 32)
165 {
166 WARN("stream too large\n");
167 goto done;
168 }
169
170 data = msi_alloc(stat.cbSize.QuadPart);
171 if (!data)
172 goto done;
173
174 hr = IStream_Read(stm, data, stat.cbSize.QuadPart, &count);
175 if (FAILED(hr) || count != stat.cbSize.QuadPart)
176 {
177 WARN("failed to read stream: %08x\n", hr);
178 goto done;
179 }
180
181 name = strdupW(MSI_RecordGetString(rec, 1));
182 if (!name)
183 {
184 WARN("failed to retrieve stream name\n");
185 goto done;
186 }
187
188 encname = encode_streamname(FALSE, name);
189 msi_destroy_stream(sv->db, encname);
190
191 r = write_stream_data(sv->db->storage, name, data, count, FALSE);
192 if (r != ERROR_SUCCESS)
193 {
194 WARN("failed to write stream data: %d\n", r);
195 goto done;
196 }
197
198 stream = create_stream(sv, name, FALSE, NULL);
199 if (!stream)
200 goto done;
201
202 hr = IStorage_OpenStream(sv->db->storage, encname, 0,
203 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream);
204 if (FAILED(hr))
205 {
206 WARN("failed to open stream: %08x\n", hr);
207 msi_free(stream);
208 goto done;
209 }
210
211 sv->streams[row] = stream;
212
213 done:
214 msi_free(name);
215 msi_free(data);
216 msi_free(encname);
217
218 IStream_Release(stm);
219
220 return r;
221 }
222
223 static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
224 {
225 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
226 UINT i;
227
228 TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);
229
230 if (!streams_set_table_size(sv, ++sv->num_rows))
231 return ERROR_FUNCTION_FAILED;
232
233 if (row == -1)
234 row = sv->num_rows - 1;
235
236 /* shift the rows to make room for the new row */
237 for (i = sv->num_rows - 1; i > row; i--)
238 {
239 sv->streams[i] = sv->streams[i - 1];
240 }
241
242 return STREAMS_set_row(view, row, rec, 0);
243 }
244
245 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
246 {
247 FIXME("(%p %d): stub!\n", view, row);
248 return ERROR_SUCCESS;
249 }
250
251 static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
252 {
253 TRACE("(%p, %p)\n", view, record);
254 return ERROR_SUCCESS;
255 }
256
257 static UINT STREAMS_close(struct tagMSIVIEW *view)
258 {
259 TRACE("(%p)\n", view);
260 return ERROR_SUCCESS;
261 }
262
263 static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
264 {
265 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
266
267 TRACE("(%p, %p, %p)\n", view, rows, cols);
268
269 if (cols) *cols = NUM_STREAMS_COLS;
270 if (rows) *rows = sv->num_rows;
271
272 return ERROR_SUCCESS;
273 }
274
275 static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
276 UINT *type, BOOL *temporary, LPCWSTR *table_name )
277 {
278 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary,
279 table_name);
280
281 if (n == 0 || n > NUM_STREAMS_COLS)
282 return ERROR_INVALID_PARAMETER;
283
284 switch (n)
285 {
286 case 1:
287 if (name) *name = szName;
288 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
289 break;
290
291 case 2:
292 if (name) *name = szData;
293 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
294 break;
295 }
296 if (table_name) *table_name = szStreams;
297 if (temporary) *temporary = FALSE;
298 return ERROR_SUCCESS;
299 }
300
301 static UINT streams_find_row(MSISTREAMSVIEW *sv, MSIRECORD *rec, UINT *row)
302 {
303 LPCWSTR str;
304 UINT r, i, id, data;
305
306 str = MSI_RecordGetString(rec, 1);
307 r = msi_string2id(sv->db->strings, str, -1, &id);
308 if (r != ERROR_SUCCESS)
309 return r;
310
311 for (i = 0; i < sv->num_rows; i++)
312 {
313 STREAMS_fetch_int(&sv->view, i, 1, &data);
314
315 if (data == id)
316 {
317 *row = i;
318 return ERROR_SUCCESS;
319 }
320 }
321
322 return ERROR_FUNCTION_FAILED;
323 }
324
325 static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
326 {
327 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
328 UINT r, row;
329
330 r = streams_find_row(sv, rec, &row);
331 if (r != ERROR_SUCCESS)
332 return ERROR_FUNCTION_FAILED;
333
334 return STREAMS_set_row(view, row, rec, 0);
335 }
336
337 static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
338 {
339 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
340 UINT r, row;
341
342 r = streams_find_row(sv, rec, &row);
343 if (r == ERROR_SUCCESS)
344 return streams_modify_update(view, rec);
345
346 return STREAMS_insert_row(view, rec, -1, FALSE);
347 }
348
349 static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
350 {
351 UINT r;
352
353 TRACE("%p %d %p\n", view, eModifyMode, rec);
354
355 switch (eModifyMode)
356 {
357 case MSIMODIFY_ASSIGN:
358 r = streams_modify_assign(view, rec);
359 break;
360
361 case MSIMODIFY_INSERT:
362 r = STREAMS_insert_row(view, rec, -1, FALSE);
363 break;
364
365 case MSIMODIFY_UPDATE:
366 r = streams_modify_update(view, rec);
367 break;
368
369 case MSIMODIFY_VALIDATE_NEW:
370 case MSIMODIFY_INSERT_TEMPORARY:
371 case MSIMODIFY_REFRESH:
372 case MSIMODIFY_REPLACE:
373 case MSIMODIFY_MERGE:
374 case MSIMODIFY_DELETE:
375 case MSIMODIFY_VALIDATE:
376 case MSIMODIFY_VALIDATE_FIELD:
377 case MSIMODIFY_VALIDATE_DELETE:
378 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
379 r = ERROR_CALL_NOT_IMPLEMENTED;
380 break;
381
382 default:
383 r = ERROR_INVALID_DATA;
384 }
385
386 return r;
387 }
388
389 static UINT STREAMS_delete(struct tagMSIVIEW *view)
390 {
391 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
392 UINT i;
393
394 TRACE("(%p)\n", view);
395
396 for (i = 0; i < sv->num_rows; i++)
397 {
398 if (sv->streams[i])
399 {
400 if (sv->streams[i]->stream)
401 IStream_Release(sv->streams[i]->stream);
402 msi_free(sv->streams[i]);
403 }
404 }
405
406 msi_free(sv->streams);
407 msi_free(sv);
408
409 return ERROR_SUCCESS;
410 }
411
412 static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col,
413 UINT val, UINT *row, MSIITERHANDLE *handle)
414 {
415 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
416 UINT index = PtrToUlong(*handle);
417
418 TRACE("(%p, %d, %d, %p, %p)\n", view, col, val, row, handle);
419
420 if (col == 0 || col > NUM_STREAMS_COLS)
421 return ERROR_INVALID_PARAMETER;
422
423 while (index < sv->num_rows)
424 {
425 if (sv->streams[index]->str_index == val)
426 {
427 *row = index;
428 break;
429 }
430
431 index++;
432 }
433
434 *handle = UlongToPtr(++index);
435
436 if (index > sv->num_rows)
437 return ERROR_NO_MORE_ITEMS;
438
439 return ERROR_SUCCESS;
440 }
441
442 static const MSIVIEWOPS streams_ops =
443 {
444 STREAMS_fetch_int,
445 STREAMS_fetch_stream,
446 STREAMS_get_row,
447 STREAMS_set_row,
448 STREAMS_insert_row,
449 STREAMS_delete_row,
450 STREAMS_execute,
451 STREAMS_close,
452 STREAMS_get_dimensions,
453 STREAMS_get_column_info,
454 STREAMS_modify,
455 STREAMS_delete,
456 STREAMS_find_matching_rows,
457 NULL,
458 NULL,
459 NULL,
460 NULL,
461 NULL,
462 NULL,
463 };
464
465 static INT add_streams_to_table(MSISTREAMSVIEW *sv)
466 {
467 IEnumSTATSTG *stgenum = NULL;
468 STATSTG stat;
469 STREAM *stream = NULL;
470 HRESULT hr;
471 UINT r, count = 0, size;
472 LPWSTR encname;
473
474 hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum);
475 if (FAILED(hr))
476 return -1;
477
478 sv->max_streams = 1;
479 sv->streams = msi_alloc_zero(sizeof(STREAM *));
480 if (!sv->streams)
481 return -1;
482
483 while (TRUE)
484 {
485 size = 0;
486 hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size);
487 if (FAILED(hr) || !size)
488 break;
489
490 if (stat.type != STGTY_STREAM)
491 {
492 CoTaskMemFree(stat.pwcsName);
493 continue;
494 }
495
496 /* table streams are not in the _Streams table */
497 if (*stat.pwcsName == 0x4840)
498 {
499 CoTaskMemFree(stat.pwcsName);
500 continue;
501 }
502
503 stream = create_stream(sv, stat.pwcsName, TRUE, NULL);
504 if (!stream)
505 {
506 count = -1;
507 CoTaskMemFree(stat.pwcsName);
508 break;
509 }
510
511 /* these streams appear to be unencoded */
512 if (*stat.pwcsName == 0x0005)
513 {
514 r = msi_get_raw_stream(sv->db, stat.pwcsName, &stream->stream);
515 }
516 else
517 {
518 encname = encode_streamname(FALSE, stat.pwcsName);
519 r = msi_get_raw_stream(sv->db, encname, &stream->stream);
520 msi_free(encname);
521 }
522 CoTaskMemFree(stat.pwcsName);
523
524 if (r != ERROR_SUCCESS)
525 {
526 WARN("unable to get stream %u\n", r);
527 count = -1;
528 break;
529 }
530
531 if (!streams_set_table_size(sv, ++count))
532 {
533 count = -1;
534 break;
535 }
536
537 sv->streams[count - 1] = stream;
538 }
539
540 IEnumSTATSTG_Release(stgenum);
541 return count;
542 }
543
544 UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
545 {
546 MSISTREAMSVIEW *sv;
547 INT rows;
548
549 TRACE("(%p, %p)\n", db, view);
550
551 sv = msi_alloc_zero( sizeof(MSISTREAMSVIEW) );
552 if (!sv)
553 return ERROR_FUNCTION_FAILED;
554
555 sv->view.ops = &streams_ops;
556 sv->db = db;
557 rows = add_streams_to_table(sv);
558 if (rows < 0)
559 {
560 msi_free( sv );
561 return ERROR_FUNCTION_FAILED;
562 }
563 sv->num_rows = rows;
564
565 *view = (MSIVIEW *)sv;
566
567 return ERROR_SUCCESS;
568 }