diff options
Diffstat (limited to 'libmsi/streams.c')
-rw-r--r-- | libmsi/streams.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/libmsi/streams.c b/libmsi/streams.c new file mode 100644 index 0000000..71616e9 --- /dev/null +++ b/libmsi/streams.c @@ -0,0 +1,563 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2007 James Hawkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "msipriv.h" +#include "query.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define NUM_STREAMS_COLS 2 + +typedef struct tabSTREAM +{ + UINT str_index; + IStream *stream; +} STREAM; + +typedef struct tagMSISTREAMSVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + STREAM **streams; + UINT max_streams; + UINT num_rows; + UINT row_size; +} MSISTREAMSVIEW; + +static BOOL streams_set_table_size(MSISTREAMSVIEW *sv, UINT size) +{ + if (size >= sv->max_streams) + { + sv->max_streams *= 2; + sv->streams = msi_realloc_zero(sv->streams, sv->max_streams * sizeof(STREAM *)); + if (!sv->streams) + return FALSE; + } + + return TRUE; +} + +static STREAM *create_stream(MSISTREAMSVIEW *sv, LPCWSTR name, BOOL encoded, IStream *stm) +{ + STREAM *stream; + WCHAR decoded[MAX_STREAM_NAME_LEN]; + + stream = msi_alloc(sizeof(STREAM)); + if (!stream) + return NULL; + + if (encoded) + { + decode_streamname(name, decoded); + TRACE("stream -> %s %s\n", debugstr_w(name), debugstr_w(decoded)); + name = decoded; + } + + stream->str_index = msi_addstringW(sv->db->strings, name, -1, 1, StringNonPersistent); + stream->stream = stm; + return stream; +} + +static UINT STREAMS_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, val); + + if (col != 1) + return ERROR_INVALID_PARAMETER; + + if (row >= sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + *val = sv->streams[row]->str_index; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %d, %d, %p)\n", view, row, col, stm); + + if (row >= sv->num_rows) + return ERROR_FUNCTION_FAILED; + + IStream_AddRef(sv->streams[row]->stream); + *stm = sv->streams[row]->stream; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("%p %d %p\n", sv, row, rec); + + return msi_view_get_row( sv->db, view, row, rec ); +} + +static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + STREAM *stream; + IStream *stm; + STATSTG stat; + LPWSTR encname = NULL, name = NULL; + USHORT *data = NULL; + HRESULT hr; + ULONG count; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask); + + if (row > sv->num_rows) + return ERROR_FUNCTION_FAILED; + + r = MSI_RecordGetIStream(rec, 2, &stm); + if (r != ERROR_SUCCESS) + return r; + + hr = IStream_Stat(stm, &stat, STATFLAG_NONAME); + if (FAILED(hr)) + { + WARN("failed to stat stream: %08x\n", hr); + goto done; + } + + if (stat.cbSize.QuadPart >> 32) + { + WARN("stream too large\n"); + goto done; + } + + data = msi_alloc(stat.cbSize.QuadPart); + if (!data) + goto done; + + hr = IStream_Read(stm, data, stat.cbSize.QuadPart, &count); + if (FAILED(hr) || count != stat.cbSize.QuadPart) + { + WARN("failed to read stream: %08x\n", hr); + goto done; + } + + name = strdupW(MSI_RecordGetString(rec, 1)); + if (!name) + { + WARN("failed to retrieve stream name\n"); + goto done; + } + + encname = encode_streamname(FALSE, name); + msi_destroy_stream(sv->db, encname); + + r = write_stream_data(sv->db->storage, name, data, count, FALSE); + if (r != ERROR_SUCCESS) + { + WARN("failed to write stream data: %d\n", r); + goto done; + } + + stream = create_stream(sv, name, FALSE, NULL); + if (!stream) + goto done; + + hr = IStorage_OpenStream(sv->db->storage, encname, 0, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream); + if (FAILED(hr)) + { + WARN("failed to open stream: %08x\n", hr); + goto done; + } + + sv->streams[row] = stream; + +done: + msi_free(name); + msi_free(data); + msi_free(encname); + + IStream_Release(stm); + + return r; +} + +static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT i; + + TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary); + + if (!streams_set_table_size(sv, ++sv->num_rows)) + return ERROR_FUNCTION_FAILED; + + if (row == -1) + row = sv->num_rows - 1; + + /* shift the rows to make room for the new row */ + for (i = sv->num_rows - 1; i > row; i--) + { + sv->streams[i] = sv->streams[i - 1]; + } + + return STREAMS_set_row(view, row, rec, 0); +} + +static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row) +{ + FIXME("(%p %d): stub!\n", view, row); + return ERROR_SUCCESS; +} + +static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record) +{ + TRACE("(%p, %p)\n", view, record); + return ERROR_SUCCESS; +} + +static UINT STREAMS_close(struct tagMSIVIEW *view) +{ + TRACE("(%p)\n", view); + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + + TRACE("(%p, %p, %p)\n", view, rows, cols); + + if (cols) *cols = NUM_STREAMS_COLS; + if (rows) *rows = sv->num_rows; + + return ERROR_SUCCESS; +} + +static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name, + UINT *type, BOOL *temporary, LPCWSTR *table_name ) +{ + TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary, + table_name); + + if (n == 0 || n > NUM_STREAMS_COLS) + return ERROR_INVALID_PARAMETER; + + switch (n) + { + case 1: + if (name) *name = szName; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN; + break; + + case 2: + if (name) *name = szData; + if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE; + break; + } + if (table_name) *table_name = szStreams; + if (temporary) *temporary = FALSE; + return ERROR_SUCCESS; +} + +static UINT streams_find_row(MSISTREAMSVIEW *sv, MSIRECORD *rec, UINT *row) +{ + LPCWSTR str; + UINT r, i, id, data; + + str = MSI_RecordGetString(rec, 1); + r = msi_string2idW(sv->db->strings, str, &id); + if (r != ERROR_SUCCESS) + return r; + + for (i = 0; i < sv->num_rows; i++) + { + STREAMS_fetch_int(&sv->view, i, 1, &data); + + if (data == id) + { + *row = i; + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +static UINT streams_modify_update(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT r, row; + + r = streams_find_row(sv, rec, &row); + if (r != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + return STREAMS_set_row(view, row, rec, 0); +} + +static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT r, row; + + r = streams_find_row(sv, rec, &row); + if (r == ERROR_SUCCESS) + return streams_modify_update(view, rec); + + return STREAMS_insert_row(view, rec, -1, FALSE); +} + +static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) +{ + UINT r; + + TRACE("%p %d %p\n", view, eModifyMode, rec); + + switch (eModifyMode) + { + case MSIMODIFY_ASSIGN: + r = streams_modify_assign(view, rec); + break; + + case MSIMODIFY_INSERT: + r = STREAMS_insert_row(view, rec, -1, FALSE); + break; + + case MSIMODIFY_UPDATE: + r = streams_modify_update(view, rec); + break; + + case MSIMODIFY_VALIDATE_NEW: + case MSIMODIFY_INSERT_TEMPORARY: + case MSIMODIFY_REFRESH: + case MSIMODIFY_REPLACE: + case MSIMODIFY_MERGE: + case MSIMODIFY_DELETE: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_DELETE: + FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + r = ERROR_INVALID_DATA; + } + + return r; +} + +static UINT STREAMS_delete(struct tagMSIVIEW *view) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT i; + + TRACE("(%p)\n", view); + + for (i = 0; i < sv->num_rows; i++) + { + if (sv->streams[i]) + { + if (sv->streams[i]->stream) + IStream_Release(sv->streams[i]->stream); + msi_free(sv->streams[i]); + } + } + + msi_free(sv->streams); + msi_free(sv); + + return ERROR_SUCCESS; +} + +static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col, + UINT val, UINT *row, MSIITERHANDLE *handle) +{ + MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; + UINT index = PtrToUlong(*handle); + + TRACE("(%p, %d, %d, %p, %p)\n", view, col, val, row, handle); + + if (col == 0 || col > NUM_STREAMS_COLS) + return ERROR_INVALID_PARAMETER; + + while (index < sv->num_rows) + { + if (sv->streams[index]->str_index == val) + { + *row = index; + break; + } + + index++; + } + + *handle = UlongToPtr(++index); + + if (index > sv->num_rows) + return ERROR_NO_MORE_ITEMS; + + return ERROR_SUCCESS; +} + +static const MSIVIEWOPS streams_ops = +{ + STREAMS_fetch_int, + STREAMS_fetch_stream, + STREAMS_get_row, + STREAMS_set_row, + STREAMS_insert_row, + STREAMS_delete_row, + STREAMS_execute, + STREAMS_close, + STREAMS_get_dimensions, + STREAMS_get_column_info, + STREAMS_modify, + STREAMS_delete, + STREAMS_find_matching_rows, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +static INT add_streams_to_table(MSISTREAMSVIEW *sv) +{ + IEnumSTATSTG *stgenum = NULL; + STATSTG stat; + STREAM *stream = NULL; + HRESULT hr; + UINT r, count = 0, size; + LPWSTR encname; + + hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum); + if (FAILED(hr)) + return -1; + + sv->max_streams = 1; + sv->streams = msi_alloc_zero(sizeof(STREAM *)); + if (!sv->streams) + return -1; + + while (TRUE) + { + size = 0; + hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size); + if (FAILED(hr) || !size) + break; + + if (stat.type != STGTY_STREAM) + { + CoTaskMemFree(stat.pwcsName); + continue; + } + + /* table streams are not in the _Streams table */ + if (*stat.pwcsName == 0x4840) + { + CoTaskMemFree(stat.pwcsName); + continue; + } + + stream = create_stream(sv, stat.pwcsName, TRUE, NULL); + if (!stream) + { + count = -1; + CoTaskMemFree(stat.pwcsName); + break; + } + + /* these streams appear to be unencoded */ + if (*stat.pwcsName == 0x0005) + { + r = msi_get_raw_stream(sv->db, stat.pwcsName, &stream->stream); + } + else + { + encname = encode_streamname(FALSE, stat.pwcsName); + r = msi_get_raw_stream(sv->db, encname, &stream->stream); + msi_free(encname); + } + CoTaskMemFree(stat.pwcsName); + + if (r != ERROR_SUCCESS) + { + WARN("unable to get stream %u\n", r); + count = -1; + break; + } + + if (!streams_set_table_size(sv, ++count)) + { + count = -1; + break; + } + + sv->streams[count - 1] = stream; + } + + IEnumSTATSTG_Release(stgenum); + return count; +} + +UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view) +{ + MSISTREAMSVIEW *sv; + INT rows; + + TRACE("(%p, %p)\n", db, view); + + sv = msi_alloc_zero( sizeof(MSISTREAMSVIEW) ); + if (!sv) + return ERROR_FUNCTION_FAILED; + + sv->view.ops = &streams_ops; + sv->db = db; + rows = add_streams_to_table(sv); + if (rows < 0) + { + msi_free( sv ); + return ERROR_FUNCTION_FAILED; + } + sv->num_rows = rows; + + *view = (MSIVIEW *)sv; + + return ERROR_SUCCESS; +} |