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