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