upgrade.c 7.34 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Implementation of the Microsoft Installer (msi.dll)
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 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
 */

/*
 * Actions focused on in this module
 *
 * FindRelatedProducts
 * MigrateFeatureStates (TODO)
 * RemoveExistingProducts (TODO)
 */

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winreg.h"
#include "wine/debug.h"
#include "msidefs.h"
#include "msipriv.h"
#include "winuser.h"

WINE_DEFAULT_DEBUG_CHANNEL(msi);

static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
{
    DWORD langdword;

    if (!lang2 || lang2[0]==0)
        return TRUE;

49
    langdword = wcstol(lang2, NULL, 10);
50 51 52 53 54 55 56

    if (attributes & msidbUpgradeAttributesLanguagesExclusive)
        return (lang1 != langdword);
    else
        return (lang1 == langdword);
}

57
static BOOL find_product( const WCHAR *list, const WCHAR *product )
58
{
59
    const WCHAR *p = list, *q;
60

61 62 63 64 65 66 67 68 69
    if (!list) return FALSE;
    for (;;)
    {
        while (*p && *p != '{') p++;
        if (*p != '{') return FALSE;
        q = p;
        while (*q && *q != '}') q++;
        if (*q != '}') return FALSE;
        q++;
70
        if (q - p < lstrlenW( product )) return FALSE;
71 72 73 74 75
        if (!memcmp( p, product, (q - p) * sizeof(WCHAR) )) return TRUE;
        p = q + 1;
        while (*p && *p != ';') p++;
        if (*p != ';') break;
    }
76

77 78
    return FALSE;
}
79

80 81 82 83 84
static void append_productcode( MSIPACKAGE *package, const WCHAR *action_prop, const WCHAR *product )
{
    WCHAR *prop, *newprop;
    DWORD len = 0;
    UINT r;
85

86 87 88 89
    prop = msi_dup_property( package->db, action_prop );
    if (find_product( prop, product ))
    {
        TRACE( "related product property %s already contains %s\n", debugstr_w(action_prop), debugstr_w(product) );
90
        free( prop );
91 92
        return;
    }
93

94 95
    if (prop) len += lstrlenW( prop );
    len += lstrlenW( product ) + 2;
96
    if (!(newprop = malloc( len * sizeof(WCHAR) ))) return;
97 98
    if (prop)
    {
99
        lstrcpyW( newprop, prop );
100
        lstrcatW( newprop, L";" );
101
    }
102
    else newprop[0] = 0;
103
    lstrcatW( newprop, product );
104

105
    r = msi_set_property( package->db, action_prop, newprop, -1 );
106
    if (r == ERROR_SUCCESS && !wcscmp( action_prop, L"SourceDir" ))
107
        msi_reset_source_folders( package );
108

109
    TRACE( "related product property %s now %s\n", debugstr_w(action_prop), debugstr_w(newprop) );
110

111 112
    free( prop );
    free( newprop );
113 114 115 116
}

static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
{
117
    MSIPACKAGE *package = param;
118
    WCHAR product[SQUASHED_GUID_SIZE];
119
    DWORD index = 0, attributes = 0, sz = ARRAY_SIZE(product);
120 121 122
    LPCWSTR upgrade_code;
    HKEY hkey = 0;
    UINT rc = ERROR_SUCCESS;
123
    MSIRECORD *uirow;
124 125 126 127 128 129 130

    upgrade_code = MSI_RecordGetString(rec,1);

    rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;

131
    uirow = MSI_CreateRecord(1);
132
    attributes = MSI_RecordGetInteger(rec,5);
133

134 135 136 137 138 139
    while (rc == ERROR_SUCCESS)
    {
        rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
        if (rc == ERROR_SUCCESS)
        {
            WCHAR productid[GUID_SIZE];
140 141
            LPCWSTR ver, language, action_property;
            DWORD check = 0, comp_ver, sz = 0x100;
142 143 144
            HKEY hukey;
            INT r;

145
            TRACE( "looking at index %lu product %s\n", index, debugstr_w(product) );
146

147
            unsquash_guid(product, productid);
148 149 150
            if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) &&
                MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) &&
                MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE))
151
            {
152
                TRACE("product key not found\n");
153 154 155 156
                rc = ERROR_SUCCESS;
                index ++;
                continue;
            }
157

158
            sz = sizeof(DWORD);
159 160 161
            RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz);

            /* check version minimum */
162
            ver = MSI_RecordGetString(rec,2);
163
            if (ver)
164
            {
165 166 167 168
                comp_ver = msi_version_str_to_dword(ver);
                r = check - comp_ver;
                if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive)))
                {
169
                    TRACE("version below minimum\n");
170 171 172 173
                    RegCloseKey(hukey);
                    index ++;
                    continue;
                }
174 175
            }

176
            /* check version maximum */
177
            ver = MSI_RecordGetString(rec,3);
178
            if (ver)
179
            {
180 181 182 183 184 185 186 187
                comp_ver = msi_version_str_to_dword(ver);
                r = check - comp_ver;
                if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive)))
                {
                    RegCloseKey(hukey);
                    index ++;
                    continue;
                }
188
                TRACE("version above maximum\n");
189 190
            }

191
            /* check language */
192
            sz = sizeof(DWORD);
193
            RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz);
194 195 196 197 198
            RegCloseKey(hukey);
            language = MSI_RecordGetString(rec,4);
            if (!check_language(check, language, attributes))
            {
                index ++;
199
                TRACE("language doesn't match\n");
200
                continue;
201
            }
202
            TRACE("found related product\n");
203

204 205 206
            action_property = MSI_RecordGetString(rec, 7);
            append_productcode(package, action_property, productid);
            MSI_RecordSetStringW(uirow, 1, productid);
207
            MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
208 209 210 211
        }
        index ++;
    }
    RegCloseKey(hkey);
212
    msiobj_release( &uirow->hdr);
213

214 215 216 217 218 219
    return ERROR_SUCCESS;
}

UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
{
    MSIQUERY *view;
220
    UINT rc;
221

222
    if (msi_get_property_int(package->db, L"Installed", 0))
223 224 225 226
    {
        TRACE("Skipping FindRelatedProducts action: product already installed\n");
        return ERROR_SUCCESS;
    }
227
    if (msi_action_is_unique(package, L"FindRelatedProducts"))
228
    {
229
        TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n");
230
        return ERROR_SUCCESS;
231 232
    }
    else
233
        msi_register_unique_action(package, L"FindRelatedProducts");
234

235
    rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Upgrade`", &view);
236 237
    if (rc != ERROR_SUCCESS)
        return ERROR_SUCCESS;
238

239 240 241 242
    rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
    msiobj_release(&view->hdr);
    return rc;
}