1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2007 James Hawkins
5 * Copyright 2015 Hans Leidekker for CodeWeavers
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <stdarg.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "msi.h"
30 #include "msiquery.h"
31 #include "objbase.h"
32 #include "msipriv.h"
33 #include "query.h"
34
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
38
39 #define NUM_STREAMS_COLS 2
40
41 struct streams_view
42 {
43 MSIVIEW view;
44 MSIDATABASE *db;
45 UINT num_cols;
46 };
47
streams_resize_table(MSIDATABASE * db,UINT size)48 static BOOL streams_resize_table( MSIDATABASE *db, UINT size )
49 {
50 if (!db->num_streams_allocated)
51 {
52 if (!(db->streams = calloc( size, sizeof(MSISTREAM) ))) return FALSE;
53 db->num_streams_allocated = size;
54 return TRUE;
55 }
56 while (size >= db->num_streams_allocated)
57 {
58 MSISTREAM *tmp;
59 UINT new_size = db->num_streams_allocated * 2;
60 if (!(tmp = realloc( db->streams, new_size * sizeof(*tmp) ))) return FALSE;
61 memset( tmp + db->num_streams_allocated, 0, (new_size - db->num_streams_allocated) * sizeof(*tmp) );
62 db->streams = tmp;
63 db->num_streams_allocated = new_size;
64 }
65 return TRUE;
66 }
67
STREAMS_fetch_int(struct tagMSIVIEW * view,UINT row,UINT col,UINT * val)68 static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
69 {
70 struct streams_view *sv = (struct streams_view *)view;
71
72 TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
73
74 if (col != 1)
75 return ERROR_INVALID_PARAMETER;
76
77 if (row >= sv->db->num_streams)
78 return ERROR_NO_MORE_ITEMS;
79
80 *val = sv->db->streams[row].str_index;
81
82 return ERROR_SUCCESS;
83 }
84
STREAMS_fetch_stream(struct tagMSIVIEW * view,UINT row,UINT col,IStream ** stm)85 static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
86 {
87 struct streams_view *sv = (struct streams_view *)view;
88 LARGE_INTEGER pos;
89 HRESULT hr;
90
91 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
92
93 if (row >= sv->db->num_streams)
94 return ERROR_FUNCTION_FAILED;
95
96 pos.QuadPart = 0;
97 hr = IStream_Seek( sv->db->streams[row].stream, pos, STREAM_SEEK_SET, NULL );
98 if (FAILED( hr ))
99 return ERROR_FUNCTION_FAILED;
100
101 *stm = sv->db->streams[row].stream;
102 IStream_AddRef( *stm );
103
104 return ERROR_SUCCESS;
105 }
106
STREAMS_set_string(struct tagMSIVIEW * view,UINT row,UINT col,const WCHAR * val,int len)107 static UINT STREAMS_set_string( struct tagMSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
108 {
109 ERR("Cannot modify primary key.\n");
110 return ERROR_FUNCTION_FAILED;
111 }
112
STREAMS_set_stream(MSIVIEW * view,UINT row,UINT col,IStream * stream)113 static UINT STREAMS_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
114 {
115 struct streams_view *sv = (struct streams_view *)view;
116 IStream *prev;
117
118 TRACE("view %p, row %u, col %u, stream %p.\n", view, row, col, stream);
119
120 prev = sv->db->streams[row].stream;
121 IStream_AddRef(sv->db->streams[row].stream = stream);
122 if (prev) IStream_Release(prev);
123 return ERROR_SUCCESS;
124 }
125
STREAMS_set_row(struct tagMSIVIEW * view,UINT row,MSIRECORD * rec,UINT mask)126 static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
127 {
128 struct streams_view *sv = (struct streams_view *)view;
129
130 TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
131
132 if (row > sv->db->num_streams || mask >= (1 << sv->num_cols))
133 return ERROR_INVALID_PARAMETER;
134
135 if (mask & 1)
136 {
137 const WCHAR *name = MSI_RecordGetString( rec, 1 );
138
139 if (!name) return ERROR_INVALID_PARAMETER;
140 sv->db->streams[row].str_index = msi_add_string( sv->db->strings, name, -1, FALSE );
141 }
142 if (mask & 2)
143 {
144 IStream *old, *new;
145 HRESULT hr;
146 UINT r;
147
148 r = MSI_RecordGetIStream( rec, 2, &new );
149 if (r != ERROR_SUCCESS)
150 return r;
151
152 old = sv->db->streams[row].stream;
153 hr = IStream_QueryInterface( new, &IID_IStream, (void **)&sv->db->streams[row].stream );
154 IStream_Release( new );
155 if (FAILED( hr ))
156 {
157 return ERROR_FUNCTION_FAILED;
158 }
159 if (old) IStream_Release( old );
160 }
161
162 return ERROR_SUCCESS;
163 }
164
streams_find_row(struct streams_view * sv,MSIRECORD * rec,UINT * row)165 static UINT streams_find_row( struct streams_view *sv, MSIRECORD *rec, UINT *row )
166 {
167 const WCHAR *str;
168 UINT r, i, id, val;
169
170 str = MSI_RecordGetString( rec, 1 );
171 r = msi_string2id( sv->db->strings, str, -1, &id );
172 if (r != ERROR_SUCCESS)
173 return r;
174
175 for (i = 0; i < sv->db->num_streams; i++)
176 {
177 STREAMS_fetch_int( &sv->view, i, 1, &val );
178
179 if (val == id)
180 {
181 if (row) *row = i;
182 return ERROR_SUCCESS;
183 }
184 }
185
186 return ERROR_FUNCTION_FAILED;
187 }
188
STREAMS_insert_row(struct tagMSIVIEW * view,MSIRECORD * rec,UINT row,BOOL temporary)189 static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
190 {
191 struct streams_view *sv = (struct streams_view *)view;
192 UINT i, r, num_rows = sv->db->num_streams + 1;
193
194 TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);
195
196 r = streams_find_row( sv, rec, NULL );
197 if (r == ERROR_SUCCESS)
198 return ERROR_FUNCTION_FAILED;
199
200 if (!streams_resize_table( sv->db, num_rows ))
201 return ERROR_FUNCTION_FAILED;
202
203 if (row == -1)
204 row = num_rows - 1;
205
206 /* shift the rows to make room for the new row */
207 for (i = num_rows - 1; i > row; i--)
208 {
209 sv->db->streams[i] = sv->db->streams[i - 1];
210 }
211
212 r = STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
213 if (r == ERROR_SUCCESS)
214 sv->db->num_streams = num_rows;
215
216 return r;
217 }
218
STREAMS_delete_row(struct tagMSIVIEW * view,UINT row)219 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
220 {
221 MSIDATABASE *db = ((struct streams_view *)view)->db;
222 UINT i, num_rows = db->num_streams - 1;
223 const WCHAR *name;
224 WCHAR *encname;
225 HRESULT hr;
226
227 TRACE("(%p %d)\n", view, row);
228
229 if (!db->num_streams || row > num_rows)
230 return ERROR_FUNCTION_FAILED;
231
232 name = msi_string_lookup( db->strings, db->streams[row].str_index, NULL );
233 if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
234 IStream_Release( db->streams[row].stream );
235
236 for (i = row; i < num_rows; i++)
237 db->streams[i] = db->streams[i + 1];
238 db->num_streams = num_rows;
239
240 hr = IStorage_DestroyElement( db->storage, encname );
241 free( encname );
242 return FAILED( hr ) ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
243 }
244
STREAMS_execute(struct tagMSIVIEW * view,MSIRECORD * record)245 static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
246 {
247 TRACE("(%p, %p)\n", view, record);
248 return ERROR_SUCCESS;
249 }
250
STREAMS_close(struct tagMSIVIEW * view)251 static UINT STREAMS_close(struct tagMSIVIEW *view)
252 {
253 TRACE("(%p)\n", view);
254 return ERROR_SUCCESS;
255 }
256
STREAMS_get_dimensions(struct tagMSIVIEW * view,UINT * rows,UINT * cols)257 static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
258 {
259 struct streams_view *sv = (struct streams_view *)view;
260
261 TRACE("(%p, %p, %p)\n", view, rows, cols);
262
263 if (cols) *cols = sv->num_cols;
264 if (rows) *rows = sv->db->num_streams;
265
266 return ERROR_SUCCESS;
267 }
268
STREAMS_get_column_info(struct tagMSIVIEW * view,UINT n,LPCWSTR * name,UINT * type,BOOL * temporary,LPCWSTR * table_name)269 static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
270 UINT *type, BOOL *temporary, LPCWSTR *table_name )
271 {
272 struct streams_view *sv = (struct streams_view *)view;
273
274 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, table_name);
275
276 if (!n || n > sv->num_cols)
277 return ERROR_INVALID_PARAMETER;
278
279 switch (n)
280 {
281 case 1:
282 if (name) *name = L"Name";
283 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
284 break;
285
286 case 2:
287 if (name) *name = L"Data";
288 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
289 break;
290 }
291 if (table_name) *table_name = L"_Streams";
292 if (temporary) *temporary = FALSE;
293 return ERROR_SUCCESS;
294 }
295
streams_modify_update(struct tagMSIVIEW * view,MSIRECORD * rec)296 static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
297 {
298 struct streams_view *sv = (struct streams_view *)view;
299 UINT r, row;
300
301 r = streams_find_row(sv, rec, &row);
302 if (r != ERROR_SUCCESS)
303 return ERROR_FUNCTION_FAILED;
304
305 return STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
306 }
307
streams_modify_assign(struct tagMSIVIEW * view,MSIRECORD * rec)308 static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
309 {
310 struct streams_view *sv = (struct streams_view *)view;
311 UINT r;
312
313 r = streams_find_row( sv, rec, NULL );
314 if (r == ERROR_SUCCESS)
315 return streams_modify_update(view, rec);
316
317 return STREAMS_insert_row(view, rec, -1, FALSE);
318 }
319
STREAMS_modify(struct tagMSIVIEW * view,MSIMODIFY eModifyMode,MSIRECORD * rec,UINT row)320 static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
321 {
322 UINT r;
323
324 TRACE("%p %d %p\n", view, eModifyMode, rec);
325
326 switch (eModifyMode)
327 {
328 case MSIMODIFY_ASSIGN:
329 r = streams_modify_assign(view, rec);
330 break;
331
332 case MSIMODIFY_INSERT:
333 r = STREAMS_insert_row(view, rec, -1, FALSE);
334 break;
335
336 case MSIMODIFY_UPDATE:
337 r = streams_modify_update(view, rec);
338 break;
339
340 case MSIMODIFY_DELETE:
341 r = STREAMS_delete_row(view, row - 1);
342 break;
343
344 case MSIMODIFY_VALIDATE_NEW:
345 case MSIMODIFY_INSERT_TEMPORARY:
346 case MSIMODIFY_REFRESH:
347 case MSIMODIFY_REPLACE:
348 case MSIMODIFY_MERGE:
349 case MSIMODIFY_VALIDATE:
350 case MSIMODIFY_VALIDATE_FIELD:
351 case MSIMODIFY_VALIDATE_DELETE:
352 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
353 r = ERROR_CALL_NOT_IMPLEMENTED;
354 break;
355
356 default:
357 r = ERROR_INVALID_DATA;
358 }
359
360 return r;
361 }
362
STREAMS_delete(struct tagMSIVIEW * view)363 static UINT STREAMS_delete(struct tagMSIVIEW *view)
364 {
365 struct streams_view *sv = (struct streams_view *)view;
366
367 TRACE("(%p)\n", view);
368
369 free(sv);
370 return ERROR_SUCCESS;
371 }
372
373 static const MSIVIEWOPS streams_ops =
374 {
375 STREAMS_fetch_int,
376 STREAMS_fetch_stream,
377 NULL,
378 STREAMS_set_string,
379 STREAMS_set_stream,
380 STREAMS_set_row,
381 STREAMS_insert_row,
382 STREAMS_delete_row,
383 STREAMS_execute,
384 STREAMS_close,
385 STREAMS_get_dimensions,
386 STREAMS_get_column_info,
387 STREAMS_modify,
388 STREAMS_delete,
389 NULL,
390 NULL,
391 NULL,
392 NULL,
393 NULL,
394 };
395
open_stream(MSIDATABASE * db,const WCHAR * name,IStream ** stream)396 static HRESULT open_stream( MSIDATABASE *db, const WCHAR *name, IStream **stream )
397 {
398 HRESULT hr;
399
400 hr = IStorage_OpenStream( db->storage, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
401 if (FAILED( hr ))
402 {
403 MSITRANSFORM *transform;
404
405 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
406 {
407 hr = IStorage_OpenStream( transform->stg, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
408 if (SUCCEEDED( hr ))
409 break;
410 }
411 }
412 return hr;
413 }
414
find_stream(MSIDATABASE * db,const WCHAR * name)415 static MSISTREAM *find_stream( MSIDATABASE *db, const WCHAR *name )
416 {
417 UINT r, id, i;
418
419 r = msi_string2id( db->strings, name, -1, &id );
420 if (r != ERROR_SUCCESS)
421 return NULL;
422
423 for (i = 0; i < db->num_streams; i++)
424 {
425 if (db->streams[i].str_index == id) return &db->streams[i];
426 }
427 return NULL;
428 }
429
append_stream(MSIDATABASE * db,const WCHAR * name,IStream * stream)430 static UINT append_stream( MSIDATABASE *db, const WCHAR *name, IStream *stream )
431 {
432 UINT i = db->num_streams;
433
434 if (!streams_resize_table( db, db->num_streams + 1 ))
435 return ERROR_OUTOFMEMORY;
436
437 db->streams[i].str_index = msi_add_string( db->strings, name, -1, FALSE );
438 db->streams[i].stream = stream;
439 db->num_streams++;
440
441 TRACE("added %s\n", debugstr_w( name ));
442 return ERROR_SUCCESS;
443 }
444
load_streams(MSIDATABASE * db)445 static UINT load_streams( MSIDATABASE *db )
446 {
447 WCHAR decoded[MAX_STREAM_NAME_LEN + 1];
448 IEnumSTATSTG *stgenum;
449 STATSTG stat;
450 HRESULT hr;
451 ULONG count;
452 UINT r = ERROR_SUCCESS;
453 IStream *stream;
454
455 hr = IStorage_EnumElements( db->storage, 0, NULL, 0, &stgenum );
456 if (FAILED( hr ))
457 return ERROR_FUNCTION_FAILED;
458
459 for (;;)
460 {
461 count = 0;
462 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
463 if (FAILED( hr ) || !count)
464 break;
465
466 /* table streams are not in the _Streams table */
467 if (stat.type != STGTY_STREAM || *stat.pwcsName == 0x4840)
468 {
469 CoTaskMemFree( stat.pwcsName );
470 continue;
471 }
472 decode_streamname( stat.pwcsName, decoded );
473 if (find_stream( db, decoded ))
474 {
475 CoTaskMemFree( stat.pwcsName );
476 continue;
477 }
478 TRACE("found new stream %s\n", debugstr_w( decoded ));
479
480 hr = open_stream( db, stat.pwcsName, &stream );
481 CoTaskMemFree( stat.pwcsName );
482 if (FAILED( hr ))
483 {
484 ERR( "unable to open stream %#lx\n", hr );
485 r = ERROR_FUNCTION_FAILED;
486 break;
487 }
488
489 r = append_stream( db, decoded, stream );
490 if (r != ERROR_SUCCESS)
491 break;
492 }
493
494 TRACE("loaded %u streams\n", db->num_streams);
495 IEnumSTATSTG_Release( stgenum );
496 return r;
497 }
498
msi_get_stream(MSIDATABASE * db,const WCHAR * name,IStream ** ret)499 UINT msi_get_stream( MSIDATABASE *db, const WCHAR *name, IStream **ret )
500 {
501 MSISTREAM *stream;
502 WCHAR *encname;
503 HRESULT hr;
504 UINT r;
505
506 if ((stream = find_stream( db, name )))
507 {
508 LARGE_INTEGER pos;
509
510 pos.QuadPart = 0;
511 hr = IStream_Seek( stream->stream, pos, STREAM_SEEK_SET, NULL );
512 if (FAILED( hr ))
513 return ERROR_FUNCTION_FAILED;
514
515 *ret = stream->stream;
516 IStream_AddRef( *ret );
517 return ERROR_SUCCESS;
518 }
519
520 if (!(encname = encode_streamname( FALSE, name )))
521 return ERROR_OUTOFMEMORY;
522
523 hr = open_stream( db, encname, ret );
524 free( encname );
525 if (FAILED( hr ))
526 return ERROR_FUNCTION_FAILED;
527
528 r = append_stream( db, name, *ret );
529 if (r != ERROR_SUCCESS)
530 {
531 IStream_Release( *ret );
532 return r;
533 }
534
535 IStream_AddRef( *ret );
536 return ERROR_SUCCESS;
537 }
538
STREAMS_CreateView(MSIDATABASE * db,MSIVIEW ** view)539 UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
540 {
541 struct streams_view *sv;
542 UINT r;
543
544 TRACE("(%p, %p)\n", db, view);
545
546 r = load_streams( db );
547 if (r != ERROR_SUCCESS)
548 return r;
549
550 if (!(sv = calloc( 1, sizeof(*sv) )))
551 return ERROR_OUTOFMEMORY;
552
553 sv->view.ops = &streams_ops;
554 sv->num_cols = NUM_STREAMS_COLS;
555 sv->db = db;
556
557 *view = (MSIVIEW *)sv;
558
559 return ERROR_SUCCESS;
560 }
561
write_stream(IStream * dst,IStream * src)562 static HRESULT write_stream( IStream *dst, IStream *src )
563 {
564 HRESULT hr;
565 char buf[4096];
566 STATSTG stat;
567 LARGE_INTEGER pos;
568 ULONG count;
569 UINT size;
570
571 hr = IStream_Stat( src, &stat, STATFLAG_NONAME );
572 if (FAILED( hr )) return hr;
573
574 hr = IStream_SetSize( dst, stat.cbSize );
575 if (FAILED( hr )) return hr;
576
577 pos.QuadPart = 0;
578 hr = IStream_Seek( dst, pos, STREAM_SEEK_SET, NULL );
579 if (FAILED( hr )) return hr;
580
581 for (;;)
582 {
583 size = min( sizeof(buf), stat.cbSize.QuadPart );
584 hr = IStream_Read( src, buf, size, &count );
585 if (FAILED( hr ) || count != size)
586 {
587 WARN( "failed to read stream: %#lx\n", hr );
588 return E_INVALIDARG;
589 }
590 stat.cbSize.QuadPart -= count;
591 if (count)
592 {
593 size = count;
594 hr = IStream_Write( dst, buf, size, &count );
595 if (FAILED( hr ) || count != size)
596 {
597 WARN( "failed to write stream: %#lx\n", hr );
598 return E_INVALIDARG;
599 }
600 }
601 if (!stat.cbSize.QuadPart) break;
602 }
603
604 return S_OK;
605 }
606
msi_commit_streams(MSIDATABASE * db)607 UINT msi_commit_streams( MSIDATABASE *db )
608 {
609 UINT i;
610 const WCHAR *name;
611 WCHAR *encname;
612 IStream *stream;
613 HRESULT hr;
614
615 TRACE("got %u streams\n", db->num_streams);
616
617 for (i = 0; i < db->num_streams; i++)
618 {
619 name = msi_string_lookup( db->strings, db->streams[i].str_index, NULL );
620 if (!wcscmp( name, L"\5SummaryInformation" )) continue;
621
622 if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
623 TRACE("saving stream %s as %s\n", debugstr_w(name), debugstr_w(encname));
624
625 hr = IStorage_CreateStream( db->storage, encname, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stream );
626 if (SUCCEEDED( hr ))
627 {
628 hr = write_stream( stream, db->streams[i].stream );
629 if (FAILED( hr ))
630 {
631 ERR( "failed to write stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
632 free( encname );
633 IStream_Release( stream );
634 return ERROR_FUNCTION_FAILED;
635 }
636 hr = IStream_Commit( stream, 0 );
637 IStream_Release( stream );
638 if (FAILED( hr ))
639 {
640 ERR( "failed to commit stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
641 free( encname );
642 return ERROR_FUNCTION_FAILED;
643 }
644 }
645 else if (hr != STG_E_FILEALREADYEXISTS)
646 {
647 ERR( "failed to create stream %s (hr = %#lx)\n", debugstr_w(encname), hr );
648 free( encname );
649 return ERROR_FUNCTION_FAILED;
650 }
651 free( encname );
652 }
653
654 return ERROR_SUCCESS;
655 }
656