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