streams.c 13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * 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"
32
#include "query.h"
33 34

#include "wine/debug.h"
35
#include "wine/unicode.h"
36 37 38 39 40 41 42

WINE_DEFAULT_DEBUG_CHANNEL(msidb);

#define NUM_STREAMS_COLS    2

typedef struct tabSTREAM
{
43
    UINT str_index;
44 45 46 47 48 49 50 51 52 53 54 55 56
    IStream *stream;
} STREAM;

typedef struct tagMSISTREAMSVIEW
{
    MSIVIEW view;
    MSIDATABASE *db;
    STREAM **streams;
    UINT max_streams;
    UINT num_rows;
    UINT row_size;
} MSISTREAMSVIEW;

57
static BOOL streams_set_table_size(MSISTREAMSVIEW *sv, UINT size)
58
{
59
    if (size >= sv->max_streams)
60 61
    {
        sv->max_streams *= 2;
62
        sv->streams = msi_realloc_zero(sv->streams, sv->max_streams * sizeof(STREAM *));
63 64 65 66 67 68 69
        if (!sv->streams)
            return FALSE;
    }

    return TRUE;
}

70
static STREAM *create_stream(MSISTREAMSVIEW *sv, LPCWSTR name, BOOL encoded, IStream *stm)
71 72 73 74 75 76 77 78 79 80 81 82
{
    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));
83
        name = decoded;
84 85
    }

86
    stream->str_index = msi_addstringW(sv->db->strings, name, -1, 1, StringNonPersistent);
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    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;
}

123 124 125 126
static UINT STREAMS_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
{
    MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;

127
    TRACE("%p %d %p\n", sv, row, rec);
128

129
    return msi_view_get_row( sv->db, view, row, rec );
130 131
}

132 133 134 135 136 137
static UINT STREAMS_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
{
    MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
    STREAM *stream;
    IStream *stm;
    STATSTG stat;
138
    LPWSTR encname = NULL, name = NULL;
139 140 141 142 143
    USHORT *data = NULL;
    HRESULT hr;
    ULONG count;
    UINT r = ERROR_FUNCTION_FAILED;

144
    TRACE("(%p, %d, %p, %08x)\n", view, row, rec, mask);
145 146 147

    if (row > sv->num_rows)
        return ERROR_FUNCTION_FAILED;
148 149 150 151 152 153 154 155 156 157 158 159 160

    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)
161 162
    {
        WARN("stream too large\n");
163
        goto done;
164
    }
165 166 167 168 169 170 171 172 173 174 175 176 177 178

    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)
179 180
    {
        WARN("failed to retrieve stream name\n");
181
        goto done;
182
    }
183

184
    encname = encode_streamname(FALSE, name);
185
    msi_destroy_stream(sv->db, encname);
186

187 188 189 190 191 192 193 194 195 196 197
    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;

198
    hr = IStorage_OpenStream(sv->db->storage, encname, 0,
199 200 201 202 203 204
                             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream->stream);
    if (FAILED(hr))
    {
        WARN("failed to open stream: %08x\n", hr);
        goto done;
    }
205

206
    sv->streams[row] = stream;
207 208 209 210

done:
    msi_free(name);
    msi_free(data);
211
    msi_free(encname);
212 213 214 215 216 217

    IStream_Release(stm);

    return r;
}

218
static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
219 220
{
    MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
221
    UINT i;
222

223 224
    TRACE("(%p, %p, %d, %d)\n", view, rec, row, temporary);

225 226 227
    if (!streams_set_table_size(sv, ++sv->num_rows))
        return ERROR_FUNCTION_FAILED;

228 229 230
    if (row == -1)
        row = sv->num_rows - 1;

231 232 233 234 235
    /* 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];
    }
236 237

    return STREAMS_set_row(view, row, rec, 0);
238 239
}

240 241 242 243 244 245
static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
{
    FIXME("(%p %d): stub!\n", view, row);
    return ERROR_SUCCESS;
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
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;
}

270 271
static UINT STREAMS_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
                                     UINT *type, BOOL *temporary, LPCWSTR *table_name )
272
{
273 274
    TRACE("(%p, %d, %p, %p, %p, %p)\n", view, n, name, type, temporary,
          table_name);
275 276 277 278 279 280 281

    if (n == 0 || n > NUM_STREAMS_COLS)
        return ERROR_INVALID_PARAMETER;

    switch (n)
    {
    case 1:
282
        if (name) *name = szName;
283
        if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MAX_STREAM_NAME_LEN;
284 285 286
        break;

    case 2:
287
        if (name) *name = szData;
288 289 290
        if (type) *type = MSITYPE_STRING | MSITYPE_VALID | MSITYPE_NULLABLE;
        break;
    }
291 292
    if (table_name) *table_name = szStreams;
    if (temporary) *temporary = FALSE;
293 294 295
    return ERROR_SUCCESS;
}

296 297 298
static UINT streams_find_row(MSISTREAMSVIEW *sv, MSIRECORD *rec, UINT *row)
{
    LPCWSTR str;
299
    UINT r, i, id, data;
300 301

    str = MSI_RecordGetString(rec, 1);
302 303 304
    r = msi_string2idW(sv->db->strings, str, &id);
    if (r != ERROR_SUCCESS)
        return r;
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

    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);

341
    return STREAMS_insert_row(view, rec, -1, FALSE);
342 343
}

344
static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
345
{
346 347 348 349 350 351
    UINT r;

    TRACE("%p %d %p\n", view, eModifyMode, rec);

    switch (eModifyMode)
    {
352 353 354 355
    case MSIMODIFY_ASSIGN:
        r = streams_modify_assign(view, rec);
        break;

356
    case MSIMODIFY_INSERT:
357
        r = STREAMS_insert_row(view, rec, -1, FALSE);
358 359
        break;

360 361 362 363
    case MSIMODIFY_UPDATE:
        r = streams_modify_update(view, rec);
        break;

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    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;
382 383 384 385 386
}

static UINT STREAMS_delete(struct tagMSIVIEW *view)
{
    MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
387
    UINT i;
388 389 390 391 392

    TRACE("(%p)\n", view);

    for (i = 0; i < sv->num_rows; i++)
    {
393 394 395 396 397 398
        if (sv->streams[i])
        {
            if (sv->streams[i]->stream)
                IStream_Release(sv->streams[i]->stream);
            msi_free(sv->streams[i]);
        }
399 400 401
    }

    msi_free(sv->streams);
402
    msi_free(sv);
403 404 405 406 407 408 409 410

    return ERROR_SUCCESS;
}

static UINT STREAMS_find_matching_rows(struct tagMSIVIEW *view, UINT col,
                                       UINT val, UINT *row, MSIITERHANDLE *handle)
{
    MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view;
411
    UINT index = PtrToUlong(*handle);
412

413
    TRACE("(%p, %d, %d, %p, %p)\n", view, col, val, row, handle);
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428

    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++;
    }

429
    *handle = UlongToPtr(++index);
430 431

    if (index > sv->num_rows)
432 433 434 435 436 437 438 439 440
        return ERROR_NO_MORE_ITEMS;

    return ERROR_SUCCESS;
}

static const MSIVIEWOPS streams_ops =
{
    STREAMS_fetch_int,
    STREAMS_fetch_stream,
441
    STREAMS_get_row,
442 443
    STREAMS_set_row,
    STREAMS_insert_row,
444
    STREAMS_delete_row,
445 446 447 448 449 450
    STREAMS_execute,
    STREAMS_close,
    STREAMS_get_dimensions,
    STREAMS_get_column_info,
    STREAMS_modify,
    STREAMS_delete,
451 452 453
    STREAMS_find_matching_rows,
    NULL,
    NULL,
454
    NULL,
455
    NULL,
456
    NULL,
457
    NULL,
458 459
};

460
static INT add_streams_to_table(MSISTREAMSVIEW *sv)
461 462 463 464 465
{
    IEnumSTATSTG *stgenum = NULL;
    STATSTG stat;
    STREAM *stream = NULL;
    HRESULT hr;
466 467
    UINT r, count = 0, size;
    LPWSTR encname;
468 469 470 471 472 473

    hr = IStorage_EnumElements(sv->db->storage, 0, NULL, 0, &stgenum);
    if (FAILED(hr))
        return -1;

    sv->max_streams = 1;
474
    sv->streams = msi_alloc_zero(sizeof(STREAM *));
475 476 477 478 479 480 481 482 483 484
    if (!sv->streams)
        return -1;

    while (TRUE)
    {
        size = 0;
        hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &size);
        if (FAILED(hr) || !size)
            break;

485
        if (stat.type != STGTY_STREAM)
Hans Leidekker's avatar
Hans Leidekker committed
486 487
        {
            CoTaskMemFree(stat.pwcsName);
488
            continue;
Hans Leidekker's avatar
Hans Leidekker committed
489
        }
490

491 492
        /* table streams are not in the _Streams table */
        if (*stat.pwcsName == 0x4840)
493 494
        {
            CoTaskMemFree(stat.pwcsName);
495
            continue;
496
        }
497 498 499 500 501

        stream = create_stream(sv, stat.pwcsName, TRUE, NULL);
        if (!stream)
        {
            count = -1;
502
            CoTaskMemFree(stat.pwcsName);
503 504 505
            break;
        }

506 507
        /* these streams appear to be unencoded */
        if (*stat.pwcsName == 0x0005)
508
        {
509
            r = msi_get_raw_stream(sv->db, stat.pwcsName, &stream->stream);
510 511 512 513
        }
        else
        {
            encname = encode_streamname(FALSE, stat.pwcsName);
514
            r = msi_get_raw_stream(sv->db, encname, &stream->stream);
515 516
            msi_free(encname);
        }
517
        CoTaskMemFree(stat.pwcsName);
518

519
        if (r != ERROR_SUCCESS)
520
        {
521
            WARN("unable to get stream %u\n", r);
522 523 524 525
            count = -1;
            break;
        }

526
        if (!streams_set_table_size(sv, ++count))
527 528 529 530
        {
            count = -1;
            break;
        }
531 532

        sv->streams[count - 1] = stream;
533 534 535 536 537 538 539 540 541
    }

    IEnumSTATSTG_Release(stgenum);
    return count;
}

UINT STREAMS_CreateView(MSIDATABASE *db, MSIVIEW **view)
{
    MSISTREAMSVIEW *sv;
542
    INT rows;
543 544 545

    TRACE("(%p, %p)\n", db, view);

546
    sv = msi_alloc_zero( sizeof(MSISTREAMSVIEW) );
547 548 549 550 551
    if (!sv)
        return ERROR_FUNCTION_FAILED;

    sv->view.ops = &streams_ops;
    sv->db = db;
552 553
    rows = add_streams_to_table(sv);
    if (rows < 0)
554 555
    {
        msi_free( sv );
556
        return ERROR_FUNCTION_FAILED;
557
    }
558
    sv->num_rows = rows;
559 560 561 562 563

    *view = (MSIVIEW *)sv;

    return ERROR_SUCCESS;
}