aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Iis/ca/scaweberr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Iis/ca/scaweberr.cpp')
-rw-r--r--src/ext/Iis/ca/scaweberr.cpp371
1 files changed, 371 insertions, 0 deletions
diff --git a/src/ext/Iis/ca/scaweberr.cpp b/src/ext/Iis/ca/scaweberr.cpp
new file mode 100644
index 00000000..2441f006
--- /dev/null
+++ b/src/ext/Iis/ca/scaweberr.cpp
@@ -0,0 +1,371 @@
1// 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.
2
3#include "precomp.h"
4
5enum eWebErrorQuery { weqErrorCode = 1, weqSubCode, weqParentType, weqParentValue, weqFile, weqURL };
6
7static HRESULT AddWebErrorToList(SCA_WEB_ERROR** ppsweList);
8
9void ScaWebErrorFreeList(SCA_WEB_ERROR *psweList)
10{
11 SCA_WEB_ERROR *psweDelete = psweList;
12 while (psweList)
13 {
14 psweDelete = psweList;
15 psweList = psweList->psweNext;
16
17 MemFree(psweDelete);
18 }
19}
20
21HRESULT ScaWebErrorRead(
22 SCA_WEB_ERROR **ppsweList,
23 __inout LPWSTR *ppwzCustomActionData
24 )
25{
26// AssertSz(0, "Debug ScaWebErrorRead here");
27 HRESULT hr = S_OK;
28 MSIHANDLE hRec;
29 LPWSTR pwzData = NULL;
30 SCA_WEB_ERROR* pswe;
31 WCA_WRAPQUERY_HANDLE hWrapQuery = NULL;
32
33 ExitOnNull(ppsweList, hr, E_INVALIDARG, "Failed to read web error, because no web error was provided to read");
34
35 hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData);
36 ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead");
37
38 if (0 == WcaGetQueryRecords(hWrapQuery))
39 {
40 WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebErrorRead() - required tables not present.");
41 ExitFunction1(hr = S_FALSE);
42 }
43
44 // loop through all the web errors
45 while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec)))
46 {
47 hr = AddWebErrorToList(ppsweList);
48 ExitOnFailure(hr, "failed to add web error to list");
49
50 pswe = *ppsweList;
51
52 hr = WcaGetRecordInteger(hRec, weqErrorCode, &(pswe->iErrorCode));
53 ExitOnFailure(hr, "failed to get IIsWebError.ErrorCode");
54
55 hr = WcaGetRecordInteger(hRec, weqSubCode, &(pswe->iSubCode));
56 ExitOnFailure(hr, "failed to get IIsWebError.SubCode");
57
58 hr = WcaGetRecordInteger(hRec, weqParentType, &(pswe->iParentType));
59 ExitOnFailure(hr, "failed to get IIsWebError.ParentType");
60
61 hr = WcaGetRecordString(hRec, weqParentValue, &pwzData);
62 ExitOnFailure(hr, "Failed to get IIsWebError.ParentValue");
63 hr = ::StringCchCopyW(pswe->wzParentValue, countof(pswe->wzParentValue), pwzData);
64 ExitOnFailure(hr, "Failed to copy IIsWebError.ParentValue");
65
66 hr = WcaGetRecordString(hRec, weqFile, &pwzData);
67 ExitOnFailure(hr, "Failed to get IIsWebError.File");
68 hr = ::StringCchCopyW(pswe->wzFile, countof(pswe->wzFile), pwzData);
69 ExitOnFailure(hr, "Failed to copy IIsWebError.File");
70
71 hr = WcaGetRecordString(hRec, weqURL, &pwzData);
72 ExitOnFailure(hr, "Failed to get IIsWebError.URL");
73 hr = ::StringCchCopyW(pswe->wzURL, countof(pswe->wzURL), pwzData);
74 ExitOnFailure(hr, "Failed to copy IIsWebError.URL");
75
76 // If they've specified both a file and a URL, that's invalid
77 if (*(pswe->wzFile) && *(pswe->wzURL))
78 ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), "Both File and URL specified for web error. File: %ls, URL: %ls", pswe->wzFile, pswe->wzURL);
79 }
80
81 if (E_NOMOREITEMS == hr)
82 {
83 hr = S_OK;
84 }
85 ExitOnFailure(hr, "Failure while processing web errors");
86
87LExit:
88 WcaFinishUnwrapQuery(hWrapQuery);
89
90 ReleaseStr(pwzData);
91
92 return hr;
93}
94
95HRESULT ScaGetWebError(int iParentType, LPCWSTR wzParentValue, SCA_WEB_ERROR **ppsweList, SCA_WEB_ERROR **ppsweOut)
96{
97 HRESULT hr = S_OK;
98 SCA_WEB_ERROR* psweAdd = NULL;
99 SCA_WEB_ERROR* psweLast = NULL;
100
101 *ppsweOut = NULL;
102
103 if (!*ppsweList)
104 return hr;
105
106 SCA_WEB_ERROR* pswe = *ppsweList;
107 while (pswe)
108 {
109 if (iParentType == pswe->iParentType && 0 == lstrcmpW(wzParentValue, pswe->wzParentValue))
110 {
111 // Found a match, take this one out of the list and add it to the matched out list
112 psweAdd = pswe;
113
114 if (psweLast)
115 {
116 // If we're not at the beginning of the list tell the last node about it's new next (since we're taking away it's current next)
117 psweLast->psweNext = psweAdd->psweNext;
118 }
119 else
120 {
121 // If we are at the beginning (no psweLast) update the beginning (since we're taking it)
122 *ppsweList = pswe->psweNext;
123 }
124 pswe = pswe->psweNext; // move on
125
126 // Add the one we've removed to the beginning of the out list
127 psweAdd->psweNext = *ppsweOut;
128 *ppsweOut = psweAdd;
129 }
130 else
131 {
132 psweLast = pswe; // remember the last we that didn't match
133 pswe = pswe->psweNext; // move on
134 }
135 }
136
137 return hr;
138}
139
140HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList)
141{
142// AssertSz(0, "Debug ScaWriteWebError here");
143 Assert(*wzRoot && psweList);
144
145 HRESULT hr = S_OK;
146
147 DWORD cchData = 0;
148 LPWSTR pwzSearchKey = NULL;
149 LPWSTR pwz = NULL;
150 LPWSTR pwzErrors = NULL;
151
152 LPWSTR pwzCodeSubCode = NULL;
153 LPWSTR pwzAcceptableCodeSubCode = NULL;
154 LPCWSTR wzFoundCodeSubCode = NULL;
155 DWORD_PTR dwFoundCodeSubCodeIndex = 0xFFFFFFFF;
156 BOOL fOldValueFound = FALSE;
157 LPWSTR pwzAcceptableErrors = NULL;
158
159 LPWSTR pwzNewError = NULL;
160
161 METADATA_RECORD mr;
162 ::ZeroMemory(&mr, sizeof(mr));
163
164 ExitOnNull(piMetabase, hr, E_INVALIDARG, "Failed to write web error, because no metabase was provided");
165 ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write web error, because no root was provided");
166
167 // get the set of all valid custom errors from the metabase
168 mr.dwMDIdentifier = MD_CUSTOM_ERROR_DESC;
169 mr.dwMDAttributes = METADATA_INHERIT;
170 mr.dwMDUserType = IIS_MD_UT_SERVER;
171 mr.dwMDDataType = ALL_METADATA;
172 mr.dwMDDataLen = cchData = 0;
173 mr.pbMDData = NULL;
174
175 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/Info", &mr);
176 ExitOnFailure(hr, "Unable to get set of acceptable error codes for this server.");
177
178 pwzAcceptableErrors = reinterpret_cast<LPWSTR>(mr.pbMDData);
179
180 // Check if web errors already exist here
181 mr.dwMDIdentifier = MD_CUSTOM_ERROR;
182 mr.dwMDAttributes = METADATA_INHERIT;
183 mr.dwMDUserType = IIS_MD_UT_SERVER;
184 mr.dwMDDataType = ALL_METADATA;
185 mr.dwMDDataLen = cchData = 0;
186 mr.pbMDData = NULL;
187
188 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr);
189 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr)
190 {
191 //
192 // If we don't have one already, find an appropriate one to start with
193 //
194
195 // we can walk up key by key and look for custom errors to inherit
196
197 hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0);
198 ExitOnFailure(hr, "Failed to copy root string: %ls", wzRoot);
199
200 pwz = pwzSearchKey + lstrlenW(pwzSearchKey);
201
202 while (NULL == pwzErrors)
203 {
204 // find the last slash
205 while (*pwz != '/' && pwz != pwzSearchKey)
206 pwz --;
207
208 if (pwz == pwzSearchKey)
209 break;
210
211 *pwz = L'\0';
212
213 // Try here. If it's not found, keep walking up the path
214 hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr);
215 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr)
216 hr = S_FALSE;
217 ExitOnFailure(hr, "failed to discover default error values to start with for web root: %ls while walking up the tree", wzRoot);
218
219 if (S_OK == hr)
220 {
221 pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData);
222 break;
223 }
224
225 // Don't keep going if we're at the root
226 if (0 == lstrcmpW(pwz + 1, L"W3SVC"))
227 break;
228 }
229 }
230 else
231 {
232 pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData);
233 }
234 ExitOnFailure(hr, "failed to discover default error values to start with for web root: %ls", wzRoot);
235
236 // The above code should have come up with some value to start pwzErrors off with. Make sure it did.
237 if (NULL == pwzErrors)
238 {
239 ExitOnFailure(hr = E_UNEXPECTED, "failed to discover default error values to start with for web root: %ls", wzRoot);
240 }
241
242 // Loop through the web errors
243 for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext)
244 {
245 // Assume that we will have to replace
246 fOldValueFound = TRUE;
247
248 // If the subcode is 0, that means "*" in MD_CUSTOM_ERROR (thus the special formatting logic)
249 if (0 == pswe->iSubCode)
250 {
251 hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,*", pswe->iErrorCode);
252 ExitOnFailure(hr, "failed to create error code string while installing web error");
253 }
254 else
255 {
256 hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode);
257 ExitOnFailure(hr, "failed to create error code,subcode string while installing web error");
258 }
259
260 hr = MultiSzFindSubstring(pwzErrors, pwzCodeSubCode, &dwFoundCodeSubCodeIndex, &wzFoundCodeSubCode);
261 ExitOnFailure(hr, "failed to find existing error code,subcode: %ls", pwzCodeSubCode);
262
263 // If we didn't find this error code/sub code pair in the list already, make sure it's acceptable to add
264 if (S_FALSE == hr)
265 {
266 //
267 // Make sure this error code/sub code pair is in the "acceptable" list
268 //
269
270 // If the subcode is 0, that means "0" in MD_CUSTOM_ERROR_DESC (no special formatting logic needed)
271 hr = StrAllocFormatted(&pwzAcceptableCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode);
272 ExitOnFailure(hr, "failed to create error code,subcode string while installing web error");
273
274 // We don't care where it is, just whether it's there or not
275 hr = MultiSzFindSubstring(pwzAcceptableErrors, pwzAcceptableCodeSubCode, NULL, NULL);
276 ExitOnFailure(hr, "failed to find whether or not error code, subcode: %ls is supported", pwzCodeSubCode);
277
278 if (S_FALSE == hr)
279 {
280 WcaLog(LOGMSG_VERBOSE, "Skipping error code, subcode: %ls because it is not supported by the server.", pwzCodeSubCode);
281 continue;
282 }
283
284 // If we didn't find it (and its an acceptable error) then we have nothing to replace
285 fOldValueFound = FALSE;
286 }
287
288 // Set up the new error string if needed
289 if (*(pswe->wzFile))
290 {
291 hr = StrAllocFormatted(&pwzNewError, L"%s,FILE,%s", pwzCodeSubCode, pswe->wzFile);
292 ExitOnFailure(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile);
293 }
294 else if (*(pswe->wzURL))
295 {
296 hr = StrAllocFormatted(&pwzNewError, L"%s,URL,%s", pwzCodeSubCode, pswe->wzURL);
297 ExitOnFailure(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile);
298 }
299 else if (fOldValueFound)
300 {
301 // If no File or URL was specified, they want a default error so remove the old value from the MULTISZ and move on
302 hr = MultiSzRemoveString(&pwzErrors, dwFoundCodeSubCodeIndex);
303 ExitOnFailure(hr, "failed to remove string for error code sub code: %ls in order to make it 'default'", pwzCodeSubCode);
304 continue;
305 }
306
307 // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter)
308 if (fOldValueFound)
309 {
310 hr = MultiSzReplaceString(&pwzErrors, dwFoundCodeSubCodeIndex, pwzNewError);
311 ExitOnFailure(hr, "failed to replace old error string with new error string for error code,subcode: %ls", pwzCodeSubCode);
312 }
313 else
314 {
315 hr = MultiSzPrepend(&pwzErrors, NULL, pwzNewError);
316 ExitOnFailure(hr, "failed to prepend new error string for error code,subcode: %ls", pwzCodeSubCode);
317 }
318 }
319
320 // now write the CustomErrors to the metabase
321 if (weptWeb == iParentType)
322 {
323 hr = ScaWriteMetabaseValue(piMetabase, wzRoot, L"/Root", MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors);
324 ExitOnFailure(hr, "Failed to write Web Error to /Root");
325 }
326 else
327 {
328 hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors);
329 ExitOnFailure(hr, "Failed to write Web Error");
330 }
331
332LExit:
333 ReleaseStr(pwzErrors);
334 ReleaseStr(pwzSearchKey);
335 ReleaseStr(pwzCodeSubCode);
336 ReleaseStr(pwzAcceptableCodeSubCode);
337 ReleaseStr(pwzAcceptableErrors);
338
339 return hr;
340}
341
342static HRESULT AddWebErrorToList(SCA_WEB_ERROR** ppsweList)
343{
344 HRESULT hr = S_OK;
345
346 SCA_WEB_ERROR* pswe = static_cast<SCA_WEB_ERROR*>(MemAlloc(sizeof(SCA_WEB_ERROR), TRUE));
347 ExitOnNull(pswe, hr, E_OUTOFMEMORY, "failed to allocate memory for new web error list element");
348
349 pswe->psweNext = *ppsweList;
350 *ppsweList = pswe;
351
352LExit:
353 return hr;
354}
355
356HRESULT ScaWebErrorCheckList(SCA_WEB_ERROR* psweList)
357{
358 if (!psweList)
359 {
360 return S_OK;
361 }
362
363 while (psweList)
364 {
365 WcaLog(LOGMSG_STANDARD, "WebError code: %d subcode: %d for parent: %ls not used!", psweList->iErrorCode, psweList->iSubCode, psweList->wzParentValue);
366 psweList = psweList->psweNext;
367 }
368
369 return E_FAIL;
370}
371