registry.c 29.6 KB
Newer Older
1 2 3
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
4
 * Copyright 2005 Mike McCormack for CodeWeavers
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright 2005 Aric Stewart for CodeWeavers
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
 */

#include <stdarg.h>

#define COBJMACROS
#define NONAMELESSUNION

#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "shlwapi.h"
#include "wine/debug.h"
#include "msi.h"
#include "msipriv.h"
#include "wincrypt.h"
#include "wine/unicode.h"
#include "winver.h"
#include "winuser.h"

WINE_DEFAULT_DEBUG_CHANNEL(msi);


/* 
 * This module will be all the helper functions for registry access by the
 * installer bits. 
 */
static const WCHAR szUserFeatures_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'I','n','s','t','a','l','l','e','r','\\',
'F','e','a','t','u','r','e','s','\\',
'%','s',0};

static const WCHAR szInstaller_Features[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'F','e','a','t','u','r','e','s',0 };

static const WCHAR szInstaller_Features_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'F','e','a','t','u','r','e','s','\\',
'%','s',0};

static const WCHAR szInstaller_Components[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'C','o','m','p','o','n','e','n','t','s',0 };

static const WCHAR szInstaller_Components_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'C','o','m','p','o','n','e','n','t','s','\\',
'%','s',0};

88 89 90 91 92 93 94
static const WCHAR szUser_Components_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'I','n','s','t','a','l','l','e','r','\\',
'C','o','m','p','o','n','e','n','t','s','\\',
'%','s',0};

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 123 124 125 126
static const WCHAR szUninstall_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'U','n','i','n','s','t','a','l','l','\\',
'%','s',0 };

static const WCHAR szUserProduct_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'I','n','s','t','a','l','l','e','r','\\',
'P','r','o','d','u','c','t','s','\\',
'%','s',0};

static const WCHAR szInstaller_Products[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'P','r','o','d','u','c','t','s',0};

static const WCHAR szInstaller_Products_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'P','r','o','d','u','c','t','s','\\',
'%','s',0};

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
static const WCHAR szInstaller_UpgradeCodes[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'U','p','g','r','a','d','e','C','o','d','e','s',0};

static const WCHAR szInstaller_UpgradeCodes_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'U','p','g','r','a','d','e','C','o','d','e','s','\\',
'%','s',0};

144 145 146 147 148 149 150 151 152 153 154 155 156 157
static const WCHAR szInstaller_UserUpgradeCodes[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'I','n','s','t','a','l','l','e','r','\\',
'U','p','g','r','a','d','e','C','o','d','e','s',0};

static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'I','n','s','t','a','l','l','e','r','\\',
'U','p','g','r','a','d','e','C','o','d','e','s','\\',
'%','s',0};


158 159
#define SQUISH_GUID_SIZE 33

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
{
    DWORD i,n=0;

    out[n++]='{';
    for(i=0; i<8; i++)
        out[n++] = in[7-i];
    out[n++]='-';
    for(i=0; i<4; i++)
        out[n++] = in[11-i];
    out[n++]='-';
    for(i=0; i<4; i++)
        out[n++] = in[15-i];
    out[n++]='-';
    for(i=0; i<2; i++)
    {
        out[n++] = in[17+i*2];
        out[n++] = in[16+i*2];
    }
    out[n++]='-';
    for( ; i<8; i++)
    {
        out[n++] = in[17+i*2];
        out[n++] = in[16+i*2];
    }
    out[n++]='}';
    out[n]=0;
    return TRUE;
}

BOOL squash_guid(LPCWSTR in, LPWSTR out)
{
192 193
    DWORD i,n=1;
    GUID guid;
194

195
    if (FAILED(CLSIDFromString((LPOLESTR)in, &guid)))
196
        return FALSE;
197

198 199
    for(i=0; i<8; i++)
        out[7-i] = in[n++];
200
    n++;
201 202
    for(i=0; i<4; i++)
        out[11-i] = in[n++];
203
    n++;
204 205
    for(i=0; i<4; i++)
        out[15-i] = in[n++];
206
    n++;
207 208 209 210 211
    for(i=0; i<2; i++)
    {
        out[17+i*2] = in[n++];
        out[16+i*2] = in[n++];
    }
212
    n++;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    for( ; i<8; i++)
    {
        out[17+i*2] = in[n++];
        out[16+i*2] = in[n++];
    }
    out[32]=0;
    return TRUE;
}


/* tables for encoding and decoding base85 */
static const unsigned char table_dec85[0x80] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
};

static const char table_enc85[] =
"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
"yz{}~";

/*
 *  Converts a base85 encoded guid into a GUID pointer
 *  Base85 encoded GUIDs should be 20 characters long.
 *
 *  returns TRUE if successful, FALSE if not
 */
BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
{
    DWORD i, val = 0, base = 1, *p;

250 251 252
    if (!str)
        return FALSE;

253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    p = (DWORD*) guid;
    for( i=0; i<20; i++ )
    {
        if( (i%5) == 0 )
        {
            val = 0;
            base = 1;
        }
        val += table_dec85[str[i]] * base;
        if( str[i] >= 0x80 )
            return FALSE;
        if( table_dec85[str[i]] == 0xff )
            return FALSE;
        if( (i%5) == 4 )
            p[i/5] = val;
        base *= 85;
    }
    return TRUE;
}

/*
 *  Encodes a base85 guid given a GUID pointer
 *  Caller should provide a 21 character buffer for the encoded string.
 *
 *  returns TRUE if successful, FALSE if not
 */
BOOL encode_base85_guid( GUID *guid, LPWSTR str )
{
    unsigned int x, *p, i;

    p = (unsigned int*) guid;
    for( i=0; i<4; i++ )
    {
        x = p[i];
        *str++ = table_enc85[x%85];
        x = x/85;
        *str++ = table_enc85[x%85];
        x = x/85;
        *str++ = table_enc85[x%85];
        x = x/85;
        *str++ = table_enc85[x%85];
        x = x/85;
        *str++ = table_enc85[x%85];
    }
    *str = 0;

    return TRUE;
}

302 303 304 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
DWORD msi_version_str_to_dword(LPCWSTR p)
{
    DWORD major, minor = 0, build = 0, version = 0;

    if (!p)
        return version;

    major = atoiW(p);

    p = strchrW(p, '.');
    if (p)
    {
        minor = atoiW(p+1);
        p = strchrW(p+1, '.');
        if (p)
            build = atoiW(p+1);
    }

    return MAKELONG(build, MAKEWORD(minor, major));
}

LPWSTR msi_version_dword_to_str(DWORD version)
{
    const WCHAR fmt[] = { '%','u','.','%','u','.','%','u',0 };
    LPWSTR str = msi_alloc(20);
    sprintfW(str, fmt,
             (version&0xff000000)>>24,
             (version&0x00ff0000)>>16,
              version&0x0000ffff);
    return str;
}

334 335 336
LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
{
    DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0;
337
    return RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)value, len );
338 339 340 341 342 343 344
}

LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
{
    LPCWSTR p = value;
    while (*p) p += lstrlenW(p) + 1;
    return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
345
                           (const BYTE *)value, (p + 1 - value) * sizeof(WCHAR) );
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
}

LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
{
    return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
}

LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
{
    HKEY hsubkey = 0;
    LONG r;

    r = RegCreateKeyW( hkey, path, &hsubkey );
    if (r != ERROR_SUCCESS)
        return r;
    r = msi_reg_set_val_str( hsubkey, name, val );
    RegCloseKey( hsubkey );
    return r;
}
365

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
LPWSTR msi_reg_get_val_str( HKEY hkey, LPCWSTR name )
{
    DWORD len = 0;
    LPWSTR val;
    LONG r;

    r = RegQueryValueExW(hkey, name, NULL, NULL, NULL, &len);
    if (r != ERROR_SUCCESS)
        return NULL;

    len += sizeof (WCHAR);
    val = msi_alloc( len );
    if (!val)
        return NULL;
    val[0] = 0;
    RegQueryValueExW(hkey, name, NULL, NULL, (LPBYTE) val, &len);
    return val;
}

BOOL msi_reg_get_val_dword( HKEY hkey, LPCWSTR name, DWORD *val)
{
    DWORD type, len = sizeof (DWORD);
    LONG r = RegQueryValueExW(hkey, name, NULL, &type, (LPBYTE) val, &len);
    return r == ERROR_SUCCESS && type == REG_DWORD;
}

392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR keypath[0x200];
    TRACE("%s\n",debugstr_w(szProduct));

    sprintfW(keypath,szUninstall_fmt,szProduct);

    if (create)
        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
    else
        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);

    return rc;
}

UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szProduct));
    squash_guid(szProduct,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szUserProduct_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);

    return rc;
}

UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szProduct));
    squash_guid(szProduct,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szUserFeatures_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);

    return rc;
}

UINT MSIREG_OpenFeatures(HKEY* key)
{
    return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
}

UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szProduct));
    squash_guid(szProduct,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szInstaller_Features_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);

    return rc;
}

UINT MSIREG_OpenComponents(HKEY* key)
{
    return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
}

UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_cc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szComponent));
    squash_guid(szComponent,squished_cc);
    TRACE("squished (%s)\n", debugstr_w(squished_cc));

    sprintfW(keypath,szInstaller_Components_fmt,squished_cc);

    if (create)
        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);

    return rc;
}

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_cc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szComponent));
    squash_guid(szComponent,squished_cc);
    TRACE("squished (%s)\n", debugstr_w(squished_cc));

    sprintfW(keypath,szUser_Components_fmt,squished_cc);

    if (create)
        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);

    return rc;
}

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szProduct));
    squash_guid(szProduct,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szInstaller_Products_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);

    return rc;
}
537

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szUpgradeCode));
    squash_guid(szUpgradeCode,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);

    return rc;
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
{
    UINT rc;
    WCHAR squished_pc[GUID_SIZE];
    WCHAR keypath[0x200];

    TRACE("%s\n",debugstr_w(szUpgradeCode));
    squash_guid(szUpgradeCode,squished_pc);
    TRACE("squished (%s)\n", debugstr_w(squished_pc));

    sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);

    if (create)
        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
    else
        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);

    return rc;
}


579 580 581 582 583 584 585 586 587
/*************************************************************************
 *  MsiDecomposeDescriptorW   [MSI.@]
 *
 * Decomposes an MSI descriptor into product, feature and component parts.
 * An MSI descriptor is a string of the form:
 *   [base 85 guid] [feature code] '>' [base 85 guid]
 *
 * PARAMS
 *   szDescriptor  [I]  the descriptor to decompose
588 589 590
 *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
 *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
 *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
 *   pUsed         [O]  the length of the descriptor
 *
 * RETURNS
 *   ERROR_SUCCESS             if everything worked correctly
 *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
 *
 */
UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
                LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
{
    UINT r, len;
    LPWSTR p;
    GUID product, component;

    TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
          szFeature, szComponent, pUsed);

    r = decode_base85_guid( szDescriptor, &product );
    if( !r )
        return ERROR_INVALID_PARAMETER;

    TRACE("product %s\n", debugstr_guid( &product ));

    p = strchrW(&szDescriptor[20],'>');
    if( !p )
        return ERROR_INVALID_PARAMETER;

    len = (p - &szDescriptor[20]);
    if( len > MAX_FEATURE_CHARS )
        return ERROR_INVALID_PARAMETER;

622
    TRACE("feature %s\n", debugstr_wn( &szDescriptor[20], len ));
623 624 625 626 627 628 629

    r = decode_base85_guid( p+1, &component );
    if( !r )
        return ERROR_INVALID_PARAMETER;

    TRACE("component %s\n", debugstr_guid( &component ));

630 631 632 633
    if (szProduct)
        StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
    if (szComponent)
        StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
634 635 636 637 638
    if (szFeature)
    {
        memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
        szFeature[len] = 0;
    }
639 640 641 642 643 644 645 646 647 648 649 650 651 652
    len = ( &p[21] - szDescriptor );

    TRACE("length = %d\n", len);
    *pUsed = len;

    return ERROR_SUCCESS;
}

UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
                LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
{
    WCHAR product[MAX_FEATURE_CHARS+1];
    WCHAR feature[MAX_FEATURE_CHARS+1];
    WCHAR component[MAX_FEATURE_CHARS+1];
653
    LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
654
    UINT r;
655 656 657 658

    TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
          szFeature, szComponent, pUsed);

659 660 661 662 663 664 665 666 667 668
    str = strdupAtoW( szDescriptor );
    if( szDescriptor && !str )
        return ERROR_OUTOFMEMORY;

    if (szProduct)
        p = product;
    if (szFeature)
        f = feature;
    if (szComponent)
        c = component;
669

670
    r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
671

672 673 674 675 676 677 678 679 680
    if (r == ERROR_SUCCESS)
    {
        WideCharToMultiByte( CP_ACP, 0, p, -1,
                             szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
        WideCharToMultiByte( CP_ACP, 0, f, -1,
                             szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
        WideCharToMultiByte( CP_ACP, 0, c, -1,
                             szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
    }
681

682
    msi_free( str );
683 684 685

    return r;
}
686 687 688 689 690 691

UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
{
    DWORD r;
    WCHAR szwGuid[GUID_SIZE];

692 693
    TRACE("%d %p\n", index, lpguid);

694 695 696 697 698 699 700 701 702 703 704 705 706
    if (NULL == lpguid)
        return ERROR_INVALID_PARAMETER;
    r = MsiEnumProductsW(index, szwGuid);
    if( r == ERROR_SUCCESS )
        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);

    return r;
}

UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
{
    HKEY hkeyFeatures = 0;
    DWORD r;
707
    WCHAR szKeyName[SQUISH_GUID_SIZE];
708

709
    TRACE("%d %p\n", index, lpguid);
710 711 712 713 714 715

    if (NULL == lpguid)
        return ERROR_INVALID_PARAMETER;

    r = MSIREG_OpenFeatures(&hkeyFeatures);
    if( r != ERROR_SUCCESS )
716
        return ERROR_NO_MORE_ITEMS;
717

718 719 720 721
    r = RegEnumKeyW(hkeyFeatures, index, szKeyName, SQUISH_GUID_SIZE);
    if( r == ERROR_SUCCESS )
        unsquash_guid(szKeyName, lpguid);
    RegCloseKey(hkeyFeatures);
722 723 724 725 726 727 728 729 730 731 732

    return r;
}

UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
      LPSTR szFeature, LPSTR szParent)
{
    DWORD r;
    WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
    LPWSTR szwProduct = NULL;

733
    TRACE("%s %d %p %p\n", debugstr_a(szProduct), index, szFeature, szParent);
734 735 736

    if( szProduct )
    {
737 738 739
        szwProduct = strdupAtoW( szProduct );
        if( !szwProduct )
            return ERROR_OUTOFMEMORY;
740 741 742 743 744 745 746 747 748 749 750
    }

    r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
    if( r == ERROR_SUCCESS )
    {
        WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
                            szFeature, GUID_SIZE, NULL, NULL);
        WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
                            szParent, GUID_SIZE, NULL, NULL);
    }

751
    msi_free( szwProduct);
752 753 754 755 756 757 758 759 760 761

    return r;
}

UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
      LPWSTR szFeature, LPWSTR szParent)
{
    HKEY hkeyProduct = 0;
    DWORD r, sz;

762
    TRACE("%s %d %p %p\n", debugstr_w(szProduct), index, szFeature, szParent);
763

764 765 766
    if( !szProduct )
        return ERROR_INVALID_PARAMETER;

767 768
    r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
    if( r != ERROR_SUCCESS )
769
        return ERROR_NO_MORE_ITEMS;
770 771 772

    sz = GUID_SIZE;
    r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
773
    RegCloseKey(hkeyProduct);
774 775 776 777 778 779 780 781 782

    return r;
}

UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
{
    DWORD r;
    WCHAR szwGuid[GUID_SIZE];

783
    TRACE("%d %p\n", index, lpguid);
784 785 786 787 788 789 790 791 792 793 794 795

    r = MsiEnumComponentsW(index, szwGuid);
    if( r == ERROR_SUCCESS )
        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);

    return r;
}

UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
{
    HKEY hkeyComponents = 0;
    DWORD r;
796
    WCHAR szKeyName[SQUISH_GUID_SIZE];
797

798
    TRACE("%d %p\n", index, lpguid);
799 800 801

    r = MSIREG_OpenComponents(&hkeyComponents);
    if( r != ERROR_SUCCESS )
802
        return ERROR_NO_MORE_ITEMS;
803

804 805 806 807
    r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
    if( r == ERROR_SUCCESS )
        unsquash_guid(szKeyName, lpguid);
    RegCloseKey(hkeyComponents);
808 809 810 811 812 813 814 815 816 817

    return r;
}

UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
{
    DWORD r;
    WCHAR szwProduct[GUID_SIZE];
    LPWSTR szwComponent = NULL;

818
    TRACE("%s %d %p\n", debugstr_a(szComponent), index, szProduct);
819 820 821

    if( szComponent )
    {
822 823 824
        szwComponent = strdupAtoW( szComponent );
        if( !szwComponent )
            return ERROR_OUTOFMEMORY;
825 826 827 828 829 830 831 832 833
    }

    r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
    if( r == ERROR_SUCCESS )
    {
        WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
                            szProduct, GUID_SIZE, NULL, NULL);
    }

834
    msi_free( szwComponent);
835 836 837 838 839 840 841 842

    return r;
}

UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
{
    HKEY hkeyComp = 0;
    DWORD r, sz;
843
    WCHAR szValName[SQUISH_GUID_SIZE];
844

845
    TRACE("%s %d %p\n", debugstr_w(szComponent), index, szProduct);
846 847 848

    r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
    if( r != ERROR_SUCCESS )
849
        return ERROR_NO_MORE_ITEMS;
850

851
    sz = SQUISH_GUID_SIZE;
852
    r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
853 854
    if( r == ERROR_SUCCESS )
        unsquash_guid(szValName, szProduct);
855

856
    RegCloseKey(hkeyComp);
857 858 859 860

    return r;
}

861
static UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
862 863 864
                awstring *lpQualBuf, DWORD* pcchQual,
                awstring *lpAppBuf, DWORD* pcchAppBuf )
{
865
    DWORD name_sz, val_sz, name_max, val_max, type, ofs;
866 867 868 869
    LPWSTR name = NULL, val = NULL;
    UINT r, r2;
    HKEY key;

870
    TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
871 872 873 874 875 876 877 878 879 880
          lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);

    if (!szComponent)
        return ERROR_INVALID_PARAMETER;

    r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
    if (r != ERROR_SUCCESS)
        return ERROR_UNKNOWN_COMPONENT;

    /* figure out how big the name is we want to return */
881 882 883 884
    name_max = 0x10;
    r = ERROR_OUTOFMEMORY;
    name = msi_alloc( name_max * sizeof(WCHAR) );
    if (!name)
885 886
        goto end;

887 888 889
    val_max = 0x10;
    r = ERROR_OUTOFMEMORY;
    val = msi_alloc( val_max );
890 891 892
    if (!val)
        goto end;

893 894 895 896 897 898 899 900 901 902 903 904 905 906
    /* loop until we allocate enough memory */
    while (1)
    {
        name_sz = name_max;
        val_sz = val_max;
        r = RegEnumValueW( key, iIndex, name, &name_sz,
                           NULL, &type, (LPBYTE)val, &val_sz );
        if (r == ERROR_SUCCESS)
            break;
        if (r != ERROR_MORE_DATA)
            goto end;
 
        if (type != REG_MULTI_SZ)
        {
907
            ERR("component data has wrong type (%d)\n", type);
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923
            goto end;
        }

        r = ERROR_OUTOFMEMORY;
        if ((name_sz+1) >= name_max)
        {
            name_max *= 2;
            msi_free( name );
            name = msi_alloc( name_max * sizeof (WCHAR) );
            if (!name)
                goto end;
            continue;
        }
        if (val_sz > val_max)
        {
            val_max = val_sz + sizeof (WCHAR);
924
            msi_free( val );
925 926 927 928 929
            val = msi_alloc( val_max * sizeof (WCHAR) );
            if (!val)
                goto end;
            continue;
        }
930
        ERR("should be enough data, but isn't %d %d\n", name_sz, val_sz );
931
        goto end;
932
    }
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954

    ofs = 0;
    r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
    if (r != ERROR_SUCCESS)
        goto end;

    TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));

    r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual );
    r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf );

    if (r2 != ERROR_SUCCESS)
        r = r2;

end:
    msi_free(val);
    msi_free(name);
    RegCloseKey(key);

    return r;
}

955 956 957
/*************************************************************************
 *  MsiEnumComponentQualifiersA [MSI.@]
 */
958
UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
959
                LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
960
                LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
961
{
962 963 964
    awstring qual, appdata;
    LPWSTR comp;
    UINT r;
965

966
    TRACE("%s %08x %p %p %p %p\n", debugstr_a(szComponent), iIndex,
967 968
          lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
          pcchApplicationDataBuf);
969

970 971 972
    comp = strdupAtoW( szComponent );
    if (szComponent && !comp)
        return ERROR_OUTOFMEMORY;
973

974 975
    qual.unicode = FALSE;
    qual.str.a = lpQualifierBuf;
976

977 978
    appdata.unicode = FALSE;
    appdata.str.a = lpApplicationDataBuf;
979

980 981 982 983
    r = MSI_EnumComponentQualifiers( comp, iIndex,
              &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
    msi_free( comp );
    return r;
984 985
}

986 987 988
/*************************************************************************
 *  MsiEnumComponentQualifiersW [MSI.@]
 */
989
UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
990 991 992
                LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
                LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
{
993
    awstring qual, appdata;
994

995
    TRACE("%s %08x %p %p %p %p\n", debugstr_w(szComponent), iIndex,
996 997
          lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
          pcchApplicationDataBuf);
998

999 1000
    qual.unicode = TRUE;
    qual.str.w = lpQualifierBuf;
1001

1002 1003
    appdata.unicode = TRUE;
    appdata.str.w = lpApplicationDataBuf;
1004

1005 1006
    return MSI_EnumComponentQualifiers( szComponent, iIndex,
                 &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
1007 1008
}

1009 1010 1011 1012
/*************************************************************************
 *  MsiEnumRelatedProductsW   [MSI.@]
 *
 */
1013 1014 1015
UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
                                    DWORD iProductIndex, LPWSTR lpProductBuf)
{
1016
    UINT r;
1017
    HKEY hkey;
1018
    WCHAR szKeyName[SQUISH_GUID_SIZE];
1019

1020
    TRACE("%s %u %u %p\n", debugstr_w(szUpgradeCode), dwReserved,
1021
          iProductIndex, lpProductBuf);
1022 1023 1024 1025 1026 1027

    if (NULL == szUpgradeCode)
        return ERROR_INVALID_PARAMETER;
    if (NULL == lpProductBuf)
        return ERROR_INVALID_PARAMETER;

1028 1029 1030
    r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
    if (r != ERROR_SUCCESS)
        return ERROR_NO_MORE_ITEMS;
1031

1032 1033 1034
    r = RegEnumKeyW(hkey, iProductIndex, szKeyName, SQUISH_GUID_SIZE);
    if( r == ERROR_SUCCESS )
        unsquash_guid(szKeyName, lpProductBuf);
1035 1036
    RegCloseKey(hkey);

1037
    return r;
1038 1039
}

1040 1041 1042 1043
/*************************************************************************
 *  MsiEnumRelatedProductsA   [MSI.@]
 *
 */
1044 1045 1046
UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
                                    DWORD iProductIndex, LPSTR lpProductBuf)
{
1047 1048 1049
    LPWSTR szwUpgradeCode = NULL;
    WCHAR productW[GUID_SIZE];
    UINT r;
1050

1051
    TRACE("%s %u %u %p\n", debugstr_a(szUpgradeCode), dwReserved,
1052
          iProductIndex, lpProductBuf);
1053

1054 1055 1056 1057 1058 1059
    if (szUpgradeCode)
    {
        szwUpgradeCode = strdupAtoW( szUpgradeCode );
        if( !szwUpgradeCode )
            return ERROR_OUTOFMEMORY;
    }
1060

1061 1062 1063 1064 1065 1066
    r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
                                 iProductIndex, productW );
    if (r == ERROR_SUCCESS)
    {
        WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
                             lpProductBuf, GUID_SIZE, NULL, NULL );
1067
    }
1068
    msi_free( szwUpgradeCode);
1069
    return r;
1070
}
1071 1072 1073 1074

/***********************************************************************
 * MsiEnumPatchesA            [MSI.@]
 */
1075
UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex,
1076 1077
        LPSTR lpPatchBuf, LPSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
{
1078
    FIXME("%s %d %p %p %p\n", debugstr_a(szProduct),
1079 1080 1081 1082 1083 1084 1085
          iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
    return ERROR_NO_MORE_ITEMS;
}

/***********************************************************************
 * MsiEnumPatchesW            [MSI.@]
 */
1086
UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex,
1087 1088
        LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
{
1089
    FIXME("%s %d %p %p %p\n", debugstr_w(szProduct),
1090 1091 1092
          iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
    return ERROR_NO_MORE_ITEMS;
}