]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/ErrorDetailManager.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / ssl / ErrorDetailManager.cc
CommitLineData
bbc27441 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
582c2af2 9#include "squid.h"
02259ff8 10#include "ErrorDetail.h"
02259ff8 11#include "ErrorDetailManager.h"
602d9612 12#include "errorpage.h"
4f1c93a7 13#include "http/ContentLengthInterpreter.h"
b6149797 14#include "mime_header.h"
02259ff8
CT
15
16void Ssl::errorDetailInitialize()
17{
18 Ssl::ErrorDetailsManager::GetInstance();
19}
20
21void Ssl::errorDetailClean()
22{
23 Ssl::ErrorDetailsManager::Shutdown();
24}
25
26namespace Ssl
27{
28
29/// manages error detail templates
dc49061a
A
30class ErrorDetailFile : public TemplateFile
31{
02259ff8 32public:
8ff2520a 33 explicit ErrorDetailFile(ErrorDetailsList::Pointer const details): TemplateFile("error-details.txt", ERR_NONE) {
dc49061a 34 theDetails = details;
02259ff8
CT
35 }
36
37private:
02259ff8 38 ErrorDetailsList::Pointer theDetails;
7e6eabbc 39 virtual bool parse() override;
02259ff8
CT
40};
41}// namespace Ssl
42
43/******************/
44bool
13cd7dee 45Ssl::ErrorDetailsList::getRecord(Security::ErrorCode value, ErrorDetailEntry &entry)
02259ff8
CT
46{
47 const ErrorDetails::const_iterator it = theList.find(value);
48 if (it != theList.end()) {
49 entry.error_no = it->second.error_no;
50 entry.name = it->second.name;
51 entry.detail = it->second.detail;
52 entry.descr = it->second.descr;
53 return true;
54 }
55 return false;
56}
57
58const char *
13cd7dee 59Ssl::ErrorDetailsList::getErrorDescr(Security::ErrorCode value)
02259ff8
CT
60{
61 const ErrorDetails::const_iterator it = theList.find(value);
62 if (it != theList.end()) {
63 return it->second.descr.termedBuf();
64 }
65
66 return NULL;
67}
68
69const char *
13cd7dee 70Ssl::ErrorDetailsList::getErrorDetail(Security::ErrorCode value)
02259ff8
CT
71{
72 const ErrorDetails::const_iterator it = theList.find(value);
73 if (it != theList.end()) {
74 return it->second.detail.termedBuf();
75 }
76
77 return NULL;
78}
79
80Ssl::ErrorDetailsManager *Ssl::ErrorDetailsManager::TheDetailsManager = NULL;
81
82Ssl::ErrorDetailsManager &Ssl::ErrorDetailsManager::GetInstance()
83{
84 if (!TheDetailsManager)
85 TheDetailsManager = new Ssl::ErrorDetailsManager;
86
87 assert(TheDetailsManager);
88 return *TheDetailsManager;
89}
90
91void Ssl::ErrorDetailsManager::Shutdown()
92{
93 delete TheDetailsManager;
94 TheDetailsManager = NULL;
95}
96
02259ff8
CT
97Ssl::ErrorDetailsManager::ErrorDetailsManager()
98{
99 theDefaultErrorDetails = new ErrorDetailsList();
100 ErrorDetailFile detailTmpl(theDefaultErrorDetails);
101 detailTmpl.loadDefault();
102}
103
104Ssl::ErrorDetailsList::Pointer Ssl::ErrorDetailsManager::getCachedDetails(const char *lang)
105{
106 Cache::iterator it;
107 it = cache.find(lang);
108 if (it != cache.end()) {
dc49061a
A
109 debugs(83, 8, HERE << "Found template details in cache for language: " << lang);
110 return it->second;
02259ff8
CT
111 }
112
113 return NULL;
114}
115
116void Ssl::ErrorDetailsManager::cacheDetails(ErrorDetailsList::Pointer &errorDetails)
117{
118 const char *lang = errorDetails->errLanguage.termedBuf();
119 assert(lang);
120 if (cache.find(lang) == cache.end())
121 cache[lang] = errorDetails;
122}
123
124bool
13cd7dee 125Ssl::ErrorDetailsManager::getErrorDetail(Security::ErrorCode value, const HttpRequest::Pointer &request, ErrorDetailEntry &entry)
02259ff8
CT
126{
127#if USE_ERR_LOCALES
128 String hdr;
789217a2 129 if (request != NULL && request->header.getList(Http::HdrType::ACCEPT_LANGUAGE, &hdr)) {
02259ff8
CT
130 ErrorDetailsList::Pointer errDetails = NULL;
131 //Try to retrieve from cache
132 size_t pos = 0;
133 char lang[256];
134 // Get the first ellement of the Accept-Language header
135 strHdrAcptLangGetItem(hdr, lang, 256, pos);
136 errDetails = getCachedDetails(lang); // search in cache
137
138 if (!errDetails) { // Else try to load from disk
139 debugs(83, 8, HERE << "Creating new ErrDetailList to read from disk");
140 errDetails = new ErrorDetailsList();
141 ErrorDetailFile detailTmpl(errDetails);
b248c2a3 142 if (detailTmpl.loadFor(request.getRaw())) {
02259ff8
CT
143 if (detailTmpl.language()) {
144 debugs(83, 8, HERE << "Found details on disk for language " << detailTmpl.language());
145 errDetails->errLanguage = detailTmpl.language();
146 cacheDetails(errDetails);
147 }
148 }
149 }
150
151 if (errDetails != NULL && errDetails->getRecord(value, entry))
152 return true;
153 }
154#endif
155
156 // else try the default
157 if (theDefaultErrorDetails->getRecord(value, entry)) {
158 debugs(83, 8, HERE << "Found default details record for error: " << GetErrorName(value));
159 return true;
160 }
161
162 return false;
163}
164
165const char *
13cd7dee 166Ssl::ErrorDetailsManager::getDefaultErrorDescr(Security::ErrorCode value)
02259ff8
CT
167{
168 return theDefaultErrorDetails->getErrorDescr(value);
169}
170
171const char *
13cd7dee 172Ssl::ErrorDetailsManager::getDefaultErrorDetail(Security::ErrorCode value)
02259ff8
CT
173{
174 return theDefaultErrorDetails->getErrorDetail(value);
175}
176
177// Use HttpHeaders parser to parse error-details.txt files
dc49061a
A
178class DetailEntryParser: public HttpHeader
179{
02259ff8
CT
180public:
181 DetailEntryParser():HttpHeader(hoErrorDetail) {}
182};
183
184//The end of an error detrail entry is a double "\n". The headersEnd
185// functions can detect it
186inline size_t detailEntryEnd(const char *s, size_t len) {return headersEnd(s, len);}
187
188bool
7e6eabbc 189Ssl::ErrorDetailFile::parse()
02259ff8
CT
190{
191 if (!theDetails)
192 return false;
193
7e6eabbc
CT
194 auto buf = template_;
195 buf.append("\n\n"); // ensure detailEntryEnd() finds the last entry
02259ff8 196
7e6eabbc
CT
197 while (const auto size = detailEntryEnd(buf.rawContent(), buf.length())) {
198 auto *s = buf.c_str();
199 const auto e = s + size;
02259ff8
CT
200
201 //ignore spaces, new lines and comment lines (starting with #) at the beggining
7e6eabbc 202 for (; (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
dc49061a 203 if (*s == '#')
d7ae3534
FC
204 while (s<e && *s != '\n')
205 ++s; // skip untill the end of line
02259ff8
CT
206 }
207
208 if ( s != e) {
209 DetailEntryParser parser;
4f1c93a7
EB
210 Http::ContentLengthInterpreter interpreter;
211 // no applyStatusCodeRules() -- error templates lack HTTP status code
212 if (!parser.parse(s, e - s, interpreter)) {
02259ff8
CT
213 debugs(83, DBG_IMPORTANT, HERE <<
214 "WARNING! parse error on:" << s);
215 return false;
216 }
217
81c203c9
AR
218 const String errorName = parser.getByName("name");
219 if (!errorName.size()) {
02259ff8
CT
220 debugs(83, DBG_IMPORTANT, HERE <<
221 "WARNING! invalid or no error detail name on:" << s);
222 return false;
223 }
224
13cd7dee 225 Security::ErrorCode ssl_error = Ssl::GetErrorCode(errorName.termedBuf());
645deacc 226 if (ssl_error != SSL_ERROR_NONE) {
02259ff8 227
645deacc
CT
228 if (theDetails->getErrorDetail(ssl_error)) {
229 debugs(83, DBG_IMPORTANT, HERE <<
230 "WARNING! duplicate entry: " << errorName);
231 return false;
232 }
02259ff8 233
645deacc
CT
234 ErrorDetailEntry &entry = theDetails->theList[ssl_error];
235 entry.error_no = ssl_error;
236 entry.name = errorName;
237 String tmp = parser.getByName("detail");
5a537e54 238 const int detailsParseOk = httpHeaderParseQuotedString(tmp.termedBuf(), tmp.size(), &entry.detail);
645deacc 239 tmp = parser.getByName("descr");
5a537e54 240 const int descrParseOk = httpHeaderParseQuotedString(tmp.termedBuf(), tmp.size(), &entry.descr);
7e6eabbc 241 // TODO: Validate "descr" and "detail" field values.
645deacc 242
b38b26cb 243 if (!detailsParseOk || !descrParseOk) {
645deacc
CT
244 debugs(83, DBG_IMPORTANT, HERE <<
245 "WARNING! missing important field for detail error: " << errorName);
246 return false;
247 }
02259ff8 248
645deacc 249 } else if (!Ssl::ErrorIsOptional(errorName.termedBuf())) {
02259ff8 250 debugs(83, DBG_IMPORTANT, HERE <<
645deacc 251 "WARNING! invalid error detail name: " << errorName);
02259ff8
CT
252 return false;
253 }
645deacc 254
02259ff8
CT
255 }// else {only spaces and black lines; just ignore}
256
257 buf.consume(size);
258 }
7e6eabbc 259 debugs(83, 9, Raw("unparsed data", buf.rawContent(), buf.length()));
02259ff8
CT
260 return true;
261}
f53969cc 262