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 bool
281 NotePairs::find(SBuf &resultNote, const char *noteKey, const char *sep) const
282 {
283  resultNote.clear();
284  for (const auto &e: entries) {
285  if (!e->name().cmp(noteKey)) {
286  if (!resultNote.isEmpty())
287  resultNote.append(sep);
288  resultNote.append(e->value());
289  }
290  }
291  return resultNote.length();
292 }
293 
294 void
295 NotePairs::print(std::ostream &os, const char * const nameValueSeparator, const char * const entryTerminator) const
296 {
297  for (const auto &e: entries)
298  os << e->name() << nameValueSeparator << e->value() << entryTerminator;
299 }
300 
301 const char *
302 NotePairs::findFirst(const char *noteKey) const
303 {
304  for (const auto &e: entries)
305  if (!e->name().cmp(noteKey))
306  return const_cast<SBuf &>(e->value()).c_str();
307  return nullptr;
308 }
309 
310 void
311 NotePairs::add(const char *key, const char *note)
312 {
313  entries.push_back(new NotePairs::Entry(key, note));
314 }
315 
316 void
317 NotePairs::add(const SBuf &key, const SBuf &note)
318 {
319  entries.push_back(new NotePairs::Entry(key, note));
320 }
321 
322 void
323 NotePairs::remove(const char *key)
324 {
325  Entries::iterator i = entries.begin();
326  while (i != entries.end())
327  i = (*i)->name().cmp(key) ? i+1 : entries.erase(i);
328 }
329 
330 void
332 {
333  Entries::iterator i = entries.begin();
334  while (i != entries.end())
335  i = (*i)->name() == key ? entries.erase(i) : i+1;
336 }
337 
338 static void
339 AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
340 {
341  Parser::Tokenizer tok(val);
342  const auto tokenCharacters = delimiters.complement("non-delimiters");
343  do {
344  SBuf token;
345  (void)tok.prefix(token, tokenCharacters);
346  entries.push_back(new NotePairs::Entry(key, token)); // token may be empty
347  } while (tok.skipOne(delimiters));
348 }
349 
350 const NotePairs::Entries &
352 {
353  if (delimiters) {
354  static NotePairs::Entries expandedEntries;
355  expandedEntries.clear();
356  for (const auto &entry: entries)
357  AppendTokens(expandedEntries, entry->name(), entry->value(), *delimiters);
358  return expandedEntries;
359  }
360  return entries;
361 }
362 
363 void
364 NotePairs::addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
365 {
366  AppendTokens(entries, key, values, delimiters);
367 }
368 
369 bool
370 NotePairs::hasPair(const SBuf &key, const SBuf &value) const
371 {
372  for (const auto &e: entries)
373  if (e->name() == key && e->value() == value)
374  return true;
375  return false;
376 }
377 
378 void
380 {
381  for (const auto &e: src->entries)
382  entries.push_back(new NotePairs::Entry(e->name(), e->value()));
383 }
384 
385 void
387 {
388  for (const auto &e: src->entries) {
389  if (!hasPair(e->name(), e->value()))
390  entries.push_back(new NotePairs::Entry(e->name(), e->value()));
391  }
392 }
393 
394 void
396 {
397  for (const auto &e: src->entries) {
398  if (std::find(appendables.begin(), appendables.end(), e->name()) == appendables.end())
399  remove(e->name());
400  }
401  append(src);
402 }
403 
404 void
406 {
407  for (const auto &e: src->entries)
408  remove(e->name());
409  append(src);
410 }
411 
void appendNewOnly(const NotePairs *src)
Definition: Notes.cc:386
void replaceOrAdd(const NotePairs *src)
Definition: Notes.cc:405
ACLList * aclList
The access list used to determine if this value is valid for a request.
Definition: Notes.h:59
#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:302
const char * descr
identifies note source in error messages
Definition: Notes.h:168
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:295
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:104
static bool LastTokenWasQuoted()
Definition: ConfigParser.h:117
NotesList notes
The Note::Pointer objects array list.
Definition: Notes.h:167
Definition: SBuf.h:93
CharacterSet complement(const char *complementLabel=nullptr) const
Definition: CharacterSet.cc:74
SBuf theKey
The note key.
Definition: Notes.h:103
bool empty() const
Definition: Notes.h:260
bool parse(const char *def)
Definition: Format.cc:66
std::vector< SBuf > Names
Definition: Notes.h:206
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:185
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:49
static char * keys[]
@ mhReplace
Definition: Notes.h:49
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:116
static void AppendTokens(NotePairs::Entries &entries, const SBuf &key, const SBuf &val, const CharacterSet &delimiters)
Definition: Notes.cc:339
void printAsAnnotationAclParameters(std::ostream &) const
Definition: Notes.cc:270
const SBuf & key() const
Definition: Notes.h:92
bool find(SBuf &resultNote, const char *noteKey, const char *sep=",") const
Definition: Notes.cc:281
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:66
const Entries & expandListEntries(const CharacterSet *delimiters) const
Definition: Notes.cc:351
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
void addStrList(const SBuf &key, const SBuf &values, const CharacterSet &delimiters)
Definition: Notes.cc:364
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:67
Stores a value for the note.
Definition: Notes.h:43
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:379
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:170
Definition: parse.c:160
bool hasPair(const SBuf &key, const SBuf &value) const
Definition: Notes.cc:370
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:323
void reset()
Definition: MemBuf.cc:129
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:317
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:205
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:37
#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:171
void replaceOrAddOrAppend(const NotePairs *src, const Names &appendables)
Definition: Notes.cc:395
~Value() override
Definition: Notes.cc:31

 

Introduction

Documentation

Support

Miscellaneous