ErrorDetailManager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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 
9 #include "squid.h"
10 #include "base/Raw.h"
11 #include "ErrorDetail.h"
12 #include "ErrorDetailManager.h"
13 #include "errorpage.h"
15 #include "mime_header.h"
16 #include "sbuf/Stream.h"
17 #include "sbuf/StringConvert.h"
18 
20 {
22 }
23 
25 {
27 }
28 
30 static SBuf
31 SlowlyParseQuotedField(const char * const description, const HttpHeader &parser, const char * const fieldName)
32 {
33  String fieldValue;
34  if (!parser.hasNamed(fieldName, strlen(fieldName), &fieldValue))
35  throw TextException(ToSBuf("Missing ", description), Here());
36  return Http::SlowlyParseQuotedString(description, fieldValue.termedBuf(), fieldValue.size());
37 }
38 
40  name(aName),
41  detail(SlowlyParseQuotedField("error 'detail' field", fields, "detail")),
42  descr(SlowlyParseQuotedField("error 'descr' field", fields, "descr"))
43 {
44  // TODO: Warn about and report extra/unrecognized error detail fields.
45  // TODO: Validate formatting %codes inside parsed quoted field values.
46 }
47 
48 namespace Ssl
49 {
50 
53 {
54 public:
55  explicit ErrorDetailFile(ErrorDetailsList::Pointer const details): TemplateFile("error-details.txt", ERR_NONE) {
56  theDetails = details;
57  }
58 
59 private:
61  bool parse() override;
62 };
63 }// namespace Ssl
64 
65 /******************/
68 {
69  const ErrorDetails::const_iterator it = theList.find(value);
70  return it != theList.end() ? &it->second : nullptr;
71 }
72 
74 
76 {
77  if (!TheDetailsManager)
78  TheDetailsManager = new Ssl::ErrorDetailsManager;
79 
80  assert(TheDetailsManager);
81  return *TheDetailsManager;
82 }
83 
85 {
86  delete TheDetailsManager;
87  TheDetailsManager = nullptr;
88 }
89 
91 {
92  theDefaultErrorDetails = new ErrorDetailsList();
93  ErrorDetailFile detailTmpl(theDefaultErrorDetails);
94  detailTmpl.loadDefault();
95 }
96 
98 Ssl::ErrorDetailsManager::getCachedDetails(const char * const lang) const
99 {
100  Cache::iterator it;
101  it = cache.find(SBuf(lang));
102  if (it != cache.end()) {
103  debugs(83, 8, "Found template details in cache for language: " << lang);
104  return it->second;
105  }
106 
107  return nullptr;
108 }
109 
110 void
112 {
113  const auto &lang = errorDetails->errLanguage;
114  if (cache.find(lang) == cache.end())
115  cache[lang] = errorDetails;
116 }
117 
118 const Ssl::ErrorDetailEntry *
120 {
121 #if USE_ERR_LOCALES
122  String hdr;
123  if (request != nullptr && request->header.getList(Http::HdrType::ACCEPT_LANGUAGE, &hdr)) {
124  ErrorDetailsList::Pointer errDetails = nullptr;
125  //Try to retrieve from cache
126  size_t pos = 0;
127  char lang[256];
128  // Get the first ellement of the Accept-Language header
129  strHdrAcptLangGetItem(hdr, lang, 256, pos);
130  errDetails = getCachedDetails(lang); // search in cache
131 
132  if (!errDetails) { // Else try to load from disk
133  debugs(83, 8, "Creating new ErrDetailList to read from disk");
134  errDetails = new ErrorDetailsList();
135  ErrorDetailFile detailTmpl(errDetails);
136  if (detailTmpl.loadFor(request.getRaw())) {
137  if (detailTmpl.language()) {
138  debugs(83, 8, "Found details on disk for language " << detailTmpl.language());
139  errDetails->errLanguage = detailTmpl.language();
140  cacheDetails(errDetails);
141  }
142  }
143  }
144 
145  assert(errDetails);
146  if (const auto entry = errDetails->findRecord(value))
147  return entry;
148  }
149 #else
150  (void)request;
151 #endif
152 
153  return findDefaultDetail(value);
154 }
155 
156 const Ssl::ErrorDetailEntry *
158 {
159  return theDefaultErrorDetails->findRecord(value);
160 }
161 
162 // Use HttpHeaders parser to parse error-details.txt files
164 {
165 public:
167 };
168 
169 //The end of an error detrail entry is a double "\n". The headersEnd
170 // functions can detect it
171 inline size_t detailEntryEnd(const char *s, size_t len) {return headersEnd(s, len);}
172 
173 bool
175 {
176  if (!theDetails)
177  return false;
178 
179  auto buf = template_;
180  buf.append("\n\n"); // ensure detailEntryEnd() finds the last entry
181 
182  while (const auto size = detailEntryEnd(buf.rawContent(), buf.length())) {
183  auto *s = buf.c_str();
184  const auto e = s + size;
185 
186  //ignore spaces, new lines and comment lines (starting with #) at the beginning
187  for (; (*s == '\n' || *s == ' ' || *s == '\t' || *s == '#') && s < e; ++s) {
188  if (*s == '#')
189  while (s<e && *s != '\n')
190  ++s; // skip until the end of line
191  }
192 
193  if ( s != e) {
194  DetailEntryParser parser;
195  Http::ContentLengthInterpreter interpreter;
196  // no applyStatusCodeRules() -- error templates lack HTTP status code
197  if (!parser.parse(s, e - s, interpreter)) {
198  debugs(83, DBG_IMPORTANT, "WARNING: parse error on:" << s);
199  return false;
200  }
201 
202  const String errorName = parser.getByName("name");
203  if (!errorName.size()) {
204  debugs(83, DBG_IMPORTANT, "WARNING: invalid or no error detail name on:" << s);
205  return false;
206  }
207 
208  Security::ErrorCode ssl_error = Ssl::GetErrorCode(errorName.termedBuf());
209  if (ssl_error != SSL_ERROR_NONE) {
210 
211  if (theDetails->findRecord(ssl_error)) {
212  debugs(83, DBG_IMPORTANT, "WARNING: duplicate entry: " << errorName);
213  return false;
214  }
215 
216  try {
217  theDetails->theList.try_emplace(ssl_error, StringToSBuf(errorName), parser);
218  }
219  catch (...) {
220  // TODO: Reject the whole file on this and surrounding problems instead of
221  // keeping/using just the previously parsed entries while telling the admin
222  // that we "failed to find or read error text file error-details.txt".
223  debugs(83, DBG_IMPORTANT, "ERROR: Ignoring bad " << errorName << " detail entry: " << CurrentException);
224  return false;
225  }
226 
227  } else if (!Ssl::ErrorIsOptional(errorName.termedBuf())) {
228  debugs(83, DBG_IMPORTANT, "WARNING: invalid error detail name: " << errorName);
229  return false;
230  }
231 
232  }// else {only spaces and black lines; just ignore}
233 
234  buf.consume(size);
235  }
236  debugs(83, 9, Raw("unparsed data", buf.rawContent(), buf.length()));
237  return true;
238 }
239 
size_t detailEntryEnd(const char *s, size_t len)
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:131
#define Here()
source code location of the caller
Definition: Here.h:15
HttpHeader header
Definition: Message.h:74
bool parse() override
post-process the loaded template
ErrorDetailsList::Pointer theDetails
int parse(const char *header_start, size_t len, Http::ContentLengthInterpreter &interpreter)
Definition: HttpHeader.cc:349
static SBuf SlowlyParseQuotedField(const char *const description, const HttpHeader &parser, const char *const fieldName)
ErrorDetailEntry constructor helper that extracts a quoted HTTP field value.
Definition: SBuf.h:93
String getList(Http::HdrType id) const
Definition: HttpHeader.cc:788
ErrorDetailEntry(const SBuf &aName, const HttpHeader &)
extracts quoted detail and descr fields from the given header
C * getRaw() const
Definition: RefCount.h:89
@ ERR_NONE
Definition: forward.h:15
ErrorDetailFile(ErrorDetailsList::Pointer const details)
static ErrorDetailsManager * TheDetailsManager
An instance of ErrorDetailsManager to be used by squid (ssl/ErrorDetails.*)
void errorDetailInitialize()
Definition: Raw.h:20
const ErrorDetailEntry * findRecord(Security::ErrorCode) const
void loadDefault()
Definition: errorpage.cc:363
int size
Definition: ModDevPoll.cc:69
SBuf errLanguage
The language of the error-details.txt template, if any.
void errorDetailClean()
@ ACCEPT_LANGUAGE
ErrorDetailsList::Pointer getCachedDetails(const char *lang) const
Return cached error details list for a given language if exist.
bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
Definition: errorpage.cc:472
SBuf StringToSBuf(const String &s)
create a new SBuf from a String by copying contents
Definition: StringConvert.h:17
Definition: Xaction.cc:39
const ErrorDetailEntry * findDefaultDetail(Security::ErrorCode) const
#define assert(EX)
Definition: assert.h:17
bool hasNamed(const SBuf &s, String *value=nullptr) const
Definition: HttpHeader.cc:865
SBuf SlowlyParseQuotedString(const char *description, const char *start, size_t length)
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
Security::ErrorCode GetErrorCode(const char *name)
The Security::ErrorCode code of the error described by "name".
Definition: ErrorDetail.h:30
static void Shutdown()
reset the ErrorDetailsManager instance
@ hoErrorDetail
Definition: HttpHeader.h:39
static ErrorDetailsManager & GetInstance()
Instance class.
const ErrorDetailEntry * findDetail(Security::ErrorCode value, const HttpRequest::Pointer &request) const
const char * language()
The language used for the template.
Definition: errorpage.h:313
void cacheDetails(const ErrorDetailsList::Pointer &errorDetails) const
cache the given error details list.
String getByName(const SBuf &name) const
Definition: HttpHeader.cc:848
const char * termedBuf() const
Definition: SquidString.h:92
an std::runtime_error with thrower location info
Definition: TextException.h:20
manages error detail templates
size_type size() const
Definition: SquidString.h:73
bool loadFor(const HttpRequest *request)
Definition: errorpage.cc:526
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
#define DBG_IMPORTANT
Definition: Stream.h:38
bool ErrorIsOptional(const char *name)
Definition: ErrorDetail.cc:149
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:17
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192

 

Introduction

Documentation

Support

Miscellaneous