aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/FormatFiles.cpp
blob: d153399905e1b6cc3a3e68e1e7490ac63c262eed (plain)
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
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
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.

#include "precomp.h"

const UINT COST_FILEFORMATTING = 2000;


//
// WixSchedFormatFiles - immediate CA to schedule format files CAs
//
extern "C" UINT __stdcall WixSchedFormatFiles(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    PSCZ sczBinaryKey;
    PSCZ sczFileKey;
    PSCZ sczComponentKey;
    PSCZ sczFormattedFile;
    PSCZ sczFilePath;
    PMSIHANDLE hView;
    PMSIHANDLE hRec;
    PSCZ sczFileContent;
    PSCZ sczFormattedContent;
    PSCZ sczExecCustomActionData;
    PSCZ sczRollbackCustomActionData;

    LPCWSTR wzQuery =
        L"SELECT `Wix4FormatFile`.`Binary_`, `Wix4FormatFile`.`File_`, `File`.`Component_` "
        L"FROM `Wix4FormatFile`, `File` "
        L"WHERE `Wix4FormatFile`.`File_` = `File`.`File`";
    enum eQuery { eqBinaryKey = 1, eqFileKey, eqComponentKey };

    // initialize
    hr = WcaInitialize(hInstall, "WixSchedFormatFiles");
    ExitOnFailure(hr, "Failed to initialize for WixSchedFormatFiles.");

    // query and loop through all the files
    hr = WcaOpenExecuteView(wzQuery, &hView);
    ExitOnFailure(hr, "Failed to open view on Wix4FormatFile table");

    DWORD cFiles = 0;
    while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
    {
        ++cFiles;

        hr = WcaGetRecordString(hRec, eqBinaryKey, &sczBinaryKey);
        ExitOnFailure(hr, "Failed to get Binary table key.");

        hr = WcaGetRecordString(hRec, eqFileKey, &sczFileKey);
        ExitOnFailure(hr, "Failed to get File table key.");

        hr = WcaGetRecordString(hRec, eqComponentKey, &sczComponentKey);
        ExitOnFailure(hr, "Failed to get Component table key.");

        // we need to know if the component's being installed, uninstalled, or reinstalled
        WCA_TODO todo = WcaGetComponentToDo(sczComponentKey);
        if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
        {
            // turn the file key into the path to the target file
            hr = StrAllocFormatted(&sczFormattedFile, L"[#%ls]", sczFileKey);
            ExitOnFailure(hr, "Failed to format file string for file: %ls", sczFileKey);
            hr = WcaGetFormattedString(sczFormattedFile, &sczFilePath);
            ExitOnFailure(hr, "Failed to get path for file: %ls", sczFileKey);

            // extract binary to string
            WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
            hr = WcaExtractBinaryToString(sczBinaryKey, &sczFileContent, &encoding);
            ExitOnFailure(hr, "Failed to extract binary: %ls", sczBinaryKey);

            // format string
            hr = WcaGetFormattedString(sczFileContent, &sczFormattedContent);
            ExitOnFailure(hr, "Failed to format file content: %ls", sczFileContent);

            // write to deferred custom action data
            hr = WcaWriteStringToCaData(sczFilePath, &sczExecCustomActionData);
            ExitOnFailure(hr, "Failed to write deferred custom action data for file: %ls", sczFilePath);

            hr = WcaWriteIntegerToCaData(encoding, &sczExecCustomActionData);
            ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);

            hr = WcaWriteStringToCaData(sczFormattedContent, &sczExecCustomActionData);
            ExitOnFailure(hr, "Failed to write deferred custom action data for file content: %ls", sczFilePath);

            // write to rollback custom action data
            hr = WcaWriteStringToCaData(sczFilePath, &sczRollbackCustomActionData);
            ExitOnFailure(hr, "Failed to write rollback custom action data for file: %ls", sczFilePath);

            hr = WcaWriteIntegerToCaData(encoding, &sczRollbackCustomActionData);
            ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);

            hr = WcaWriteStringToCaData(sczFileContent, &sczRollbackCustomActionData);
            ExitOnFailure(hr, "Failed to write rollback custom action data for file content: %ls", sczFilePath);
        }
    }

    // reaching the end of the list is actually a good thing, not an error
    if (E_NOMOREITEMS == hr)
    {
        hr = S_OK;
    }
    ExitOnFailure(hr, "Failure occurred while processing Wix4FormatFile table");

    // schedule deferred CAs if there's anything to do
    if (sczRollbackCustomActionData && *sczRollbackCustomActionData)
    {
        hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFormatFiles"), sczRollbackCustomActionData, cFiles * COST_FILEFORMATTING);
        ExitOnFailure(hr, "Failed to schedule RollbackFormatFiles");
    }

    if (sczExecCustomActionData && *sczExecCustomActionData)
    {
        hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFormatFiles"), sczExecCustomActionData, cFiles * COST_FILEFORMATTING);
        ExitOnFailure(hr, "Failed to schedule ExecFormatFiles");
    }

LExit:
    return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
}


//
// WixExecFormatFiles - deferred and rollback CAs to write formatted files
//
extern "C" UINT __stdcall WixExecFormatFiles(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    PSCZ sczCustomActionData;
    LPWSTR pwz = NULL;
    PSCZ sczFilePath;
    PSCZ sczFileContent;
    LPSTR psz = NULL;

    // initialize
    hr = WcaInitialize(hInstall, "WixExecFormatFiles");
    ExitOnFailure(hr, "Failed to initialize for WixExecFormatFiles.");

    hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
    ExitOnFailure(hr, "Failed to get CustomActionData.");
#ifdef _DEBUG
    WcaLog(LOGMSG_STANDARD, "CustomActionData: %ls", sczCustomActionData);
#endif

    // loop through all the passed in data
    pwz = sczCustomActionData;
    while (pwz && *pwz)
    {
        // extract the custom action data
        hr = WcaReadStringFromCaData(&pwz, &sczFilePath);
        ExitOnFailure(hr, "Failed to read file path from custom action data");

        WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
        hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&encoding));
        ExitOnFailure(hr, "Failed to read encoding from custom action data");

        hr = WcaReadStringFromCaData(&pwz, &sczFileContent);
        ExitOnFailure(hr, "Failed to read file content from custom action data");

        // re-encode content
        LPCBYTE pbData = NULL;
        size_t cbData = 0;
        switch (encoding)
        {
        case WCA_ENCODING_UTF_16:
            pbData = reinterpret_cast<LPCBYTE>(LPCWSTR(sczFileContent));
            cbData = lstrlenW(sczFileContent) * sizeof(WCHAR);
            break;

        case WCA_ENCODING_UTF_8:
            hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_UTF8);
            ExitOnFailure(hr, "Failed to convert Unicode to UTF-8.");
            pbData = reinterpret_cast<LPCBYTE>(psz);

            hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
            ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
            break;

        case WCA_ENCODING_ANSI:
            hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_ACP);
            ExitOnFailure(hr, "Failed to convert Unicode to ANSI.");
            pbData = reinterpret_cast<LPCBYTE>(psz);

            hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
            ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
            break;

        default:
            break;
        }

#ifdef _DEBUG
        WcaLog(LOGMSG_STANDARD, "File: %ls", sczCustomActionData);
        WcaLog(LOGMSG_STANDARD, "Content: %ls", sczFileContent);
#endif

        // write file and preserve modified time
        FILETIME filetime;

        hr = FileGetTime(sczFilePath, NULL, NULL, &filetime);
        ExitOnFailure(hr, "Failed to get modified time of file : %ls", sczFilePath);

        hr = FileWrite(sczFilePath, FILE_ATTRIBUTE_NORMAL, pbData, cbData, NULL);
        ExitOnFailure(hr, "Failed to write file content: %ls", sczFilePath);

        hr = FileSetTime(sczFilePath, NULL, NULL, &filetime);
        ExitOnFailure(hr, "Failed to set modified time of file : %ls", sczFilePath);

        // Tick the progress bar
        hr = WcaProgressMessage(COST_FILEFORMATTING, FALSE);
        ExitOnFailure(hr, "Failed to tick progress bar for file: %ls", sczFilePath);
    }

LExit:
    ReleaseStr(psz);

    return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
}