Notes.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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 "AccessLogEntry.h"
11 #include "acl/FilledChecklist.h"
12 #include "acl/Gadgets.h"
13 #include "acl/Tree.h"
14 #include "client_side.h"
15 #include "ConfigParser.h"
16 #include "globals.h"
17 #include "http/Stream.h"
18 #include "HttpReply.h"
19 #include "HttpRequest.h"
20 #include "parser/Tokenizer.h"
21 #include "sbuf/Stream.h"
22 #include "sbuf/StringConvert.h"
23 #include "SquidConfig.h"
24 #include "Store.h"
25 #include "StrList.h"
26 
27 #include <algorithm>
28 #include <ostream>
29 #include <string>
30 
32 {
34  delete valueFormat;
35 }
36 
37 Note::Value::Value(const char *aVal, const bool quoted, const char *descr, const Method m)
38  : aclList(nullptr), valueFormat(nullptr), theValue(aVal), theMethod(m)
39 {
40  if (quoted) {
41  valueFormat = new Format::Format(descr ? descr : "Notes");
42  if (!valueFormat->parse(theValue.c_str())) {
43  delete valueFormat;
44  throw TextException(ToSBuf("failed to parse annotation value ", theValue), Here());
45  }
46  }
47 }
48 
49 const SBuf &
51 {
52  if (al && valueFormat) {
53  static MemBuf mb;
54  mb.reset();
55  valueFormat->assemble(mb, al, 0);
56  theFormattedValue.assign(mb.content());
57  return theFormattedValue;
58  }
59  return theValue;
60 }
61 
63 Note::addValue(const char *value, const bool quoted, const char *descr, const Value::Method m)
64 {
65  values.push_back(new Value(value, quoted, descr, m));
66  return values.back();
67 }
68 
69 bool
70 Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al, SBuf &matched)
71 {
72  ACLFilledChecklist ch(nullptr, request);
73  ch.updateAle(al);
74  ch.updateReply(reply);
75  ch.syncAle(request, nullptr);
76 
77  for (const auto &v: values) {
78  assert(v->aclList);
79  const auto &ret = ch.fastCheck(v->aclList);
80  debugs(93, 5, "Check for header name: " << theKey << ": " << v->value() <<
81  ", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
82  if (ret.allowed()) {
83  matched = v->format(al);
84  return true;
85  }
86  }
87  matched.clear();
88  return false;
89 }
90 
91 void
93 {
94  for (const auto &v: values) {
95  const SBuf &formatted = v->format(al);
96  if (!pairs->empty() && v->method() == Value::mhReplace)
97  pairs->remove(theKey);
98  if (delimiters)
99  pairs->addStrList(key(), formatted, *delimiters);
100  else
101  pairs->add(key(), formatted);
102  }
103 }
104 
105 void
106 Note::printAsNoteDirective(StoreEntry * const entry, const char * const directiveName) const
107 {
108  PackableStream os(*entry);
109  for (const auto &v: values) {
110  os << directiveName << ' ' << key() << ' ' << ConfigParser::QuoteString(SBufToString(v->value()));
111  if (v->aclList) {
112  // TODO: Use Acl::dump() after fixing the XXX in dump_acl_list().
113  for (const auto &item: ToTree(v->aclList).treeDump("", &Acl::AllowOrDeny)) {
114  if (item.isEmpty()) // treeDump("") adds this prefix
115  continue;
116  if (item.cmp("\n") == 0) // treeDump() adds this suffix
117  continue;
118  os << ' ' << item; // ACL name
119  }
120  }
121  os << '\n';
122  }
123 }
124 
125 void
127 {
128  auto separator = "";
129  for (const auto &v: values) {
130  os << separator;
131  os << key() << (v->method() == Value::mhReplace ? "=" : "+=") << v->value();
132  separator = " ";
133  }
134 }
135 
136 const Notes::Keys &
138 {
139  // these keys are used for internal Squid-helper communication
140  static const char *names[] = {
141  "group",
142  "ha1",
143  "log",
144  "message",
145  "password",
146  "rewrite-url",
147  "status",
148  "tag",
149  "ttl",
150  "url",
151  "user"
152  };
153 
154  static Keys keys(std::begin(names), std::end(names));
155  return keys;
156 }
157 
158 Notes::Notes(const char *aDescr, const Keys *extraReservedKeys, bool allowFormatted):
159  descr(aDescr),
160  formattedValues(allowFormatted)
161 {
162  if (extraReservedKeys)
163  reservedKeys = *extraReservedKeys;
164 }
165 
167 Notes::add(const SBuf &noteKey)
168 {
169  if (Note::Pointer p = find(noteKey))
170  return p;
171  notes.push_back(new Note(noteKey));
172  return notes.back();
173 }
174 
176 Notes::find(const SBuf &noteKey)
177 {
178  for (const auto &n: notes)
179  if (n->key() == noteKey)
180  return n;
181  return nullptr;
182 }
183 
184 void
185 Notes::banReservedKey(const SBuf &key, const Keys &banned) const
186 {
187  if (std::find(banned.begin(), banned.end(), key) != banned.end())
188  throw TextException(ToSBuf("cannot use a reserved ", descr, " name: ", key), Here());
189 }
190 
191 void
192 Notes::validateKey(const SBuf &key) const
193 {
196 
197  // TODO: fix code duplication: the same set of specials is produced
198  // by isKeyNameChar().
199  static const CharacterSet allowedSpecials = CharacterSet::ALPHA +
200  CharacterSet::DIGIT + CharacterSet("specials", "-_");
201  const auto specialIndex = key.findFirstNotOf(allowedSpecials);
202  if (specialIndex != SBuf::npos) {
203  debugs(28, DBG_CRITICAL, "WARNING: used special character '" <<
204  key[specialIndex] << "' within annotation name. " <<
205  "Future Squid versions will not support this.");
206  }
207 }
208 
211 {
212  const char *tok = ConfigParser::NextToken();
213  if (!tok)
214  fatalf("FATAL: Missing note key");
215  SBuf key(tok);
216  validateKey(key);
218  const char *val = ConfigParser::NextQuotedToken();
219  if (!val)
220  fatalf("FATAL: Missing note value");
222  Note::Pointer note = add(key);
223  Note::Value::Pointer noteValue = note->addValue(val, formattedValues && ConfigParser::LastTokenWasQuoted(), descr);
224  key.append('=');
225  key.append(val);
226  aclParseAclList(parser, &noteValue->aclList, key.c_str());
227  return note;
228 }
229 
230 void
232  char *k, *v;
233  int parsedPairs = 0;
234  while (ConfigParser::NextKvPair(k, v)) {
235  int keyLen = strlen(k);
236  const Note::Value::Method method = (k[keyLen - 1] == '+') ? Note::Value::mhAppend : Note::Value::mhReplace;
237  if (method == Note::Value::mhAppend)
238  keyLen--;
239  else {
240  assert(method == Note::Value::mhReplace);
241  if (Note::Pointer oldNote = find(SBuf(k, keyLen)))
242  debugs(28, DBG_CRITICAL, "WARNING: annotation configuration with key " << k <<
243  " already exists and will be overwritten");
244  }
245  SBuf key(k, keyLen);
246  validateKey(key);
247  Note::Pointer note = add(key);
248  (void)note->addValue(v, formattedValues && ConfigParser::LastTokenWasQuoted(), descr, method);
249  parsedPairs++;
250  }
251  if (!parsedPairs)
252  fatalf("FATAL: Missing annotation kv pair");
253 }
254 
255 void
257 {
258  for (const auto &n: notes)
259  n->updateNotePairs(pairs, delimiters, al);
260 }
261 
262 void
263 Notes::printAsNoteDirectives(StoreEntry *entry, const char * const directiveName) const
264 {
265  for (const auto &n: notes)
266  n->printAsNoteDirective(entry, directiveName);
267 }
268 
269 void
271 {
272  const char *separator = "";
273  for (const auto &note: notes) {
274  os << separator;
275  note->printAsAnnotationAclParameters(os);
276  separator = " ";
277  }
278 }
279 
280 std::optional<SBuf>
281 NotePairs::find(const char *noteKey, const char *sep) const
282 {
283  SBuf resultNote;
284 
285  for (const auto &e: entries) {
286  if (!e->name().cmp(noteKey)) {
287  if (!resultNote.isEmpty())
288  resultNote.append(sep);
289  resultNote.append(e->value());
290  }
291  }
292 
293  if (resultNote.isEmpty())
294  return std::nullopt;
295 
296  return resultNote;
297 }
298 
299 void
300 NotePairs::print(std::ostream &os, const char * const nameValueSeparator, const char * const entryTerminator) const
301 {
302  for (const auto &e: entries)
303  os << e->name() << nameValueSeparator << e->value() << entryTerminator;
304 }
305 
306 const char *
307 NotePairs::findFirst(const char *noteKey) const
308 {
309  for (const auto &e: entries)
310  if (!e->name().cmp(noteKey))
311  return const_cast<SBuf &>(e->value()).c_str();
312  return nullptr;
313 }
314 
315 void
316 NotePairs::add(const char *key, const char *note)
317 {
318  entries.push_back(new NotePairs::Entry(key, note));
319 }
320 
321 void
322 NotePairs::add(const SBuf &key, const SBuf &note)
323 {
324  entries.push_back(new NotePairs::Entry(key, note));
325 }
326 
327 void
328 NotePairs::remove(const char *key)
329 {
330  Entries::iterator i = entries.begin();
331  while (i != entries.end())
332  i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
333 }
334 
335 void
337 {
338  Entries::iterator i = entries.begin();
339  while (i != entries.end())
340  i = (*i)->name() == key ? entries.erase(i) : i+1;
341 }
342 
343 static void
344 AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
345 {
346  Parser::Tokenizer tok(val);
347  const auto tokenCharacters = delimiters.complement("non-delimiters");
348  do {
349  SBuf token;
350  (void)tok.prefix(token, tokenCharacters);
351  entries.push_back(new NotePairs::Entry(key, token)); // token may be empty
352  } while (tok.skipOne(delimiters));
353 }
354 
355 const NotePairs::Entries &
357 {
358  if (delimiters) {
359  static NotePairs::Entries expandedEntries;
360  expandedEntries.clear();
361  for (const auto &entry: entries)
362  AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
363  return expandedEntries;
364  }
365  return entries;
366 }
367 
368 void
369 NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
370 {
371  AppendTokens(entries, key, values, delimiters);
372 }
373 
374 bool
375 NotePairs::hasPair(const SBuf &key, const SBuf &value) const
376 {
377  for (const auto &e: entries)
378  if (e->name() == key && e->value() == value)
379  return true;
380  return false;
381 }
382 
383 void
385 {
386  for (const auto &e: src->entries)
387  entries.push_back(new NotePairs::Entry(e->name(), e->value()));
388 }
389 
390 void
392 {
393  for (const auto &e: src->entries) {
394  if (!hasPair(e->name(), e->value()))
395  entries.push_back(new NotePairs::Entry(e->name(), e->value()));
396  }
397 }
398 
399 void
401 {
402  for (const auto &e: src->entries) {
403  if (std::find(appendables.begin(), appendables.end(), e->name()) == appendables.end())
404  remove(e->name());
405  }
406  append(src);
407 }
408 
409 void
411 {
412  for (const auto &e: src->entries)
413  remove(e->name());
414  append(src);
415 }
416 
void appendNewOnly(const NotePairs *src)
Definition: Notes.cc:391
void replaceOrAdd(const NotePairs *src)
Definition: Notes.cc:410
ACLList * aclList
The access list used to determine if this value is valid for a request.
Definition: Notes.h:60
std::optional< SBuf > find(const char *noteKey, const char *sep=",") const
Definition: Notes.cc:281
#define Here()
source code location of the caller
Definition: Here.h:15
void parseKvPair()
Parses an annotate line with "key=value" or "key+=value" formats.
Definition: Notes.cc:231
#define DBG_CRITICAL
Definition: Stream.h:37
void validateKey(const SBuf &key) const
Definition: Notes.cc:192
static char * NextQuotedToken()
const char * findFirst(const char *noteKey) const
Definition: Notes.cc:307
const char * descr
identifies note source in error messages
Definition: Notes.h:169
bool isEmpty() const
Definition: SBuf.h:435
static void EnableMacros()
Allow macros inside quoted strings.
Definition: ConfigParser.h:144
void print(std::ostream &os, const char *nameValueSeparator, const char *entryTerminator) const
Definition: Notes.cc:300
Value(const char *aVal, const bool quoted, const char *descr, const Method method=mhReplace)
Definition: Notes.cc:37
Note::Pointer parse(ConfigParser &parser)
Parses a notes line and returns a pointer to the parsed Note object.
Definition: Notes.cc:210
Values values
The possible values list for the note.
Definition: Notes.h:105
static bool LastTokenWasQuoted()
Definition: ConfigParser.h:117
NotesList notes
The Note::Pointer objects array list.
Definition: Notes.h:168
Definition: SBuf.h:93
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
SBuf theKey
The note key.
Definition: Notes.h:104
bool empty() const
Definition: Notes.h:260
bool parse(const char *def)
Definition: Format.cc:66
std::vector< SBuf > Names
Definition: Notes.h:207
void clear()
Definition: SBuf.cc:175
void syncAle(HttpRequest *adaptedRequest, const char *logUri) const override
assigns uninitialized adapted_request and url ALE components
static const CharacterSet ALPHA
Definition: CharacterSet.h:76
const char * AllowOrDeny(const Answer &action)
Definition: Tree.h:53
void updateReply(const HttpReply::Pointer &)
size_t aclParseAclList(ConfigParser &, ACLList **config, const char *label)
Definition: Gadgets.cc:184
Used to store a note key/value pair.
Definition: Notes.h:186
static void DisableMacros()
Do not allow macros inside quoted strings.
Definition: ConfigParser.h:147
void printAsAnnotationAclParameters(std::ostream &) const
Definition: Notes.cc:126
@ mhAppend
Definition: Notes.h:50
static char * keys[]
@ mhReplace
Definition: Notes.h:50
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
Definition: Notes.cc:256
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
Note::Pointer find(const SBuf &noteKey)
Definition: Notes.cc:176
Definition: MemBuf.h:23
std::vector< SBuf > Keys
unordered annotation names
Definition: Notes.h:117
static void AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
Definition: Notes.cc:344
void printAsAnnotationAclParameters(std::ostream &) const
Definition: Notes.cc:270
const SBuf & key() const
Definition: Notes.h:93
Entries entries
The key/value pair entries.
Definition: Notes.h:269
#define assert(EX)
Definition: assert.h:17
static const Notes::Keys & ReservedKeys()
always prohibited key names
Definition: Notes.cc:137
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
void printAsNoteDirective(StoreEntry *, const char *directiveName) const
Prints key and value(s) using a "note" directive format (including directive name).
Definition: Notes.cc:106
Format
whether Action report uses valid YAML or unspecified/legacy formatting
SBufList treeDump(const char *name, ActionToStringConverter converter) const
Definition: Tree.h:60
static bool NextKvPair(char *&key, char *&value)
static const CharacterSet DIGIT
Definition: CharacterSet.h:84
Format::Format * valueFormat
Compiled annotation value format.
Definition: Notes.h:67
const Entries & expandListEntries(const CharacterSet *delimiters) const
Definition: Notes.cc:356
const char * c_str()
Definition: SBuf.cc:516
void addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
Definition: Notes.cc:369
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
void updateNotePairs(NotePairsPointer pairs, const CharacterSet *delimiters, const AccessLogEntryPointer &al)
Definition: Notes.cc:92
Notes()=default
static char * NextToken()
static const size_type npos
Definition: SBuf.h:100
static const char * QuoteString(const String &var)
size_type findFirstNotOf(const CharacterSet &set, size_type startPos=0) const
Definition: SBuf.cc:746
SBuf theValue
Definition: Notes.h:68
Stores a value for the note.
Definition: Notes.h:44
Note::Pointer add(const SBuf &noteKey)
Definition: Notes.cc:167
Value::Pointer addValue(const char *value, const bool quoted, const char *descr, const Value::Method m=Value::mhAppend)
Definition: Notes.cc:63
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition: Notes.cc:384
void printAsNoteDirectives(StoreEntry *, const char *directiveName) const
Prints notes using "note" squid.conf directive format, one directive per stored note.
Definition: Notes.cc:263
Keys reservedKeys
a list of additional prohibited key names
Definition: Notes.h:171
Definition: parse.c:160
bool hasPair(const SBuf &key, const SBuf &value) const
Definition: Notes.cc:375
bool match(HttpRequest *request, HttpReply *reply, const AccessLogEntryPointer &al, SBuf &matched)
Definition: Notes.cc:70
an std::runtime_error with thrower location info
Definition: TextException.h:20
char * content()
start of the added data
Definition: MemBuf.h:41
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
void remove(const char *key)
Definition: Notes.cc:328
void reset()
Definition: MemBuf.cc:129
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:322
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
std::vector< Entry::Pointer > Entries
The key/value pair entries.
Definition: Notes.h:206
String SBufToString(const SBuf &s)
Definition: StringConvert.h:26
const Tree & ToTree(const TreePointer *cfg)
Definition: Gadgets.cc:123
void aclDestroyAclList(ACLList **list)
Definition: Gadgets.cc:214
Definition: Notes.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void banReservedKey(const SBuf &key, const Keys &banned) const
Makes sure the given key is not on the given list of banned names.
Definition: Notes.cc:185
void updateAle(const AccessLogEntry::Pointer &)
const SBuf & format(const AccessLogEntryPointer &al)
Definition: Notes.cc:50
bool formattedValues
whether to expand quoted logformat codes
Definition: Notes.h:172
void replaceOrAddOrAppend(const NotePairs *src, const Names &appendables)
Definition: Notes.cc:400
~Value() override
Definition: Notes.cc:31

 

Introduction

Documentation

Support

Miscellaneous