webservices: Accept zero write option in WsWriteType.
[wine.git] / dlls / msi / streams.c
blob7f9582cd4bc5ec098c687aea0b35573778e5ad0d
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2007 James Hawkins
5 * Copyright 2015 Hans Leidekker for CodeWeavers
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.
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.
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
22 #include <stdarg.h>
24 #define COBJMACROS
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"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
40 #define NUM_STREAMS_COLS 2
42 typedef struct tagMSISTREAMSVIEW
44 MSIVIEW view;
45 MSIDATABASE *db;
46 UINT num_cols;
47 } MSISTREAMSVIEW;
49 static BOOL streams_resize_table( MSIDATABASE *db, UINT size )
51 if (!db->num_streams_allocated)
53 if (!(db->streams = msi_alloc_zero( size * sizeof(MSISTREAM) ))) return FALSE;
54 db->num_streams_allocated = size;
55 return TRUE;
57 while (size >= db->num_streams_allocated)
59 MSISTREAM *tmp;
60 UINT new_size = db->num_streams_allocated * 2;
61 if (!(tmp = msi_realloc_zero( db->streams, new_size * sizeof(MSISTREAM) ))) return FALSE;
62 db->streams = tmp;
63 db->num_streams_allocated = new_size;
65 return TRUE;
68 static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
70 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
72 TRACE("(%p, %d, %d, %p)\n", view, row, col, val);
74 if (col != 1)
75 return ERROR_INVALID_PARAMETER;
77 if (row >= sv->db->num_streams)
78 return ERROR_NO_MORE_ITEMS;
80 *val = sv->db->streams[row].str_index;
82 return ERROR_SUCCESS;
85 static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
87 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
88 LARGE_INTEGER pos;
89 HRESULT hr;
91 TRACE("(%p, %d, %d, %p)\n", view, row, col, stm);
93 if (row >= sv->db->num_streams)
94 return ERROR_FUNCTION_FAILED;
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;
101 *stm = sv->db->streams[row].stream;
102 IStream_AddRef( *stm );
104 return ERROR_SUCCESS;
107 static UINT STREAMS_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
109 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
111 TRACE("%p %d %p\n", sv, row, rec);
113 return msi_view_get_row( sv->db, view, row, rec );
116 static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
118 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
120 TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
122 if (row > sv->db->num_streams || mask >= (1 << sv->num_cols))
123 return ERROR_INVALID_PARAMETER;
125 if (mask & 1)
127 const WCHAR *name = MSI_RecordGetString( rec, 1 );
129 if (!name) return ERROR_INVALID_PARAMETER;
130 sv->db->streams[row].str_index = msi_add_string( sv->db->strings, name, -1, StringNonPersistent );
132 if (mask & 2)
134 IStream *old, *new;
135 HRESULT hr;
136 UINT r;
138 r = MSI_RecordGetIStream( rec, 2, &new );
139 if (r != ERROR_SUCCESS)
140 return r;
142 old = sv->db->streams[row].stream;
143 hr = IStream_QueryInterface( new, &IID_IStream, (void **)&sv->db->streams[row].stream );
144 if (FAILED( hr ))
146 IStream_Release( new );
147 return ERROR_FUNCTION_FAILED;
149 if (old) IStream_Release( old );
152 return ERROR_SUCCESS;
155 static UINT streams_find_row( MSISTREAMSVIEW *sv, MSIRECORD *rec, UINT *row )
157 const WCHAR *str;
158 UINT r, i, id, val;
160 str = MSI_RecordGetString( rec, 1 );
161 r = msi_string2id( sv->db->strings, str, -1, &id );
162 if (r != ERROR_SUCCESS)
163 return r;
165 for (i = 0; i < sv->db->num_streams; i++)
167 STREAMS_fetch_int( &sv->view, i, 1, &val );
169 if (val == id)
171 if (row) *row = i;
172 return ERROR_SUCCESS;
176 return ERROR_FUNCTION_FAILED;
179 static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
181 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
182 UINT i, r, num_rows = sv->db->num_streams + 1;
184 TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);
186 r = streams_find_row( sv, rec, NULL );
187 if (r == ERROR_SUCCESS)
188 return ERROR_FUNCTION_FAILED;
190 if (!streams_resize_table( sv->db, num_rows ))
191 return ERROR_FUNCTION_FAILED;
193 if (row == -1)
194 row = num_rows - 1;
196 /* shift the rows to make room for the new row */
197 for (i = num_rows - 1; i > row; i--)
199 sv->db->streams[i] = sv->db->streams[i - 1];
202 r = STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
203 if (r == ERROR_SUCCESS)
204 sv->db->num_streams = num_rows;
206 return r;
209 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
211 FIXME("(%p %d): stub!\n", view, row);
212 return ERROR_SUCCESS;
215 static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record)
217 TRACE("(%p, %p)\n", view, record);
218 return ERROR_SUCCESS;
221 static UINT STREAMS_close(struct tagMSIVIEW *view)
223 TRACE("(%p)\n", view);
224 return ERROR_SUCCESS;
227 static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
229 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
231 TRACE("(%p, %p, %p)\n", view, rows, cols);
233 if (cols) *cols = sv->num_cols;
234 if (rows) *rows = sv->db->num_streams;
236 return ERROR_SUCCESS;
239 static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
240 UINT *type, BOOL *temporary, LPCWSTR *table_name )
242 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
244 TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, table_name);
246 if (!n || n > sv->num_cols)
247 return ERROR_INVALID_PARAMETER;
249 switch (n)
251 case 1:
252 if (name) *name = szName;
253 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
254 break;
256 case 2:
257 if (name) *name = szData;
258 if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
259 break;
261 if (table_name) *table_name = szStreams;
262 if (temporary) *temporary = FALSE;
263 return ERROR_SUCCESS;
266 static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec)
268 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
269 UINT r, row;
271 r = streams_find_row(sv, rec, &row);
272 if (r != ERROR_SUCCESS)
273 return ERROR_FUNCTION_FAILED;
275 return STREAMS_set_row( view, row, rec, (1 << sv->num_cols) - 1 );
278 static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
280 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
281 UINT r;
283 r = streams_find_row( sv, rec, NULL );
284 if (r == ERROR_SUCCESS)
285 return streams_modify_update(view, rec);
287 return STREAMS_insert_row(view, rec, -1, FALSE);
290 static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
292 UINT r;
294 TRACE("%p %d %p\n", view, eModifyMode, rec);
296 switch (eModifyMode)
298 case MSIMODIFY_ASSIGN:
299 r = streams_modify_assign(view, rec);
300 break;
302 case MSIMODIFY_INSERT:
303 r = STREAMS_insert_row(view, rec, -1, FALSE);
304 break;
306 case MSIMODIFY_UPDATE:
307 r = streams_modify_update(view, rec);
308 break;
310 case MSIMODIFY_VALIDATE_NEW:
311 case MSIMODIFY_INSERT_TEMPORARY:
312 case MSIMODIFY_REFRESH:
313 case MSIMODIFY_REPLACE:
314 case MSIMODIFY_MERGE:
315 case MSIMODIFY_DELETE:
316 case MSIMODIFY_VALIDATE:
317 case MSIMODIFY_VALIDATE_FIELD:
318 case MSIMODIFY_VALIDATE_DELETE:
319 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
320 r = ERROR_CALL_NOT_IMPLEMENTED;
321 break;
323 default:
324 r = ERROR_INVALID_DATA;
327 return r;
330 static UINT STREAMS_delete(struct tagMSIVIEW *view)
332 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
334 TRACE("(%p)\n", view);
336 msi_free(sv);
337 return ERROR_SUCCESS;
340 static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col,
341 UINT val, UINT *row, MSIITERHANDLE *handle)
343 MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
344 UINT index = PtrToUlong(*handle);
346 TRACE("(%p, %d, %d, %p, %p)\n", view, col, val, row, handle);
348 if (!col || col > sv->num_cols)
349 return ERROR_INVALID_PARAMETER;
351 while (index < sv->db->num_streams)
353 if (sv->db->streams[index].str_index == val)
355 *row = index;
356 break;
358 index++;
361 *handle = UlongToPtr(++index);
363 if (index > sv->db->num_streams)
364 return ERROR_NO_MORE_ITEMS;
366 return ERROR_SUCCESS;
369 static const MSIVIEWOPS streams_ops =
371 STREAMS_fetch_int,
372 STREAMS_fetch_stream,
373 STREAMS_get_row,
374 STREAMS_set_row,
375 STREAMS_insert_row,
376 STREAMS_delete_row,
377 STREAMS_execute,
378 STREAMS_close,
379 STREAMS_get_dimensions,
380 STREAMS_get_column_info,
381 STREAMS_modify,
382 STREAMS_delete,
383 STREAMS_find_matching_rows,
384 NULL,
385 NULL,
386 NULL,
387 NULL,
388 NULL,
389 NULL,
392 static HRESULT open_stream( MSIDATABASE *db, const WCHAR *name, IStream **stream )
394 HRESULT hr;
396 hr = IStorage_OpenStream( db->storage, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
397 if (FAILED( hr ))
399 MSITRANSFORM *transform;
401 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
403 hr = IStorage_OpenStream( transform->stg, name, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, stream );
404 if (SUCCEEDED( hr ))
405 break;
408 return hr;
411 static MSISTREAM *find_stream( MSIDATABASE *db, const WCHAR *name )
413 UINT r, id, i;
415 r = msi_string2id( db->strings, name, -1, &id );
416 if (r != ERROR_SUCCESS)
417 return NULL;
419 for (i = 0; i < db->num_streams; i++)
421 if (db->streams[i].str_index == id) return &db->streams[i];
423 return NULL;
426 static UINT append_stream( MSIDATABASE *db, const WCHAR *name, IStream *stream )
428 UINT i = db->num_streams;
430 if (!streams_resize_table( db, db->num_streams + 1 ))
431 return ERROR_OUTOFMEMORY;
433 db->streams[i].str_index = msi_add_string( db->strings, name, -1, StringNonPersistent );
434 db->streams[i].stream = stream;
435 db->num_streams++;
437 TRACE("added %s\n", debugstr_w( name ));
438 return ERROR_SUCCESS;
441 static UINT load_streams( MSIDATABASE *db )
443 WCHAR decoded[MAX_STREAM_NAME_LEN + 1];
444 IEnumSTATSTG *stgenum;
445 STATSTG stat;
446 HRESULT hr;
447 UINT count, r = ERROR_SUCCESS;
448 IStream *stream;
450 hr = IStorage_EnumElements( db->storage, 0, NULL, 0, &stgenum );
451 if (FAILED( hr ))
452 return ERROR_FUNCTION_FAILED;
454 for (;;)
456 count = 0;
457 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
458 if (FAILED( hr ) || !count)
459 break;
461 /* table streams are not in the _Streams table */
462 if (stat.type != STGTY_STREAM || *stat.pwcsName == 0x4840)
464 CoTaskMemFree( stat.pwcsName );
465 continue;
467 decode_streamname( stat.pwcsName, decoded );
468 if (find_stream( db, decoded ))
470 CoTaskMemFree( stat.pwcsName );
471 continue;
473 TRACE("found new stream %s\n", debugstr_w( decoded ));
475 hr = open_stream( db, stat.pwcsName, &stream );
476 CoTaskMemFree( stat.pwcsName );
477 if (FAILED( hr ))
479 ERR("unable to open stream %08x\n", hr);
480 r = ERROR_FUNCTION_FAILED;
481 break;
484 r = append_stream( db, decoded, stream );
485 if (r != ERROR_SUCCESS)
486 break;
489 TRACE("loaded %u streams\n", db->num_streams);
490 IEnumSTATSTG_Release( stgenum );
491 return r;
494 UINT msi_get_stream( MSIDATABASE *db, const WCHAR *name, IStream **ret )
496 MSISTREAM *stream;
497 WCHAR *encname;
498 HRESULT hr;
499 UINT r;
501 if ((stream = find_stream( db, name )))
503 LARGE_INTEGER pos;
505 pos.QuadPart = 0;
506 hr = IStream_Seek( stream->stream, pos, STREAM_SEEK_SET, NULL );
507 if (FAILED( hr ))
508 return ERROR_FUNCTION_FAILED;
510 *ret = stream->stream;
511 IStream_AddRef( *ret );
512 return ERROR_SUCCESS;
515 if (!(encname = encode_streamname( FALSE, name )))
516 return ERROR_OUTOFMEMORY;
518 hr = open_stream( db, encname, ret );
519 msi_free( encname );
520 if (FAILED( hr ))
521 return ERROR_FUNCTION_FAILED;
523 r = append_stream( db, name, *ret );
524 if (r != ERROR_SUCCESS)
526 IStream_Release( *ret );
527 return r;
530 IStream_AddRef( *ret );
531 return ERROR_SUCCESS;
534 UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
536 MSISTREAMSVIEW *sv;
537 UINT r;
539 TRACE("(%p, %p)\n", db, view);
541 r = load_streams( db );
542 if (r != ERROR_SUCCESS)
543 return r;
545 if (!(sv = msi_alloc_zero( sizeof(MSISTREAMSVIEW) )))
546 return ERROR_OUTOFMEMORY;
548 sv->view.ops = &streams_ops;
549 sv->num_cols = NUM_STREAMS_COLS;
550 sv->db = db;
552 *view = (MSIVIEW *)sv;
554 return ERROR_SUCCESS;
557 static HRESULT write_stream( IStream *dst, IStream *src )
559 HRESULT hr;
560 char buf[4096];
561 STATSTG stat;
562 LARGE_INTEGER pos;
563 UINT count, size;
565 hr = IStream_Stat( src, &stat, STATFLAG_NONAME );
566 if (FAILED( hr )) return hr;
568 hr = IStream_SetSize( dst, stat.cbSize );
569 if (FAILED( hr )) return hr;
571 pos.QuadPart = 0;
572 hr = IStream_Seek( dst, pos, STREAM_SEEK_SET, NULL );
573 if (FAILED( hr )) return hr;
575 for (;;)
577 size = min( sizeof(buf), stat.cbSize.QuadPart );
578 hr = IStream_Read( src, buf, size, &count );
579 if (FAILED( hr ) || count != size)
581 WARN("failed to read stream: %08x\n", hr);
582 return E_INVALIDARG;
584 stat.cbSize.QuadPart -= count;
585 if (count)
587 size = count;
588 hr = IStream_Write( dst, buf, size, &count );
589 if (FAILED( hr ) || count != size)
591 WARN("failed to write stream: %08x\n", hr);
592 return E_INVALIDARG;
595 if (!stat.cbSize.QuadPart) break;
598 return S_OK;
601 UINT msi_commit_streams( MSIDATABASE *db )
603 UINT i;
604 const WCHAR *name;
605 WCHAR *encname;
606 IStream *stream;
607 HRESULT hr;
609 TRACE("got %u streams\n", db->num_streams);
611 for (i = 0; i < db->num_streams; i++)
613 name = msi_string_lookup( db->strings, db->streams[i].str_index, NULL );
614 if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY;
616 hr = IStorage_CreateStream( db->storage, encname, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stream );
617 if (SUCCEEDED( hr ))
619 hr = write_stream( stream, db->streams[i].stream );
620 if (FAILED( hr ))
622 ERR("failed to write stream %s (hr = %08x)\n", debugstr_w(encname), hr);
623 msi_free( encname );
624 IStream_Release( stream );
625 return ERROR_FUNCTION_FAILED;
627 hr = IStream_Commit( stream, 0 );
628 IStream_Release( stream );
629 if (FAILED( hr ))
631 ERR("failed to commit stream %s (hr = %08x)\n", debugstr_w(encname), hr);
632 msi_free( encname );
633 return ERROR_FUNCTION_FAILED;
636 else if (hr != STG_E_FILEALREADYEXISTS)
638 ERR("failed to create stream %s (hr = %08x)\n", debugstr_w(encname), hr);
639 msi_free( encname );
640 return ERROR_FUNCTION_FAILED;
642 msi_free( encname );
645 return ERROR_SUCCESS;